THE COMPLETE CMSB COOKBOOK

CLICK TO VIEW THE RECIPES AS A "TABLE OF CONTENTS"

We now have 559 recipes!

Section 1 (Recipes 001 - 100) - Download PDF
Section 2 (Recipes 101 - 200) - Download PDF
Section 3 (Recipes 201 - 300) - Download PDF
Section 4 (Recipes 301 - 400) - Download PDF
Section 5 (Recipes 401 - 500) - Download PDF
Section 6 (Recipes 501 - 559) - Download PDF


LAST UPDATE - Aug 8th, 2023

Click View Recipe To View The Recipe

Click Here To Skip Down To The Complete Cookbook




INTRODUCTION

If you’re reading this then you have probably started to create your first web sites with CMS Builder, either from
scratch or as conversions of existing web sites. You’ve also probably started to research the CMSB forum and have
found, like I did when I first started designing with CMSB, that this PHP stuff is like learning a new language.  

Although I was pretty proficient with HTML coding and using Dreamweaver, when I started applying this new approach, it
was clear that the devil is in the details and that I had a lot to learn about those details.

I started documenting everything that I learned, either from much trial and many errors, from the excellent Interactive
Tools users forum, or from Dave and others on the Interactive Tools staff, who freely gave of their time to help us
newbees solve what seemed to be the same problems over and over again.

This cookbook is designed to answer many of the questions that you’ll have as you try to understand this powerful and
versatile new web design paradigm.

The first chapter, “Where to Begin” describes the basic philosophy of how CMS Builder works and describes some basic
approaches to understanding some of the concepts required to make your design work correctly.

As you continue to develop pages, designing with content management in mind will become easier, the new concepts will
become clearer, and you’ll discover, as I did, that CMS Builder is the best professional content management system in
the marketplace.

THE FINE PRINT

Since, in most cases the examples in the cookbook came from a number of designers, you’ll have to replace paths, table
names, field names and other information to match your particular application. 

We don’t guarantee that the recipes provided in our cookbook will be appropriate for your application exactly as
written, but they should give you a good idea of how to approach and accomplish a given task.

Although we’ve made an effort to correct mistakes and ambiguities in the examples, there are probably some that got
away from us. 

Also, as CMS Builder has developed over time, some menus and functions have been modified. Because of this, some of the
details in the examples may reference the placement or menu entries in previous versions.

If you find any mistakes or ambiguities we’d appreciate it if you’d drop us an e-mail about them, including your
corrections, to: corrections@thecmsbcookbook.com We’ll incorporate your suggestions into subsequent editions of the
cookbook and post them on our web site. 

The CMSB Cookbook is a compilation of some of the hottest ideas from web designers around the world who have discovered
the power and flexibility of CMS Builder and were willing to share their discoveries with the rest of the CMSB user
community. 

While developing your applications, you’ll be learning new tricks and having some “Ah Ha” moments. It’s
important that you document those discoveries so that they’ll be available to you the next time they’re needed. We
hope that you’ll share your discoveries with us so that we can share them with other CMSBers by sending them to:
share@thecmsbcookbook.com


WHAT IS CMS BUILDER AND HOW DOES IT WORK?

CMS Builder is a PHP based Content Management System (CMS) that lets non-technical users update the content on their
website using a simple browser based interface, without learning any HTML or programming skills. 

From the web designers perspective CMS Builder allows us to design web pages that rely on an on-line database to store
their content. It also facilitates the retrieval of that information from database records and the insertion of that
information into specific locations on a web page.

From the end user’s perspective, CMS Builder facilitates the modification and addition of content to both the database
record and web pages by means of a simple, browser based interface. Modifications to existing pages can be made from any
computer that has internet access. Information is modified using any web browser (Internet Explorer, FireFox, Safari,
etc.), and no special software is required on the home computer.

WHAT'S PHP?
PHP, which is an acronym for Hypertext PreProcessor, is a general-purpose scripting language that’s especially suited
for web development.

CMSB uses PHP to both populate pages and to access a MySQL database and manipulate the acquired data to render dynamic,
client controlled web pages.

A major strength of PHP is that it’s server side scripting, so the end user has no access to the actual PHP code. It
can be embedded into HTML coding and interpreted by the web server, which renders the output as HTML.  

PHP was originally created sometime in 1994 or 1995 by the Danish/Greenlandic programmer Rasmus Lerdorf, but has since
been improved and updated by a group called the PHP group. 

As your skills and your curiosity grow, you can learn a lot more about PHP at:

http://www.php.net

HOW DO I IMPLEMENT CMS BUILDER?
Once you’ve installed CMS Builder following the really simple instructions on the Interactive Tools web site, you’ll
probably want to dive right in and create your first web site. I would urge you to wait until you’ve watched the
videos on the Interactive Tools website, and until you’ve read at least the introductory chapter of this cookbook.
I’d also suggest that you follow the creation of the 2 sample pages later in this chapter.

In order to use CMS Builder effectively, and to minimize the frustration that’s common to all first timers, there are
a few new concepts that you’ll have to get your head around.

TYPES OF DATA BASE INTERFACES USED
CMS Builder uses 2 basic types of database interfaces to manage web pages. 

1) A single record interface, for information that may be changed but only exist in one form at a time. Examples of this
type of information would be an “about me” page, “contact information” and the directions to a specific
location.

2) A multi record interface for information about a series of items that have the same basic informational structure,
(name, address, description). Examples of this type of information would be a list of  images, a calendar of events, and
a list of products. 

The multi record interface manages 2 types of pages. "List" pages that show a listing of either image thumbnails, event
titles and dates, or products, and "details" pages that shows detailed information about a specific item in the list.

SECTION EDITORS
In CMSB, the database record interface that you’ll create is called a section editor. A section editor contains the
menu of fields that will be used to organize the content of your web page. It is where you will set up certain
restrictions regarding the type of data that can be entered (only certain file types, certain characters), how much
information can be entered (300 characters, 12 images), how that information is processed and displayed (show or hide
the record on a certain date), and in some cases who is allowed to enter the information (the administrator only, or the
original author).

You can create either Multi Record editors or Single Record editors depending on the type of data they are to manage,
and create a menu structure that contains the types of fields that suite your application. 

You can also choose from a set of preset menu structures, and even use copies of your own editors to create additional
editors for use on the same or different sites. 

FIELDS
Once you’ve created either a basic single record or multi record section editor, you can add a number of types of
fields to that editor’s menu structure to facilitate the entry of various types of information. 

The simplest of these fields is a “text” field, which is a one line field that can only accept text as it’s input.

The next type is the “text box”. A text box is similar to a text field, but it can accept multi line text entry.

If you need a greater degree of flexibility in entering their data, you can create “WYSIWYG” fields that present a
WYSIWYG editor interface, either allowing uploads or not, your choice.

There are “list” fields that can take their input from either single or multiple entry pull down lists or radio
buttons.

There are “checkbox” fields that output different values depending on whether they’re checked or not.

There are “upload” fields that allow users to upload documents, images and even flash, video and audio files. Upload
fields have a great deal of flexibility attached to them and are among the most complex.

There are also some “special” fields that can let you show or hide a record from a web page, enter a date and sort
or control your records using those dates, decide when to publish a record to a website or when to remove or un-publish
that record, and other special functions that help to customize your implementation.

Once you understand these few basic tools that are in your arsenal, you can begin to implement CMSB into your web
design.

QUALIFIERS
PHP and MySQL offer a full range of qualifiers and search criteria including if, then, else, and, not, equals, where,
limit, and others that can add complex functionality to your pages.  

GENERATING THE PHP CODE REQUIRED FOR YOUR WEB PAGES
After you've created your section editors and included various fields to enter your data you’ll need build web pages
to display your data. The good news is that the basic code to accomplish this can be automatically generated by the code
generator that is built in to CMS Builder.


SOME TERMS YOU'LL NEED TO KNOW (AND SOME THAT YOU KNOW ALREADY)

CSS - This Acronym stand for Cascading Style Sheets. Styles are a series of instruction groups that specify how specific
items should appear on a Web page.

“External” Style sheets are separate files that contain the styling information that determines what text, links,
font sizes, borders, and other elements will look like across an entire web site.

Using these predefined styles is an easy way to standardize the “look” of a web site.

If you want to change the look of a particular type of item, say how links are displayed on your web pages, that styling
information can be changed in one external style sheet and the item's appearance will automatically be changed on the
entire web site.

DATABASE - This is a specially structured file that is used to store collections of information. CMS Builder stores
information in a database that is located on your on-line server. Any type of information can be stored in a database.
Images and other uploaded documents are not stored in the on-line database, however the information on where to find
these documents is stored in the database records.

PUBLISH - When you modify the information in your database records using the  CMS Builder interface, you’re working on
an editing screen (called a section editor) that’s located on the computer you’re working on. When you save that
information, you’re saving your changes to a record in your on-line database.

CMS Builder automatically publishes the information stored in the database to the location you’ve specified in the
design of your web page.

RECORD - A database can store a great deal of information in a structured format. Each structured group of information
is called a record. In some situations, the information in these records can be modified. When you add or revise
information using the CMS Builder interface, that information is stored in a database record.

SECTION EDITORS - The interfaces that you create to manage the information in the various records in your database.

FIELD - This refers to a specific information entry area in a database record. It can be a single line (text field), or
a multi-line field (text box), a date field, a check box, or other special information entry area types.

MENU - The set of fields within a Section Editor

SERVER - This refers to a computer at the company that supplies your web site hosting services that is always connected
to the internet via a high capacity, high speed connection. When users log on to a web page from their home computer,
the server “serves” or allows the home computer to download that web page, as well as any associated images and
other files that are stored there.

For even more definitions, explore the Glossary on the Interactive Tools web site at:
http://www.interactivetools.com/docs/cmsbuilder/glossary.html


CMSB SECURITY AND ACCESS CONTROL

Updating privileges for each Section Editor are controlled and protected by an unlimited series of user names and
passwords.

Access to specific editors can be assigned to specific users with varying levels of permissions. (Manager,
Administrator, or Writer (Author)) Only those pages and content areas that are associated with a particular user name
and password combination are accessible to be updated by that user.

Access rights can be changed or reassigned by the administrator at any time.

Another concern is that of hackers and your CMSB website. 

The discussion seems to center around 2 areas. Passwords and File Permissions.

Dave Edis of Interactive Tools weighed in with these thoughts. 

Our site is constantly being scanned for vulnerable scripts. It adds thousands of lines to our 404 log. 

My advice would be to not use dictionary words for passwords (add a number and special character !@# for added
security).

I suggested it might not be a bad idea to change passwords on both FTP access and CMSB access on a regular basis. 

On the topic of file permissions, while a file permission of 777 may be OK for installation, it’s not really a secure
enough permission to remain on your web site unattended. Many feel that a 755 permission is more secure. 

WILL CMS BUILDER WORK WITH THE 755 PERMISSION

On that topic, Dave Edis, Senior programmer at Interactive tools had this to say:

Basically, if you (can) change permissions on /data/settings.dat.php and then you can update: Admin > General > Program
Name, it means everything works. 

So lower away, but don’t forget to check to make sure that you won’t have issues later on.


WHAT’S THE BEST WAY TO BEGIN USING CMSB?

The easiest way to begin using CMS Builder is to design your page using standard HTML and CSS first. Then list the areas
that you’d like to allow CMSB to control, and those areas that you want hard coded into your design.

Once that’s done, you should look for similarities in the design. (A list of identical entries that all have a title,
a description and an image associated with them, etc.)

HINT: If you know that you are going to show many items with a similar structure, you should enter at least enough of
them to show the basic layout of your page, like the table structure, etc.

Once that’s done, you can start to think about the format that you can use to store and retrieve that information.
Here are 2 examples that will illustrate the process. We’ll be designing an “About Us” page using a single record
section editor and then create an “Events” page using a multi record section editor.

We suggest that you create your own pages for this exercise from scratch, using your favorite text or html editor.

If you download our sample pages from:

http://www.thecmsbcookbook.com/downloads/firstpages.zip 

you’ll be able to troubleshoot the process by comparing our results to yours.

DESIGNING YOUR FIRST CMSB PAGES



CREATING AN "ABOUT US" PAGE WITH A SINGLE RECORD SECTION EDITOR - Jun 7th, 2015

AN “ABOUT US” PAGE
This “About Us” page get it’s content through a single record section editor interface.

Our “About Us” page contains the company name, an address, a phone number, and a few paragraphs describing the
company. 

CREATING THE “ABOUT US” HTML PAGE
The first step is to design the “About Us” page using HTML and to make sure that it looks the way you want it to.

Then you’d make a list of all the areas (fields) that you want the user to be able to modify. In our example we’ll
want the user to be able to modify the company’s name, their current address, phone number, and a company description.

THE SINGLE RECORD SECTION EDITOR

Since this page will contain only one set of information, it uses a single record section editor.  

Log on to your CMS Builder interface as administrator and click on the admin tab on the menu. Then click on the section
editors tab and again on the “Add New Editor” button. From there, click on the “Single Record” radio button.
Then enter “About Us” in the “Menu Name” field and click on ”Create New Menu”.

On the next screen, which is a list of existing section editors, click on “modify” next to the “About Us” entry.
That will bring you to a screen that will allow you to add new information fields to the section editor’s field menu. 

We’ll keep things pretty simple to begin with, so for the menu of fields on your section editor, in addition to the
“Title” and “Content” fields that are automatically created, you’ll be creating a text field called “Street
Address”,  a text field called “City” a text field called “State” a text field called “Zipcode”, a text
field called “Phone” and a text box called “Description”.

You’ll add these fields by clicking on the “Add Field” button at the bottom of the field list. 

For the first field, type “Street Address” in the “Field Label” box. 

You’ll notice that your entry is reflected in the Field Name” box, but there are only lower case letters and
numbers, and that any spaces or special characters are replaced by underscores. After you’ve created a field, you can
modify your “Field Label” to say anything that you want to, but the “Field Name”, which is the actual name of
your field in your MySQL database, is restricted to those types of characters.

From the “Field Type” pull down select “text field”. 

Click on the Show All link next to “Input Validation” and check the box that says “Required” so that the user
can not leave this field blank.

You’ll be creating a number of text fields, so instead of clicking on “Save”, click on “Save & Copy”. You’ll
notice that the Field Label now says “Copy of Street Address” and all the other parameters (like the “Required”
checkbox) are the same as they were for the original field.    

Change the “Field Label” to “City”. Notice that the “Field Name” changes as well.

Click on “Save & Copy” to create the “State” field, the “Zipcode” field, and the “Phone” field. Then
click on “Save” because this is the last text field that we’ll be creating for this exercise.

HINT: To create a field that maintains all of the parameters of any original field, click on “modify” next to the
original field and then click on “Save and Copy” to create the new field and then change the “Field Label” of
the new field.

There’s one more information field to create and that is the text box for the “Description”. You should be able to
do that on your own.

For this exercise we won’t be using the “content” field so you can click on the “erase” link next to the
“content” entry.


ADDING A SEPARATOR FIELD - Jun 7th, 2015

The “About Us” editor is a pretty simple section editor, but if you had a large number of fields, you’d probably
want to be able to group them into sections, and then insert header bars at the top of each section. With CMSB it’s a
snap to create as many header bars as you need. You can also customize their color and other properties to meet your
specific needs using standard HTML tags..

For this editor, let’s create a header bar that says “Company Information”. 

Click on “Add Field” and in the “Field Type” pull down select “–separator--” 

This type of field doesn’t use a “Field Label” or “Field Name”, so click on the “Header Bar” check box and
type “Company Information” into the box just below the radio button. Then click save.


RESTRICTING ACCEPTABLE DATA - Jun 7th, 2015

Now it’s time to modify the parameters of the “State” field, so click on modify next to the “State” field
entry. 

If the input validation fields are not showing, click on “show all”.

The Required box should already be checked. 

State/Possession abbreviations all have 2 letters so enter a 2 in the “Min” and the “Max” length boxes. 

State/Possession abbreviations are always capital letters, so select “only allow characters” from the “Allowed
Content” menu and type all the capital letters A through Z (no commas and no spaces) in the box to the right. Don’t
include a “B” or a “Q” since they do not appear in any of the official abbreviations.

In the “Field Description” Box type “Enter only official 2 capital letter abbreviations”. Then click “Save”
to save your changes to this field.

!!!YOU MUST ALSO CLICK ON THE “SAVE DETAIL” BOX OR YOUR CHANGES WILL NOT BE SAVED TO YOUR DATABASE!!!


PUTTING YOUR FIELDS IN A LOGICAL ORDER - Jun 7th, 2015

You’ve created 7 information fields and one separator field, but when you look at the list of fields, they are not in
an order that makes the most sense, so for convenience, let’s rearrange them so that the header bar appears at the
top, followed by Title, Street Address, City, State, Zipcode Phone and Description.  Just click on one of the up/down
arrows in the “Drag” column and drag any field up or down in the list to where you want it to appear. 

HINT: You can rearrange any field list using this technique. It does not affect your web pages, just it’s position on
the field list.


ENTER THE “ABOUT US” DATA - Jun 7th, 2015

Now that you’ve created all of the fields needed for your “About Us” page, it’s time to enter some data so that
it will appear on your page. Click on “About Us” in the menu on the left side of your page. This will display a user
interface with all of the fields that you created. Enter the appropriate information into all of the fields and click
“Save”. 

CAUTION: Your information is not uploaded to the database until you click “Save”. If you don’t save your work, it
will be lost when you close the page in your browser.

HINT: Instead of typing information directly into the interface, you can copy and paste text from your favorite word
processor.


CONVERTING YOUR HTML PAGE - Jun 7th, 2015

Now it’s time to convert your “About Us” web page to work with CMS Builder.

Make a copy of your HTML page and change the file extension from .html to .php, then open the new .php page in your web
page editor.

In your CMSB interface, click on the Admin tab on the CMS Builder main page and then on “Code Generator”. This is
one of the most powerful and useful tools in CMS Builder. It allows you to copy and paste code snippets into your web
page minimizing the need for retyping and the possibility for error.

Select the About Us” section from the pull down menu and click on the “Detail Page” radio button. Leave all of the
other entries at their default settings and click on “Show Code”.

The first thing that you’ll want to change on your web page is the code above the <head> tag. Using copy and paste,
replace all the code above the <head> tag on your web page with the code from the Code Generator that’s above the
<head> tag.

Then, in the “Step 2 area” of the code generator code, locate the word “Title:”. The code that follows,
including the less than and greater than sign will replace the text for Company Name on your web page. Don’t replace
any of the formatting, just the text.

Repeat this for the code next to Street Address:, City:, State:, Zipcode:, Phone:, and Description:. Then save your new
page and upload it to your server. 

If you’ve done all the steps correctly, you should see your web page, exactly as you designed it.

If you get error, open the sample PHP page and compare the code with the code on your page. The most common mistakes are
missing a less than “<”, or greater than “>” sign when copying and pasting, or missing some of the information
above the <head> tag.

Now that you’ve succeeded in creating your first page, let’s move on to create the next one.


CREATING AN “EVENTS” PAGE WITH A MULTI RECORD SECTION EDITOR - Jun 7th, 2015

This page utilizes a multi record section editor to manage it’s content.

In your “Events” section editor, you’ll  want to be able to enter a thumbnail image, a title, some dates and
times, and a brief description for each event.  

On your “Events” listing web page, you’ll want surfers to be able to click on one of the event listings and be
taken to that event’s “detail” page which displays the event title, the event date and times, and much more detail
about that event.


UNDERSTANDING A LIST PAGE - Jun 7th, 2015

Here’s where things get a little different, so stick with me.

In the table that we created to hold the content for this example there are 2 columns and 2 rows . The left column holds
the thumbnail image for an event and the right column holds an event title and a link, The date and time of the event,
and a brief description of the event.



<table width="800" border="0" cellpadding="5">
<tr>
<td><img src="images/image1.jpg" width="150" height="250" alt="image 1" /></td>
 <td><a href="event1.htm">Event # 1Title</a><br />List Page Description<br />
Date and Time<br />
</td>
</tr>
<tr>
<td><img src="images/image2.jpg" width="150" height="250" alt="image 2" /></td>
<td><a href="event2.htm">Event # 2 Title</a><br />List Page Description<br />
Date and Time</td>
  </tr>
</table>


There are 2 events listed in the 2 rows of the table.

But wait you say, if we’re going to display all of the events listed in the multi record section editor, the basic
idea for the layout is what we want, but we need a more flexible approach then a new hard coded row for each event. 

So here’s the more flexible approach.

First, get rid of the extra row in the table, since it will no longer be used:



<table width="800" border="0" cellpadding="5">
<tr>
<td><img src="images/image1.jpg" width="150" height="250" alt="image 1" /></td>
 <td><a href="event1.htm">Event # 1Title</a><br />List Page Description<br />
Date and Time<br />
</td>
</tr>
</table>


Then, inside the beginning of the table, insert an instruction that says for each record in our events database table,
execute the following steps, and at the end of the table, insert an instruction that says stop executing the
instructions and look for the next record. Our table will then look like this:



<table width="800" Align="center" border="0" cellpadding="5">
<?PHP foreach ($eventsRecords as $record): ?>
<tr>
<td>
<img src="images/image1.jpg" width="150" height="250" alt="image 1" /></td>
 <td><a href="event1.htm">Event # 1Title</a><br />List Page Description<br />
Date and Time<br />
</td>
</tr>
<?PHP endforeach; ?> 
</table>


Since the looping instructions surround the code necessary to create a new row, each event will be displayed in a
separate row. 

Then, from the “Step 2 area” of the Code Generator code, locate the word “Title:”. The code that follows,
including the less than “<,”and greater than “>” sign, will replace the text for the event name on your web
page. Don’t replace any of the HTML formatting, just the text. Repeat this process for the code next to “Dates and
Times:”, and “List Page Description:”. Then find the code next to” _link:” and insert one set in place of the
link URL. Like this:



<table width="800" border="0" cellpadding="5">
<?PHP foreach ($eventsRecords as $record): ?>
<tr>
<td><img src="images/image1.jpg" width="150" height="250" alt="image 1" /></td>
 <td><a href="<?PHP echo $record['_link'?>"><?PHP echo $record['title'?></a><br /><?PHP echo
$record['list_page_description'?><br />
<?PHP echo $record['date_and_time'?><br />
</td>
</tr>
<?PHP endforeach; ?> 
</table>


That leaves only the image thumbnail to deal with.

There's a lot of extra code in the “Step2a” area in the Code Generator code. There's a complete walk through of what
it all means in a later recipe, but for now, just  paste the following code into your table to replace the entire <img
src.../> statement on your page to display the image thumbnail on your web page.



<?php foreach ($record['image'] as $index => $upload): ?>

<img src="<?PHP echo $upload['thumbUrlPath'?>" width="<?PHP echo $upload['thumbWidth'?>" height="<?PHP echo
$upload['thumbHeight'?>" alt="" /><br />

<?PHP endforeach ?>


The table on your page should now look like this:



<table width="800" border="0" cellpadding="5">
<?PHP foreach ($eventsRecords as $record): ?>
<tr>
<td>
<?php foreach ($record['image'] as $index => $upload): ?>

<img src="<?PHP echo $upload['thumbUrlPath'?>" width="<?PHP echo $upload['thumbWidth'?>" height="<?PHP echo
$upload['thumbHeight'?>" alt="" /><br />

<?PHP endforeach ?>
</td>
 <td><a href="<?PHP echo $record['_link'?>"><?PHP echo $record['title'?></a><br />
<?PHP echo $record['date_and_time'?><br /><?PHP echo $record['list_page_description'?>>
</td>
</tr>
<?PHP endforeach; ?> 
</table>


And that completes the listing page. Save it and move to the detail page.


CREATING THE HTML PAGES - Jun 7th, 2015

First, as before, design both the list page and the detail page in HTML and make sure that they look the way you want
them to and that all the functions work as planned. Include a thumbnail image on the list page. Call them events.html
and eventsdetail.html

For the content area of the example events listing page, create a simple table that looks like this:




<table width="800" Align="center" border="0" cellpadding="5">
<tr>
<td><img src="images/image1.jpg" width="150" height="250>" alt="" /></td>
 <td><a href="event1.htm">Event # 1Title</a><br />List Page Description<br />
Date and Time<br />
</td>
</tr>
<tr>
<td><img src="images/image2.jpg" width="150" height="250&gt;" alt="" /></td>
<td><a href="event2.htm">Event # 2 Title</a><br />List Page Description<br />
Date and Time</td>
  </tr>
</table>


For the html detail page, you only need to code the information for a single event.

Then make a list of all the areas (fields) that you want to be able to enter information about.
In this example you’d have an Event title, event dates, an image that’s representative of the event. You’d also
have a short description of the event and a longer detailed description of that event.


CREATING THE MULTI RECORD “EVENTS” SECTION EDITOR - Jun 7th, 2015

Since you’re going to have multiple events on your listing page you’ll use a multi record section editor. That way
the user can create additional events and all the events will be listed on your list page.

For the menu of fields in your section editor, in addition to the title and content fields that are automatically
created you’ll add a text field called “Date and Time” a text box called “List Page Description”, a text box
called “Long Description” and an upload field called “Image”

After you have added these four fields, you should erase the automatically created field called “content” since you
won’t be using a WYSIWYG field for this example.


RESTRICTING ACCEPTABLE DATA - Jun 7th, 2015

Now let’s set up some important parameters for the upload field.

First click on “modify” next to the upload field. We’ll assume that the thumbnail on your list page should be a
maximum of 150 pixels wide and 150 pixels high, which is the default size, so all we’ll have to do is remove the
entries in the “file extensions allowed” area that we don’t want. You should be left with jpg,png (just the 2
extensions with a comma in between them and no spaces or periods)

HINT: There’s nothing special about these entries, so you can remove them all and just enter the few that you want.
You can add ones that are not on the list and the program will apply that filter to the upload.

HINT: CMS Builder does not change the aspect ratio of your images when it resizes them. It also will not enlarge your
images to fit an allotted space. It just shrinks them to fit within the allotted dimensions.

Next change maximum uploads from 25 to 1, and maximum upload size from 5120 Kbytes to 1000 Kbytes (1 megabyte) 

Scroll down and delete the word “Title” and the word “Caption” from the “info” fields since you won’t be
using these optional fields in this example, and click on “save”.

Then click on the Viewer URL tab and enter “/event.php” for the List Page URL and “/eventdetail.php” for the
Detail Page URL (don’t forget the slashes but leave out the quotation marks)

!!!YOU MUST CLICK ON THE “SAVE DETAIL” BOX OR YOUR CHANGES WILL NOT BE SAVED TO YOUR DATABASE!!!


PUTTING FIELDS AND EVENT LISTINGS IN A LOGICAL ORDER - Jun 7th, 2015

As in the “About Us” example, you can click on one of the up/down arrows in the “Drag” column and drag any field
up or down in the list to where you want it to appear.  Also, if you want to change the order that events appear on your
events listing page, you can drag them in the events list to rearrange them.


CREATING THE EVENTS - Jun 7th, 2015

Now you’re going create 2 events for our page and enter some data for them into the section editor, so you’ll need 2
images that are each less than 1mb in size.

On the CMS Builder main interface page, click on Events in the menu on the left side of your screen. Then click on
“Create” and a new blank record will appear.

The text information in this record is entered the same way as it was in the “About Us” editor interface.  

Upload fields work a little differently. You’ll see a box next to the Image “Field Label”, and under that a link
that says “Add or Upload File(s)”. If you click on that link you’ll see an “Upload File” box. Click on the
“Browse” button and navigate to the image that you want to appear on that event listing. Then click on Upload. When
the upload is complete you will see a thumbnail of the image in the upload field box.

In addition to uploading the image, CMS Builder has automatically created thumbnails of your image with the maximum
dimensions that you specified when you created the upload field.

After you’re done, click “Save” to save your work (upload it to the on-line database)


CONVERTING YOUR HTML PAGES - Jun 7th, 2015

Now it’s time to convert the 2 web pages to .PHP pages that will work with CMS Builder.

As in the “About Us” example, make copies of your 2 web pages and change their extensions to .PHP

The information above the <head> tag is going to be different on each of your pages.

Open the list .PHP page.

In the Code Generator, choose “Events” from the pull down menu but this time leave the “List Page” radio button
checked. Don’t change any of the default choices and click on “Show Code”.

As in the “About Us” example, the first thing to do is to replace the new page code above the <head> tag with the
code from the Code Generator.


CREATING THE EVENTS DETAIL PAGE - Jun 7th, 2015

Now let’s create the event detail page. Remember, this page contains only the event title, event dates and times, and
a long description of the event.

Go back to the main CMSB admin page and click on the Code Generator. Select the “Events” Section editor, but this
time click on the “Detail Page” radio button. Leave the default setting for everything else and click on “Show
Code”.

Open your HTML detail page and replace the code before the <head> tag as before.

Then find the code after Title:, Date and Time:, and Long Description:, and replace the text in your page with the
appropriate PHP code.

And that’s it. Upload events.php and eventsdetails.php to your server and everything should work as planned. Again, if
you get any errors, compare the code of your PHP pages with the sample pages. Again, the most common mistakes are
missing a less than or greater than sign when copying and pasting, or missing some of the information above the <head>
tag.

Now that you understand the basics of CMSB, happy coding. If you’re stumped, look for an example here in the cookbook,
or visit the CMS Builder Forum at:

          http://www.interactivetools.com/iforum/CMS_Builder_F35/ 


UNDERSTANDING THE BUILT IN CODE GENERATOR - Jun 7th, 2015

The code generator built in to the CMSB Interface (Adnin>Code Generator can save time and effort when creating viewers
for your site, but it can be a bit confusing at first.

To help you to understand the code generator, after you’ve created your 2 section editors, click on “Code
Generator” in the admin section of your CMSB interface.

You’ll see a list of options under “Create a Viewer”. The only two you should focus on are “List Page” and
“Detail Page”. 

List pages are used to show the records in a multi-record editor. Detail pages are used to show data from a single
record editor or from a specific record in a multi record editor.

Once you’ve populated your sections with some data, you can copy the entire generated code into a blank PHP document
and upload it to your server to see what that generated code looks like in a viewer. 

DETAIL PAGE CODE 
Start by clicking on “Detail Page”. Click on the “Single record sections: Load first record in database” radio
button and choose “About Us” from the pull down list. And click on “Show Code”

The first lines of code up to <!DOCTYPE html PUBLIC... are required to load CMSB and the data from your “about_us”
database.

From  <!DOCTYPE html PUBLIC... through <!-- STEP2: Display Record... is pretty standard HTML code

After that you’ll find the PHP code that pulls data from your database and displays that data. 

For example, the line of code Title: <?php echo htmlencode($about_usRecord['title']) ?> pulls the information from the
“title” field of your “abou_us” editor and displays (echos) that information after the text “Title:”.
“htmlencode” is not mandatory, but it helps to insure that the code is displayed correctly.

LIST PAGE CODE
Now let’s look at the code that gets generated for a list page.

Go Back to Admin>Code Generator but this time choose List Page from the “Create A Viewer” list “Events”. Then
choose “Events” from the pull down menu, make sure that “Show all records” is checked, and Click on Show Code.

Notice that the LOAD RECORDS code at the top of the page is different then it was for the Detail Page. This is because
you will be looping through the records in this multi-record editor and showing specific data from all “events”
records on your list page viewer, together with links to show detailed information about a specific record on your
detail page. 

In Step2: you’ll see <?php foreach ($eventsRecords as $record): ?>. This tells the database to loop through each
record in the database and pull specific information from each record.

The next lines, for example, Title: <?php echo htmlencode($record['title']) ?> pull the data from the title field of the
record, and displays (echoes) that information after the text “Title:”.   

 STEP 2a: Display Uploads for field 'images' 
STEP 2a may seem a bit confusing at first because of the amount of information that it contains, but let’s take it
piece by piece and it will become clearer.

The first lines through the <blockquote> is just a reminder of some of the special field names can be used inside this
foreach loop to pull data from your records. 

Next you’ll find a second foreach loop. <?php foreach ($record['images'] as $index => $upload): ?>
This one says loop through each upload in the field “images” (in the record that the first foreach loop is looking
at) so that some information about that upload can be displayed.

This is where it gets a bit confusing...

Next, there’s  Upload Url: <?php echo $upload['urlPath'?>, which is the code that you’d use to display (echo) the
URL of the upload.

Then there’s a line of commented code <!-- Uploads: Copy the tags from below that you want to use, and erase the ones
you don't need.

You’ll need to remove that line if you want to display the entire generated code as a viewer. 

The next lines of code are the ones that you will use in your viewer to display different types of uploads.

Since you have uploaded images and have generated thumbnails for those images, you would use the line:  <img src="<?php
echo $upload['thumbUrlPath'?>" width="<?php echo $upload['thumbWidth'?>" height="<?php echo $upload['thumbHeight']
?>" alt="" /> as your image tag.

Since foreach loops have to be instructed to end, you would need to use one <?php endforeach ?> tag to end the $upload
loop and another <?php endforeach ?> to end the $record loop in your viewer code.

DETAIL PAGE FOR A MULTI-RECORD EDITOR
This time, when you go to Admin>Code Generator, choose Detail Page, then choose “Events” from the pull down list,
check  Multi record sections: Get record # from end of url, and click on “Show Code”.

Again the LOAD RECORDS Code is slightly different from the others and in STEP 2 and 2A the code specifies the name of
the section in the PHP code, as it did for Single Record viewers.

CHAPTER 1 - SECTION EDITORS



CREATING A NEW PAGE FROM AN EXISITING HTML PAGE OR A TEMPLATE - Aug 1st, 2010

1.    Examine your HTML page to determine what data fields will be necessary.
2.    Create a new section editor from the preset template that most closely meets your needs.
3.    Modify or add any fields as required for the particular HTML page.
4.    Enter any default information as required.
5.    Save.
6.    View the code generator.
7.    Make a copy of the html page.
8.    Change the .html file extension to .php.
9.    From the code generator, copy all code above the <head> tag and paste it into the new PHP document Replacing all the
information above the <head> tag.
10.    Copy the appropriate PHP echo code to replace the variable text segments in your new PHP document, keeping any
formatting or CSS tags in place.
11.    Upload your new PHP document to your server and test it.
12.    After you’re happy, delete the old HTML version from your server.


ERROR MESSAGES ABOUT FIELD NAMES NOT FOUND - Aug 1st, 2010

If after you delete fields from a multi-record section editor, you get errors about field names that are not found
Go to “viewer URLS” and” searching” and 'sorting" tabs on the section modification screen and delete the field
names that no longer exist or change the field names as appropriate. 

You should also check the ListPage Fields on the "general" tab to make sure that the correct fields will be displayed on
the editor list page.


DUPLICATE A RECORD WITHOUT HAVING TO RETYPE THE DATA - Dec 29th, 2018

BLUKABM did, and said, “That way I could modify the copied record instead of creating a new one and re-entering data
that won't change much. “

In Dave’s (unsupported) approach to add a "Save as Copy" button to the bottom left of the edit page he said:

“It doesn't support uploads or all the field types but worked for this case. I'm posting in here in case it turns out
to be useful for someone else as well.”

1.    - Create a backup copy of /lib/menus/default/edit.php
2.    - Open /lib/menus/default/edit.php
3.    - Search for showWysiwygGeneratorCode
4.    - Add the new code below(about 15 lines above showWysiwygGeneratorCode)

    
<table border="0" cellspacing="0" cellpadding="0" width="690"> 
 <tr> 
<!-- Insert This Code -->
  <td> 
   <input type="submit" name="action=save" value="<?PHP _e('Save as Copy'?>" 
          onclick="document.getElementById('num').value='';
document.getElementById('preSaveTempId').value='1234567890abc'" 
          class="inputButton" /> 
  </td> 
<!-- End of Insert-->
  <td align="right"> 
   <input type="submit" name="action=save" value="<?PHP _e('Save'?>" class="inputButton" /> 
   <input type="button" name="cancel" value="<?PHP _e('Cancel'?>" class="inputButton" 
             onclick="window.location='?menu=<?PHP print urlencode($menu?>'" /> 
  </td> 
 </tr> 
</table> 
</form> 
 
<?PHP showWysiwygGeneratorCode() ?>


This approach was made into a free plugin in November 2010. The plugin adds the functionality of copying the record and
its upload as well. Pretty cool.

You can download the free plugin from:

http://www.interactivetools.com/add-ons/?user_submitted=1


GET RID OF (OR REPLACE) “THESE UPLOADS MAY BE DISPLAYED OR LINKED ON OUTPUT PAGE.” ABOVE ALL THE IMAGE UPLOAD FIELDS. (LEGACY). - Aug 1st, 2010

Navigate to your cmsAdmin/lib/menus/default folder and backup the edit_functions.php file

Then open the file and search for “// display field”

You’ll find the comment there and you can either delete the comment between the ‘ marks, or change it to something
more appropriate to your needs.


SPECIAL FIELD NAMES - Aug 1st, 2010

These "special" fields can add functionality to your sections. Here’s the list so you won’t have to look for them.

Fieldname - Editable -Description

createdDate -  Not editable - Automatically stores the date the record was first created

createdByUserNum - Not editable - Automatically stores the 'num' of the user who created the record. This value is also
used to identify the 'owner' of the record.

 updatedDate - Not editable - Automatically stores the date the record was last updated

updatedByUserNum - Not editable - Automatically stores the 'num' of the user who last updated the record.

publishDate - Editable Date Field - Record won't be displayed on website before this date. This allows users to prepare
content in advance and have it appear on a certain date and time.

removeDate Editable Date Field - Record won't be displayed on website after this date. This allows users to have content
automatically "expire" from website after a certain date and time.

neverRemove Editable - Checkbox - This checkbox field indicates that "removeDate" should be ignored. This lets users
have some records that expire (are hidden) after a certain date and other records that never expire.

hidden - Editable Checkbox - This checkbox field indicates that the record should be not be displayed on the website.
This lets users temporarily make records visible or not.

dragSortOrder - Editable - This field allows the user to drag records in the section editor record list to change their
order. Just create this field and add it as the first entry of "ListPage Fields" and "Order By" to enable this
functionality.


IMPLEMENTING AN APPROVAL PROCESS BEFORE A REVISION GOES LIVE (LEGACY) - Mar 10th, 2011

As of Version 2.07, the ability to preview a page before it goes "live" is built in to CMSB. 

If you're using an older version you can still implement an approval process so that you can approve entries before they
can be seen by the public.

Assuming you're using a multi-record editor, you can hide a record form your public web site pages using the special
"hidden" checkbox field. If you set it to be an admin only field to only administrators and section managers can take a
record live, but not the original author.

You could get really slick by setting up a special copy of the viewer for this information with the code:



     'where'    => " hidden = '0' ", 



In the list records call at the top of your viewer

Setting the user account permissions for editing by author and changing the owner (Created By) after approval is one way
to implement an approval process and prevent further changes by the user.

In a single record editor you’d have to create a new set of “for Approval” fields and once the new information is
approved, the admin or section manager would have to paste the approved information into the “live” fields.


POPULATE A FIELD IN A DATABASE TABLE FROM FIELDS IN ANOTHER TABLE - Aug 1st, 2010

A way to take 3 fields from one table entered in one Editor and have then populate one field in another Editor. Let’s
say in the first section there are 3 fields: code, product and format.
I enter all my products using that Editor. 
I have setup another Editor to enter some specials for some chosen products. When adding new special I would like to be
able to select products - entered in the previous Editor - from a list that is populated from the code, product, format
field from the other table (Editor). 

Under list options selected "Get options from MySQL Query (advanced)" and enter this (use your own table prefix and
table name instead of cms_tablename):



SELECT num, CONCAT_WS(" ", code, product, format) FROM cms_tablename

The CONCAT_WS mysql function means "concatenate with string" or "join these fields together with this first value". So
in the above example it would join code, product, and format together with a space and return that as the pull down
"label". I set the pull down "value" as the num field. If you want the joined value for both just use this:



SELECT CONCAT_WS(" ", code, product, format), CONCAT_WS(" ", code, product, format) FROM cms_tablename


INSERT INFORMATION FROM ONE SECTION EDITOR INTO ANOTHER - Mar 17th, 2011

Sagentic had this challenge:

We have a section called "products" .

Then we have a section called "pricing" that allows you to enter in different sizes of that product and the price. The
first field in this section is a list that pulls the name of the product from the "products" section.

In short, I want to pull all "pricing" entries for Metal Garden Sheds and put it on the "product" page for Metal Garden
Sheds - all "pricing" entries for Wood Garden Sheds and put it on the "product" page for Wood Garden Sheds - etc....

Dave Edis from Interactive Tools observed:

What you want to do is have your first viewer that loads the product accept "url searching" like this: products.php?4
That's the default so that should be fine.

For your price viewer you want it to ignore the search values and hard code the search as a where. That's because you
want to base the search off the value of the product record, not what's in the url. First add a line to get the escaped
productName value:



$escapedProductName = mysql_real_escape_string( $productsRecord['name'] );


Next add this in your price viewer code:



'allowSearch' => false,
'where' => " product = '$escapedProductName' ", 


ANOTHER EXAMPLE
Here’s an example of how to use this approach to restrict the records shown on a video list page to only those that
belong to a specific exhibition.

For this example 2 multi record editors are involved.

One multi record editor, called “exhibitions” contains a separate record for each exhibition. Each record has a
field for a unique exhibition_name and an upload field containing a series of still images of those exhibitions. The
exhibitions detail page has a link to the video list page with the exhibitions record number appended .

The other, called “videos” has a separate record for each video with a field for the exhibition_name (which gets
it’s value from a radio button list populated from the exhibition_name field of the exhibitions editor) and an upload
field containing one of the video taken at that exhibitions.


<a href='<a href='videos.php?num=<?php echo $exhibitionsRecord['num'?>'>WATCH THE VIDEO FROM THIS EXHIBITION</a>


Then in the video list page in the get records area:


<?php header('Content-type: text/html; charset=utf-8'); ?> 
<?php 

  // load viewer library 
  $libraryPath 'cmsAdmin/lib/viewer_functions.php'
  
$dirsToCheck = array('/path_to_your_server/','','../','../../','../../../'); 
  foreach (
$dirsToCheck as $dir) { if (@include_once("$dir$libraryPath")) { break; }} 
  if (!
function_exists('getRecords')) { die("Couldn't load viewer library, check filepath in sourcecode."); } 
  
// load records 
  list($exhibitionsRecords$exhibitionsMetaData) = getRecords(array( 
    
'tableName'   => 'exhibitions'
  )); 
  
$exhibitionsRecord = @$exhibitionsRecords[0]; // get first record 
  $escapedExhibitionName mysql_real_escape_string$exhibitionsRecord['exhibition_name'] );  
  
 
 
// load records 
  list($videosRecords$videosMetaData) = getRecords(array( 
    
'tableName'   => 'videos'
    
'allowSearch' => '0'
'where' => " exhibition_name = '$escapedExhibitionName' ",  
  )); 
 
?>


And in the body of the videos list page a straightforward (except I’ve included the MaxWords function that you can
find in the recipe “MAXWORDS - LIMITING THE NUMBER OF WORDS SHOWN ON A PAGE”):


<table>
   <?php foreach ($videosRecords as $record): ?>
<tr> 
<td>     
<?php foreach ($record['list_image'] as $upload): ?>
<a href="<?php echo $record['_link'?>"><img src="<?php echo $upload['thumbUrlPath2'?>" width="<?php echo
$upload['thumbWidth2'?>" height="<?php echo $upload['thumbHeight2'?>" alt="" /></a>
<?php endforeach; ?>
</td>
<td><a href="<?php echo $record['_link'?>"><?php echo $record['video_title'?></a><br /></td>
</tr>
<tr>
<td colspan="2">
<?PHP echo maxWords($record['description'], 25);
?>...<a href="<?php echo $record['_link']; ?>">Read More...</a></td>
</tr>
<tr>
<td colspan="2"><hr align="center" width="300" color="#99945e"/></td>
</tr>
<?php endforeach; ?>
</table>


NOTE: If you have other tables being accessed in your get records calls, you may have to include the line: 


'allowSearch' =>  false,


in those calls, so that the search at the end of the URL does not affect the records returned from those tables. (This
will cause some very strange results on your pages) 


CHOOSING A BACKGROUND IMAGE FROM A LIST - Jan 8th, 2012

My client  wanted to change the background image behind their masthead to reflect the seasons. They required the process
to be both easy (one click selection) and flexible (the list needed to be dynamic)

Here’s what I came up with.

1) First, I created a multi-record editor called Masthead Background Image Bank with only a title field and an image
upload field. The field allowed only a single image to be uploaded. I then created a record for each background image
and uploaded them.

And, in a single record section that I call common_information:

1) I created a list field that’s called masthead_background_image. This list field gets it’s list options from a
database (the masthead_background_image_bank) with option values from the num (record number) field and option labels
from the title field.

2) Then I added an image upload field called masthead_image which allows only one image to be uploaded and that contains
the foreground masthead image. HINT: If transparency is important, restrict this field to accept png images only, and
create a 24bit png for the actual masthead. 

At the top of my viewers I added load records calls for both section editors.

// load records
  list($common_informationRecords, $common_informationMetaData) = getRecords(array(
    'tableName'   => 'common_information',
    'allowSearch' => '0',
    'limit'       => '1',
  ));
  $common_informationRecord = @$common_informationRecords[0]; // get first record

// load records
  list($masthead_background_image_bankRecords, $masthead_background_image_bankMetaData) = getRecords(array(
    'tableName'   => 'masthead_background_image_bank',
  ));



Then, in the viewer body, where I wanted  show the masthead, I used:

NOTE: Since I'm pulling the information for the $recnum variable from a list field, the option value (in this case the
record number) is displayed by default. If for some reason I needed to display the option label instead, I'd display the
pseudo field :label value by adding that to the fieldname like this: 
$common_informationRecord['masthead_background:label']


<table width="100%" border="0" cellspacing="0" cellpadding="0">
<tr>
<?php $recnum $common_informationRecord['masthead_background'?>
<td  background="<?php foreach ($masthead_background_image_bankRecords as $record ): ?><?php if ($record['num'] ==
$recnum): ?><?php foreach ($record['image'] as $upload): ?><?PHP echo $upload['urlPath'?><?PHP endforeach; ?><?php
endif ?><?PHP endforeach; ?>" >

<img src="<?php foreach ($common_informationRecord['masthead_image'] as $upload): ?><?PHP echo $upload['urlPath'?>"
width="<?php echo $upload['width'?>" height="<?php echo $upload['height'?>" alt="" <?PHP endforeach; ?>">
</td>
</tr>
</table>



USING "RELATED RECORDS" TO POPULATE PAGES FROM A MASTER “ADDRESS BOOK” OR “PRODUCT LIST” - Aug 30th, 2017

Let’s say you have a number of address book entries that contain things like name, address, phone, travel directions,
map URL, etc. Or you have a number of products with detailed information about each of them. 

Instead of retyping all of your information each time you want to display it on a list or detail page, you can pull that
information directly from your master information list. Chris Waddell from Interactive Tools created a
“relatedRecordLookupFunctions” plugin to add the required functionality this and suggested the following approach, I
modified the table and field names for this “Address Book” example:

MULTI-VALUE LIST FIELDS (THE CODE REQUIRED FOR SINGLE VALUE LIST FIELDS FOLLOWS)

Step 1: Set up your address book section. for this example call it “Venue Address Book”.

This is a standard multi-record section with the necessary information fields. Since this example is an address book of
venues for an events listing, I’ve set up fields for Venue Name, Venue Address, Venue Phone, Venue Contact E-mail,
Venue URL, Venue URL link text (I like to keep these separate to add flexibility for my clients but I set the default
text to WEB SITE or CLICK HERE FOR WEB SITE), Venue Travel Directions, and Venue Map URL (either from Mapquest or Google
maps). 

Step 2: In the section where you want this information to appear you’ll add a list type field called “Venue” with
the following parameters.

Field Label: Venue 
Field Name: venue 
Field Type: list   
   
Field Options:   
    Display As: pull-down or checkbox (multi-value for multi-value lists only)
    List Options: Get options from database (advanced)   
    Section Table name: venue_address_book 
    Use this field for option values: num   
    Use this field for option labels: venue_name


Step 3: Install the Related Record Lookup Functions Plugin which you can download from:

       http://www.thecmsbcookbook.com/downloads/relatedRecordLookupFunctions.zip

Upload the file to your server in the /cmsAdmin/plugins directory, then log into CMSB, go to Admin > Plugins, and click
Activate on it.

LIST PAGES
 Step 3: To set up your viewer to display the fields required you'll need to add some code to the top of your page. You
probably already have most of the required code in your PHP source code. Just add this code:



beta_lookupRelatedFields(array(   
    'table'      => 'e_blast_events_notice',   
    'recordList' => &$e_blast_events_noticeRecords,   
    'fieldList'  => array( 'venue' )   
  
  )); 
  $e_blast_events_noticeRecord = @$e_blast_events_noticeRecords[0]; // get first record 


 to your existing code, like this:  

 


<?php header('Content-type: text/html; charset=utf-8'); ?>
<?php
  
  require_once "/your_path_to/cmsAdmin/lib/viewer_functions.php";

 list(
$e_blast_events_noticeRecords$e_blast_events_noticeMetaData) = getRecords(array(
    
'tableName'   => 'e_blast_events_notice',
    
'where'       => whereRecordNumberInUrl(1),
    
'limit'       => '1',
  ));
 
  
?>
<?php if ($e_blast_events_noticeRecords): ?>
<?php
  beta_lookupRelatedFields(array(   
    
'table'      => 'e_blast_events_notice',   
    
'recordList' => &$e_blast_events_noticeRecords,   
    
'fieldList'  => array( 'venue' )   
  
  )); 
  
$e_blast_events_noticeRecord = @$e_blast_events_noticeRecords[0]; // get first record 

?>  
<?php endif ?>

<?php
  // show error message if no matching record is found
  if (!$e_blast_events_noticeRecord) {
    
header("HTTP/1.0 404 Not Found");
    print 
"Record not found!";
    exit;
  }

?>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">


Step 4: Then in the body, where you want to display your address book fields:



<?php if (empty($e_blast_events_noticeRecord['venue'])): ?>   
No Venue Information is available.   
<?php else: ?>   
 <div align="left"> Venue Information: <br />

    <?php foreach ($e_blast_events_noticeRecord['venue'] as $venue): ?> 
         
        <?php echo $venue['venue_name'?><br />
        <?php echo $venue['venue_address'?><br />
        <?php echo $venue['venue_contact_e_mail'?>  <br />
        <?php echo $venue['venue_phone'?><br />
         Directions: <br /><?php echo $venue['venue_travel_directions'?> <br />
        <a href="<?php echo $venue['venue_map_url'?>">Click / Tap for a Map</a><br />
   
    <?php endforeach ?> 
  </div>
<?php endif ?>


You can display the fields separately but you’ll need a foreach loop for each field or set of fields that you want to
display.

DETAIL PAGES:

To set up your viewer to display the fields required you'll need to add some code to the top of your page. You probably
already have most of the required code in your PHP source code. Just add this code:


The 
<?php if ($e_blast_events_noticeRecords): ?>
 <?php beta_lookupRelatedFields(array(   
    
'table'      => 'e_blast_events_notice',   
    
'recordList' => &$e_blast_events_noticeRecords,   
    
'fieldList'  => array( 'venue' )   
  
  )); 
  
$e_blast_events_noticeRecord = @$e_blast_events_noticeRecords[0]; // get first record   
  ?>
<?php endif ?>


Then, on the detail page where you want the information to be displayed:


<?php $venue $e_blast_events_noticeRecord['venue']; ?>
                 <?php echo $venue['venue_name'?><br />
        <?php echo $venue['venue_address'?><br />
        <?php echo $venue['venue_contact_e_mail'?>  <br />
        <?php echo $venue['venue_phone'?><br />
         Directions: <br /><?php echo $venue['venue_travel_directions'?> <br />
        <a href="<?php echo $venue['venue_map_url'?>">Click / Tap for a Map</a><br />

SINGLE VALUE LIST FIELDS 
According to Chris Waddell from Interactive Tools, “The process works with single value lists too, but the interface
is slightly different.(Don't forget to make your list a single value pulldown or check box)  The plugin replaces the
field with the associated record, instead of a list of records. For this reason, you wouldn't use foreach to loop over
the records.”

Here’s how. First, define the variable $venue before it's first use:



<?php $venue $e_blast_events_noticeRecord['venue']; ?>


Then where you want to echo the fields in your viewer:



<?php echo $venue['venue_name']; ?>


And if you wanted to include an “if” statement the code would look like this:



<?php if ($venue['venue_name']): ?>
<?php echo $venue['venue_name']; ?>
<?php endif; ?>  


Or you could leave out the define variable step and just use:



<?php echo $e_blast_events_noticeRecord['venue']['venue_name']; ?>


LIST PAGES
If you want to implement this on a list page:

Again here’s the code for the head of the list page:



<?php
  
  require_once "/hsphere/local/home/apbcweb/artistsofpalmbeachcounty.org/cmsAdmin/lib/viewer_functions.php";

  list(
$e_blast_events_noticeRecords$e_blast_events_noticeMetaData) = getRecords(array(
    
'Table name'   => 'e_blast_events_notice',


  ));

?>
 <?php if ($e_blast_events_noticeRecords): ?>
<?php 
   beta_lookupRelatedFields(array(   
    
'table'      => 'e_blast_events_notice',   
    
'recordList' => &$e_blast_events_noticeRecords,   
    
'fieldList'  => array( 'venue' )   
  
  )); 
  
$e_blast_events_noticeRecord = @$e_blast_events_noticeRecords[0]; // get first record 
  
?>
<?php endif; ?> 


And here’s the code to display the related records in the body of the list page:


 <?php foreach ($e_blast_events_noticeRecords as $record): ?> 
<?php $venue $record['venue']; ?>
       
      Location:<br />
      <?php echo $venue['venue_name']; ?><br />
 <span class="body-text"><?php echo $venue['venue_address'?><br />
<?php endforeach ?>

 
Note: Whether you’re using this once, or multiple times on your page, the: 



<?php if ($e_blast_events_noticeRecords): ?> 


before the: 



<?php 
   beta_lookupRelatedFields... 


makes sure that if there are no records matching your particular criteriayou wont get a “lookupReferringRecords:
'recordList' option missing” error.

IMAGES
When implementing this function for imagesDan Maitland from Heelatch.com found that the “if” statement was
important



<?php if ($venue['venue_image']): ?>
<?php foreach ($venue['venue_image'] as $upload): ?>
<img src="<?php echo $upload['thumbUrlPath'?>" width="<?php echo $upload['thumbWidth'?>" height="<?php echo
$upload['thumbHeight'?>" alt="" /><br />
<?php endforeach ?> <?php endif; ?>


The code from the venue_address_book code generator was



<?php foreach ($venue_address_book['venue_image'] as $upload): ?>

 
I changed that to: 



<?php foreach ($venue['venue_image'] as $upload): ?> 


to match the rest of the code in my viewer.

Of course, as always, styling is up to you.


PULLING DATA FROM AN ACCOUNT RECORD USING THE CREATEDBY FUNCTIONALITY BUILT INTO CMSB - Feb 1st, 2017

If you need to be able to pull information from the account record of the record owner (determined by the CreateBy field
of the record), there's a built in function that will save you a great deal of grief.

I had a number of product listing records that were created by subscribers, and wanted to give the subscribers the
option of filling in all of their contact information on each of the product listing records, or using their user
profile information to automatically populate those contact information fields in a viewer.  

Ross Fairbairn, the consulting guru at Interactive Tools came to my rescue with a really easy fix.

His suggestion, "Just substitute:

$your_listing_tableRecord['createdBy.your_field'] instead of  $your_listing_tableRecord['your_field'] (or use
$record['createdBy.your_field'] in your foreach loop)

This will use the built in createdBy function to do all the heavy lifting for you."

To offer the subscriber a choice, just include a checkbox in your listing table. and call it something like,
"use_profile_contact_information"

Then use an if statement to display the appropriate code.

<?php if($your_listing_tableRecord['use_profile_contact_information'] == '0'):?>
                                            <?php if ($your_listing_tableRecord['your_field']) :?>
                                            <b>Contact:</b> <?php echo $your_listing_tableRecord['your_field'?>
                                            <?php endif ?>
                                            <?php elseif ($your_listing_tableRecord['use_profile_contact_information']
== 
'1'):?>
                                            <?php if ($your_listing_tableRecord['createdBy.your_field']) :?>
                                            <b>Contact:</b> <?php echo $your_listing_tableRecord['createdBy.your_field']
?>
                                            <?php endif ?>

Ross added that, "createdBy fields load by default. You can disable them if you want in getRecords with:

'loadCreatedBy'       => FALSE,

He went on to say that, "The only reason to disable them would be to save a on overhead when loading the page. In some
cases we've seen sites with 10,000+ users and loading the createdBy data can have a noticeable slowing effect on page
load times."
 


USING SEPARATORS - Sep 6th, 2010

Separators are fields whose only function is to neaten up your section editor layout. 
You can either:
Leave a blank line between groups of fields (blank line), 
Create a text heading (Heading) for a groups of fields, or create a heading for the editor with special features like
font size, bolding, or colored text (HTML)

CUSTOMIZING THE LOOK OF A SEPARATOR
You can use html coding to change the look and contents of the header bar, so if you want the separator to be a
horizontal line 300px wide that is aligned too the left you would type this in the header bar field: 



<hr align="left" width="300" color="#FF0000">


If you want your header bar text to be red and bold you could type:



<font color=”#FF0000" ><b>YOUR HEADER TEXT</b></font>


If you get the wrong result, make sure that any leading and trailing quotation marks are the correct double quote
symbol. Sometimes they can be different than what’s expected and cause a problem.


CREATING COLOR CODED LEGENDS AND COLOR CODED FIELD LABELS IN SECTION EDITORS - Aug 2nd, 2010

To make it easier for those doing the data entry on some section editors, my client asked if, in addition to heading
separators, I could create a color coded legend at the top of the Section Editor and color code the “Field Labels”
in certain places.

Here’s what I did. First I added an HTML separator field to my section editor (Add field - Field type “separator”
- click on HTML)
Next I added the following HTML code in between the <tr> and the </td> tags, replacing the <td colspan='2'> (I wanted
the legend cell background to be a light grey).



<tr>
 <!-- Insert This Code -->
<td bgcolor="lightgrey" colspan='2'>
<font size="5"><b>LEGEND</b></font>
<font size="3"> All fields in this editor are color coded according to where they will appear on the events web pages
<hr>
<font color="red">Special Information Only</font>
<font color="darkorchid">Public List Page Only</font>
<font color="green">Members standard form only</font>
<font color="yellow">Appears on all Pages</font> 
 </font>
<!-- End of Insert-->
</td>
</tr> 


Then, after I had created and saved all of my fields for the editor, I modified the field labels to the font size I
wanted and to the colors that I set up in the legend with the following code: 



<font size="3" color="Red"><b>Your Field Label</b></font>


If you save the field before you go back and modify the “Field Label”, your changes will not affect the “Field
Name”.

You can change the values to suite your needs. My client found this approach very helpful and I hope that you will also.


USING CATEGORY MENUS - Aug 1st, 2010

The Basics...

If you want to create a series of “Nested” or “Unordered List” menus on your page and display the contents of
that record on the page here’s how.

Create a new section editor called “Category” of the advanced menu type “Category Menu”. (You can call the new
section anything that you want to, but “Category” will work with the demo code below)

Under Admin > Section Editors > Your Category Section > Viewer URLs - set both the List page and Detail page viewer URLs
to /categoryList.php

Now add some Main Category and some Sub Category records, using the “Parent Category” pull down menu to organize
them. (The list will automatically grow with Parent and Child categories after you create new records). 

Enter some descriptive text in the content fields, like “this is the description of Main Category 1, this is the
description of sub category 1, etc. so that you can follow the results.

Click on the up and down “order” links to reorder the records. (Changing the order of a parent category moves the
entire category up or down)

Now create a sample List Page viewer called categoryList.php

At the top of your page 



<?php 
   
 require_once "your server _path /cmsAdmin/lib/viewer_functions.php"
 
  list(
$categoryRecords$selectedCategory) = getCategories(array( 
    
'Table name'           => 'category'
    
'selectedCategoryNum' => '',         // defaults to getNumberFromEndOfUrl() 
    'categoryFormat'      => 'showall',  // showall, onelevel, twolevel 
  )); 
 
?> 


Use this code where you want the category menu to appear:



<!-- category menu --> 
  <h3>Nested Menu</h3> 
  <?php foreach ($categoryRecords as $categoryRecord): ?> 
 
    <?php echo str_repeat("&nbsp; &nbsp; &nbsp;"$categoryRecord['depth']); ?> 
 
    <?php if ($categoryRecord['_isSelected']): ?><b><?php endif; ?> 
    <a href="<?php echo $categoryRecord['_link'?>"><?php echo $categoryRecord['name'?></a> 
    <?php if ($categoryRecord['_isSelected']): ?></b><?php endif; ?> 
    <br /> 
  <?php endforeach; ?> 
<!-- /category menu -->


OR - use this code to display the menu in <ul> format:



  <!-- unordered list --> 
  <h3>Unordered List Menu</h3> 
  <ul> 
  <?php foreach ($categoryRecords as $categoryRecord): ?> 
    <?php echo $categoryRecord['_listItemStart'?> 
 
    <?php if ($categoryRecord['_isSelected']): ?> 
      <b><a href="<?php echo $categoryRecord['_link'?>"><?php echo $categoryRecord['name'?></a></b> 
    <?php else: ?> 
      <a href="<?php echo $categoryRecord['_link'?>"><?php echo $categoryRecord['name'?></a> 
    <?php endif; ?> 
 
    <?php echo $categoryRecord['_listItemEnd'?> 
  <?php endforeach; ?> 
  </ul> 
  <!-- /unordered list -->


And then use this code to display the selected category:



<?php if (!$selectedCategory): ?> 
  No record selected 
<?php endif; ?> 
 
<?php if ($selectedCategory): ?> 
 
  <h3><?php echo $selectedCategory['name'?></h3> 
  <?php echo $selectedCategory['content'?> 
 
<?php endif; ?>


Those are the basics of using the Category Menu feature that’s built in to Version 1.20 and above.

According to Dave Edis at Interactive Tools, you can control which field is displayed in the URL before the "id" by
setting "filename fields" under: Section Editors > Viewer URLS.

The simplest solution might be to just set this to 'name' instead of 'breadcrumb'

But by default it will use 'breadcrumb' and convert non-word characters to "-" so if you have a breadcrumb of: News :
Technology : Web. So it becomes: News-Technology-Web

You can also create the links in whatever format you like instead of using _link. For example if you created a fieldname
called filename you could have:



<a href="/path/to/viewer.php?<?php echo $categoryRecord['filename'?>-<?php echo $categoryRecord['num'?>">


To link as: 



viewer.php?filename-here-123


You can learn more about using the Category Menu feature from the posts at:

      http://www.interactivetools.com/forum/gforum.cgi?post=64259

If any of you have found interesting ways to use this feature, please share them in an e-mail to:

      cookbook@thecmsbcookbook.com

Thanks


USING “RESERVED” MYSQL WORDS FOR FIELD NAMES - Mar 24th, 2015

A number of field names are “reserved” words in the MySQL world. If you use one of them by mistake, you may get a
MySQL error similar to:

MySQL Error: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for
the right syntax to use...

It’s a pretty long list , and you can find it at:

      http://dev.mysql.com/doc/refman/5.0/en/reserved-words.html

If you must use one of those words as a field name, Dave Edis from Interactive Tools has a suggestion. He says, '' A
trick for dealing with those is either renaming the field -or- surrounding it with `backticks` (beside the 1 on most
keyboards). Since ''show'' is a reserved word, you’d surround it with backticks like this:



'where' => 'active ="1"' . ' AND `show` ="1" ', 


Backticks are a way of telling MySQL that you mean a table or fieldname and not anything else. 

Most of the time it's just easier to rename the field, though. :)''


DUPLICATING A RECORD INSTEAD OF RE-ENTERING THE DATA - Dec 29th, 2018

NOTE:
 This function has been turned into a free plugin that you can access at 

http://www.interactivetools.com/add-ons/detail.php?Save-Copy-Button-1030

This plugin requires at least CMSB Version 2.11. The information below is implementation into earlier versions.


User blukabm was curious if there was a way to duplicate a record. “That way I could modify the copied record instead
of creating a new one and re-entering data that won't change much.”

Dave Edis from Interactive Tools came up with an answer for both versions before and after 2.x

He said:

“Here's an (unsupported) hack I wrote up to add a "Save as Copy" button to the bottom left of the edit page.

(Note:) It doesn't support uploads or all the field types.”

- Create a backup copy of /lib/menus/default/edit.php
- Open /lib/menus/default/edit.php

FOR CMSB VERSIONS PRIOR TO 2.X 

- Search for: showWysiwygGeneratorCode

- Add this code:



  <td> 
   <input type="submit" name="action=save" value="<?php _e('Save as Copy'?>" 
          onclick="document.getElementById('num').value='';
document.getElementById('preSaveTempId').value='1234567890abc'" 
          class="inputButton" /> 
  </td> 


to the existing code(about 15 lines above showWysiwygGeneratorCode)



<table border="0" cellspacing="0" cellpadding="0" width="690"> 
 <tr> 
 <!-- Insert This Code -->
 <td> 
   <input type="submit" name="action=save" value="<?php _e('Save as Copy'?>" 
          onclick="document.getElementById('num').value='';
document.getElementById('preSaveTempId').value='1234567890abc'" 
          class="inputButton" /> 
  </td> 
<!-- End of Insert-->
  <td align="right"> 
   <input type="submit" name="action=save" value="<?php _e('Save'?>" class="inputButton" /> 
   <input type="button" name="cancel" value="<?php _e('Cancel'?>" class="inputButton" 
             onclick="window.location='?menu=<?php print urlencode($menu?>'" /> 
  </td> 
 </tr> 
</table> 
</form> 
 
<?php showWysiwygGeneratorCode() ?>


FOR CMSB VERSION 2.X AND ABOVE

search for: "_action=save"

HINT: It occurs in 2 places line 34 and line 72
Add this code to the existing code in both places.



    <input type="submit" 
          name="action=save" 
          value="<?php et('Save As Copy'?>"  
          onclick="document.getElementById('num').value='';
document.getElementById('preSaveTempId').value='1234567890abc'"  
          class="button" />  




  <div style="float:right"> 
<!-- Insert This Code -->
    <input type="submit" 
          name="action=save" 
          value="<?php et('Save As Copy'?>"  
          onclick="document.getElementById('num').value='';
document.getElementById('preSaveTempId').value='1234567890abc'"  
          class="button" />  
<!-- End of Insert-->
      <input class="button" type="submit" name="_action=save" value="<?php et('Save'?>"  /> 
     <input class="button" type="button" name="cancel" value="<?php et('Cancel'?>" 
onclick="window.location='?menu=<?php print urlencode($menu?>'" /> 
  </div>


CREATE MULTIPLE RECORDS WITH ONE MYSQL QUERY - May 8th, 2011

If you need to populate a database with multiple records, instead of creating each record individually, you can create a
MySQL query in your word processor and then create all the records and populate all the fields at once.

Here’s how:

Create a new PHP document and load the viewer_functions.php with this code at the top of your page (don’t forget to
change the server path information), or copy the code block from one of your other viewers.



 <?php header('Content-type: text/html; charset=utf-8'); ?>
<?php 
/require_once "/path_to_your/cmsAdmin/lib/viewer_functions.php";


Then for adding values to a single field use this format:



$query "INSERT INTO cms_table_name (field_1) VALUES 

(‘field_1_value1'),
(‘field_1_value2’),
(‘field_1_value3’),
(‘field_1_value4’)”;
mysql_query($query) or die("MySQL Error" .mysql_error(). " with query $query\n");

?>


If you wanted to populate 2 fields at the same time, you’d use: 



$query = "INSERT INTO cms_table_name (field_1,field_2VALUES

(‘field_1_value1',‘field_2_value1'),
(
‘field_1_value2’,‘field_2_value2'),
(‘field_1_value3’,‘field_2_value3'),
(
‘field_1_value4’,‘field_2_value4')”;
mysql_query($query) or die("MySQL Error: " .mysql_error(). " with query $query\n");

?> 


NOTE 1: There is no comma after the last set of values.
NOTE 2: Unless you’ve changed the default prefix for your table names, the correct cms_table_name format in the MySQL
query for a table called “articles” would be cms_articles
NOTE 3: If you execute the query more than once, you'll insert more than one set of records into your table.



EXAMPLES:

To Insert individual records for all 50 United State's State names (plus the District of Columbia) into a table called
"states" with fields for State and Country you can use the following MySQL query:

$query = "INSERT INTO cms_states (state,country) VALUES 

('Alabama','UNITED STATES'),
('Alaska','UNITED STATES'),
('Arizona','UNITED STATES'),
('Arkansas','UNITED STATES'),
('California','UNITED STATES'),
('Colorado','UNITED STATES'),
('Connecticut','UNITED STATES'),
('Delaware','UNITED STATES'),
('District of Columbia','UNITED STATES'),
('Florida','UNITED STATES'),
('Georgia','UNITED STATES'),
('Guam','UNITED STATES'),
('Hawaii','UNITED STATES'),
('Idaho','UNITED STATES'),
('Illinois','UNITED STATES'),
('Indiana','UNITED STATES'),
('Iowa','UNITED STATES'),
('Kansas','UNITED STATES'),
('Kentucky','UNITED STATES'),
('Louisiana','UNITED STATES'),
('Maine','UNITED STATES'),
('Maryland','UNITED STATES'),
('Massachusetts','UNITED STATES'),
('Michigan','UNITED STATES'),
('Minnesota','UNITED STATES'),
('Mississippi','UNITED STATES'),
('Missouri','UNITED STATES'),
('Montana','UNITED STATES'),
('Nebraska','UNITED STATES'),
('Nevada','UNITED STATES'),
('New Hampshire','UNITED STATES'),
('New Jersey','UNITED STATES'),
('New Mexico','UNITED STATES'),
('New York','UNITED STATES'),
('North Carolina','UNITED STATES'),
('North Dakota','UNITED STATES'),
('Ohio','UNITED STATES'),
('Oklahoma','UNITED STATES'),
('Oregon','UNITED STATES'),
('Pennsylvania','UNITED STATES'),
('Rhode Island','UNITED STATES'),
('South Carolina','UNITED STATES'),
('South Dakota','UNITED STATES'),
('Tennessee','UNITED STATES'),
('Texas','UNITED STATES'),
('Utah','UNITED STATES'),
('Vermont','UNITED STATES'),
('Virginia','UNITED STATES'),
('Washington','UNITED STATES'),
('West Virginia','UNITED STATES'),
('Wisconsin','UNITED STATES'),
('Wyoming','UNITED STATES')"; 

mysql_query($query) or die("MySQL Error: " .mysql_error(). " with query $query\n");
?>


DRAG SORT QUICKLY TO TOP (OR BOTTOM) OF A FIELD LIST, RECORD LIST OR IMAGE LIST INCLUDING MOVING BY PAGE - Aug 19th, 2022

This one's from Chris Waddell from Interactive Tools:

Maybe this trick is obvious to some, but I've found myself taking forever to move items to the top of a long list by
dragging a page-length, scrolling the page, dragging a page-length, scrolling the page, etc.

Here's a much faster method:

1. Click and hold the item you want to move, and start dragging the item down (or up) in the list until it's between 2
fields. 
2. While continuing to hold the item with the left mouse key, Press the <END>, <PAGE UP>, <PAGE DOWN>, or <HOME> key on
your keyboard (this will scroll the visible part of the list to (near) the new location on the list. 
NOTE:As long as you don't let go of the mouse button, you can combine <END>, <PAGE UP>, <PAGE DOWN>, or <HOME> commands
as many times as necessary to get you near the place you want to drop the field.
3. Make sure that the dragged item is visible at the new location on the list (if necessary, drag it up or down slightly
to make it visible)
4. Drop the item (release the left mouse key).

Being between field locations before using the navigation commands and visibility before letting go seems to be keys,
along with a little practice. 

Dave Edis from Interactive Tools added:

"So that works because the images are listed in an iframe.  So any keyboard shortcuts that jump you to the top/bottom of
a page will work.  

If it's not working in the iframe you can try making sure that you've clicked on something in the iframe first so the
browser knows which page to navigate.  And you can also try ctrl-home, ctrl-end or the mac equivalents.  It may be
limited by the browser, though, but that's what's happening.  Just find keyboard shortcuts for navigating the page."

According to Daniel Louwe, Technical lead at Interactive Tools, 

While it's not currently possible to "Quick Add" in a specific location, as of CMSB v3.52 the section editor does have
an "Insert Field Here" feature. Hovering over a row in the field list will show a "(+)" along the right-hand side.
Clicking this allows you to insert a field at that spot.

CREATING USER FRIENDLY MENUS



USING V1.33 + MENU ENHANCEMENTS - Aug 2nd, 2010

    
These are some really cool enhancements, but it took a bit of playing around to really get the picture.

The first thing that you’ll notice when you upgrade (or install) to v1.33 or above is that when you click on Admin,
instead of an Admin home page that lists all of your admin options, you’ll see an accordion style menu. When you click
on one of these options you’ll get to the associated admin function. Cool you say, but how do I create one of these
for my users?

Enter Menu Groups:
Lets say that you’ve got a series of product categories and each one has it’s own section editor. Up to now, no
matter whether you’re client is thinking of updating one of these products or not, they will see the whole list of
product section editors and may have to scroll past them to find the editor that they really want to modify.

1) log in as Admin, 
2) click on the “Admin” menu group and then on “section editors”.
3) click on “Add New Editor” (a bit counterintuitive, but it works nonetheless. 
4) Instead of creating a new editor, click on the “Advanced Menus” pull down and choose “Menu Group”.
5) Enter the group name you want into the “Menu Name” field. 
6) click on “Create New Menu” and the group heading will appear in your section editor list. 

Now here’s the best part. 

Any existing section editors that are below this heading in the list, (and before the next Group Menu) will
automatically become part of this group, and when you click on the group heading the group will expand and show its
members. 

Even better, any other expanded group will collapse, so there’s less clutter to confuse a user. 

If you combine this ability with the “Indent on menubar” option on the section editor’s “modify” page (which
does exactly what is says) and “Separator” fields that you can add to any section editor, you can really get some
serious organization going.

Now, how about Text Links”.
To quote Dave Edis::” You can now add text links to the menu bar that link to external sites or any url. This makes it
easier to provide links to other applications or services your clients might need and more tightly integrate your system
with CMSB. This is great for adding links back to your company website (for your clients), your support page, and links
to any other control panels or web applications the client might need to access.

So how do I do this? Really simply. 

1) As you did with the Group Menus, go to “Add New Editor”, click on the “Advanced Menus” pull down but this
time choose “Text Link”.
2) In the “Menu Name” field enter the words that you want to appear on the item in the menu list. You could choose
Help with Products, Go to my Web Site, or whatever you want. 
3) When you click on “Create New Menu” the text link will appear in your section editor list. 
4) Click on “Modify” and enter the URL that you want the link to lead to. 
5) Save and then just as you would with other section editors, drag the link item in the section editor list to where
you want it to appear in the menu.

The difference is that when you click on the menu item, it will open  the URL that you specified in a new browser
window. 

So, now you can create  as many “context sensitive” links for your clients as you like.


CHANGING HIDDEN FIELD TO DISPLAY YES/NO INSTEAD OF “0" /"’1" (LEGACY) - May 7th, 2011

Jason from RentItToday asked if it was possible to change the 0 and1 that displays as the hidden field value in the
record list to something more meaningful. (This is incorporated in all current versions)

Dave answered with a resounding “YES”. He said, “ Just make sure you're using a recent version of CMS Builder and
try setting the checked and unchecked value for the "hidden" field in the field editor to “Yes” and “No”
(without the double quotes) under: Admin > Section Editors > Your Section > Hidden 

Sometimes the simplest solutions are not the most obvious.


USING MYSQL QUERIES IN LIST FIELDS - Oct 30th, 2015

For this example, create a multi record section called "rules_list" with only one text field called "rules"

Then create another multi record section with one list field called "test" and choose  "Get options from mySQL Query
(Advanced)" from the list options pull down.

You'll be presented with this generic query as an example. 

SELECT field1, field2
  FROM `<?php echo $TABLE_PREFIX ?>your_table`

field1 is the equivalent of the "Options Value"
field2 is the equivalent of the "Options Label" that will be displayed
your_table is the name of the section to pull these options from.

In this case, the section you'll get your options labels and options values from is the rules_list" table so enter
"rules_list" in place of your_table in the sample code
You'll be getting the Options Labels from the text field called "rules" so enter that as field2 
Your options values will come from the "record number" so enter num as field1.

Your query should now look like this:

SELECT num, rule
  FROM `<?php echo $TABLE_PREFIX ?>rules_list`


If you want to filter your list into groups you can add a new list field called "group" to the "rules_list" table, with
the sample values, Guidelines, Presentation Rules, and Required Entry Materials.

Then you can list only those records where the "group" field contains the text between the % signs below.

This example, we want to list all of the records that have Guidelines selected as the "group" value, so enter "guide"
between the % signs as below

SELECT num, rule
  FROM `<?php echo $TABLE_PREFIX ?>rules_list`
WHERE `group` LIKE '%guide%'


To list only those records that are part of the "Required Entry Materials" group, change the characters between the %
signs to "reqi" or "mater"

Just make sure that the character string is unique to that group.

NOTE There are 2 sets of back ticks (under the tilde character "~") and one set of single quotes used in the code. 


SHOW MULTIPLE FIELD VALUES FOR LIST OPTION LABELS IN A MULTI- VALUE LIST FIELD USING ‘GET OPTIONS FROM MYSQL QUERY’ - Nov 7th, 2017

The task was to show the field values for both the 'presentation_date' and the 'presentation_title' for the option
labels in a multi-value list field that pulled information from the table 'all_presentations'.

In a post on the forum, Daryl Maximo, a programmer at Interactive Tools suggested that I could add two or more field
values for my list option labels by using "Get options from MySQL query (advance)" instead of "Get options from database
(advance)" and use CONCAT() to combine two or more columns.

Here’s the final query code:

SELECT num, CONCAT(DATE_FORMAT(`presentation_date`, '%b %D, %Y'), ' - ', presentation_title)
  FROM `<?php echo $TABLE_PREFIX ?>all_presentations`
WHERE hidden = 0
ORDER BY presentation_date DESC

CHAPTER 2 - VIEWERS

VIEWERS-CODING



IMPLEMENTING CLIENT PREVIEW VIEWERS (LEGACY) - May 7th, 2011

As of Version 2.07, a "preview" function is built in to the program that allows an author to see how their live page
will look before it goed "live". 

For those of you who for some reason are using an earlier version of CMS Builder,  Dave Edis from Interactive Tools had
an interesting and simple workaround.

He suggested adding a "hidden" check box to the editor, and hide the record from the "real" page until you're satisfied
with the contents.

To see a "preview" of the contents, make a copy of your detail viewer and use this option in the record call in the head
section which will ignore the "hidden" flag and show records even if they are hidden:



'ignoreHidden' => true, // don't hide records with hidden flag set


or


'where'    => " hidden = '1' ",




WRAPPING TEXT AROUND AN IMAGE - Dec 25th, 2018

Here’s a simple way to wrap an image in the first few lines of text, like a head shot surrounded by descriptive text
on an about page.

The concept depends on some css to wrap the text around the image.

<style type="text/css">
.pad { border-right: 20px solid transparent;
}
.padr { padding-right:20px;
}
</style>

And in the body:

<div  style="float:left"><div class="padr"  style="float:none"> <?php foreach ($record['head_shot'] as $index =>
$upload): ?><img src="<?php echo htmlencode($upload['thumbUrlPath2']) ?>" width="<?php echo $upload['thumbWidth2'?>"
height="<?php echo $upload['thumbHeight2'?>" alt="" /><?php endforeach ?></div>
</div>
<div><?php echo $record['content'?></div>


You can see a screen shot of the result here: http://www.thecmsbcookbook.com/downloads/image-wrap.jpg


CHANGING METATAG OR OTHER DATA FROM WITHIN CMSB - Aug 2nd, 2010

One key is that this type of information is usually best called from a single record editor. The good news is that you
can simultaneously use information from any combination of editors to populate your web pages.

The first step is to create the fields required for the information.  So say your single record section editor is called
"Pages". You would go into: Admin > Section Editors > Pages and add a new textfield called "Page Title", one called
“Meta Keywords” and a third called “Meta Description”, and save the result and the detail.
Now go to your section editor and enter the values that you’d like to appear in these fields. (Remember to follow the
syntax conventions required for the particular field) 

You'll have tags in your viewer page that look like this: 



<?PHP echo $pagesRecord['page_title']; ?> 
<?PHP echo $pagesRecord['meta_keywords']; ?> and
<?PHP echo $pagesRecord['meta_description']; ?>


In your web page, you would display your field information like this: 


<title><?PHP echo $pagesRecord['page_title']; ?> </title>
<META NAME="KeyWords" CONTENT="<?PHP echo $pagesRecord['meta_keywords']; ?>"> 
<META NAME="Description" CONTENT="<?PHP echo $pagesRecord['meta_description']; ?>"> 


You can use this approach to populate (almost) any html tag information. (I’m sure that there’s one that won’t
work somewhere.)


USING CMSB TO CUSTOMIZE LINK INFORMATION IN A LIST - Aug 2nd, 2010

You can replace the link text and URL in a link, or any other area in an HTML document by placing a PHP call in place of
the area you want to make variable, and creating textfields in a multi record section editor. Here’s an example of
making the text and url in a link variable:


<?PHP foreach ($other_eventsRecords as $record): ?>
                           <p align="left"><a class="special" href="<?PHP echo $record['url'?>"><?PHP echo
$record['organization_name'?></a><br />
                       <?PHP endforeach ?> 
 

 
If you’re going to use this to display a list of links on your web page, don’t forget to remove the: 


 'where'       => whereRecordNumberInUrl(1),
 'limit'       => '1', 


and


$other_eventsRecord = @$other_eventsRecords[0]; // get first record


from the code before the head or you’ll only show a single listing on your page.


FIXING VIEWER SHOWING ONLY THE FIRST RECORD - Aug 2nd, 2010

If you’re only seeing information on the first record and not on subsequent records, or you’re getting “Undefined
index” errors about fields you know exist, you’re not alone.

According to Dave Edis, of Interactive Tools the fix may be really simple. He says:

What may be happening is that the line:


'where'       => whereRecordNumberInUrl(1), 


in the “required” code at the top of the page is trying to load the record number that is on the end of the url:


 list($your_tableRecords, $your_tableMetaData) = getRecords(array( 
    'Table name'   => 'your_table', 
    'where'       => whereRecordNumberInUrl(1), 
    'limit'       => '1', 
  ));


So when the URL ends in 1 (or nothing) that works fine, but when it's another record number, it won't work. Try removing
that ‘where’ line: 

Dave added: You can also, add:



allowSearch => false, 


so later when you add searching capability it won't "pre-filter" those records, since he’s assuming you’ll want to
show them all the time:


 list($your_tableRecords, $your_tableMetaData) = getRecords(array( 
    'Table name'   => 'your_table', 
    'limit'       => '1', 
    'allowSearch' => false, 
  ));


FIXING WEIRD RESULTS IN A MULTI RECORD VIEWER - Aug 2nd, 2010

The placement  of  "foreach" and "endforeach" elements can make a huge difference in how a list of records appears in
your viewer. If your results are not what you expected, before you panic, check the placement of those elements.

For example, if the elements are outside your table, a new table will be created for each record. You can place the
"foreach"elements between a <tr> and a <td>, or anyplace else in the table code, but they're very literal, so everything
between the "foreach" and the "endforeach" will be recreated for each record.:  

If tyhat's not what you had in mind, make sure that the: 

<?PHP foreach ($your_table_nameRecords as $record): ?> and <?PHP endforeach ?>

elements are inside your table. Like this:


 <table>
 <?PHP foreach ($your_table_nameRecords as $record): ?>
 <tr>
<td><div align="left"><span class="your_css" ><?PHP echo date("D, M jS, Y g:i a"strtotime($record['your_first_
field'])) ?></span></div></td>
                               <td><div align="left"><span class="your_css" >Location: <?PHP echo $record['l
your_second_ field'?></span> 
</div>
</td>
</tr><?PHP endforeach ?>
</table>


DISPLAY LINKS ON YOUR WEB PAGE WITHOUT ERRORS - Nov 23rd, 2012

REVISED TO ACCOMMODATE  HTTPS:// WEBSITE URLs

If a link URL is entered into a textfield without an http://, you'll end up with a broken link when you try to follow
that link in you viewer.

Here’s how to make sure that your URL’s work, even if the person entering the information doesn’t enter http://  

Note: The solution in this recipe is not case sensitive. This means that if you’ve entered HTTP:// , or Http://  the
code will still work. 

In this example the code is designed for use with a single field. The field containing the URL is called
web_site_link_url, and the field containing the link text is called web_site_link_text.

Once you understand the concepts involved, to use this for multiple link fields in the same viewer, see USING A FUNCTION
TO ACCOMPLISH THE SAME RESULT, later in this recipe.

An "if" statement is used to check if the URL starts with http:// and if not, it adds one to the beginning of the URL.

 So, for a multi-record list viewer, you’d put the if statement before the link code in the viewer like this:



<?php if(!preg_match("/^https:\/\//i"$record['web_site_link_url'] )):?>
 <?PHP  if  (!preg_match("/^http:\/\//i"$record['web_site_link_url'])) {
 
$record['web_site_link_url'] = "http://" $record['web_site_link_url'];   } ?>

<?PHP endif ?>

<a href="<?PHP echo $record['web_site_link_url'?>"><?PHP echo $record['web_site_link_text'?></a> 


And for a detail viewer:


<?php if(!preg_match("/^https:\/\//i"$yourRecord['web_site_link_url'] )):?>
<?PHP  if  (!preg_match("/^http:\/\//i"$yourRecord['web_site_link_url'])) {
 
$yourRecord['web_site_link_url'] = "http://" $yourRecord['web_site_link_url'];   } ?>
<?PHP endif ?>
<a href="<?php echo $yourRecord['web_site_link_url'?>"><?php echo $yourRecord['web_site_link_text'?></a>


Or if you’re using one of the info fields in an upload to contain the URL:



<?php if(!preg_match("/^https:\/\//i"$upload['info1'] )):?>
<?php if  (!preg_match("/^http:\/\//i"$upload['info1'])) {
 
$upload['info1'] = "http://" $upload['info1'];   } 
?><?php endif ?>

<a href="<?php echo $upload['info1'?>"><img src="<?php echo $upload['thumbUrlPath'?>" width="<?php echo
$upload['thumbWidth'?>" height="<?php echo $upload['thumbHeight'?>" alt="" /></a>


Or for a single record viewer:



<?php if(!preg_match("/^https:\/\//i"$your_tableRecord['web_site_link_url'] )):?>
<?PHP  if  (!preg_match("/^http:\/\//i"$your_tableRecord['web_site_link_url'])) {
 
$your_tableRecord['web_site_link_url'] = "http://" $your_tableRecord['web_site_link_url'];   } ?>
<?php endif ?>
<a href="<?php echo $your_tableRecord['web_site_link_url'?>"><?php echo $your_tableRecord['web_site_link_text'?></a>


PLACEMENT ON THE PAGE
As long as the preg_match PHP code is before the link code, this technique will work. So, for consistency, you might
want to insert all you preg_match PHP code blocks at the top of your <body> code.

HOW IT WORKS 

 The first line is an if statement that surrounds the rest of the regex operations



<?php if(!preg_match("/^https:\/\//i"$record['web_site_link_url'] )):?>


This statement looks for https:// at the beginning of a field called “web_site_link_url“. 

If https:// is not found then the next line is executed.

(The “i” make the expression case insensitive. If you want to make it case sensitive, just remove the “i”)



!preg_match("/^http:\/\//i", $record['web_site'] 


This code looks for http:// at the beginning of a field called “web_site_link_url“. 

(Again, the “i” make the expression case insensitive. If you want to make it case sensitive, just remove the
“i”)

If http:// is not found (! = NOT) then the next line adds it to the beginning of 'web_site_link_url: 



$record['web_site_link_url'] = "http://" . $record['web_site_link_url']

 
This line uses the concatenation operator, which is just a dot (.) The above expression can be thought of like 2 = 1 + 1
or in our case maybe http://www.cnn.com = (http://) + (www.cnn.com

The end if statement <?PHP endif ?>  ends the conditions that must be met.

The last line would go where you want the link to appear, as ling as it's below the if statement.



 <a href="<?PHP echo $record['web_site_link_url'?>"><?PHP echo $record['web_site_link_text'?></a> 


This displays the $record[web_site_link_ur] as the link and the $record['web_site_link_text'] as the link text.

USING A FUNCTION TO ACCOMPLISH THE SAME RESULT
Instead of having to duplicate the code and change the field name, you can use a function that can easily be called for
each occurrence.

Just remember to insert the function before and outside of any foreach loops that require it. 


              <?php function ensure_url($url)
        { if(!
preg_match("/^https:\/\//i"$url ))
         { 
  if (!
preg_match("/^http:\/\//i"$url)) { 
    
$url "http://" $url
  } }
  return 
$url
}
  
?> 


Then, each time you need to check the format of a link, call the function like this:


<?php echo ensure_url($record['your_link_field']) ?>


or



<?php echo ensure_url($your_tableRecord['your_link_field']) ?>


Here's a Regex quick reference to help you to customize your regular expressions to meet your needs

[abc]     A single character: a, b or c
[^abc]     Any single character but a, b, or c
[a-z]     Any single character in the range a-z
[a-zA-Z]     Any single character in the range a-z or A-Z
^     Start of line
$     End of line
\A     Start of string
\z     End of string
.     Any single character
\s     Any whitespace character
\S     Any non-whitespace character
\d     Any digit
\D     Any non-digit
\w     Any word character (letter, number, underscore)
\W     Any non-word character
\b     Any word boundary character
(...)     Capture everything enclosed
(a|b)     a or b
a?     Zero or one of a
a*     Zero or more of a
a+     One or more of a
a{3}     Exactly 3 of a
a{3,}     3 or more of a
a{3,6}     Between 3 and 6 of a

options: i case insensitive m make dot match newlines x ignore whitespace in regex o perform #{...} substitutions only
once


DISPLAYING ONLY CERTAIN CATEGORIES AND/OR ALPHABETICAL LIMITATIONS - Aug 27th, 2010

I had an Artist’s membership database and I wanted to allow searches for specific categories of artists (disciplines)
or limit a search to particular alphabetical criteria.

The disciplines field contained a V" for Visual Artists, "M" for Media Artists, "P" for Performance Artists, "L" for
Literary Artists

Here’s the link code:



http://www.mysite.com/visualartists.php?sort_discipline=V



Which works to search for visual artists when combined with the code at the head of the visualartists.php web page



<?PHP
  require_once "/hsphere/local/home/web/mysite.com/cmsAdmin/lib/viewer_functions.php";

  list(
$artistsRecords$artistsMetaData) = getRecords(array(
    
'Table name'   => 'artists',
// Insert This Code
'where' => "discipline_code LIKE '%" .mysql_real_escape_string(@$_REQUEST['sort_discipline']). "%'",
// End of Insert
 ));
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>



Of course you’ll have to replace the path information with your own. 

NOTE: The word "sort" is not necessary in the link code or $_REQUEST, it's just a visual indicator that you are sorting
by a specific criteria. the request can be anything as long as you're consistent.

If you need to meet multiple parameters, then you could change the link to:



http://www.mysite.com/visualartists.php?sort_letter=A&sort_discipline=V



An extra % after LIKE will match the occurrence of that letter anywhere in the field:



'where' => "last_name LIKE '%" .mysql_real_escape_string(@$_REQUEST['sort_discipline']). "%'",
'where' => "last_name LIKE '%" .mysql_real_escape_string(@$_REQUEST['sort_letter']). "%' AND discipline_code = '"
.mysql_real_escape_string(@$_REQUEST['sort_discipline']). "'",



This format will match the first letter of the fields only...



'where' => "last_name LIKE '" .mysql_real_escape_string(@$_REQUEST['sort_discipline']). "%'",



or



'where' => "last_name LIKE '" .mysql_real_escape_string(@$_REQUEST['sort_letter']). "%' AND discipline_code = '"
.mysql_real_escape_string(@$_REQUEST['sort_discipline']). "'",



For a message regarding no members found, you could add:



<?PHP if (!$artistsRecords): ?> 
<div class="body-text-bold" align="center"><?PHP echo 
 
"Sorry, but no active members were found who match this category.<br /><br />"?> 
<?PHP endif ?><div>





SETTING UP IF/THEN CONDITIONS - Aug 2nd, 2010

This time it was me that was in need of some guidance and Dave Edis from Interactive Tools was able to help.

Here was my situation:
I had set up a multi record section editor and included a pull down list field with a list of categories. IE: “book,
CD, Magazine, etc. 

(BTW, the difference between a simple pull down list and a multi value pull down list is that in a multi value pull down
if you hold the CTL key you can select more than one value)

Here’s what I was trying to do:

If the group_code of the particular record is “book” then I wanted to display field “A”, if it’s “CD”
I’d like to display field “B”, if it’s “magazine” I’d like to display field “C”, and if it’s none of
those I’d like to display field “D”. 

The task is pretty straightforward once you know the correct format to use.

If you’re using it on multi record a list page: 


<?PHP foreach ($pub_category_listRecords as $record): ?>
<?PHP if ($record['group_code'] == "book"): ?> 
  <?PHP echo $record['fieldA'?> 
<?PHP elseif ($record['group_code'] == "CD"): ?> 
  <?PHP echo $record['fieldB'?> 
<?PHP elseif ($record['group_code'] == "Magazine"): ?> 
  <?PHP echo $record['fieldC'?> 
<?PHP else: ?> 
  <?PHP echo $record['fieldD'?> 
  Sorry, couldn't find a match for '<?PHP echo $record['group_code'?>' 
<?PHP endif ?>
 <?PHP endforeach; ?>



And if it’s on a single page or a detail page:



<?PHP if ($pub_category_listRecord['group_code'] == "book"): ?> 
  <?PHP echo $pub_category_listRecord['fieldA'?> 
<?PHP elseif ($pub_category_listRecord['group_code'] == "CD"): ?> 
  <?PHP echo $pub_category_listRecord['fieldB'?> 
<?PHP elseif ($pub_category_listRecord['group_code'] == "Magazine"): ?> 
  <?PHP echo $pub_category_listRecord['fieldC'?> 
<?PHP else: ?> 
  <?PHP echo $pub_category_listRecord['fieldD'?> 
  Sorry, couldn't find a match for '<?PHP echo $pub_category_listRecord['group_code'?>' 
<?PHP endif ?>



You can use this to display anything that you want to, in another situation I’m using it to display different
thumbnail groups for different group codes. Just replace the <?PHP echo $record['fieldn'?> codes with the code that
will display what you want to.

There’s much more on understanding and using if statements later in the Cookbook.


SHOWING HIDDEN RECORDS IN A VIEWER - Aug 2nd, 2010

If you need to show a list of the records that have been hidden using the special ‘hidden’ field, you’ll need to
put a set of ‘where’ statements in the getrecords() block at the top of your viewer page.



list($artistsRecords, $artistsMetaData) = getRecords(array(
'Table name'   => 'artists',
// Insert This Code
 'where'    => " hidden = '0' ", 
'orWhere'    => " hidden = '1' ", 
   ));
// End of Insert



Then you can use the  ‘if’ statements:



 <?PHP if ($record['hidden'] == '1'): ?> and <?PHP if ($record['hidden'] == '0'): ?> 

after your ‘foreach ‘statements to show the records that match either criteria, like this:

<?PHP foreach ($artistsRecords as $record ): ?>
<?PHP if ($record['hidden'] == '1'): ?>
<?PHP echo $record['last_name'?>, <?PHP echo $record['first_name'?>
<?PHP endif ?>
<?PHP endforeach; ?>



I was curious, why is it necessary to use the 'where' statements in the getRecord() block for the "hidden"
field and not necessary for any regular fields?

According to Dave Edis from Interactive Tools:
The "hidden" field is a special field. Anytime it exists CMS Builder automatically adds "WHERE hidden = 0" to the query.
You can see the MySQL query CMS Builder creates by temporarily adding this option below the others:


'debugSql' => true,



You can see a list of other "special" fieldnames here:

      http://www.interactivetools.com/docs/cmsbuilder/special_fieldnames.html

Since the purpose and design of the “hidden” field is to hide those records, when you want to show them instead we
need to do some extra work to make it work. :)

Thanks Dave, it makes sense now


OVERRIDING THE HIDDEN VALUE, PUBLISHDATE & REMOVEDATE TO SHOW RECORDS - Jul 31st, 2013

User In-House-Logic needed to override the hidden value and the publishStart & publishEnd date fields to show records on
a web page.

Greg Thomas from Interactive Tools came to the rescue...

He said:

 If you're using a getRecords function to retrieve your data you can get it to override the hidden and display
before/after dates with these variables:


  // load records from 'blog'
list($blogRecords, $blogMetaData) = getRecords(array(
  'tableName'         => 'blog',
  'loadUploads'       => true,
  'allowSearch'       => false,
  'ignoreHidden'      => true,  // don't hide records with hidden flag set
  'ignorePublishDate' => true,  // don't hide records with publishDate > now
  'ignoreRemoveDate'  => true,  // don't hide records with removeDate < now
));


So these array keys tell the getRecords function to ignore the hidden field, publish and remove dates.


AUTOMATICALLY HIDE AND SHOW RECORDS BY DATE RANGE - Aug 2nd, 2010

You can automatically have records show and be hidden with the special fields 'publishDate' and 'removeDate'. You’ll
want to create date fields for those.

Once that's done, your list should only show records within the date range, but if you only want to show the first
matching record, add this to your viewer code



list($yourRecords, $yourDetails) = getRecords(array( 
  'Table name'   => 'yourTable', 
 // Insert This Code
 'limit'       => '1', 
// End of Insert
));




RESTRICTING VIEWER ACCESS TO LOGGED IN USERS ONLY - Dec 29th, 2018

If you're using a current version of CMSB (2.51+) Greg Thomas from InteractiveTools explains how to restrict access. He
said:

There is a new getCurrentUserFromCMS function you can use to get the current CMS user, and it works if you're not using
the website membership plugin on a site:

<?php if (!defined('START_SESSION')) { define('START_SESSION'true); }
  
// load viewer library
  $libraryPath 'cmsAdmin/lib/viewer_functions.php';
  
$dirsToCheck = array('/path_to_your_server/','','../','../../','../../../');
  foreach (
$dirsToCheck as $dir) { if (@include_once("$dir$libraryPath")) { break; }}
  if (!
function_exists('getRecords')) { die("Couldn't load viewer library, check filepath in sourcecode."); }
  
  
//Get the current CMS users details
  $CMS_USER getCurrentUserFromCMS(); 
  
  
//redirect the browser if no user is currently logged into the back end
  if (!@$CMS_USER['num']){ 
    
redirectBrowserToUrl("http:/your_site.com/cmsAdmin/admin.php?redirectUrl=" $_SERVER['REQUEST_URI']);
    exit;
  }
?>


*** LOG OUT ISSUES ***

Using this approach, you might discover a log out issue, especially If you're trying to restrict access to Admins only,
or show information a page for users and additional information for admins.

If you do, a possible fix is in a companion recipe called LOG OUT NOT LOGGING OUT IN VER 2.51+ ? at 
http://thecmsbcookbook.com/recipedetail.php?475
 
LEGACY (Version 2.1+)
If you surround your "require once" code with the 2 if statements, the page will be hidden from anyone who is not logged
in to the CMS Interface and all they will see is a notice that says "You must login first".




if (!defined('START_SESSION')) { define('START_SESSION', true); }
   require_once "/path_to_your/cmsAdmin/lib/viewer_functions.php";
  if (!@$_SESSION['username']) { die("You must login first!"); }



If you want to redirect your users to the login screen, replace the following line:



if (!@$_SESSION['username']) { die("You must login first!"); }



with this one:



  if (!@$_SESSION['username']) { header("Location: http://mydomain.com/cmsAdmin/admin.php"); exit; }



and replace the URL with the (full) URL to your login screen.

Alternatively, to automatically direct the user back to your "hidden" page, you could use this instead:



if (!@$_SESSION['username']) { header("Location: http://mydomain.com/cmsAdmin/admin.php?redirectUrl=" .
$_SERVER['REQUEST_URI']); exit; }



There are some interesting ways to create forms to "hide" the real location of the login screen that can be found on
this post:

http://www.interactivetools.com/forum/gforum.cgi?post=73920


AUTO LOGOFF WITH REDIRECT - Apr 1st, 2017

In the Website Membership Plugin I had my post_logoff page set to a generic "log-out" page.

I was having trouble automatically logging a user out of the system and then automatically redirecting them to a special
"log-out" page after they updated their profile.

Dave Edis, Senior Guru at Interactive Tools reminded me that the user_logoff() is an internal CMSB function and takes an
optional redirect url as an argument.  

So, in your profile update form, search for:


### Change Password


Before the brace "}" that precedes ### Change Password, insert the code


 ### Automatic Log Off and Redirect
user_logoff("http://www.my_site.com/logout_index.php");


That will execute the logoff and redirect after the profile has been updated.


CREATING A SIMPLE PHP HIT COUNTER - Aug 2nd, 2010

For each page that you want to count hits on, create a text document with the number that you want your counter to start
with, and a unique file name, like: aboutpagecounter.txt and upload it to your site. 

I usually insert the number 500 in my text files, so that my counters will not display a really small number at first,
but you can start them with any number you want to. 

If you have many separate counters, you might want to create a folder for your counter text files.

Then somewhere in the body of the page that you want to count, insert this code, changing the file name and path to suit
your particular situation:

 

<?php
$count_my_page = ("aboutpagecounter.txt");
$hits file($count_my_page);
$hits[0] ++;
$fp fopen($count_my_page "w");
fputs($fp "$hits[0]");
fclose($fp);?>


Then, where you want your hit count to appear, insert this line of code:



<?php echo $hits[0]; ?>


You can, of course style your counter any way you want to.


CREATE A HIT COUNTER USING THE BUILT IN INCREMENTCOUNTERFIELD FUNCTION - Jul 24th, 2020

Using the built in incrementCounterField function, you can create a hit counter for a single record editor or a detail
page of a multi record editor like this:

In the section editor, create a text field called 'hits' (or whatever you want to call it). Then insert this code on
your viewer page.



<?php incrementCounterField('your_table_name''your_field_name'$your_table_name Record['num']); ?>



Or if you don’t want the counter to be incremented when the page is accessed by a particular IP Address (yours for
example), try this recommendation by Chris Waddell from Interactive Tools:



<?php if ($_SERVER['REMOTE_ADDR'] != '127.0.0.1') { incrementCounterField('your_table_name''your_field_name',
$your_tableRecord['num']); } ?>



Wherever on the viewer that you want to display the number of hits, insert this code as you would for any other field. 



<?php echo $your_table_name['your_field_name '?>



LIST PAGE IMPLEMENTATION
For a counter on the list page of a multi-record editor, the process gets a bit more complex. This is because if you
used the approach above in a “foreach” loop, all the counters on all your records would increment each time the list
page was accessed.

Create a table called “Counter” or something similar.

Create a text field for each list page that you want to count. Name the fields something that will be meaningful,
preferably with the same name as the editor where you’ll be implementing the count.

Insert the record call to that table at the top of your viewer:



list($counterRecords, $counterMetaData) = getRecords(array(
    'Table name'   => 'counter',
    'where'       => whereRecordNumberInUrl(1),
    'limit'       => '1',
  ));
  $counterRecord = @$counterRecords[0]; // get first record



Then insert:



<?php incrementCounterField('counter''your_field_name'$counter Record['num']); ?>



Or again, if you don’t want the counter to be incremented when the page is accessed by a particular IP Address (yours
for example), try this version of Chris’s recommendation:



<?php if ($_SERVER['REMOTE_ADDR'] != '127.0.0.1') { incrementCounterField('counter' 'your_field_name',
$counterRecord['num']); } ?>



Into your viewer and:



<?php echo $counter['your_field_name '?>



Where you want the counter to appear.

As an added plus, you can enter any starting number in any of the counter fields and the count will begin from there, so
none of your pages need to show only a small number of hits. 

GOING FURTHER
If you're using your counter to show how many times a particular record's detail page was viewed, you can insert your
counter display into your list page code and have it appear only if an admin has logged in.

Here's how:

Put this code after your load records calls to determine if an admin is logged in.


<?php  $CMS_USER getCurrentUserFromCMS();   ?>


Then, in the body of the viewer, where you want the counter to appear, use this:


<?php if( $CMS_USER['isAdmin'] && $record['your_field_name'] > 0): ?>This page was viewed <?php echo
$record['your_field_name'?> time<?php if($record[your_field_name'] == 1): ?><?php else :?>s<?php endif ?><?php endif
?>


If you wanted to decrement the counter, according to Chris Waddell from Interactive Tools:

There's nothing built-inbut you could copy the coderename it, and change it to do what you want.

Here's an example. Call it with -1 as the fourth parameter to decrement a counter.


function incrementCounterFieldBy($tablename, $fieldname, $recordNumber, $amount = 1) { 
  global $VIEWER_NAME; 
 
  // error checking 
  if (!$tablename)    { die(__FUNCTION__ . ": No 'tablename' value specified!"); } 
  if (!$fieldname)    { die(__FUNCTION__ . ": No 'fieldname' value specified!"); } 
  if (!$recordNumber) { die(__FUNCTION__ . ": No 'recordNumber' value specified!"); } 
 
  // update counter 
  $escapedTableName = mysql_escape(getTableNameWithPrefix($tablename)); 
  $query  = "UPDATE `$escapedTableName` SET `$fieldname` = IFNULL(`$fieldname`,0) + $amount"; 
  $query .= " WHERE `num` = '" .mysql_escape($recordNumber). "'"; 
  $result = @mysql_query($query); 
  if (!$result) { die(__FUNCTION__ . " MySQL Error: ". htmlspecialchars(mysql_error()) . "\n"); } 
  if (!mysql_affected_rows()) { 
    die(__FUNCTION__ . ": Couldn't find record '" .htmlspecialchars($recordNumber). "'!"); 
  } 
 
}



DISPLAYING THE FIRST X CHARACTERS ON A LISTING PAGE - Feb 27th, 2011

SAMSAM92 asked: If, for example I have a field <?PHP echo $record['your_field'?>, how would I show only the first 100
characters?

And the answer found was:

Just add this function at the top of the page:



<?PHP function textLimit($string$length$replacer '...')
{
if(
strlen($string) > $length)
return (
preg_match('/^(.*)\W.*$/'substr($string0$length+1), $matches) ? $matches[1] : substr($string0$length))
$replacer;

return 
$string;


 
?>



and then inside the foreach loop where you want to display your content: 


 
<?PHP echo textLimit($record['your_field'], 100?>



You can change the 100 to any character count that you want.

Note:

If you're using this for a URL or anything with either a "." (period) or a "/" (forward slash) it won't work.

As soon as there's a fix, I'll post it here


DISPLAYING THE FIRST X CHARACTERS IN A WYSIWYG FIELD ON A LISTING PAGE - May 8th, 2011

User Deborah had this challenge. 

She said:
I am (trying to use)  a cutText function to limit the number of characters in a WYSIWYG field. Because the WYSIWYG
content automatically generates a closing </p> tag, I am unable to append a dot leader and "more" link such as this at
the end of the snipped text:



<p>WYSIWYG content blah, blah, blah ... <a href="<?php echo $record['_link']; ?>”>Read More</a>></p>



The first step in using cutText is to define the function in the head section of your viewer.



<?php function cutText($string$setlength) {
$length $setlength;
if(
$length<strlen($string)){
while ((
$string{$length} != " ") AND ($length 0)) {
$length--;}
if (
$length == 0) return substr($string0$setlength);
else return 
substr($string0$length);}
else return 
$string; }
?>



But when Deborah tried to implement the  cutText function and preg_replace to accomplish her goal, it resulted in this
code: 



<?php echo cutText($record['my_field']= preg_replace("|</p>\s*$|i""...</p>"$record['my_field'] ), 90); ?> 



but that did not work consistently.

Chris Waddell from Interactive Tools said:

Using the cutText() function (in  this way) is (sometimes) stripping off the </p>. With no </p>, the preg_replace()
can't find the right place to add the ...

For consistency, I'd recommend stripping the </p> off first, then calling cutText(), then adding your read more link,
and finally replacing the </p> using the following code in the foreach loop: 



<?php 
  $html preg_replace("|</p>\s*$|i"""$record['my_field'] ); 
  
$html cutText($html90); 
  
$html $html "...</p>"
  echo 
$html
?>


MAXWORDS - LIMITING THE NUMBER OF WORDS SHOWN ON A PAGE - Sep 2nd, 2011

If you use the character limiting function that’s exactly what you’ll get, and words can be cut off. But if you want
to limit the word count, Dave Edis from Interactive Tools shares his usual uncanny wisdom:

Insert this function  at the top of your page, or before you want to invoke the word limiting function:



  <?PHP
  function maxWords($textOrHtml$maxWords) { 
  
$text strip_tags($textOrHtml); 
  
$words preg_split("/\s+/"$text$maxWords+1); 
  if (
count($words) > $maxWords) { unset($words[$maxWords]); } 
  
$output join(' '$words); 
 
  return 
$output
  } 
?> 


Then put this code where you want the words limited (to 5 words):



<?PHP  echo maxWords($record['content'], 5); 
?>
If you want to add  ...more and a link, use something like this:

<?PHP  echo maxWords($record['content'], 5); 
?>...<a href="<?php echo $record['_link']; ?>”>Read More</a>


or



<?PHP  echo maxWords($record['content'], 5); 
?>...<a class="read-more" href="http://www.your_site.com/your_detail_page.php?<?php echo $record['num'?>">(Read
More)</a>



To allow certain tags like  or <p> to appear in your text, change the strip_tags code: 



 $text = strip_tags($textOrHtml); 



to  



$text = strip_tags($textOrHtml, '<br />'); 



The function is pretty literal, but it seems to automatically include the closing tag, so include all the flavors of all
the tags (spaces and slashes, slashes with no space, etc.) that you want to include. Like this:



 $text = strip_tags($textOrHtml, '<br /><p>'); 



The strip_tags function is optional and is used to insure that extra tags, spaces, etc are not counted in the word
count. If this is not a great concern (like if you're using text box fields, you can eliminate the line of code
entirely.

WYSIWYG EDITORS
If you’re using a WYSIWYG Editor and are losing some formatting when using the MaxWord function, Jason Sauchuk of
Interactive Tools suggests using this version of the function instead:



 <?PHP  
  function maxWords($textOrHtml$maxWords) { 
    
$text=str_replace("<p>","*P*",$textOrHtml); 
    
$textstr_replace("</p>","*/P*",$text); 
    
$text strip_tags($text);   
    
$words preg_split("/\s+/"$text$maxWords+1);   
    if (
count($words) > $maxWords) { unset($words[$maxWords]); }   
    
$output join(' '$words);   
    
$output=str_replace("*P*","<p>",$output); 
    
$output=str_replace("*/P*","</p>",$output); 
    
$output.="</p>"
 
    return 
$output;   
  }   
?>


If you want to display an alternate link text, or nothing if the field contains less words then you've specified,
according to Jason, you'll first have get the actual number of words from your original string. 

Here' a function he offered that goes through the same process as maxWords, but just returns the count. You'd add it to
your code in addition to the maxWords function:

    

<?PHP  
function wordCount($textOrHtml) {  
  
$text strip_tags($textOrHtml"<b></b><i></i>");  
  
$words preg_split("/\s+/"$text);  
 
  return 
count($words); 
}  
?>


Then in your body code you can use this: (Since my client wanted to control the amount of words allowed in the maxWords
call, I added a field called "words" in a single record editor called "common_information" and defined the variable
$word1 to reflect that value )



<?php $word1 $common_informationRecord['words'?>  
<?PHP echo maxWords($record['description'], $word1); ?> 
<?php if (wordCount($record['description']) > $word1) : ?>...<a href="<?php echo $record['_link']; ?>">Read
More</a><?php endif ?> 


You can also add alternative text or links using an <?php else :?> like this:


<?php if (wordCount($record['description']) > $word1) : ?>...<a href="<?php echo $record['_link']; ?>">Read
More</a><?php else :?> <a href="<?php echo $record['_link']; ?>">Your Text Here</a><?php endif ?> 




REDIRECTING A PAGE IF A CONDITION ISN’T MET - Mar 31st, 2015

According to Dave Edis at Interactive Tools, if you wanted to redirect a page if there were no records in a table,
here’s how:

Just put this at the top of your page, after the get records call, and  before any viewer code is output.



<?PHP 
list($your_tableRecords$your_tableMetaData) = getRecords(array(
    
'tableName'   => your_table',

  ));

  if (!$your_tableRecords) { 
    header("Location: http://www.example.com/newurl.php"); 
    exit; 
  } 
?>


If you want to change this to when a condition is met, take out the ! Which is the “not” operator in PHP.


And, if you wanted to redirect a page if there was only one record in the table, here's how you'd do that:


<?PHP 
list($your_tableRecords, $your_tableMetaData) = getRecords(array(
    'tableName'   => your_table',

  ));

if(
$your_tableMetaData['totalRecords'] == 1)
 {
header("Location: http://www.example.com/newurl.php");
exit;
}
?>


If you wanted to redirect if there was more than one record, just change the == 1 to > 1

______________________________________________

Expanding on this, if you want to redirect and pass one or more variables to the new page, try a variation of this.

In this case there are 2 variables;
group_code which is the value of a field in the portfolio_title_details table, and 
noback which is a way to hide certain code on the new page

Right after the load records calls, insert the following code:


<?php if($portfolio_title_detailsMetaData['totalRecords'] == 1):?>
<?php foreach ($portfolio_title_detailsRecords as $record): ?>
<?php $group_code$record['group_code'?>
<?php endforeach; ?>
<?php header("Location: http://www.your_domain.com/your_new_page.php?group_code=$group_code&no_back=1");
exit;
?>


Then on the new page, define the variables $group_code and $no_back

<?php  $var1$_REQUEST['group_code']; ?>
Use $var1 however you see fit.


and


<?php  $var2$_REQUEST['no_back']; ?>
<?php if (!$var2 == ):?>
Your code to hide...
<?php endif ?>

 


SORTING BY GROUP AND INSERTING GROUP HEADINGS IN A LIST VIEWER - Aug 2nd, 2010

If you’d like to display group headings on your list page here’s one approach:

For this example, a multi-record editor, called “events” has the following fields:

Title (a text field)
Type (a pull down list of “Groups” to minimize entry errors)
Starting Date (a date field) * in the viewer, only the Month, Day and Year are visible
Preview (a short description of the event for the listing page)

In your application, you could add a full description of the event for the details page, images, etc.

The sort is by Type (ASC)ending and then by Start Date (ASC)ending

On the list viewer page the following code appears where you want your list to appear .



<?php
$old_group ''// init blank var.
foreach ($eventsRecords as $record):
$group $record['type']; // load sub-group value from record.
if ($group != $old_group) { // If different from the last sub-group value, print the sub-group name.
echo "<h2>$group</h2>";
}
?>
<a href="<?php echo $record['_link'?>"><?php echo $record['title'?></a>
<?php echo $record['preview'?>
<br /><br />
<?php $old_group $group// retain sub-group name before moving to new record. ?>
<?php endforeach ?>



If you want to use alternate text or graphics to represent your groups you could use the following.

For Text:



<?php
$old_group ''// init blank var.
foreach ($eventsRecords as $record):
$group $record['type']; // load sub-group value from record.
if ($group != $old_group && $record['type'] == "Group 1") { // If different from the last sub-group value, and equals
Group 1, print the sub-group name.
echo 
"<h2>Group 1 text </h2>";}

if (
$group != $old_group && $record['type'] == "Group 2") { echo "<h2>Group 2 text</h2>";}

if (
$group != $old_group && $record['type'] == "Group 3") { echo "<h2>Group 3 text</h2>";}

if (
$group != $old_group && $record['type'] == "Group 4") { echo "<h2>Group 4 text</h2>";}
?>
<a href="<?php echo $record['_link'?>"><?php echo $record['title'?></a>
<?php echo $record['preview'?>
<br /><br />
<?php $old_group $group// retain sub-group name before moving to new record. ?>
<?php endforeach ?>



To display images instead of text for the headings, one approach would be to create a separate single record editor
called “Graphics”  with each image as a separate upload field named Group 1 Graphic, Group 2 Graphic, etc., and use
something like this on the viewer page (don’t forget to add the getrecord call at the top of your page).



<?php $old_group ''?>

<?php foreach ($eventsRecords as $record): ?> 

<?php $group $record['type'];  ?>
 
<?php if ($group != $old_group && $record['type'] == "Group1"): ?> 

<div align="center"><?php foreach ($graphicsRecord['group_1_graphic'] as $upload): ?>
 <img src="<?php echo $upload['thumbUrlPath'?>" width="<?php echo $upload['thumbWidth'?>" height="<?php echo
$upload['thumbHeight'?>" alt='' /><br /><br /></div>
<?php endforeach ?>  <?php endif ?> 

<?php if ($group != $old_group && $record['type'] == "Group 2"): ?> 

<div align="center"><?php foreach ($graphicsRecord['group_2_graphic'] as $upload): ?>
<img src="<?php echo $upload['thumbUrlPath'?>" width="<?php echo $upload['thumbWidth'?>" height="<?php echo
$upload['thumbHeight'?>" alt='' /><br /><br /></div>
<?php endforeach ?>  <?php endif ?> 

<?php if ($group != $old_group && $record['type'] == "Group 3"): ?>
<div align="center"><?php foreach ($graphicsRecord['group_3_graphic'] as $upload): ?>
<img src="<?php echo $upload['thumbUrlPath'?>" width="<?php echo $upload['thumbWidth'?>" height="<?php echo
$upload['thumbHeight'?>" alt='' /><br /><br /></div>

 <?php endforeach ?>  <?php endif ?> 



SORTING BY GROUP AND INSERTING GROUP HEADINGS IN A LIST VIEWER (ANOTHER APPROACH) - Mar 23rd, 2012

This approach seems to be a bit easier to implement but it only worked with a bit of help from Jason Sauchuk, from
Interactive Tools.

I wanted to allow for an expanding list of categories (actually board of director positions in an organization) that
could be managed by my client from the management interface, without resorting to any coding changes.

The first thing I did was to create a multi-record editor called board_of_director_positions with only one text field
called position. I set the sort to dragSortOrder DESC so that the positions would appear in the correct order.

Then, in the accounts editor, since some board members held 2 positions, I populated 2 single value list fields
(board_of_director_position_1 and board_of_director_position_2)  using the board_of_director_positions table as the
source, the num field for the option values and the position field for the label values.

Then I used array_pluck after the list records call to create a variable containing a list of the positions so that I
could display their names in my viewer 


<?php

 list($accountsRecords$accountsMetaData) = getRecords(array(
    
'tableName'   => 'accounts',

  ));

 list(
$board_of_director_positionsRecords$board_of_director_positionsMetaData) = getRecords(array(
    
'tableName'   => 'board_of_director_positions',
   ));

  
$directorposition array_filter(array_pluck($board_of_director_positionsRecords'position')); ?>


In the body of the viewer, I looped through and listed all of the positions, and then looped through all of the accounts
editor records for matches to those positions.

After much consternation, Jason suggested adding the pseudo field :label to the accounts record fields, since otherwise
I was trying to compare a record number to a text label. And that popped the office holders names into their correct
slots.



?php foreach ($directorposition as $position): ?> 

<h3 class="your_class_1"><?php echo strtoupper($position); ?></h3>

<?php foreach ($accountsRecords as $record): ?>

<?php if ($record['board_of_director_position_1:label'] == $position || $record['board_of_director_position_2:label'] ==
$position) : ?>

<span class="your_class_2"><?php echo strtoupper($record[full_name']); ?></span>   
<?php endif ?>
                                             
<?php endforeach ?> <hr />   
<?php endforeach ?> 


This concept is easily applied to something like an FAQ page where there are multiple categories and multiple questions
in each category.

Create a multi-record FAQ CATEGORIES editor with one text field called category, a dragSortOrder field and an optional
hidden checkbox.
Create a multi-record FAQ editor with a single value pull down list field populated using the faq_categories table as
the source, the num field for the option values and the position field for the label values.

Then, as above, I used array_pluck after the list records call to create a variable containing a list of the categories
so that I could display their names in my viewer 



  $faqgroup = array_filter(array_pluck($faq_categoriesRecords, 'category'));



Again as above, In the body of the viewer, I looped through and listed all of the categories, and then looped through
all of the FAQ editor records for matches to those categories.


<?php foreach ($faqgroup as $group): ?> 
<h1 class="your_class_1"><?php echo strtoupper($group); ?></h1>
<?php foreach ($faqRecords as $record): ?>
<?php if ($record['category:label'] == $group) : ?>
<?php $question = htmlspecialchars($record['question']); ?> <span class="your_class_2"><?php echo strtoupper($question);
?></span>
<div align="left" class="your_class_3"><?php echo maxWords($record['answer'], 25); ?>
<?php if (wordCount($record['answer']) > 25) : ?><a class="special" href="<?php echo $record['_link']; ?>"><span
class="body-text-bold-10">... read more</span></a><?php endif; ?>
</div>   
<?php endif ?>
                                             
<?php endforeach ?>
<hr />   
<?php endforeach ?> 


Since I'm using a detail page for the complete answersI included functions called maxWords and wordCount to show a
"read more" link only if there were more than 25 words in the answerThese functions are defined in the head section of
my viewerwith:



<?PHP
function maxWords($textOrHtml$maxWords) {
$text strip_tags($textOrHtml);
$words preg_split("/\s+/"$text$maxWords+1);
if (
count($words) > $maxWords) { unset($words[$maxWords]); }
$output join(' '$words);

return 
$output;
}
?>

<?PHP
function wordCount($textOrHtml) {
$text strip_tags($textOrHtml"<b></b><i></i>");
$words preg_split("/\s+/"$text);

return 
count($words);
}
?>


This works well and allows for discontinuous category entries in the record list, but I'd like to be able to skip
categories headings with no record matches.

Any ideas? Pass them on through the contact page. 


ALTERNATING THE BACKGROUND COLOR OF TABLE ROWS AND MORE - Jun 30th, 2012

Markr wanted to know how to automatically alternate between 2 background colors in a table.

Dave Edis, from Interactive Tools had an effective approach as always, he said:

Here's some code to do that:



<?php $bgColor = (@$bgColor == '#F8F8F8') ? '#EFEFEF' '#F8F8F8'?>

Then you can display it like this: <?php echo $bgColor ?> 



and the result will be:



<table>
<tr>
<th>title</th>
<th>name</th>
<th>phone</th>
<th>email</th>
</tr>
<?php foreach ($contact_listRecords as $record): ?>
<?php $bgColor = (@$bgColor == '#F8F8F8') ? '#EFEFEF' '#F8F8F8'?>
<tr bgcolor="<?php echo $bgColor ?>">
<td><?php echo $record['title'?></td>
<td><?php echo $record['name'?></td>
<td><?php echo $record['phone'?></td>
<td><a href="mailto:<?php echo $record['email'?>"><?php echo $record['email'?></a></td> 
</tr>
<?php endforeach; ?>
</table> 



A variation on this theme might be to alternate the float of images in listings on a list page, like this:


<table width="100%" border="0" cellpadding="0">
<?php foreach ($your_tableRecords as $record): ?>

<?php $float = (@$float == 'left') ? 'right' 'left'?>
<?php $pad = (@$pad == 'padl') ? 'padr' 'padl'?>
<tr>
<td >
<div class="<?php echo $pad ?>" style="float:<?php echo $float ?>"><div style="float:none"> <?php foreach
(
$record['image_1'] as $upload): ?><img src="<?php echo $upload['thumbUrlPath'?>" width="<?php echo
$upload['thumbWidth'?>" height="<?php echo $upload['thumbHeight'?>" alt='' /><?php endforeach ?></div></div> <div
align="center" style="text-align:left"><?php echo strtoupper($record['headline']); ?><?php
<?php  echo $record['content'); ?></div></td>
</tr>

<?php endforeach; ?>  
</table>


Note: the css style for the classes "padl" and "padr" creates appropriate padding around the images like this: 


.padl { border-right: 20px solid transparent; border-left: 20px solid transparent; border-bottom: 10px solid
transparent; }
.padr {  border-left: 20px solid transparent; border-bottom: 10px solid transparent; }    


       


SHOW THE FULL NAME OF THE AUTHOR ON A WEB PAGE - Jul 21st, 2011

In earlier versions it used to be a pretty involved process to display the author’s name in a listing. Now it’s as
simple as adding one line of code to the body of your page where you want the Full Name to appear. 

For a list page,  just add:



<?php echo $record['createdBy.fullname'?>

In the foreach loop.

And for a detail page, it’s:



<?php echo $yourtableRecord['createdBy.fullname'?>

According to Dave Edis:

It loads the record of the "owner" from createdByUserNum and adds all those fields with a "createdBy." prefix. 

If, like me you need to have a Last Name and a First Name for sorting and display purposes, you can add the 2 fields
"first_name" and" last_name" to the "User Accounts" editor and then modify the createdBy code to: 



<?php echo $record['createdBy.first_name'?>&nbsp;<?php echo $record['createdBy.last_name'?>



or



<?php echo $yourtableRecord['createdBy.first_name'?>&nbsp;<?php echo $yourtableRecord['createdBy.last_name'?>



You can now display and sort your "User Account" record list by the new "last name" and "first_name" fields and even
delete the "fullname" field from the "User Accounts" table if you don't feel you will ever need it for anything. Just
don't forget to enter the new information before you delete the "fullname" field.

NOTE: You can also use this technique in the LisPage fields that show at the top of your records lists in a multi-record
editor to show the author's name instead of the createdByUserNum


DISPLAY DATA FOR CURRENT USER - Dec 29th, 2010

OK,

Let's say that you want to display some information from the current user's account record in your viewer.

For example: for user Jo Jones... 

Welcome Joe Jones,

It's pretty simple to accomplish using $CURRENT_USER 

If you have a field called "last_name" and another called "first_name" in your user account section, (If not you might
consider creating them to replace the existing "fullname" field. It's not used by CMS Builder, so you can safely delete
the "fullname" field unless you're using it in a viewer)



Welcome <?php echo $CURRENT_USER['first_name']; ?>&nbsp;<?php echo $CURRENT_USER['first_name']; ?>,



Will display the user's first name, followed by a space and then their last name followed by a coma.

According to Jason Sauchuk from Interactive Tools:

You can display any field other then an Upload field using $CURRENT USER

To display an upload field you'll first need to use the getRecords function to select the record for that user. 

You can get the userNumber like this:

$CURRENT_USER['num']


DISPLAY INFORMATION FROM MORE THAN ONE SECTION IN A SINGLE VIEWER - Aug 2nd, 2010

Setting up a page to display information from multiple sections is pretty easy once you get the hang of it. But it can
be somewhat confusing at first.

Here’s the approach for displaying information on a home page from information that’‘s in a single record editor
called “homepage”  and other information from a multi record editor called “listings”. 

First you’ll need to set up the top of your page with getRecords calls to the two sections. (You can copy the actual
code from the code generated in the admin area of the CMSB interface)



<?php
  
  require_once "/your path/cmsAdmin/lib/viewer_functions.php";

list(
$homepageRecords$homepageMetaData) = getRecords(array(
    
'Table name'   => 'homepage',
    
'where'       => whereRecordNumberInUrl(1),
    
'limit'       => '1',
  ));
  
$homepageRecord = @$homepageRecords[0]; // get first record


 list($yourlistingsRecords$yourlistingsMetaData) = getRecords(array(
    
'Table name'   => 'yourlistings',
   
  ));

?>



Then in the body where you want to display the home page information



<?php echo $homepageRecord['your_first_field'?><br />
<?php echo $homepageRecord['your_second_field'?><br />
<?php echo $homepageRecord['your_third_field'?>



and in the body where you want to display the listings records:



<?php foreach ($yourlistingsRecords as $record): ?>
<?php echo $record['your_first_field'?><br />
<?php echo $record['your_second_field'?><br />
<?php echo $record['your_third_field'?>
<?php endforeach; ?>



There’s no formatting in the above example, but it should get you started.

This concept can be expanded to display information pulled from any number of sections. 

If you find you’re getting errors when you try to display items from a single record editor on multi record detail
pages, you may need to remove the:



 'where'       => whereRecordNumberInUrl(1),



from the get records call so that you can display them.

There are situations where you may have to remove the:



 'limit'       => '1',



as well.


FORCING CHARACTERS IN A VIEWER TO DISPLAY IN A PARTICULAR CASE - Aug 2nd, 2010

If you want to force characters entered in a field to display as UPPERCASE when rendered a viewer, here’s a quick
solution.

First define a variable before it’s called. (A field called “title” in this example).

For a list page:



<?php $title htmlspecialchars($record['title']); ?>



Or for a detail page:



<?php $title = ($your_tableRecord['title']); ?>



You can also add modifying code like:



<?php $title htmlspecialchars($your_tableRecord['title']); ?>





Then when you display the characters AS UPPERCASE you can use this code:



<?php echo strtoupper($title); ?>



This approach will work with strtolower as well if you want to force lower case.

For many complex php functions, you can just wrap the function call around the "value". In this case, it looks like
this:




<?php echo strtoupper(date("D, M jS, Y"strtotime($e_blastRecord['press_release_publish_date']))) ?>



And for cases when it's getting too complicated, you can break the components up into multiple lines and use variables
so it's easier to read:



<?php 
  $time strtotime$e_blastRecord['press_release_publish_date'] ); 
  
$date date("D, M jS, Y"$time); 
  
$uppercaseDate strtoupper$date ); 
  echo 
$uppercaseDate
?>



The approaches will also work in an RSS feed if you insert the “display” code inside the appropriate tags (<title>,
<description>, etc.).

There are other strto functions that may prove interesting. They are listed at:

      http://us.php.net/manual-lookup.php?pattern=strto

There are also the functions ucwords and ucfirst that are worth checking out to force your output to display Initial
Caps for each word or only the first word in a field.

After defining your variable as above, you could use something like this to force your “title” field to display
Initial Caps:



<?php echo ucfirst(strtolower($title)); ?>



Or this to make each word capitalized:



<?php echo ucwords(strtolower($title)); ?>



If you’d rather implement a CSS solution, try the CSS function text-transform. It comes in 3 flavors Capitalize, which
will capitalize the first letter in each word, lowercase, which forces all characters to lower case, and uppercase,
which forces all characters to upper case. There’s also inherit and none, which is the default.


PREVIOUS/NEXT PAGE LINKS ONLY IF DESIRED IMAGES PER PAGE COUNT IS EXCEEDED - Feb 19th, 2013

IF UPLOAD FIELDS ARE IN SEPARATE RECORDS

I wanted to display previous and next links on a page but only if there were more than a user specified number of image
records available. 

First I created a text field called thumbnails_per_page  in the single record editor that I call common_information,
where the client could enter the maximum amount of images to allow per page.

Then,  at the top of the viewer page I defined a variable called $maximages, and added a ‘perPage’ to the “list
records” call using that variable, like this:



 $maximages = ($common_informationRecord['thumbnails_per_page']);
 list($portfolio_imagesRecords, $portfolio_imagesMetaData) = getRecords(array(
    'Table name'   => 'portfolio_images',
    'perPage' => $maximages,
    
  ));



NOTE: the variable definition has to be outside the list records call and there are no single or double quotes around
$maximages in 'perPage' => $maximages, or the variable won’t work. 

I also found it necessary to remove the:



 'where'       => whereRecordNumberInUrl(1), 



from the “list records” call that listed the common_information records.   
   
To generate the Prevous/Next links I used this code in the body of the page:



    <?php if ($portfolio_imagesMetaData['totalPages'] > 1): ?>
  
This is Page <?php echo $portfolio_imagesMetaData['page'?> of <?php echo $portfolio_imagesMetaData['totalPages'?>
                     
<?php if ($portfolio_imagesMetaData['nextPage']): ?> 
<br /><a href="<?php echo $portfolio_imagesMetaData['nextPageLink'?>">Click Here to See More of My Work.
                    
</a> 
                   
<?php endif ?>
<?php if ($portfolio_imagesMetaData['prevPage']): ?> 
<br /><a href="<?php echo $portfolio_imagesMetaData['prevPageLink'?>">Click Here to See My Previous Portfolio
Page.</a> 
                    
<?php endif ?> 
<?php endif ?>



IF ALL UPLOADS ARE IN A SINGLE UPLOAD FIELD IN THE SAME RECORD
If you want to use the same previous/next concept in a detail page where all of your uploads are in a single upload
field, the concept changes a bit since the perPage feature only works with records, not uploads within a record.
(Pointed out by Chris Waddell from Interactive Tools)

Here’s my implementation of his recommendation from http://www.interactivetools.com/forum/gforum.cgi?post=75239 on
paginating the display of uploaded images:

The plain vanilla implementation described in the post, for 6 images per page from a table called your_table is:



<table>
<tr>
<?php  
$photosPerPage 6;  
$photoPage = @$_REQUEST['photoPage'] ? $_REQUEST['photoPage'] - 0;  
$firstIndex $photoPage $photosPerPage;  
if (
$firstIndex sizeof($your_tableRecord['images'])-|| $firstIndex 0) { $firstIndex 0$photoPage 0; } 
$lastIndex min($firstIndex $photosPerPagesizeof($your_tableRecord['images'])) - 1;  
foreach (
range($firstIndex$lastIndex) as $photoIndex):  
$upload $your_tableRecord['images'][$photoIndex]  
?>  
<td align="center"><div class="pic"><a href="<?php echo $upload['urlPath'?>" rel="enlargeimage::click"
rev="photoload"><img src="<?php echo $upload['thumbUrlPath'?>" alt='' width="<?php echo $upload['thumbWidth'?>"
height="<?php echo $upload['thumbHeight'?>" align="middle" class="displayed" /></a></div></td>  
<?php $maxCols=3; if (@++$count $maxCols == 0): ?>  
</tr><tr>  
<?php endif ?>  
<?php endforeach; ?>  
</tr></table>  
<p><br />
<form action="?num=<?php echo $your_tableRecord['num'?>" method="post"> 
<?php if ($firstIndex 0): ?>  
<a href="?photoPage=<?php echo $photoPage?>&=<?php echo $your_tableRecord['num'?>">&lt;&lt; Click/Tap for previous
page</a>  
<?php endif ?>  
Page <input type="text" name="photoPage" value="<?php echo($photoPage 1); ?>" style="width: 25px;" /><input
type="submit" value="Go" /> of <?php echo(ceil(sizeof($your_tableRecord['images']) / $photosPerPage)); ?>  
<?php if ($lastIndex sizeof($your_tableRecord['images']) - 1): ?>  
<a href="?photoPage=<?php echo $photoPage+2?>&=<?php echo $your_tableRecord['num'?>">Click/Tap for next page
&gt;&gt;</a><br />  
<?php endif ?> 
</form>


I was implementing this on a detail page that also uses Dynamic Drive’s Thumbnail Viewer II (described in another
recipe) to display a grid of thumbnail images and an enlarged image on thumbnail rollover, so it may look a bit
different then the code you’ll end up with.

I also used a number of variables in a table called “common_information” to allow my client to modify the number of
images per page, the number of columns and rows displayed and some of the messages required for effective operation of
the pages.

At the top of the page:



<?php header('Content-type: text/html; charset=utf-8'); ?>
<?php
  
  require_once "/path_to_your/cmsAdmin/lib/viewer_functions.php";
  

  list(
$common_informationRecords$common_informationMetaData) = getRecords(array(
    
'tableName'   => 'common_information',
    
'allowSearch'=> false,
    
'limit'       => '1',
  ));
  
$common_informationRecord = @$common_informationRecords[0]; // get first record
  
 
  // load records
  list($bookRecords$bookMetaData) = getRecords(array(
    
'tableName'   => 'book',
    
'where'       => whereRecordNumberInUrl(1),
    
'limit'       => '1',
    
  ));
  
$bookRecord = @$bookRecords[0]; // get first record
  

?>



Note the 'allowSearch'=> false, which keeps the common_information getRecords call from searching on the record number
that’s appended to the URL when you go from the list page to the detail page.

And in the body:



<table width="975" border="0" align="center" cellpadding="5">
  
  <tr>
    <td width="474" valign="top"><div align="center"><br /><?php if ($bookRecord['images']): ?><span
class="body-text-bold-9">
    <?php echo $common_informationRecord['book_image_thumbnails_instructions'?></span><?php endif ?> <br /> <br />
        </div>
     </td></tr></table>

<table width="950" border="0" align="center">
  <tr>
    <td valign="top">
         <?php  
$photosPerPage $common_informationRecord['images_per_book_page'];  
$photoPage = @$_REQUEST['photoPage'] ? $_REQUEST['photoPage'] - 0;  
$firstIndex $photoPage $photosPerPage
$lastIndex min($firstIndex $photosPerPagesizeof($bookRecord['images'])) - 1;  
?>     

<?php if ((ceil(sizeof($bookRecord['images']) / $photosPerPage)) > 1): ?><form action="?num=<?php echo
$bookRecord['num'?>" method="post"> 
<?php if ($firstIndex 0): ?>  
<a href="?photoPage=<?php echo $photoPage?>&=<?php echo $bookRecord['num'?>"><?php echo
$common_informationRecord['previous_message'?></a>  
<?php endif ?>  

<span class="body-text-bold-9">Page  <input type="text" name="photoPage" value="<?php echo($photoPage 1); ?>"
style="width: 25px;" /> of <?php echo(ceil(sizeof($bookRecord['images']) / $photosPerPage)); ?></span>

<?php if ($lastIndex sizeof($bookRecord['images']) - 1): ?>  <input type="submit" value="Go" />
<!-- NOTE: You can use  <input type="submit" value="" /> above if you want the word Go to disappear -->
<a href="?photoPage=<?php echo $photoPage+2?>&=<?php echo $bookRecord['num'?>"><?php echo
$common_informationRecord['next_message'?></a><br />  
<?php endif ?> 
</form> 
<?php endif ?>    
    
    <table  border="0" cellspacing="3" cellpadding="3"> 
<tr>
  

<?php

if ($firstIndex sizeof($bookRecord['images'])-|| $firstIndex 0) { $firstIndex 0$photoPage 0; } 
 
foreach (
range($firstIndex$lastIndex) as $photoIndex):  
@
$upload $bookRecord['images'][$photoIndex]  
?>  


<td align="left"  valign="bottom">

<a title="<span class=&quot;body-text-9&quot;><?php echo $upload['info1'?></span>" href="<?php echo
$upload['thumbUrlPath2'?>"" 

rel="enlargeimage"

rev="targetdiv:loadarea" > <img src="<?php echo $upload['thumbUrlPath'?>" alt=""  width="<?php echo
$upload['thumbWidth'?>" height="<?php echo $upload['thumbHeight']?>" border="0" style="margin-bottom: 5px" /> </a>

</td>
<?php $maxCols=3; if (@++$count $maxCols == 0): ?></tr><tr><?php endif; ?>
<?php endforeach ?>
</tr>
</table>

<p></td>
    <td width="400"><table border="0" cellpadding="5" cellspacing="0" width="25%">
          
<tr>
<td align="left" valign="top" ><div align="left">
             
     
<div align="center" id="loadarea"  style="width: 400px; height: 500px;">
   
        <?php $upload = @$bookRecord['images'][$firstIndex?>
             
              <img src="<?php echo $upload['thumbUrlPath2'?>" alt="" border="0" /> <br /> <br />
            
            
</div> </td>
    
  </tr>
</table></td>
  </tr>
</table>


NOTE: if you want the page number to be in a transparent box instead of the default white box, add this code to your css
style sheet.


input, select { background-color:transparent; border:none; } 


SHOWING A CLICKABLE PAGE COUNT ON A PAGE ALONG WITH THE PREV & NEXT LINKS - Aug 2nd, 2010

User Equinox69 noted that the default CMSB code produces this at the bottom of the pages:



<< prev - page 1 of 5 - next >>



He asked, “How can I make it offer a clickable page number, like:



<< prev - page 1 of 5 - next >> go to page #__



or



<< prev - page 1 - next >> 1, 2, 3, 4, 5



so that the end users can click to the page they want or enter in the page number they want?”

Damon from Interactive Tools answered with:

Here is some code to generate this type of pagination:



<< prev 1 2 3 4 next >>



Just change $your_table to your actual table name



<!-- START PAGINATION CODE --> 
 <?php if ($your_tableMetaData['prevPage']): ?> 
   <a href="<?php echo $your_tableMetaData['prevPageLink'?>">&lt;&lt; prev</a> 
 <?php else: ?> 
   &lt;&lt; prev 
 <?php endif ?> 
  
 <?php  
    if (@!$_GET['page']): $current_page "1"
    else: 
$current_page $_GET['page'];    
  endif;  
?> 
  
<?php foreach (range(1,$your_tableMetaData['totalPages']) as $page): ?>  
  <?php if ($page == $current_page): ?> 
     <strong><?php echo $page?></strong> 
   <?php else: ?> 
     <a href="?page=<?php echo $page?>"><?php echo $page?></a> 
   <?php endif ?> 
<?php endforeach; ?> 
 
 <?php if ($your_tableMetaData['nextPage']): ?> 
   <a href="<?php echo $your_tableMetaData['nextPageLink'?>">next &gt;&gt;</a> 
 <?php else: ?> 
   next &gt;&gt; 
 <?php endif ?> 
<!-- /END PAGINATION CODE -->




NAVIGATING TO A PREVIOUS BROWSER PAGE USING JAVASCRIPT - May 8th, 2011

If you have a situation where the usual previous/next buttons won't work, like where different list pages are using the
same detail page, Chris Waddell from Interactive Tools suggests that you try employing this simple JavaScript link to go
back to the previous page in the browser history.

 


<a class="your_class" href="javascript:history.go(-1)">Back</a>



To go back 2 pages in the browser history, you can change the number from -1 to -2, however it may not work in some
browsers, like IE8 where it will only go back one page regardless.

If anyone has a more universal solution, let me know and I'll update this recipe.


DISPLAYING RECORDS STARTING FROM RECORD “N” - Aug 2nd, 2010

User zakcoop wanted to know if there was “..something that I should use on the News Archive page to (automatically)
pull news records, starting at the 6th record?” ( The main news page showed the first 5 records.) 

User sagentic reminded us that sometimes there are really simple solutions to seemingly difficult issues. He said,
“Add 'offset' => 4, to your top of page code to get to start at the 6th record. (record numbering starts at 0)”

Something like this:



list($newsRecords, $newsMetaData) = getRecords(array(  
    'Table name'   => 'news',  
    'offset' => 4,


Thanks Kenny.


AUTOMATICALLY CREATING ACTIVE LINKS FROM URLS IN TEXT BOX FIELDS USING THE BUILT IN HTMLPURIFY LIBRARY - Dec 29th, 2018

If you include a URL that contains http in a forum post, that URL is automatically converted to a live link.

I wanted to implement this in the recipes in my CMSB Cookbook, but couldn't figure out how until Daniel Loewe from
Interactive Tools came up with an answer.
According to Daniel, there's a PHP library called HTML Purifier (http://htmlpurifier.org/) built in to the CMSB code
that does much of the heavy lifting.

Here are 2 basic implementations of this function. The text for these examples is pulled from a text box called
$table_of_contentsRecord['recipe'].

1) for CMSB pages using the built in library.


<?php $recipe = ($table_of_contentsRecord['recipe']); ?>
<?php
$config = array();
$config['AutoFormat.Linkify'] = true;

$recipe htmlPurify($recipe$config);
echo 
$recipe;
?>


2) for code that can be used outside of CMSB:

<?php $recipe = ($table_of_contentsRecord['recipe']); ?>
<?php
$recipe preg_replace("|(https?://[\w\-@:%\+.~#?&//=]+)|"'<a href="\\1">\\1</a>'$recipe); 
echo 
$recipe;
?>


For a more complex implementation of htmlPurify have a look at 
http://www.thecmsbcookbook.com/recipedetail.php?324


CONVERTING THE CMSB COOKBOOK RECIPES TO THE ON-LINE VERSION - Jan 8th, 2023

Seems like this should be an easy task...Use a “textarea” to display all of the code snippets in the recipes as
code, indent any code snippets,  and I’m done. HA! 

It turned out that it would have been an amazingly complex task to tag all of the code and URL snippets and when I
tried, I kept running into blank walls.

I decided to use what Jason Sauchuk, a Interactive Tools programmer discovered as an approach. As part of the process,
he suggested using the PHP function “highlight_string” to convert the entire recipe into text with highlighted code
segments.

Note: To change the default colors for the various code types in highlight_string to black I inserted the following in
the viewer code.


  <?php
ini_set("highlight.comment""#000");
ini_set("highlight.default""#000");
ini_set("highlight.html""#000");
ini_set("highlight.keyword""#000;");
ini_set("highlight.string""#000");
?>


Here’s the system I developed:

1) To identify the line breaks that I ultimately wanted to show in the recipe, I did a search and replace on my recipe
code for any of the three variants of a line break z-x, z-x with a /, and z-x with a space and a /, ( no dashes) and
replaced them all with the characters “z followed by x” (no spaces). I could have used any unique character group
that wasn’t likely to show up in the recipe.

2) Then in the viewer I used preg_replace to strip all remaining line breaks in the recipe.

3) And a preg_replace to convert all occurrences of the characters “z followed by x” (no spaces) back to z-x (no
dash)

4) I used the wordwrap function to limit the length of the lines displayed in the recipe to 120 characters.

5) The code snippet indicators q followed by z followed by l,  and l followed by z followed by q, (no spaces) were all
manually inserted into the recipes around any code that I wanted to display as code.

6) I created a css style called "snippet" to style the code snippets and one to style any <code> tags

7) I used str_replace to place blockquote tags around the code snippets

In December of 2018 I wanted to automatically turn any complete URLs that were outside of any code blocks in the recipe
into clickable links, while leaving those inside of any code blocks alone. 

For that, Daniel Loewe from interactive Tools suggested using the htmlPurify function built in to CMSB. He was also
extremely helpful in working out the code to separate the code blocks from the text. 

Here’s the code we finally came up with:

*** Please note, since the cookbook code uses all instances of q-z-l and l-z-q (without the dashes), and all instances
of z-x (without the dash) to as replacement keys, you MUST remove the dashes between those letters below for the code to
work*** 

For the css style sheet:


code {
font-family: Verdana, sans-serif; color: #000000; font-size: 1em; font-weight:normal;
}
.snippet {
font-family: Verdana, sans-serif; color: #000000; font-size: 1em; font-weight:normal;
 margin: 0px 0px 0px 15px;
    padding: 8px 12px;
    border: 1px dashed #305555;
    background-color: #54ccf2;
    display: block;
    overflow-x: auto;
}


For the body of the full cookbook viewer: 


  <?php
ini_set("highlight.comment""#000");
ini_set("highlight.default""#000");
ini_set("highlight.html""#000");
ini_set("highlight.keyword""#000;");
ini_set("highlight.string""#000");
?>
                      <table align="center" width="75%" border="0" cellpadding="0">
                        <?php foreach ($table_of_contentsRecords as $record): ?>
                        <tr>
                          <td align="left" valign="top"><!--remove all remaining line breaks from the code not converted
to <br /> in wordprocessor-->
                            
                            <?PHP $record['recipe'] = preg_replace("[]"""$record['recipe'] );  // insert <b-r>
(remove the dashbetween [ and ] for the code to work ?>
                            <?PHP $record['recipe'] = preg_replace("[]"""$record['recipe'] );  // insert <b-r/>
(remove the dashbetween [ and ] for the code to work ?>
                            <?PHP $record['recipe'] = preg_replace("[]"""$record['recipe'] );  // insert <b-r />
(remove the dashbetween [ and ] for the code to work ?>
                            
                            <!--replace the valid <br in the code segment -->
                            
                            <?PHP $record['recipe'] = preg_replace("[z-x]i"""$record['recipe'] ); // insert <b-r />
(remove the dashbetween " and ", and remove the dash between z and for the code to work ?> 
                            <?PHP
$newUnixTime  strtotime($record['createdDate']); // seconds since 1970 to createdDate 
$updateUnixTime  strtotime($record['updatedDate']); // seconds since 1970 to updatedDate
$pastUnixTime strtotime(date('Y-m-d'strtotime("-30 day")));
$newdifference $newUnixTime $pastUnixTime ;
$updatedifference $updateUnixTime $pastUnixTime ;
?>
                            <?php if (!$record['recipe'] ): ?>
                            <h2 align="center" class="Heading-Black">
                              <?php $topic htmlspecialchars($record['topic']); ?>
                              <?php echo strtoupper($topic); ?></h2>
                            <?php else: ?>
                            
                            <hr color="#000000" align="center" width="600" />
                            
                            <!-- Check For New Or Revised Date -->
                            
                            <?php if ($record['new'] == 1  && $newdifference  >= 0): ?>
                            <span class="Medium-Text-Bold-Red">NEW</span><span class="Medium-Text-Bold"> - </span>
                            <?php endif ?>
                            <?php if ($record['revised'] == 1  && $updatedifference  >= 0): ?>
                            <span class="Medium-Text-Bold-Red">REVISED</span><span class="Medium-Text-Bold"> - </span>
                            <?php endif ?>
                            
                            <!-- End Date Check --> 
                            
                            <div align="center" style="width:60%; text-align:left;"><span class="Medium-Text-Bold">
                            <?php $topic htmlspecialchars($record['topic']); ?>
                            <?php echo strtoupper($topic); ?>
                            <?PHP if ($record['heirarchy'] == "Sub Topic"): ?>
                            - <?php echo date("M jS, Y"strtotime($record['updatedDate'])) ?>
                            <?php endif ?>
                            </span>
                            
                            <?php endif ?>
                            <span class="Medium-Text">
                            <?php
$recipe $record['recipe'];
$recipe wordwrap($recipe120) ;
$recipe highlight_string($recipetrue);

// Added per Daniel Loewe 12/27/18 to create links from URLs
 $config = array();
$config['AutoFormat.Linkify'] = true;                    
// split on opening blockquote
$chunks explode('q-z-l'$recipe); // remove the dashes between the q, z and l for the code to work

// loop through each chunk
foreach ($chunks as $key => $chunk) {

  
// split chunk on end blockquote
  $tmp explode('l-z-q'$chunk); // remove the dashes between the q, z and l for the code to work
  
   // did we find an ending blockquote?
  if (count$tmp ) > 1) {

    
// purify content after blockquote
    $tmp[1] = htmlPurify($tmp[1], $config);
  }
  else {

    
// purify only element
    $tmp[0] = htmlPurify($tmp[0], $config);
  }
  
// replace ending blockquote
  $chunks$key ] = implode('l-z-q'$tmp);
}

// replace opening blockquote
$recipe implode('q-z-l'$chunks); // remove the dashes between the q, z and l for the code to work

// reset block quotes
$recipe str_replace('q-z-l''<blockquote class="snippet"> '$recipe);  // remove the dashes between the q, z and l
for the code to work
$recipe str_replace('l-z-q''</blockquote> '$recipe); // remove the dashes between the q, z and l for the code to
work
 ?>
<?php echo $recipe?>
 
                            </div></td>
                        </tr>
                        <?php endforeach; ?>
                      </table>


And here's the code for the detail pages


  <?php
ini_set("highlight.comment""#000");
ini_set("highlight.default""#000");
ini_set("highlight.html""#000");
ini_set("highlight.keyword""#000;");
ini_set("highlight.string""#000");
?>
                      
                      <!--remove all remaining line breaks from the code not converted to <br /> in wordprocessor-->
                      <?php $recipe = ($table_of_contentsRecord['recipe']); ?>
                      <?PHP $recipe preg_replace("[]"""$recipe );  // insert <b-r> (remove the dash) between [ and
] for the code to work ?> 
                      <?PHP $recipe preg_replace("[]"""$recipe // insert <b-r/> (remove the dash) between [ and ]
for the code to work ?> 
                      <?PHP $recipe preg_replace("[]"""$recipe ); // insert <b-r /> (remove the dash) between [
and ] for the code to work ?> 
                      
                      <!--replace the valid <br in the code segment -->
                      <?PHP $recipe preg_replace("[z-x]i"""$recipe ); // insert <b-r /> (remove the dash) between
" and ", and remove the dash between z and for the code to work ?> 
                      <?php $recipe wordwrap($recipe120) ; ?>
                      <?php $recipe highlight_string($recipetrue); ?>
                      <?php                   
  $config = array();
$config['AutoFormat.Linkify'] = true;                    
// split on opening blockquote
$chunks explode('q-z-l'$recipe); // remove the dashes between the q, z and l for the code to work

// loop through each chunk
foreach ($chunks as $key => $chunk) {

  
// split chunk on end blockquote
  $tmp explode('l-z-q'$chunk); // remove the dashes between the q, z and l for the code to work
  
   // did we find an ending blockquote?
  if (count$tmp ) > 1) {

    
// purify content after blockquote
    $tmp[1] = htmlPurify($tmp[1], $config);
  }
  else {

    
// purify only element
    $tmp[0] = htmlPurify($tmp[0], $config);
  }
  
// replace ending blockquote
  $chunks$key ] = implode('l-z-q'$tmp); // remove the dashes between the q, z and l for the code to work
}

// replace opening blockquote
$recipe implode('q-z-l'$chunks); // remove the dashes between the q, z and l for the code to work

// reset block quotes
$recipe str_replace('q-z-l''<blockquote class="snippet"> '$recipe);  // remove the dashes between the q, z and l
for the code to work
$recipe str_replace('l-z-q''</blockquote> '$recipe); // remove the dashes between the q, z and l for the code to
work
 ?>
                      <table align="center" width="100%" border="0">
                        <tr><td>
                        <span class="Medium-Text-Bold"><b>
                            <?php $topic htmlspecialchars($table_of_contentsRecord['topic']); ?>
                            <?php echo strtoupper($topic); ?> - <?php echo date("M jS, Y",
strtotime($table_of_contentsRecord['updatedDate'])) ?></span>
                            
                            
                            </td></tr>
                        <tr>
                          <td class="Medium-Text"><?php echo $recipe?></td>
                        </tr>
                      </table>


USING CMSB TO POPULATE A CSS STYLESHEET - Feb 9th, 2011

It’s easy to populate a CSS stylesheet. from fields in a CMSB editor.

Here’s how:

In this example we'll modify the following CSS stylesheet to pull data from text fields called "heading_size", and
"heading_color", and an image upload field with one uploaded image called "page_background_image", in a single record
CMSB editor called "common_information". 

You can apply this technique to any CSS element.



#header {background-image:url(../images/your_image.jpg)};
}

h1{
    color: #c0c0c0;
    font-size = 2em;
    text-align: center;
}
__

 

If you've ever modified a site to treat html pages as PHP, using an .htaccess file, you might think that inserting:


Addhandler application/x-httpd-php.css 


in the .htaccess file would be an adequate approach to recognizing .css files as PHP, but in many situations, this can
lead to unexpected errors in how your pages are rendered.

So, to be safe, I'd use this approach.

First, add a .php the extension of your .css file (.css.php)

Then, in the head of your viewer page you’ll need to change:



<link rel="stylesheet" type="text/css" href="your_css_folder/your_css_file.css" />


to:



<link rel="stylesheet" type="text/css" href="your_css_folder/your_css_file.php" />




Now add the following PHP call before any CSS code to insure that your .css.php document is rendered correctly.

   

<?php

header("Content-type: text/css");
?>


After you’ve created the necessary fields in your editor and entered some sample values, go to the code generator for
your editor and copy the PHP require once and get records call to the head of your css.php file.

NOTE: DO NOT INCLUDE THIS LINE OF CODE:



<?php header('Content-type: text/html; charset=utf-8'); ?>




<?php
  
  require_once "path_to_your/cmsAdmin/lib/viewer_functions.php";

  list(
$common_informationRecords$common_informationMetaData) = getRecords(array(
    
'tableName'   => 'common_information',
    
'where'       => whereRecordNumberInUrl(1),
    
'limit'       => '1',
  ));
  
$common_informationRecord = @$common_informationRecords[0]; // get first record

  // show error message if no matching record is found
  if (!$common_informationRecord) {
    print 
"Record not found!";
    exit;
  }

?>
<?php 
header("Content-type: text/css");
?>

#header {background-image:url(<?php foreach ($common_informationRecord['page_background_image'] as $upload): ?>
http://www.your_site.com/<?php echo $upload['thumbUrlPath'?><?php endforeach ?>)};
}

h1{
    color: #<?php echo $common_informationRecord['heading_color'?>;
    font-size = <?php echo $common_informationRecord['heading_size'?>;
    text-align: center;
}
 


That’s it, you can replace any css element with a php call and give your clients added flexibility without them
needing to re-code your CSS stylesheet.


REMOVING THE UNWANTED 1PX BORDER AROUND LINKS IN FF & IE - Aug 28th, 2013

Here's the scenario.

Clicking on an active link brings you to the page it should.

Click the back arrow in either FF or IE and there’s a 1px dotted box around the link that was clicked on.

It seems that it involves a concept called  "focus".

And thanks to Tim Forrest from Toledoh Enterprises there's a simple fix.

He said, "Add this line to your CSS styles sheet and the problem will disappear."



:focus {
outline: 0;

__


You might want to Google :focus css to read some of the varying opinions regarding the use of this technique, but if you
need the box to go away, this will work.


VALIDATING EMAIL ADDRESSES - May 29th, 2013

There are many times when the format of an email address needs to be validated. 

There's a built in function in CMSB called isValidEmail that will do that for you, and might be used in the
errorsandAlerts section of your viewer like this:

<?php if (!isValidEmail(@$_REQUEST['email_address'])) { $errorsAndAlerts .= "Please enter a valid E-mail address
(example: user@example.com)\n"; } ?>

But to understand how it works, here's an regular expression approach that you can modify to suit your needs. They all
assume that the email address field is called email_address

To list all email addresses in an existing list with invalid formats:


<?php $count 0?>
<?php foreach ($your_tableRecords as $record ): ?
<?
php  // in the code \.[a-z0-9-]+)*(\.[a-z]{2,4}) 2 is the minimum and 4 is the maximum characters after the period to
account for TLDs like .us and .info
$email_address $record['email_address'];
if(!
eregi("^[_a-z0-9-]+(\.[_a-z0-9-]+)*@[a-z0-9-]+(\.[a-z0-9-]+)*(\.[a-z]{2,4})$"$email_address)) {
  
echo 
$record['email_address'] ;
$count++;
}
?>
<?php endforeach ?>
<br /> <br />
There are <?php echo $count ?> invalid email addresses in this list.



If you want to list all email addresses with valid formats remove the ! from before eregi

You'll find many other useful regular expressions here:


http://www.virtuosimedia.com/dev/php/37-tested-php-perl-and-javascript-regular-expressions



KEEP "ARRAY()" FROM SHOWING ON VIEWER - Aug 17th, 2011

One of the fixes to get rid of the "Array()" at the top of the page is remove this line of code if it exists:

print_r($_FILES);


INCLUDING VARIABLES IN THE CONTENTS OF A TEXT BOX - May 13th, 2012

User zip222 wanted to include a variable in a text box field so that the contents of the textbox could be customized.

Here’s what Jason Sauchuk from Interactive Tools suggested:

Welcome to the Online Student Log-in for the *school_name* This section of our website provides links...

You can then replace *school_name* when you are outputting your content.

(Note: In this example, we're assuming that the values for *school name* is in an editor called  "school_record" and the
text field is a field called "content" in a table called" your_table".)

For a list page:
    

<?php echo str_replace('*school_name*',$schoolRecord['title'],$record['content']);?>




For a detail page the code would change to:
    

<?php echo str_replace('*school_name*',$schoolRecord['title'],$your_tableRecord['content']);?>




Jason explained: 

In this code, the sub string *school_name* is replaced in the string $record['content'] with the variable
$schoolRecord['title'] 



Another way to look at this is:

If you place a variable called *var1* in a target text box field called target_field in a table called target_table

And your source for *var1* is in a text box field called source_field in a table called source_table

Then the code in the body of the viewer where you want to display the composite would be:

 


<?php echo str_replace('*var1*',$source_tableRecord['source_field'],$target_tableRecord['target_field']);?>




Another scenario:

The detail page for a series of event listings contained a PayPal payment link that redirected to a thank you page after
successful payment.

In my thank you page I wanted the text field to include the name of the event that was paid for.

I inserted *var1* in my thank you pages' target text field as above and inserted the above code in the body of my thank
you page.  

Then, I added: 
NOTE: 

 


?<?php echo $source_tableRecord['num'?>%23


to the return URL in my PayPal button code. The %23 (URL-encoding for #) inserts a # after the record number that
separates the transaction data (if any) from the record number so it will render correctly,  (of course with my own
source_table name inserted) 



And another scenario:
I had a series of variables that I wanted to insert in the thank you page above, and some of them were dependent. IE
they shouldn't show unless there was data in a specific field.
For example, if there was no special venue for an event, I didn't want to show the special venue name, address, or
warning in the thank you page.

With some help from Jason Sauchuk from Interactive Tools, here's what I came up with.
In the viewer:
 


<?php   
 
$event_start_date =  date("l, M jS, Y"strtotime($learning_centerRecord['event_start_date'])) ; 
  
 
$special_location ""
if (@
$learning_centerRecord['special_event_location_name']) { 
  
$special_location "<b>Special Location: ".$learning_centerRecord['special_event_location_name']."</b>\n"."<br
/>\n".$learning_centerRecord['special_event_location_address']."<br />\n";
}  
 
$placeHolders = array("*var1*""*var2*""*var3*""*var4*""*var5*""*var6*" );  
$replaceWith  = array($learning_centerRecord['event_title'], $learning_centerRecord['event_fee'], $event_start_date,
$learning_centerRecord['event_times'], $learning_centerRecord['event_description'], $special_location);   
    
echo 
str_replace($placeHolders$replaceWith$common_informationRecord['learning_center_thank_you_1']);   
?>


And in the Text Field ('learning_center_thank_you_1)

NOTES: The the ."<br />\n" after Special Location:puts that text on its own line on the finished page. The <br /> after
the special_event_location_name is because the address is a multi line entry. The extra ."<br />\n" at the end allows
you to remove the blank line after *var6* below, and have it appear only when there's a "special_event_location_name"

 


Congratulations, you're all signed up for <b>"*var1*"</b>  
on <b>*var3*, from *var4*</b>  
*var6* 
We suggest that you arrive at least 15 minutes early so that you can get to know some of the other attendees.  
  
Here's a copy of the description of this event.  
  
*var5*

See you there...



Another scenario:

I used this approach to insert a link and an image called "image1" from a single record editor called "graphics" in a
text box called "content" in the table "my_table" with the following code.

In the contents of the text box where I wanted the link to appear, I inserted:

 
    

<a href="my_URL.com/my_page.php"><img src="*variable*"></a>


And in the page code for my list page (in the foreach loop)

 
    

<?php $myImage $graphicsRecords[0]['image1'][0]; ?>  
       <?php echo str_replace('*variable*',$myImage['thumbUrlPath'],$record['content']);?>


And for my detail page.

 
    

 <?php $myImage $graphicsRecords[0]['image1'][0]; ?> 
      <?php echo str_replace('*variable*',$myImage['thumbUrlPath'],$my_tableRecord['content']);?>


Don't forget to include load records calls for both editors at the top of your viewers



I asked Jason how to include more than one variable (text, links or images) in the same text box and he said:

str_replace is a php function (http://ca3.php.net/str_replace) that searches for one string inside another and replaces
all instances of that string.

So, if you want to be able to insert more than 1 piece of text into a string, you'll need more placeholders (*var1*,
*var2*, *var3*, etc). The actual name of the placeholder is irrelevant, but you should choose something that is unlikely
to appear naturally in the text.

You can also use arrays with str_replace. For example:

 


<?php 
  
 $placeHolders = array("*var1*""*var2*""*var3*"); 
 
$replaceWith  = array($link1$image1$link2); 
  
 echo 
str_replace($placeHolders$replaceWith$record['content']); 
?>


In this example, str_replace will look in $record['content'] and replace all instances of *var1* with $link1, all
instances of *var2* with $image1, and all instances of *var3* with $link2.





USE PLACEHOLDER VARIABLES TO INSERT IMAGES INTO TEXT BOXES - Dec 29th, 2018

 My client wanted to use placeholder variables to insert images into text boxes from a master image library of 12
images.

Expanding on the recipe:
http://www.thecmsbcookbook.com/recipedetail.php?407 

and an old post on the Interactive Tools Forum:
 http://www.interactivetools.com/forum/forum-posts.php?71740 
 
this is what I came up with.

1) Create a single record section called “Image Library” and create 12 image upload fields, limited to 1 image per
field, set the size of thumbnail2 to suit your needs.

2) At the top of each viewer where you want the ability to insert images add the following code:
Note, I placed the code in a file called _image_library.php and used the line, <?php include ("_image_library.php"); ?>
in the head of the pages so I didn’t have to re-insert the code into each page.


 <?php // Code to add images to text boxes

list($image_libraryRecords$image_libraryMetaData) = getRecords(array(
    
'tableName'   => 'image_library',
    
'where'       => ''// load first record
    'loadUploads' => true,
    
'allowSearch' => false,
    
'limit'       => '1',
  ));

  
$image_libraryRecord = @$image_libraryRecords[0]; // get first record
  ?>
<?php if ($image_libraryRecord['image_1']):?>
<?php foreach ($image_libraryRecord['image_1'] as $index => $upload): ?>
 <?php $image1 $upload['thumbUrlPath2'?>
<?php endforeach ?>
<?php endif ?>
<?php if ($image_libraryRecord['image_2']):?>
<?php foreach ($image_libraryRecord['image_2'] as $index => $upload): ?>
 <?php $image2 $upload['thumbUrlPath2'?>
<?php endforeach ?>
<?php endif ?>
<?php if ($image_libraryRecord['image_3']):?> 
<?php foreach ($image_libraryRecord['image_3'] as $index => $upload): ?>
 <?php $image3 $upload['thumbUrlPath2'?>
<?php endforeach ?>
<?php endif ?>
<?php if ($image_libraryRecord['image_4']):?>
<?php foreach ($image_libraryRecord['image_4'] as $index => $upload): ?>
 <?php $image4 $upload['thumbUrlPath2'?>
<?php endforeach ?>
<?php endif ?>
<?php if ($image_libraryRecord['image_5']):?>
<?php foreach ($image_libraryRecord['image_5'] as $index => $upload): ?>
 <?php $image5 $upload['thumbUrlPath2'?>
<?php endforeach ?>
<?php endif ?>
<?php if ($image_libraryRecord['image_6']):?>
<?php foreach ($image_libraryRecord['image_6'] as $index => $upload): ?>
 <?php $image6 $upload['thumbUrlPath2'?>
<?php endforeach ?>
<?php endif ?>
<?php if ($image_libraryRecord['image_7']):?>
<?php foreach ($image_libraryRecord['image_7'] as $index => $upload): ?>
 <?php $image7 $upload['thumbUrlPath2'?>
<?php endforeach ?>
<?php endif ?>
<?php if ($image_libraryRecord['image_8']):?>
<?php foreach ($image_libraryRecord['image_8'] as $index => $upload): ?>
 <?php $image8 $upload['thumbUrlPath2'?>
<?php endforeach ?>
<?php endif ?>
<?php if ($image_libraryRecord['image_9']):?>
<?php foreach ($image_libraryRecord['image_9'] as $index => $upload): ?>
 <?php $image9 $upload['thumbUrlPath2'?>
<?php endforeach ?>
<?php endif ?>
<?php if ($image_libraryRecord['image_10']):?>
<?php foreach ($image_libraryRecord['image_10'] as $index => $upload): ?>
 <?php $image10 $upload['thumbUrlPath2'?>
<?php endforeach ?>
<?php endif ?>
<?php if ($image_libraryRecord['image_11']):?>
<?php foreach ($image_libraryRecord['image_11'] as $index => $upload): ?>
 <?php $image11 $upload['thumbUrlPath2'?>
<?php endforeach ?>
<?php endif ?>
<?php if ($image_libraryRecord['image_12']):?>
<?php foreach ($image_libraryRecord['image_12'] as $index => $upload): ?>
 <?php $image12 $upload['thumbUrlPath2'?>
<?php endforeach ?>
<?php endif ?>

<?php $placeHolders = array("*img1*""*img2*""*img3*""*img4*""*img5*""*img6*""*img7*""*img8*""*img9*",
"*img10*""*img11*""*img12*");
$replaceWith = array(@$image1, @$image2, @$image3, @$image4, @$image5, @$image6, @$image7, @$image8, @$image9,
@
$image10, @$image11, @$image12 ); ?>


This code creates an array called $placeHolders, containing the placeholders *img1* through *img12* and an array called
$replaceWith, containing the variables $image1 through $image12

You can use any name for the placeholders and the variables. Just make sure that the names won’t appear in the text as
well.

3) in the body of your page, (in this example an “about us” page, which gets it’s content from the text box field
“about_content” in a single record editor called “about”) where you would normally use: 

<?php echo $aboutRecord['about_content'?>

Insert the code:

<?php echo str_replace($placeHolders$replaceWith$aboutRecord['about_content']);?>


str_replace will look into the $aboutRecord['about_content'] field, and replace all instances of *img1* with $image1,
all instances of *img2* with $image2, all instances of *img3* with $image3, etc.

This code will also work for a multi record section detail page. For a multi record list page, just change
$aboutRecord['about_content'] to $record['about_content'].

4) Finally, in the “about_content” text box, insert the appropriate placeholder in a <div> exactly where you want
the image to appear, and use CSS to style the <div> to suit your needs.

<div align="center" class=”your_class”><img  src="*img4*"></div>




USING AN EMAIL TEMPLATE TO CREATE A NEW ACCOUNT RECORD - Apr 10th, 2019

My client needed to validate new listing requests for their on-line directory and then automatically create an account
record with the information from the on-line application form.

To initiate the request, the applicant filled out an on-line form. I used a built in email template to  pass the form
values to an admin for approval and to facilitate creating a new account record once the admin had verified their
information.

The problem was that spaces in the text would break the add record request until Dave Edis from interactive Tools came
to the rescue. He suggested that what was needed was to urlencode values that are put in a link. Here’s what I came up
with.

Further, you can't use php variables in an email template, but as Daniel Loewe pointed out, you can use if/else
statements to create a variable to send in as the placeholder instead.

Other challenges were passing dates to the database and passing checkbox arrays that would assign values to a list of
check boxes that gets its initial values from a list field. See the recipe at
http://www.thecmsbcookbook.com/recipedetail.php?Pre-populating-radio-buttons-in-forms-from-a-master-list-568 for more
information on this.

On the page that calls the email template:
In the 'process form' section with error checking, etc., define a variable for passing a date.

$date = date("Y-m-d",mktime(0,0,0,@$_REQUEST['month_1'],@$_REQUEST['day_1'],@$_REQUEST['year_1']));

Then build the link to be passed to the email.

<?php

// add user
if (!$errorsAndAlerts) {
// checkbox pass through code suggested by Daniel Louwe from Interactive Tools 11/1/18
$more_than_one_location_for_practice 0
$more_than_one_practitioner_in_practice 0;
$more_than_one_location_for_practicea 0;
$more_than_one_practitioner_in_practicea 0;
if (isset( 
$_REQUEST['more_than_one_location_for_practice'] )) {$more_than_one_location_for_practice 1;}
$more_than_one_practitioner_in_practice 0;
if (isset( 
$_REQUEST['more_than_one_practitioner_in_practice'] )) {$more_than_one_practitioner_in_practice 1;}  
 if (
$more_than_one_location_for_practice === 0) {
    
$more_than_one_location_for_practicea 'there is only one location for this practice.';
  }
  elseif (
$more_than_one_location_for_practice === 1) {
    
$more_than_one_location_for_practicea 'there are multiple locations for this practice.';
  }
  
    if (
$more_than_one_practitioner_in_practice === 0) {
    
$more_than_one_practitioner_in_practicea 'there is only one practitioner in this practice.';
  }
  elseif (
$more_than_one_practitioner_in_practice === 1) {
    
$more_than_one_practitioner_in_practicea 'there are multiple practitioner in this practice.';
  }
$addLink  "http://my-site.com/cmsAdmin/admin.php?menu=accounts&action=add";
$addLink .= "&username=" .urlencode(@$_REQUEST['username']);
$addLink .= "&email="    .urlencode(@$_REQUEST['email']);
$addLink .= "&password="    .'2AX3Frb' ;
$addLink .= "&first_name="    .urlencode(@$_REQUEST['first_name']);
$addLink .= "&last_name="    .urlencode(@$_REQUEST['last_name']);
$addLink .= "&practice_name="    .urlencode(@$_REQUEST['practice_name']);
$addLink .= "&practice_date="    .urlencode($date);
$addLink .= "&approved="    .'1';
$addLink .= "&practice_street_address="    .urlencode(@$_REQUEST['practice_street_address']);
$addLink .= "&practice_city="    .urlencode(@$_REQUEST['practice_city']);
$addLink .= "&practice_state="    .urlencode(@$_REQUEST['practice_state']);  
$addLink .= "&practice_zip="    .urlencode(@$_REQUEST['practice_zip']);  
$addLink .= "&practice_phone="    .urlencode(@$_REQUEST['practice_phone']);
$addLink .= "&training_experience="    .urlencode(@$_REQUEST['training_experience']);
$addLink .= "&more_than_one_location_for_practice="    .urlencode(@$more_than_one_location_for_practice); 
$addLink .= "&more_than_one_practitioner_in_practice="    .urlencode(@$more_than_one_practitioner_in_practice);
if (!empty( 
$_REQUEST['dbt_training_support'] )) {
  foreach (
$_REQUEST['dbt_training_support'] as $value) {
    
$addLink .= '&dbt_training_support[]=' urlencode($value);
  }
}
    
// send email to Admin
          $emailHeaders emailTemplate_loadFromDB(array(
    
'template_id'  => 'DIRECTORY-LISTING-REQUEST',
    
    
'placeholders' => array(
      
'contact.firstName'     =>  ($_REQUEST['first_name']),
      
'contact.lastName'      =>  ($_REQUEST['last_name']),
      
'contact.email'     =>  ($_REQUEST['email']),
      
'contact.username'     =>  ($_REQUEST['username']),
      
'practice'     =>  ($_REQUEST['practice_name']),
      
'street'     => ($_REQUEST['practice_street_address']),
      
'city'     => ($_REQUEST['practice_city']),
      
'state'     => ($_REQUEST['practice_state']),
      
'zip'     =>  ($_REQUEST['practice_zip']),
      
'phone'     =>  ($_REQUEST['practice_phone']),
      
'training' =>  ($_REQUEST['training_experience']),
      
'more_than_one_location_for_practice' =>  ($more_than_one_location_for_practicea),
      
'date1'     =>  ($_REQUEST['practice_date'])
      
'more_than_one_practitioner_in_practice'     =>  ($more_than_one_practitioner_in_practicea),
      
'addLink'   => $addLink,
           )));
      
$mailErrors   sendMessage($emailHeaders);
      if (
$mailErrors) { alert("Mail Error: $mailErrors"); }
      
      
// show thanks
     $errorsAndAlerts "
              <div class='heading_font' align='center'>THANKS FOR SUBMITTING YOUR REQUEST TO BE INCLUDED IN THE
DIRECTORY
               </div>
              <div align='center'>
                
                <div class='text_font' align='left'><b>Your directory listing request has been sent successfully.
                  
                  You'll get an email from us after we've reviewed your request.
                  
                  Best, 
                  
                  The Directory Team</b></div>
              </div>
            </div>";
      
$_REQUEST        = array(); // clear form values
      $showSignupForm  false;
    }

?>

And in the email template I then only had to add the one #addLink# placeholder for the link:

NOTE: I used the free emailOnApproved plugin http://www.thecmsbcookbook/downloads/emailonapproved.zip to send an email
with login information to the applicant if a checkbox called “approved” is checked in their account record, and the
free AlertRecordSaved plugin http://www.thecmsbcookbook/downloads/alertrecordsaved.zip to alert the admin when a profile
is updated. 

Because of encrypted passwords, I send a temporary generic password to the approved applicant and they are forced to
change their password on initial log in. 
See the recipe at http://www.thecmsbcookbook.com/recipedetail.php?287 for how to accomplish this. (***You must be using
the website membership plugin***)


Hello,

#contact.firstName# #contact.lastName# has submitted a directory listing request for #practice#.


Their address is:#street#
#city#, #state# #zip#


Their phone number is: #phone#


Their email address is: #contact.email#


They've indicated the following training/experience:
#training#

After you've determined that the request is from a valid provider, AND THE PROVIDER IS NOT ALREADY LISTED IN THE
DIRECTORY,

1) click on the link below to create a listing in the directory
2) Make sure that the "Approved" check box is checked in their new listing
3) "Save" the new listing record or it will not be available for updating

NOTE: After they've updated their profile (you'll get an email when they do), and you feel that the information is ready
for release, you'll have to un-hide their listing and re-save the record, or it will not be available in the directory.

If you cannot click on the link below, copy it and paste it into your browser.

#addLink#


The Form that was used for data input for the application contained both text and checkbox fields. Problems with the
checkbox fields were addressed by Daniel Loewe, a programmer at Interactive Tools. The DBT Training/Support field is
feeding it's values as an array, to a list field that gets it's initial data from another table with just the titles of
the support categories. :


<form method="post" action="">
                <input type="hidden" name="save" value="1" />
                <table border="0" cellspacing="0" cellpadding="2">
                  <tr>
                    <td width="40%" align="left" valign="middle" class="text_font"><b>What's The Name
                      Of the Practice
                      </b></td>
                    <td style="text-align:left" align="left" valign="middle"><input class="text" type="text"
name="practice_name" id="practice_name" value="<?php echo htmlencode(@$_REQUEST['practice_name']); ?>" /></td>
                  </tr>
                  <tr>
                    <td width="40%" align="left" valign="middle" class="text_font"><b>The Street Address
                      Of the Practice
                      </b></td>
                    <td style="text-align:left" align="left" valign="middle"><input class="text" type="text"
name="practice_street_address" id="practice_street_address" value="<?php echo
htmlencode(@$_REQUEST['practice_street_address']); ?>" /></td>
                  </tr>
                  <tr>
                    <td width="40%" align="left" valign="middle" class="text_font"><b>The Suite, Apartment, or Floor
(optional)
                      </b></td>
                    <td style="text-align:left" align="left" valign="middle"><input class="text" type="text"
name="practice_room_or_floor" id="practice_room_or_floor" value="<?php echo
htmlencode(@$_REQUEST['practice_room_or_floor']); ?>" /></td>
                  </tr>
                  <tr>
                    <td width="40%" align="left" valign="middle" class="text_font"><b>The City
                      </b></td>
                    <td style="text-align:left" align="left" valign="middle"><input class="text" type="text"
name="practice_city" id="practice_city" value="<?php echo htmlencode(@$_REQUEST['practice_city']); ?>" /></td>
                  </tr>
                  <tr>
                    <td width="40%" align="left" valign="middle" class="text_font"><b>The State - (2 Letters) </b></td>
                    <td style="text-align:left" align="left" valign="middle"><input class="text"
type="text"style='text-transform:uppercase' MAXLENGTH="2" name="practice_state" id="practice_state" value="<?php echo
htmlencode(strtoupper(@$_REQUEST['practice_state'])); ?>" /></td>
                  </tr>
                  <tr>
                    <td width="40%" align="left" valign="middle" class="text_font"><b>The Zip Code</b></td>
                    <td style="text-align:left" align="left" valign="middle"><input class="text" type="text"
name="practice_zip" id="practice_zip" value="<?php echo htmlencode(@$_REQUEST['practice_zip']); ?>" /></td>
                  </tr>
                  <tr>
                    <td width="40%" align="left" valign="middle" class="text_font"><b>The Phone Number
                      </b></td>
                    <td style="text-align:left" align="left" valign="middle"><input class="text" type="text"
name="practice_phone" placeholder="Numbers Only Please" id="practice_phone" value="<?php echo
htmlencode(@$_REQUEST['practice_phone']); ?>" /></td>
                  </tr>
                  <tr>
                    <td width="40%" align="left" valign="middle" class="text_font"><b>Is There More Than One Location
For This Practice? (Check If Yes)
                      </b></td>
                    <td style="text-align:left" align="left" valign="middle">
                    <input type="checkbox" name="more_than_one_location_for_practice" value="1" <?php checkedIf('1',
@
$_REQUEST['more_than_one_location_for_practice']); ?> />
                    </td>
                  </tr>
<tr> 
     <td width="40%" align="left" valign="middle" class="text_font" ><b>First Event Date <span style="color: #C00;
font-weight: bold;">*</span>
                      </b></td>
     <td align="left" valign="middle" class="text_font"><?php  
$lowestYear  date("Y");  
$highestYear date('Y'strtotime('+1 years'));  
?> 
Month:  
<select name="month_1">
                        <?php if (@$_REQUEST['month_1']): ?>
                        <option value="<?php echo htmlspecialchars(@$_REQUEST['month_1']) ?>">
                        <?php $month1a = @$_REQUEST['month_1'?>
                        <?php echo date("F",strtotime("0000-$month1a"));?></option>
                        <?php else :?>
                        <option value=""><?php echo 'Select Month';?></option>
                        <?php endif ?>
                        <?php foreach(range(1,12) as $month_1): ?>
                        <option value="<?php echo $month_1;?>"><?php echo
date("F",strtotime("0000-$month_1"));?></option>
                        <?php endforeach ?>
                      </select> Day:  
<select name="day_1">
                        <?php if (@$_REQUEST['day_1']): ?>
                        <option value="<?php echo htmlspecialchars(@$_REQUEST['day_1']) ?>"><?php echo
htmlspecialchars(@$_REQUEST['day_1']) ?></option>
                        <?php else :?>
                        <option value=""><?php echo 'Select Day';?></option>
                        <?php endif ?>
                        <?php foreach(range(1,31)as $day_1): ?>
                        <option value="<?php echo $day_1;?>"><?php echo $day_1;?></option>
                        <?php endforeach ?>
                      </select> Year:  
<select name="year_1">
                        <?php if (@$_REQUEST['year_1']): ?>
                        <option value="<?php echo htmlspecialchars(@$_REQUEST['year_1']) ?>"><?php echo
htmlspecialchars(@$_REQUEST['year_1']) ?></option>
                        <?php else :?>
                        <option value=""><?php echo 'Select Year';?></option>
                        <?php endif ?>
                        <?php foreach (range($lowestYear,$highestYear) as $year_1):?>
                        <option value="<?php echo $year_1;?>"><?php echo $year_1;?></option>
                        <?php endforeach?>
                      </select> 
</td>
   </tr>
                  <tr>
                    <td width="40%" align="left" valign="middle" class="text_font"><b>What's Your First Name</b></td>
                    <td style="text-align:left" align="left" valign="middle"><input class="text" type="text"
name="first_name" id="first_name" value="<?php echo htmlencode(@$_REQUEST['first_name']); ?>" /></td>
                  </tr>
                  <tr>
                    <td width="40%" align="left" valign="middle" class="text_font"><b>Your Last Name</b></td>
                    <td style="text-align:left" align="left" valign="middle"><input class="text" type="text"
name="last_name" id="last_name" value="<?php echo htmlencode(@$_REQUEST['last_name']); ?>" /></td>
                  </tr>
                  <tr>
                    <td width="40%" align="left" valign="middle" class="text_font"><b>Enter A Temporary User
Name</b></td>
                    <td style="text-align:left" align="left" valign="middle"><input class="text" type="text"
name="username" id="username" value="<?php echo htmlencode(@$_REQUEST['username']); ?>" /></td>
                  </tr>
                  <tr>
                    <td width="40%" align="left" valign="middle" class="text_font"><b>Your E-mail Address</b></td>
                    <td  style="text-align:left" align="left" valign="middle"><input class="text" type="text"
name="email" id="email" value="<?php echo htmlencode(@$_REQUEST['email']); ?>" /></td>
                  </tr>
                 
                  <tr>
                    <td width="40%" align="left" valign="middle" class="text_font"><b>Enter Your E-mail Again</b></td>
                    <td  style="text-align:left" align="left" valign="middle"><input class="text" type="text"
name="email2" id="email2" value="<?php echo htmlencode(@$_REQUEST['email2']); ?>" /></td>
                  </tr>
                  <tr>
                    <td width="40%" align="left" valign="middle" class="text_font"><b>Is There More Than One DBT Trained
Practitioner In This Practice? (Check If Yes)</b></td>
                    <td  style="text-align:left" align="left" valign="middle"> <input type="checkbox"
name="more_than_one_practitioner_in_practice" value="1" <?php checkedIf('1',
@
$_REQUEST['more_than_one_practitioner_in_practice']); ?> />
                   </td>
                  </tr>
                  <tr>
              <td class="text_font" valign="top"><b>DBT Training/Support</b></td>
              <td class="text_font"><?php 
               $fieldname 'dbt_training_support'?>
                <?php
if(is_array(@$_REQUEST[$fieldname])){
$fieldValues $_REQUEST[$fieldname];
}
else{
$fieldValues explode("\t",trim(@$_REQUEST[$fieldname],"\t"));

?>
                <?php  $idCounter 0?>
                <?php  foreach (getListOptions('accounts'$fieldname) as $value => $label): ?>
                <?php  $id "$fieldname." . ++$idCounter?>
                <input type="checkbox" name="<?php  echo $fieldname ?>[]" id="<?php echo $id ?>"
value="<?php echo htmlspecialchars($value?>" <?php  if(in_array($value,$fieldValues)):?> checked="checked" <?php endif
?>/>
                <label for="<?php  echo $id ?>">
                  <?php  echo htmlspecialchars($label?>
                </label>
                
                <?php  endforeach ?></td>
            </tr>    
                  <tr>
              <td width="40%" align="left" valign="middle" class="text_font"><b>Describe Your DBT Training/Experience
              (FOR INTERNAL USE ONLY)</b></td>
              <td  style="text-align:left" align="left" valign="middle"><textarea name="training_experience"
id="training_experience" placeholder="<?php echo
$common_informationRecord['application_training_and_experience_example_text']?>" COLS=50 ROWS=6 ><?php echo
htmlspecialchars(@$_REQUEST['training_experience']); ?></textarea>
            </td>
            </tr>
                  <tr>
                    <td colspan="2" class="text_font" style=" font-weight: bold;" valign="top">
                      Please check the "I'm not a robot" box below before submitting.
                      
                      <div class="g-recaptcha" data-theme="light"
data-sitekey="6Lfd5RcTAAAAAJOxR3jJJfn7kfDDo4T8FCBGZKfD"></div></td>
                  </tr>
                  <tr>
                    <td colspan="2" align="center">
                      <input class="button" type="submit" name="submit" value="SUBMIT YOUR LISTING REQUEST &gt;&gt;"
/></td>
                  </tr>
                </table>
              </form>


SAVE AND COPY FUNCTION FOR EMAIL TEMPLATES - Jan 30th, 2023

CMSB user Codee asked, "What's the possibility, and probability, that the Email Templates section of CMSB have the "Save
and Copy" function such as the section editors have?  Sometimes it would be great to be able to do a copy from an
already built one and then just remove or change a few pieces, instead of starting from scratch for each one."

Daniel from Interactive Tools came up with a simple fix. He said, "Here's a patch you can add to saveAndCopy.php to
enable it; on line 21 (in version 3.57) change this:


$addButton = isset($menuType) && ($menuType == 'multi' || $menuType == 'category') && ($action == 'add' || $action ==
'edit');

to this:

$addButton = isset($menuType) && ($menuType == 'multi' || $menuType == 'category' || $tableName == '_email_templates')
&& ($action == 'add' || $action == 'edit');


DETECTING BROWSERS AND OPERATING SYSTEMS - Jul 4th, 2012

There are times when it's important to be able to serve up different code or messages based on either the OS or Browser
being used.

There are more comprehensive solutions, but here's a simple function that you can use for that purpose.

The example can be pasted in the body of your page and is set up to test for the combination of Windows XP and Internet
Explorer.
 

<?php
    function os_detection ()
{

    if (isset (
$_SERVER['HTTP_USER_AGENT']))
    {
        if (
strpos ($_SERVER['HTTP_USER_AGENT'], 'MSIE') && (strpos ($_SERVER['HTTP_USER_AGENT'], 'Windows NT 5.1') !== false)
            return 
true;
        
            
            if (
strpos ($_SERVER['HTTP_USER_AGENT'], 'MSIE') && (strpos ($_SERVER['HTTP_USER_AGENT'], 'Windows XP' ) !== false)
            return 
true;    
    }

    return 
false;
}
    
?>
        <?php if (os_detection()) : ?>
        We've determined that your computer is using a combination of Windows XP and the Internet Explorer browser.Sorry
for the inconvenience, but to use this page with Windows XP, you'll need to use the another browser, like the free
Firefox browser.If you need to, you can download the latest version from <a
href="http://www.mozilla.org/en-US/firefox/new/">http://www.mozilla.org/en-US/firefox/new/</a>.Installation is
automatic, you can leave multiple browsers on your system, and they won't interfere with using Internet Explorer at
other times.Thanks,Photo Canvas Arts Webmaster
        <?PHP else: ?>
       Other Code...
        <?PHP endif ?>



By far not comprehensive, but here's a list of user Agent strings for some of the most common OS's and Browsers.

                 iPhone :  iPhone
        Windows 3.11:  Win16
        Windows 95:  Windows 95 and Win95 and Windows_95
        Windows 98:  Windows 98 and Win98
        Windows 2000:  Windows NT 5.0 and Windows 2000
        Windows XP:  Windows NT 5.1 and Windows XP
        Windows 2003:  Windows NT 5.2
        Windows Vista:  Windows NT 6.0 and Windows Vista
        Windows 7:  Windows NT 6.1 and Windows 7
        Windows NT 4.0:  Windows NT 4.0 and WinNT4.0 and WinNT and Windows NT
        Windows ME:  Windows ME
        Open BSD:  OpenBSD
        Sun OS:  SunOS
        Linux:  Linux and X11
        Safari:  Safari
        Macintosh:  Mac_PowerPC and Macintosh
        QNX:  QNX
        BeOS:  BeOS
        OS/2:  OS/2

___

               Opera:  Opera
               Mozilla Firefox: Firebird and Firefox
               Galeon:  Galeon
               Mozilla:  Gecko
               MyIE:  MyIE
               Lynx:  Lynx
               Netscape:  Mozilla/4\.75 and Netscape6 and Mozilla/4\.08 and Mozilla/4\.5 and Mozilla/4\.6 and
Mozilla/4\.79
               Konqueror:  Konqueror
               SearchBot:  nuhk and Googlebot and Yammybot and Openbot and Slurp/cat and msnbot and ia_archiver
               Internet Explorer All:   MSIE
               Internet Explorer 8:  MSIE 8\.[0-9]+
               Internet Explorer 7:  MSIE 7\.[0-9]+
               Internet Explorer 6:  MSIE 6\.[0-9]+
               Internet Explorer 5:  MSIE 5\.[0-9]+
               Internet Explorer 4:  MSIE 4\.[0-9]+

There's also a get browser PHP function that can be used:
http://php.net/manual/en/function.get-browser.php 





USING CSS GRADIENTS AS PAGE BACKGROUNDS - Aug 11th, 2012

Gradients are pretty dicey at best, since support for them varies from browser to browser and version to version. 

Here's an approach to incorporating a diagonal page background into my pages and allow admins to change the colors from
the CMSB interface.

You can examine and modify the process with two tools. The Ultimate Gradient Generator at
http://www.colorzilla.com/gradient-editor/ to generate any new style of gradient you want to create, and the base64
coder/decoder at http://www.base64decode.org/

There are lots of pitfalls in this concept. 

IE9 doesn't support CSS Gradients and so you must use a base64 SVG to generate a background gradient image to display.
Fortunately there's a php function called base64_encode that makes it possible to do this on the fly from values in your
database.

If IE is in" Compatibility" mode your gradient won't display correctly and fill the entire screen . Fortunately you can
add this as the first line in the head of your viewer to force IE not to enter compatibility mode.



<meta http-equiv="X-UA-Compatible" content="IE=8;IE=9" />



There's more in articles at: http://www.alistapart.com/articles/beyonddoctype and http://hsivonen.iki.fi/doctype/

Some older versions of browser/OS combinations don't work well with this code (***Still looking for identification and
solutions for these.***)

I found that to get the gradients to cover the full screen, without repeats, I needed to make the following additions to
my CSS style sheet: 


html {
    height: 100%;



In order to allow clients to change the colors of my diagonal gradient I created 3 text fields in a single record editor
called "Common Information" for the Upper left (darkest), Lower Left (middle) and Lower right (lightest) colors and I
inserted the following code in my body CSS in place of the Gradient Generator code :

<!-- three variables to use in the svg conversion -->


<?php $lightest $common_informationRecord['lightest_background_gradient_color']; ?> 
<?php $middle $common_informationRecord['middle_background_gradient_color']; ?>
<?php $darkest $common_informationRecord['darkest_background_gradient_color']; ?>

<!-- The Ascii version of the SVG code iIncluding the variables -->
<?php
$str '<?xml version="1.0" ?>
<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" viewBox="0 0 1 1" preserveAspectRatio="none">
  <linearGradient id="grad-ucgg-generated" gradientUnits="userSpaceOnUse" x1="0%" y1="0%" x2="100%" y2="100%">
     <stop offset="0%" stop-color="#'.$darkest.'" stop-opacity="1"/>
    <stop offset="0%" stop-color="#'.$darkest.'" stop-opacity="1"/>
    <stop offset="100%" stop-color="#'.$middle.'" stop-opacity="1"/>
    <stop offset="100%" stop-color="#'.$darkest.'" stop-opacity="1"/>
  </linearGradient>
  <rect x="0" y="0" width="1" height="1" fill="url(#grad-ucgg-generated)" />
</svg>';

?>
body {



background: url(data:image/svg+xml;base64,<?php echo base64_encode($str)?>);


background: -moz-linear-gradient(-45deg,  #<?php echo $common_informationRecord['middle_background_gradient_color']; ?>
0%, #<?php echo $common_informationRecord['darkest_background_gradient_color']; ?> 0%, #<?php echo
$common_informationRecord['lightest_background_gradient_color']; ?> 100%); 
background: -webkit-gradient(linear, left top, right bottom, color-stop(0%,#<?php echo
$common_informationRecord['middle_background_gradient_color']; ?>), color-stop(0%,#<?php echo
$common_informationRecord['darkest_background_gradient_color']; ?>), color-stop(100%,#<?php echo
$common_informationRecord['lightest_background_gradient_color']; ?>)); 
background: -webkit-linear-gradient(-45deg,  #<?php echo $common_informationRecord['middle_background_gradient_color'];
?> 0%,#<?php echo $common_informationRecord['darkest_background_gradient_color']; ?> 0%,#<?php echo
$common_informationRecord['lightest_background_gradient_color']; ?> 100%); 
background: -o-linear-gradient(-45deg,  #bf6e4e 0%,#<?php echo
$common_informationRecord['darkest_background_gradient_color']; ?> 0%,#<?php echo
$common_informationRecord['lightest_background_gradient_color']; ?> 100%); 
background: -ms-linear-gradient(-45deg,  #<?php echo $common_informationRecord['middle_background_gradient_color']; ?>
0%,#<?php echo $common_informationRecord['darkest_background_gradient_color']; ?> 0%,#<?php echo
$common_informationRecord['lightest_background_gradient_color']; ?> 100%); 
background: linear-gradient(135deg,  #<?php echo $common_informationRecord['middle_background_gradient_color']; ?>
0%,#<?php echo $common_informationRecord['darkest_background_gradient_color']; ?> 0%,#<?php echo
$common_informationRecord['lightest_background_gradient_color']; ?> 100%); 
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#<?php echo
$common_informationRecord['darkest_background_gradient_color']; ?>', endColorstr='#<?php echo
$common_informationRecord['lightest_background_gradient_color']; ?>',GradientType=1 ); 
 -ms-filter: "progid:DXImageTransform.Microsoft.gradient(GradientType=0,startColorstr='#<?php echo
$common_informationRecord['darkest_background_gradient_color']; ?>', endColorstr='#<?php echo
$common_informationRecord['lightest_background_gradient_color']; ?>')"; 

 

height: 100%;
    margin: 0;
    background-repeat: no-repeat;
    background-attachment: fixed;
}



To accommodate IE9, I also inserted the following in the head of each of my viewers and added the class="gradient" to my
body tag:


 <!--[if gte IE 9]>
  <style type="text/css">
    .gradient {
       filter: none;
    }
  </style>
<![endif]-->



To temporarily display a solid color background in IE until the "fix" is in (***solved with the compatibility mode
switch above***), I added this to each of my viewers as well:

<!--[if lte IE 8]>
  <style type="text/css">
    body{
        
  background: #<?php echo $common_informationRecord['ie_page_background_color'?>;  
  }
   .gradient {
       filter: none;
    }    
  </style>
<![endif]-->


POPULATING A FORM FROM A MULTI RECORD DATABASE, UPDATING RECORDS, CREATING NEW RECORDS - Jul 15th, 2020

Updated 1/28/19 mysql_insert and  mysql_update code

To pre-populate forms so that information about a specific record can be viewed and updated if desired, you’ll need to
choose a field value that can uniquely identify that record. It can be an email address, the record number, or any other
easily input unique identifier.

This recipe uses the email address field of a Publicity Contacts database. 

It also uses two forms. 

A short, email address submission form, and a longer form that can display all the desired information in your record
and either updated or inserted into a new record. 

For security purposes, access to this page should be restricted to appropriate individuals, but that’s covered
elsewhere in the cookbook.

First you’ll need to cycle through all of the existing records in your table and compare the submitted email address
with those found.

If a match is found, the main form needs to be populated with the existing information from the matching record, and a
procedure for updating that information begun. 

If there is no match, and a new record is to be created, appropriate error checking for required fields needs to take
place before the record is created.

At each step, there need to be appropriate error messages to guide the user.

Here’s one approach to accomplishing this task:

At the top of the viewer, just after your normal list records calls, there are some blocks of code.

The code to pre-populate the form fields if a match is found after an email address is submitted.


<?php if (@$_REQUEST['save2']  ) :?>
<?php 

 $email_address $_REQUEST['email_address'] ;
 list(
$publicity_listingsRecords$publicity_listingsMetaData) = getRecords(array(
    
'tableName'   => 'publicity_listings',
    
'allowSearch' => false,
    
'where'      => "email_address = '$email_address'"
  ));
 
?>
<?php foreach ($publicity_listingsRecords as $record):?>
<?php  foreach ($record as $name => $value):?>
<?php if (array_key_exists($name$_REQUEST)) { continue; }
    
$_REQUEST[$name] = $value;
  
?>
<?php endforeach; ?>
<?php endforeach; ?>
<?php endif ?>

The code to set a counter ($count) if a matching email addresses is found, 

<?php $count0 ?>
<?php // check for matching email
    $email mysql_escape(@$_REQUEST['email_address']);
$count mysql_count('publicity_listings'"email_address = '$email'");
?>

Based on that count, the code to determine if the next process will be to update an existing record  or insert a new
record

<?php // Set a $show update variable to change the form submit name to “Save” if no duplicate emails are found?>
<?php if ($count == 1):?>
<?php $showupdate 1 ?>
<?php else:?>
<?php $showupdate 0 ?>
<?php endif ?>

the code to check for errors if a new record is to be created,


<?php
  
  // process form
  if (@$_REQUEST['save']) {

    
// error checking
    $errorsAndAlerts "";
    if (!@
$_REQUEST['first_name'])                { $errorsAndAlerts .= "Please enter a first_name<br />\n"; }
    if (!@
$_REQUEST['last_name'])                { $errorsAndAlerts .= "Please enter a last_name<br />\n"; }
    
// if (!@$_REQUEST['media_type'])                { $errorsAndAlerts .= "Please select a media type<br />\n"; }
 if (!@$_REQUEST['specialty_group'])                { $errorsAndAlerts .= "Please select a specialty group<br />\n"; }
    if (!@
$_REQUEST['source'])                { $errorsAndAlerts .= "Please select a source<br />\n"; }
    if (!@
$_REQUEST['email_address'])                   { $errorsAndAlerts .= "Please enter an email address<br />\n"; }
    elseif(!
isValidEmail(@$_REQUEST['email_address'])) { $errorsAndAlerts .= "Please enter a valid email
address(example: user@example.com)<br />\n"; }
    
  

    
// turn off strict mysql error checking for: STRICT_ALL_TABLES
    mysqlStrictMode(false); // disable Mysql strict errors for when a field isn't defined below (can be caused when
fields are added later)

?>

The code to insert a new record into the database.

<?php 
if ($count == &&!$errorsAndAlerts ) {
 
$tableName 'publcity_listings';
     
$colsToValues = array();
      
$colsToValues['createdDate=']     = 'NOW()';
      
$colsToValues['updatedDate=']     = 'NOW()';
      
$colsToValues['createdByUserNum'] = 0;
      
$colsToValues['updatedByUserNum'] = 0;
      
$colsToValues['department']            = $_REQUEST['department'];
      
$colsToValues['first_name']             = $_REQUEST['first_name'];
       
$colsToValues['last_name']         = $_REQUEST['last_name'];
      
$colsToValues['title']            = $_REQUEST['title'];
      
$colsToValues['email_address']             = $_REQUEST['email_address'];
       
$colsToValues['street_address']         = $_REQUEST['street_address'];
      
$colsToValues['city']            = $_REQUEST['city'];
      
$colsToValues['state']             = $_REQUEST['state'];
      
$colsToValues['zip']            = $_REQUEST['zip'];
      
$colsToValues['phone']             = $_REQUEST['phone'];
      
$colsToValues['cell']            = $_REQUEST['cell'];
            
$colsToValues['description']            = $_REQUEST['description'];
      
$userNum mysql_insert($tableName$colsToValuestrue);
// On Success    
$first_name $_REQUEST['first_name'];
$last_name $_REQUEST['last_name'] ;
      
$_REQUEST        = array(); // clear form values
      $errorsAndAlerts "A record for $first_name $last_name has been successfully created.<br />You can now enter
another email address.";
    }
?>

Some additional error checking code,

<?php 
if ( @$count 0  && @$_REQUEST['save2'] && @!$_REQUEST['go']) {
$errorsAndAlerts "The email address $email already exists in the Publicity database.<br />Update the information below
as required, then re-submit the form.<br /><br />Remember, before you can update a contact's information, you must check
the check box above the 'Submit' button.";        
    
}

if ( @
$count == && @$_REQUEST['save2']  ) {

 
$email = @$_REQUEST['email_address'];
 
  
$errorsAndAlerts "The email $email is not currently in the Publicity database.<br /> Please fill out the form below
to create a new record for this contact.";    
}
?>

And, the code to update an existing record,

<?php if (@$count && @$_REQUEST['go'] && @$_REQUEST['save2'] ): ?>
<?php 
<?php $where "email_address = '$email_address'"?>
<?php $email = @$_REQUEST['email_address'];
      
$tableName 'publcity_listings';
      
$colsToValues = array();
      
$colsToValues['department']            = $_REQUEST['department'];
      
$colsToValues['first_name']             = $_REQUEST['first_name'];
       
$colsToValues['last_name']         = $_REQUEST['last_name'];
      
$colsToValues['title']            = $_REQUEST['title'];
      
$colsToValues['email_address']             = $_REQUEST['email_address'];
       
$colsToValues['street_address']         = $_REQUEST['street_address'];
      
$colsToValues['city']            = $_REQUEST['city'];
      
$colsToValues['state']             = $_REQUEST['state'];
      
$colsToValues['zip']            = $_REQUEST['zip'];
      
$colsToValues['phone']             = $_REQUEST['phone'];
      
$colsToValues['cell']            = $_REQUEST['cell'];
      
$colsToValues['description']            = $_REQUEST['description'];
      
mysql_update($tableNamenull$where$colsToValues);
    
// on success
       $first_name = @$_REQUEST['first_name'] ;
       
$last_name = @$_REQUEST['last_name'] ;
         
$_REQUEST        = array(); // clear form values
      $errorsAndAlerts "The contact information for $first_name $last_name has been updated.<br />You can now enter
another email address.";
 
?>
<?php endif ?>

In the body of the viewer are some identifying information

<h1 class="body-text-white-bold-11">ADD/MODIFY PUBLICITY RECORDS</h1>
      <span class="body-text-white-10">Enter as much information as you can.<br />
      A </span><span class="body-text-bold-red-11">*</span> <span class="body-text-white-10">indicates a required
field.</span> 
      <!-- PUBLICITY RECORD FORM -->
      <?php if (@$errorsAndAlerts): ?>
      <div style="color: #C00; font-weight: bold; font-size: 14px; font-family: arial;"><br />
        <?php echo $errorsAndAlerts?><br />
      </div>
      <?php endif ?>
      <br />
      
      <?php if (@$errorsAndAlerts ==""):?><br /><span class="body-text-white-11">Enter the contact's email address
here.<br />
      If it exists, the form will be populated with the existing information and can be updated.<br />
      If not, you can add a new record for this contact.</span><br /> <?php endif ?>

The email address submission form   

   <form method="post" action="?">
        <input type="hidden" name="save2" value="1" />
        <table border="0" cellspacing="10" cellpadding="12">
          <tr>
            <td class=" body-text-bold-white-10">Email Address</td>
            <td><input class="form" type="text" name="email_address" value="<?php echo
htmlspecialchars(@$_REQUEST['email_address']); ?>" size="50" /></td>
          </tr>
          <tr>
            <td colspan="2" align="center"><br />
              <input class="button" type="submit" name="save2" value="Click To Enter Email &gt;&gt;" /></td>
          </tr>
        </table>
      </form>

And the main information form (note the use of $showupdate variable to set the update confirmation check box and the
form name attributes for either updating or inserting records and the hidden email_address field so that the user is
forced to pre-populate existing records when email addresses match):

<form method="post" action="?">
        <input type="hidden" <?php if ($showupdate == ): ?>name="save"<?php else:?>name="save2"<?php endif ?>
value="1" />
        <table border="0" cellspacing="10" cellpadding="12">
          <tr>
            <td colspan="2"><hr  color="#b2b2b2" /></td>
          </tr>
          <tr>
            <td colspan="2"><hr  color="#b2b2b2" /></td>
          </tr>
                  <tr>
            <td colspan="2"><hr  color="#b2b2b2" /></td>
          </tr>
          <tr>
            <td class=" body-text-bold-white-10">Organization</td>
            <td><input class="form" type="text" name="organization" value="<?php echo
htmlspecialchars(@$_REQUEST['organization']); ?>" size="50" /></td>
          </tr>
          <tr>
            <td class=" body-text-bold-white-10">Department</td>
            <td><input class="form" type="text" name="department" value="<?php echo
htmlspecialchars(@$_REQUEST['department']); ?>" size="50" /></td>
          </tr>
          <tr>
            <td class=" body-text-bold-white-10"><span class="body-text-bold-red-11">*</span> First Name</td>
            <td><input class="form" type="text" name="first_name" value="<?php echo
htmlspecialchars(@$_REQUEST['first_name']); ?>" size="50" /></td>
          </tr>
          <tr>
            <td class=" body-text-bold-white-10"><span class="body-text-bold-red-11">*</span> Last Name</td>
            <td><input class="form" type="text" name="last_name" value="<?php echo
htmlspecialchars(@$_REQUEST['last_name']); ?>" size="50" /></td>
          </tr>
          <tr>
            <td class=" body-text-bold-white-10">Title</td>
            <td><input class="form" type="text" name="title" value="<?php echo htmlspecialchars(@$_REQUEST['title']);
?>" size="50" /></td>
          </tr>
          <tr>
            <td colspan="2"><hr  color="#b2b2b2" />
              <input type="hidden" class="form" name="email_address" value="<?php echo
htmlspecialchars(@$_REQUEST['email_address']); ?>"  /></td>
          </tr>
          <tr>
            <td class=" body-text-bold-white-10">Street Address</td>
            <td><input class="form" type="text" name="street_address" value="<?php echo
htmlspecialchars(@$_REQUEST['street_address']); ?>" size="50" /></td>
          </tr>
          <tr>
            <td class=" body-text-bold-white-10">City</td>
            <td><input class="form" type="text" name="city" value="<?php echo htmlspecialchars(@$_REQUEST['city']); ?>"
size="50" /></td>
          </tr>
          <tr>
            <td class=" body-text-bold-white-10">State</td>
            <td><input class="form" type="text" name="state" value="<?php echo htmlspecialchars(@$_REQUEST['state']);
?>" size="50" /></td>
          </tr>
          <tr>
            <td class=" body-text-bold-white-10">Zip Code</td>
            <td><input class="form" type="text" name="zip" value="<?php echo htmlspecialchars(@$_REQUEST['zip']); ?>"
size="50" /></td>
          </tr>
          <tr>
            <td colspan="2"><hr  color="#b2b2b2" /></td>
          </tr>
          <tr>
            <td class=" body-text-bold-white-10">Land Phone</td>
            <td><input class="form" type="text" name="phone" value="<?php echo htmlspecialchars(@$_REQUEST['phone']);
?>" size="50" /></td>
          </tr>
          <tr>
            <td class=" body-text-bold-white-10">Cell Phone</td>
            <td><input class="form" type="text" name="cell" value="<?php echo htmlspecialchars(@$_REQUEST['cell']); ?>"
size="50" /></td>
          </tr>
          <tr>
            <td colspan="2"><hr  color="#b2b2b2" /></td>
          </tr>
          <tr>
            <td class=" body-text-bold-white-10">Additional Information</td>
 <!-- Note: The first preg_replace <br /> should have no slash and no spaces between the <br and the >, the second a <br
with no space before the slash>, the third with a space before the slash>) -->
 <?PHP @$_REQUEST['description'] = preg_replace("[<br />]""", @$_REQUEST['description'] ); // suppress  in text area
?>
            <?PHP @$_REQUEST['description'] = preg_replace("[<br />]""", @$_REQUEST['description'] ); ?>
            <?PHP @$_REQUEST['description'] = preg_replace("[<br />]""", @$_REQUEST['description'] ); ?>
             <td style="text-align:left"><textarea cols="50" rows="5"  name="description" ><?php echo
@
$_REQUEST['description']; ?></textarea></td>
          </tr>
          <tr>
            <td colspan="2"><hr  color="#b2b2b2" /></td>
          </tr>
          <?php if ($showupdate == ): ?>
          <tr>
            <td colspan="2"><input type = "checkbox" id="go" name="go" value = "1" <?php checkedIf(1,
@
$_REQUEST['go']);?> />
              <span class=" body-text-bold-white-10">When you're ready to update, check this box and click
"Submit"</span></td>
          </tr>
          <?php endif ?>
          <tr>
            <td colspan="2" align="center"><br />
              <input class="button" type="submit" <?php if ($showupdate == ): ?>name="submit"<?php
else:?>name="submit2"<?php endif ?> value="Submit &gt;&gt;" /></td>
          </tr>
        </table>
      </form>


PRE-POPULATING RADIO BUTTONS IN FORMS FROM A MASTER LIST - May 28th, 2019

When used in a form, radio buttons allow only one value to be chosen from a list of values.

The trick here was to populate the list of values from the values available in separate master list, and use that list
to populate a radio button list field in the accounts section (some of the functions in this example require the Website
Membership plugin).

Here’s are the steps...

1) Create a multi-record section (called “my_values”) with a “hidden’ check box and a text field called
“contents”

2) In the accounts section, create a radio button list field (called my_list_field) that gets its options from the MySQL
query: 

SELECT num, contents
  FROM `<?php echo $TABLE_PREFIX ?>my_values`
WHERE hidden = 0
ORDER BY dragSortOrder  ASC ;

3) Near the top of the viewer that contains your form, after the load records calls, add the following variable to the
list of variables: 
 
@$my_list_field = @$_REQUEST['my_list_field'] ;

4) After any error checking include the following in your $colsToValues array

@$colsToValues['my_list_field']         = $_REQUEST['my_list_field'] ;


5) to pre-populate your form from current user values in the accounts table

  // pre-populate form with current user values
  foreach ($CURRENT_USER as $name => $value) {
    if (array_key_exists($name, $_REQUEST)) { continue; }
    $_REQUEST[$name] = $value;
  }


5) In your form, to show a radio button list of all values that are not hidden in the "my_values" section, use the code:

<tr>
<td valign="top">My Available Values</td>
<td>
<?php $fieldname 'your_list_field'?>
<?php $idCounter 0?>
<?php foreach (getListOptions(accountsTable(), $fieldname) as $value => $label): ?>
<?php $id "$fieldname." . ++$idCounter?>
<input type="radio" name="<?php echo $fieldname ?>" id="<?php echo $id ?>"
 value="<?php echo htmlencode($value?>" <?php checkedIf(@$_REQUEST[$fieldname], $value?> />
<label for="<?php echo $id ?>"><?php echo htmlencode($label?></label>
<?php endforeach ?>
</td>
</tr> 


Another Approach (shows all values):


<?php $fieldname 'practice_country'?>
<?php $idCounter 0?>
<?php foreach (getListOptions(accountsTable(), $fieldname) as $value => $label): ?>
<?php $id "$fieldname." . ++$idCounter?>
<input id="<?php echo $id ?>" name="<?php echo $fieldname ?>" type="radio" value="<?php echo htmlencode($value?>" />
<label for="<?php echo $id ?>"><?php echo htmlencode($label?></label>

<?php endforeach ?>


<b>To show only values that are not hidden</b>:
Since getListOptions() will get all available options for a list field, if you want to only show countries that have
been selected by non-hidden records, here's a different approach.

<?php $fieldname 'practice_country'?>
<?php $tablename accountsTable(); ?>
<?php
  // get all table records
  list($tableRecords$tableRecordsMeta) = getRecords(array(
    
'tableName'     => $tablename,
    
'loadUploads'   => false,
    
'loadCreatedBy' => false,
    
'allowSearch'   => false,
  ));
  
  
// group records by target field to find option keys
  $tableRecordsByField array_groupBy($tableRecords$fieldname);
  
$keys                  array_keys($tableRecordsByField);
  
  
// create option list
  $listOptions = [];
  foreach(
$keys as $key) {
    if (!empty( 
$key )) { 
      
$listOptions$key ] = $tableRecordsByField$key ][ $fieldname.':label' ];
    }
  }
?>

This will create the variable $listOptions that should contain all of the currently in-use list values and labels for
the target table/field. This should let you then update your foreach to look like this:

<?php foreach ($listOptions as $value => $label): ?>
              <?php $id "$fieldname." . ++$idCounter?>
              <input id="<?php echo $id ?>" name="<?php echo $fieldname ?>" type="radio" value="<?php echo
htmlencode($value?>" <?php checkedIf(@$_REQUEST[$fieldname], $value?>/>
              <label for="<?php echo $id ?>"><?php echo htmlencode($label?></label>
              
              <?php endforeach ?>

Note that this will only work for single-value list fields, such as radio buttons or single dropdowns.


TESTING FOR A VARIABLE PASSED AT THE END OF A URL - Sep 30th, 2013

I needed to display some specific text on my products_page.php based on the value that was passed at the end of a URL
when a user clicked a link to get to that page. In my case the link that were passed looked like this, with numbers at
the end:

products_page.php?product=1
products_page.php?product = 2

but the URL could also have a text value at the end:

products_page.php?product=“product1"

Jason Sauchuk from Interactive Tools suggested defining a variable ($var1) like this:

<?php  $var1=$_REQUEST['product']; ?>

With the variable defined, I could use if statements to produce the desired text.

<?php if ($var1 == 1):?> Text for value “1"
<?php elseif ($var1 == 2):?>Text for value “2"
<?php elseif ($var1 == “product1"):?>Text for value “product1"
<?php endif ?>



 


LETTING CLIENTS CHOOSE THEIR OWN COLOR SCHEME AND OTHER PAGE PARAMETERS - Sep 17th, 2014

If you want your client to be able to set various page parameters themselves, here;s one approach:

This example is about page background colors (or any other color choices)

Create a multi record editor (I called it “Color Choices”) with only 2 text fields: Color (I.E.: Red, Blue, Green,
etc.) and HEX Color( I.E. FF0000, 00FF00, 0000FF) with no # sign (if your client will have access to this editor you
might want to limit input to min and max characters 6 and only allow the characters AaBbCcDdEeFf1234567890 since others
are not valid in hex code. (or you can choose to not show the editor in the menu under advanced options)
Create a record for each color you want to allow. 

Then create a single record editor called “clients choices” and in that editor create a list field called “Page
Background” that gets its options from the database “Color Choices” with the option values  from the
“hex_color” field and the option labels from the “color_choices” field

Last, where you want the color choice in your viewer, (I.E. body bgcolor=), insert the <body  bgcolor="#<?php echo
$client_choicesRecord[‘page_background'] ?>">

You can use the “Color Choices” editor values for any colors on your viewers by adding another list field in the
“Client Choices” editor. Just name the list fields appropriately so you can keep track of them all. 

The concept will also work for any other parameters as well (fonts, etc). Just create a new multi record editor for each
set of parameters that are not colors, and add another appropriately named list field to the “client choice” single
record editor.


ADDING FEATURED PERFORMERS FROM A MASTER DATABASE TO AN EVENT RECORD VIEWER - Jan 28th, 2021

ADDING FEATURED PERFORMERS TO AN EVENT RECORD
For Jazz on J Street events, my client wanted me to create a multi record ‘musicians_listing’ section to contain
images and information about each musician who had performed at their events.

I wanted to be able to access that information on my client’s  events pages as well. To accomplish that, I  chose to
use the new ‘Pillbox’ list field format to list the featured performers for each event. 

Since you can’t rearrange the current ‘pills’ in a pillbox field, I used a custom pillbox plugin available in the
‘Packaged Solutions’ section of this website at: http://thecmsbcookbook.com/templates_detail.php?4 The plugin adds
the ability to drag the position of pills in the pillbox. (This is a function that may, or may not be added to a future
CMSB release.) 

To populate specific ‘events’ and listen_live listings with the information in the ‘musicians_listings’ section
in (described below),  I used the  beta_lookupRelatedFields plugin, a free plugin available at
http://thecmsbcookbook.com/downloads/relatedRecordLookupFunctions.zip Using the Related records plugin is covered in the
recipe at http://thecmsbcookbook.com/recipedetail.php?273 but I’ll also include the required code in this recipe.

The first thing is a multi record editor which I called ‘Musicians Listings’.This editor has a record for each
musician or group that’s performing. It has ‘text’ fields for the musician’s ‘first_name’,
‘middle_name’, ‘last_name’, ‘full_performer_or_group_name, ‘instruments_ played’, and ‘description’,
and an upload field for a performer image. Note: Every performer must have their ‘full_performer_or_group_name’
field filled in for this scheme to work.

To set up my pillbox I added a ‘list’ field called ‘performers’ to both my ‘listen_live’ streamed event
listings editor and my ‘events’ listing editor. I chose pillbox (multi value) as the ‘Display As’ type, chose
‘Get options from database (advanced)’ as the list options type and used the ‘num’ field as the option values
and the ‘full_performer_or_group_name’ for the option labels.

You can display the new pillbox information in a viewer with this simple code taken from the code generator, but that
will only display the values of the ‘full_performer_or_group_name field from the ‘performers’ pillbox field in the
‘listen_live’ or ‘events’records.

<?php echo join(', '$listen_liveRecord['pills:labels']); ?>


The real magic comes in when you can show the data stored in other fields in the ‘musicians_listings’section records
the in the  ‘events’ or ‘listen_live’ viewers. Or create live links in those viewers to the complete
musicians_listings’ record for that performer. Thanks to some help from Interactive tools programmer Carl Wuensche ,wa
sable to accomplish that for both list pages and detail pages with the code below .

For a list page of your ‘listen_live records you could use the following:

At the top of your page the load records call for the ‘musucians_listings’

  // load records from 'musicians_lisings'
  list($musicians_lisingsRecords, $musicians_lisingsMetaData) = getRecords(array(
    'tableName'   => 'musicians_lisings',
    'loadUploads' => true,
    'allowSearch' => false,
 ));
 


And in the body where you want to show the ‘listen_live’ record information with the related record pillbox
information, use the code below:

NOTE: The where statement will only show events with a ‘presentation_date’ coming up in the next 31 days. You can
comment that out, or change it to reflect your specific needs. If you want to show listings from another group, copy the
entire code block again and change the where statement to your reflect your specific needs. 
Some examples are:
This weeks events 

'where' => '((NOW() + INTERVAL 7 DAY) >= presentation_date  AND presentation_date >= TIMESTAMP(CURDATE(), "00:00:00"))',

Past Events:

'where' => 'presentation_date <= TIMESTAMP(CURDATE(), "00:00:00"),

Events more than 31 days in the future

  'where' => 'presentation_date <= presentation_date > TIMESTAMP(NOW()+ INTERVAL 31 DAY)',
 


 
 
<!-- BEGIN PERFORMER LISTING CODE -->
<?php // only retrieve records where the presentation date is within the upcoming 31 days
    list($listen_liveRecords$listen_liveMetaData) = getRecords(array(
    
'tableName'   => 'listen_live',
      
'where' => '((NOW() + INTERVAL 31 DAY) >= presentation_date AND presentation_date >= TIMESTAMP(CURDATE(),
"00:00:00"))',   
      
      
'orderBy'=> 'presentation_date ASC',
      ));
        
      
beta_lookupRelatedFields(array(   
    
'table'      => 'listen_live',   
    
'recordList' => &$listen_liveRecords,   
    
'fieldList'  => array( 'performers' )   
  
  )); 
  
$listen_liveRecord = @$listen_liveRecords[0]; // get first record   
?>
 <table width="100%" border="0" align="center">
 <?php foreach ($listen_liveRecords as $record): ?>
<?php // Your Event Name, Date, and Time Information Goes Here ?>
 
<?php if ( !empty($record['performers'])):?>
<?php  foreach ($record['performers'] as $performer):?>
 <tr>
<td width="25%"><a href="musicians_detail.php?<?php echo $performer['num'?>">
 <?php foreach ($performer['list_page_image'] as $upload): ?>
 <img style="padding-right: 10px; border:hidden" src="<?php echo $upload['thumbUrlPath'?>" width="<?php echo
$upload['thumbWidth'?>" height="<?php echo $upload['thumbHeight'?>" alt="" />
<?php endforeach ?> </a>
</td>
 <td><span class="sub_heading_font">Featuring: </span><a href=”musicians_detail.php?<?php echo $performer['num']
?>"><span class="sub_heading_font" style="text-decoration:underline"> <?php $full_performer_or_group_name =
strtolower($performer['full_performer_or_group_name']); 
echo 
ucwords($full_performer_or_group_name?> - <?php echo $performer['medium'?></span> </a>
</td>
 </tr>
<tr>
<td colspan="2"><hr />
</td>
 </tr>
 <?php endforeach ?>
<?php endif ?>
<!--  END PERFORMER LISTING CODE -->
 <?php endforeach ?>
  </table>      


For an ‘events’ detail page where you’re getting the record number from the end of the URL, the code is a bit
different:

At the top of the page:


 // load record from 'listen_live'
  list($listen_liveRecords, $listen_liveMetaData) = getRecords(array(
    'tableName'   => 'listen_live',
    'where'       => whereRecordNumberInUrl(0),
    'loadUploads' => true,
    'allowSearch' => false,
    'limit'       => '1',
  ));
  $listen_liveRecord = @$listen_liveRecords[0]; // get first record
  
   // load records from 'musicians_lisings'
  list($musicians_lisingsRecords, $musicians_lisingsMetaData) = getRecords(array(
    'tableName'   => 'musicians_lisings',
    'loadUploads' => true,
    'allowSearch' => false,
 // 'orderBy' => 'concert_lineup_order DESC',
  ));
  
   beta_lookupRelatedFields(array(   
    'table'      => 'listen_live',   
    'recordList' => &$listen_liveRecords,   
    'fieldList'  => array( 'performers' )   
  
  )); 
  $listen_liveRecord = @$listen_liveRecords[0]; // get first record 
?> 


And in the body of the page where you want to show the event information:

<?php // Your Expanded Event Name, Date, Time and Event Description  Information Goes Here ?>
   
<!-- BEGIN PERFORMER LISTING CODE -->
<?php  if (!empty($listen_liveRecord['performers'])): ?>
 <table>
 <tr>
 <td><span class="heading_font">
 Learn More About This Month's Featured Performers:</span></td>
 </tr>
 <tr>
 <td ><hr  /></td>
</tr>
 <?php  foreach ($listen_liveRecord['performers'] as $performer): ?>
<tr>
<td width="25%"><a href="musicians_detail.php?<?php echo $performer['num'?>">
<?php foreach ($performer['list_page_image'] as $upload): ?>
<img style="padding-right: 10px; border:hidden" src="<?php echo $upload['thumbUrlPath'?>" width="<?php echo
$upload['thumbWidth'?>" height="<?php echo $upload['thumbHeight'?>" alt="" />
<?php endforeach ?></a> 
<a href="musicians_detail.php?<?php echo $performer['num'?>"><span style="text-decoration:underline"> <?php echo
$performer['full_performer_or_group_name'?> - <?php echo $performer['instruments'?></span>  </a></td>
</tr>
 <tr>
 <td >
<hr />
</td>
 </tr>
 <?php endforeach ?>
 </table>
 <?php endif ?>
<!--  END PERFORMER LISTING CODE -->



AUTOMATIC EVENT RECORD CREATION - Jan 28th, 2021

I needed to automatically create the event records for 2 monthly events on a site for my client, Jazz on J Street.  JOJS
Presents! Events, (in a multi record ‘events’ section) and JOJS Live Jam Sessions (in a multi record
‘listen_live’ section.) 

I had also created a multi record ‘musicians_listing’ section earlier and wanted to access that information on the
events page using the new ‘Pillbox’ list field format to list the featured performers for each event. Both had a
number of additional criteria that needed to be met. All that’s in another recipe at:
http://thecmsbcookbook.com/recipedetail.php?620

Here’s how to automatically create event records every month:

There were a number of client required variables:
1) The events almost always occurred on the same day and time every month (second Monday, Third Saturday, etc.), but
that could change. 
2) JOJS Presents! events were normally only listed on the site’s special events page, and JOJS JAM Sessions were only
normally listed on their live streamed events page., but that could change. 
3) JOJS Presents! Events normally had featured performers listed but the Jam Sessions normally didn’t, but that could
change.
4) Their live streaming event page has a next event count down timer that needed to reflect the actual start time for
each event.

To deal with the required variables, I set up a single record editor called ‘Event Parameters’ with the following
fields with field descriptions about the required textual formats:

6 Text fields
JOJS Jam Day (of the month) - This should be entered as Third Tuesday, Last Friday, 1st Monday, etc.  
JOJS Presents Day (of the month) - This should be entered as Third Tuesday, Last Friday, 1st 
JOJS Jam Start Time - This should be entered as  7:00 PM or 8:30 PM, etc.
JOJS Presents Start time - This should be entered as  7:00 PM or 8:30 PM, etc.
JOJS Jam Countdown Start time -This should be entered in an hh:mm:ss format, so 7 PM would be 19:00:00 and 8:30 PM would
be 20:30:00.
JOJS Presents Countdown Start time - This should be entered in an hh:mm:ss format, so 7 PM would be 19:00:00 and 8:30 PM
would be 20:30:00.

2 Check Boxes
Streaming JOJS Presents - Check if JOJS Presents is going to be streamed going forward
Create JOJS Jam Event Listing - Check if JOJS Jams should have an event listing going forward

I sorted these under the separator headers:
'START TIMES FOR COUNTDOWN TIMER'
'JOJS WEEK, DAY AND TIMES':

HELP PAGE ENTRY
The help page entry for ‘Automatic creation of JOJS JAM and JOJS Presents! records’ is as follows:
There is a new editor called called ‘Event Parameters’ that control the parameters for automatic creation of JOJS
Jam and JOJS Presents! records. 
They are: 
Under 'START TIMES FOR COUNTDOWN TIMER':
'JOJS JAM Countdown Start time' and 'JOJS Presents Countdown Start time'. These should be entered in an hh:mm:ss format,
so 7 PM would be 19:00:00 and 8:30 PM would be 20:30:00.

Under 'JOJS WEEK, DAY AND TIMES':
'JOJS JAM Day' and 'JOJS Presents Day'. These should be entered as Third Tuesday, Last Friday, 1st Monday, etc.  
'JOJS JAM Start Time' and 'JOJS Presents Start Time'. These should be entered as  7:00 PM or 8:30 PM, etc.

It is assumed that JOJS Presents! events will not be streamed for the time being. If that changes going forward, check
the 'Streaming JOJS Presents' check box
It is assumed that JOJS Jam Sessions are listed only as 'listen-live'  events. If that changes going forward, check the
'Create JOJS Jam Event Listing' check box

Then, I created 2 record creation pages that are triggered by cron jobs on my cPanel once a month. If you're using the
Auto Backup plugin, you can get the basic command line format for the cron jobs from your 'Admin' > 'Background Tasks'
page, or from the cron tab on your cPanel. 

Just an aside, If you're not using the official Auto Backup plugin on all of your sites, I strongly suggest that you get
it (only $59.95 for a single site and $89.95 for unlimited use) and use it.
https://www.interactivetools.com/plugins/auto-backup/

I’ve combine the code for the 2 separate pages, so make 2 copies of the code below and comment out the 2 appropriate
lines for each of the pages. You can add any $colsToValues entries that you need for your specific application.


<?php  list($event_parametersRecords$event_parametersMetaData) = getRecords(array(
    
'tableName'   => 'event_parameters',
    
'where'       => ''// load first record
    'limit'       => '1',
     
'allowSearch' => false,
  ));
  
$event_parametersRecord = @$event_parametersRecords[0]; // get first record
  ?>
<?php 
// IMPORTANT! Comment out the next line for the 'Create Jam Event' page 
$presentsEventDay strtolower($event_parametersRecord['jojs_presents_day']); 
// IMPORTANT! Comment out the next line for the 'Create Presents Event' page 
$presentsEventDay strtolower($event_parametersRecord['jojs_jam_day']); 
?>
<?php 
$this1 'of this month';
$next1 'of next month';
$resultThis $presentsEventDay ' ' $this1
$resultNext $presentsEventDay ' ' .$next1
?>
<?php $eventDate = new DateTime($resultThis); ?>
<?php $currentEventDate = new DateTime($resultThis); ?>
<?php $currentEventDate1 = new DateTime($resultThis); ?>
<?php
$nextEventDate = new DateTime($resultNext); ?>
<?php if ($eventDate < new DateTime()) {
    
$eventDate->modify($resultNext); 

?>
<?php  $date2 = ($currentEventDate); ?>
<?php $date2->modify('+2 day'); ?>

<?php $currentEventDate2 $date2->format('F d, Y'); ?>
<?php $date4 = ($currentEventDate1); ?>
<?php  $date4->modify('+4 day'); ?>

<?php $currentEventDate4 $date4->format('F d, Y'); ?>
<?php $today date('F d, Y'); ?>
<?php if(($today $currentEventDate2)  && ($today $currentEventDate4)): ?>
<?php
// turn off strict mysql error checking for: STRICT_ALL_TABLES
  mysqlStrictMode(false); // disable Mysql strict errors for when a field isn't defined below (can be caused when fields
are added later)

  
// add record to listen_live streaming editor

    $tablename   'listen_live';
       
$colsToValues = array();
       
$colsToValues['hidden'] = 0;
       
$colsToValues['createdDate=']     = 'NOW()';
       
$colsToValues['updatedDate=']     = 'NOW()';
       
$colsToValues['createdByUserNum'] = 0;
       
$colsToValues['upcoming_event_status'] = 0;
       
$colsToValues['updatedByUserNum'] = 0;
       
$colsToValues['neverRemove']    = 1;
       
$colsToValues['is_this_a_jojs_presents_event']    = 1;
       
$colsToValues['publishDate=']     = 'NOW()'
       
$colsToValues['presentation_date']    = $eventDate->format('Y-m-d 00:00:00'); 
       
$colsToValues['travel_directions']    = 1;
       
$colsToValues['time']    = "7:00 to 10:00 PM";
       
$colsToValues['youtube_streaming_shortcode']    = 'your youtube channel id here (not the complete url)';
       
$colsToValues['travel_directions']    = "your travel directions here";
       
$colsToValues['attendance_fee']    = '10 at the door';
       
$colsToValues['instructions']    = 'Listen live records are created automatically a few days after the current event
is over. A default image will be shown if there is no special image uploaded. If any information that is unique to this
Jam Session, it must be changed manually.';
       
$colsToValues['announcement_lead']    = 'New talented and innovative Professional Jazz musicians are featured LIVE
every month';
       
// Remove any $colsToValues that don't apply anda dd your own $colsToValues entries here
       
    //  IMPORTANT!  Comment out the next line for 'Create Presents' Event page
      if($event_parametersRecord['streaming_jojs_presents'] == 1){ $newRecordNum mysql_insert($tablename$colsToValues,
true);} 
       
       
// add record to the events editor 

       $tablename2   'events';
       
$colsToValues = array();
       
$colsToValues['hidden'] = 0;
       
$colsToValues['createdDate=']     = 'NOW()';
       
$colsToValues['updatedDate=']     = 'NOW()';
       
$colsToValues['createdByUserNum'] = 0;
       
$colsToValues['updatedByUserNum'] = 0;
       
$colsToValues['neverRemove']    = 1;
       
$colsToValues['publishDate=']     = 'NOW()'
       
$colsToValues['presentation_date']    = $eventDate->format('Y-m-d 00:00:00');
       
$colsToValues['event_title']    = 'Jazz on J Street Presents!'
       
$colsToValues['performance_times']    = "8:00 to 9:30 PM";
      
$colsToValues['travel_directions']    = "Your Travel Directions Go Here (Don\'t forget to escape all \' and \" and
any other forbidden characters";
       
$colsToValues['attendance_fee']    = '10 at the door';
       
$colsToValues['instructions']    = 'Listen live records are created automatically a few days after the current event
is over. A default image will be shown if there is no special image uploaded. If any information that is unique to this
Jam Session, it must be changed manually.';
       
$colsToValues['event_description']    = 'Your Even Description Here';
     
// Remove any $colsToValues that don't apply anda dd your own $colsToValues entries here
     
       //  IMPORTANT!  Uncomment the next linefor Auto Create Jams Event Page 
       // if($common_informationRecord['create_jojs_jam_event_listing'] == 1){  $newRecordNum = mysql_insert($tablename2,
$colsToValuestrue); }
      
?>
<?php endif ?>
</body>
</html>

 


ELIMINATING ERROR MESSAGES WHEN RECORD NUMBER IS NOT PASSED IN A DETAIL PAGE URL (BOTS) - Dec 25th, 2022

If you're using detail pages that pass a record number at the end of their URLs, bots can cause your Developer log to
fill up with hundreds of useless errors in a very short period.

Thanks to Jenna Cooke from Interactive Tools suggested the following solution.
She said: Try putting the following near the top of the page, after your getRecords call:

<?php if (!$your_sectionRecord) { dieWith404("Record not found!"); } // show error message if no record found ?>


Jenna's suggestion was very helpful, and solved the error log issue, but shows a blank white page with the words "Record
not found!".

Since there's a slim possibility that a live visitor is receiving the message and not a bot, I used a variation on the
theme that that redirects the visitor to a custom 404 page instead if a record is not found, using the following code in
the same location:  (You'll have to create your own appropriate page for this purpose)

<?php if (!$your_sectionRecord) { header('Location: http://your_site.com/your_new_page.php'); die;} // show error page
if no record found ?>


Just remember that since you're setting an HTTP-header : you must not have sent any kind of output before (not even a
blank space at the end of an included file) or you'll throw a "Warning: Cannot modify header information - headers
already sent" error. Test your page after the modification to make sure that it works as planned, and proceed
accordingly. 

The rub is that those blank spaces may not show up in your particular code editor.

When I encounter pages that throw a 'header' error, I find it easier to find a similar detail page and copy those load
viewer library and get records calls to the top of the faulty page. That usually solves the problem. 


ORDERBY WITH MULTIPLE CRITERIA - Dec 18th, 2022

To display an image carousel on my home page I needed to pull up a limited number of the most recently created records
in a section and then to randomize the resulting records so the images would appear in random order each time the page
was accessed.

After a number of tries, here's what worked for me:

<?php // load records from 'books
$order1"RAND(),`createdDate` DESC" ;
  list(
$booksRecords$booksMetaData) = getRecords(array(
    
'tableName'   => 'books',
    
'loadUploads' => true,
    
'allowSearch' => true,
    
'orderBy' => $order1,
    
'limit' => 75,
  ));
?>

VIEWERS - LISTING PAGES



CHOOSING WHICH RECORDS TO DISPLAY USING OFFSET AND LIMIT COMMANDS - Sep 26th, 2020

Nigel Gordijk from Common Sense Design needed to be able to differentiate between groups of records so that some of
their associated images could be used in an image carousel and others could be presented in a list format.

It seemed complicated until CMSB user KennyH offered this simple suggestion using a combination of the limit and offset
command in his list records calls.

Here’ his elegant solution:

Here we load the first 5 articles

  list($article_postsRecords, $article_postsMetaData) = getRecords(array(
    'tableName'   => 'article_posts',
    'limit'       => '5',
    'loadUploads' => true,
    'allowSearch' => false,
  ));

Now, we can create add this again for the next five records, but with a few modifications:

  list($article_postsSideListRecords, $article_postsSideListMetaData) = getRecords(array(
    'tableName'   => 'article_posts',
    'limit'       => '5',
    'offset'      => '4',
    'loadUploads' => true,
    'allowSearch' => false,
  ));

$article_postsRecords, $article_postsMetaData becomes $article_postsSideListRecords, $article_postsSideListMetaData

Add 'offset' => '4', to skip the first 5 records (numbering starts at zero)

Then you would display the first 5 images on the page like this


<?php foreach ($article_postsRecords as $record): ?>

<?php foreach ($record['images'] as $index => $upload): ?>
  <img src="<?php echo htmlencode($upload['urlPath']) ?>" width="<?php echo $upload['width'?>" height="<?php echo
$upload['height'?>" alt="">
<?php endforeach ?>

<?php endforeach ?>


The next 5 images pull from $article_postsSideListRecords

<?php foreach ($article_postsSideListRecords as $record): ?>

<?php foreach ($record['images'] as $index => $upload): ?>
  <img src="<?php echo htmlencode($upload['urlPath']) ?>" width="<?php echo $upload['width'?>" height="<?php echo
$upload['height'?>" alt="">
<?php endforeach ?>

<?php endforeach ?>



USING GROUPBY TO CREATE A LIST OF RECORDS THAT HAVE THE SAME VALUES AND ONLY RETURN ONE RECORD PER GROUP - May 28th, 2019

I had created a service provider directory for a client who wanted to create a list of the countries and
states/provinces that had provider listing records (accounts). They also wanted to show a count of how many listings
there were for each country and each state/province, eliminating any hidden listings.

With a great deal of help from Daniel Loewe and Greg Thomas, here’s what we came up with.

You can see the result at https://dbtproviders.com/maplist.php

Daniel first suggested using the groupBy function to create a list of records that have the same values and only return
one record per group.


list($accountsRecords, $accountsMetaData) = getRecords(array(
  'tableName' => 'accounts',
  'orderBy' => 'practice_country ASC , practice_state ASC ',
  'groupBy' => 'practice_country, practice_state',
));


Then to display the list, he suggested the following (Greg added the code for counting):


 <?php $old_group ''// init blank var.

foreach ($accountsRecords as $record): ?>
 <?php   if (!$record['practice_country:label'] == '' ):?>
            <?php   $group $record['practice_country:label']; // load sub-group value from record. ?>
            <?php  else : ?>
            <?php $group $record['other_practice_country']; ?>
            <?php  endif ?>
          <?php $stateCount    mysql_count('accounts'mysql_escapef("`practice_state` = ? AND `hidden` = '0'
",$record['practice_state'])); ?>
          <?php $countryCount  mysql_count('accounts'mysql_escapef("`practice_country` = ? AND `hidden` = '0'
",$record['practice_country'])); ?>
          <div class="rTableRow">
            <div class="rTableCell text_font">
              <?PHP
if ($group !== $old_group) {echo "<h3>$group ($countryCount Listings)</h3>";} ?>
              <?php $state = ($record['practice_state']); ?>
              <?php $country = ($record['practice_country:label']); ?>
              <?php $recnum = ($record['num']); ?>
              <a  href="maps.php?state=<?php echo $state ?>&country=<?php echo $country ?>"><?php echo ucwords($state);
?> (<?php echo $stateCount ?>) </a></div>
          </div>
          <?PHP $old_group $group// retain sub-group name before moving to new record. ?>
          <?php endforeach; ?>

 


THE CODE FOR A 2 COLUMN LISTING PAGE WITH A LINKED IMAGE - Aug 2nd, 2010

Here’s an example of a 2 column list page that lists events in rows with a list page image in one column and a short
description of the event and the date(s) for the event in another. Note that “if” statements are used to eliminate
extra spaces and special text if there is no information in particular fields. The “list_page_image” is a field that
holds a specific image that’s uploaded for display on the list page.



<table width="90%" border="0" cellpadding="5"><?php foreach ($happeningsRecords as $record): ?>
  <tr>
<td align="center">&nbsp;</td>
<td align="left" valign="top"><hr align="left" width="300" color="#99945e"/></td>
</tr>
<tr>
<td align="center">
                             
<?php foreach ($record['list_page_image'] as $upload): ?><a href="<?php echo $record['_link'?>"><img src="<?php echo
$upload['thumbUrlPath'?>" width="<?php echo $upload['thumbWidth'?>" height="<?php echo $upload['thumbHeight'?>"
alt="" /></a> <div class="body-text-bold">
<?php if ($upload['info1']): ?><?php echo $upload['info1']; ?><?php endif; ?><br />
<?php if ($upload['info2']): ?><?php echo $upload['info2']; ?>  <?php endif; ?></div>
<?php endforeach; ?>                             
                              
</td><td align="left" valign="top"><a href="<?php echo $record['_link'?>"><span  class="heading-text-bold"><?php echo
$record['title'?></span> <?php if ($record['short_description']): ?><br /><span class="body-text-bold"><?php echo
$record['short_description'?></span><?php endif ?><br />
<span  class="body-text"><?php echo date("D, M jS, Y g:i"strtotime($record['starting_date'])) ?>
<?php if ($record['ending_date']): ?>
through <?php echo $record['ending_date'?></span>
<?php endif; ?>
<br />
</a><?php endforeach; ?> </td>
  </tr>
</table>



WORKING WITH CATEGORY FILTERS - LISTINGS PAGE ISSUES - Aug 2nd, 2010

chassa2556 was concerned that links on a listings page that were filtered by category were always going to the first
record detail page. Here’s what he said: 

I have my 'see more' link going to:



<a href="involvedDetail.php?<?PHP echo $involvedRecord['num'?>"> see more </a>



I have set it up so it all goes to one section with a category filter. The trouble is it keeps going to the first
record. This is even though I am on the second or third record and am expecting it to go to the relating detail page.

This was his header code:



list($involvedRecords, $involvedDetails) = getRecords(array( 
    'Table name'   => 'involved', 
    'where' => ' category = "Join Us" ',  
  ));



Mr.Jim from WebCamp One was able to shed some light on the problem. He said:

I'm assuming that you have two pages: a list page and then a detail page. The detail page would be the one that pops up
when you click on 'read more'. I'm also assuming that you are restricting anything listed on your list page to items
that fall within the category "Join Us".

If I'm correct, try this:

1. In your list page, add the following line to your header code:



'where' => 'category = "Join Us"',


After you add that code, that 'where' statement should be the only 'where' statement in the list page's header code.

2. Remove that same line from the header on your existing detail page and replace it with the following lines:



'where' => whereRecordNumberInUrl(1),
'limit' => '1',



3. Finally, change the code in your list page to read:



<a href="involvedDetail.php?<?PHP echo $involvedRecord['_link'?>"> see more </a>



That seemed to do the trick for chassa2556.


ADD A HORIZONTAL LINE TO SEPARATE LETTER GROUPS IN A LIST - Aug 2nd, 2010

Rconring wanted to know how to add an <hr> between the letter groups in an alphabetized list of links. Ross Fairbairn -
Product Specialist at Interactive tools had the solution. He said, Just add the indicated code to your page and you’re
good to go.



<?php foreach ($linksRecords as $record): ?>  
  <a target="_blank" title="<?php echo $record['screen_tip'?>" 
 href="<?php echo $record['web_address'?>"><?php echo $record['title'?></a><br />  
 <!-- Insert This Code -->
  <?php  
    $letter substr($record['title'], 0,1); 
    if (
$letter != @$lastLetter) { print '<hr width="50%" size="1">'; } 
    
$lastLetter $letter
  
?> 
<!-- End of Insert-->
 <?php endforeach ?>

 


GROUPING SUBTOPICS IN YOUR LIST VIEW - Aug 2nd, 2010

InHouse offered this simple way of grouping the contents of List Views. I thought it might be useful for someone. Share
and enjoy.

In the Editor, create a field to hold the group names. Use the “List” option with either a pull down or check box
format to limit user mistakes and make this field mandatory as well. In this example the field is called "type". There
are also fields called “title” and “content”.



<?PHP
$old_group ''// init blank var.
foreach ($yourtableRecord as $record):
$group $record['type']; // load sub-group value from record.
if ($group != $old_group) { // If different from the last sub-group value, print the sub-group name.
echo "<h5>$group</h5>";
}
?>

<a href="<?PHP echo $record['_link'?>"><?PHP echo $record['title'?></a>
<?PHP echo $record['content'?>

<?PHP $old_group $group// retain sub-group name before moving to new record. ?>
<?PHP endforeach ?>



If you want to only show a single sub group then use an IF statement within the FOREACH loop:



<?PHP 
$desired_group 'yourgroupname'// init target var. 
foreach ($yourtableRecord as $record):
 
if (
$record['type'] == $desired_group ) { // Examine the current Type and see if it matches the desired group type. 
?> 

<a href="<?PHP echo $record['_link']; ?>"><?PHP echo $record['title']; ?></a> 
<?PHP echo $record['content'?> 

}// End If ?> 
<?PHP endforeach ?>


Thanks InHouse!!!

Here’s how I used this idea on the CMSBuilder Cookbook Links page with a table called “resources” and fields
called “category”, url, and link_text”. The line 



<table width="800" border="0" cellpadding="0">

<?php $old_group ''// init blank var.        
foreach ($resourcesRecords as $record): 
$group $record['category']; // load sub-group value from record. ?>

<tr>
<td align="left" class="Medium-Text">
<?PHP if ($group != $old_group) {echo "<h2>$group</h2>";}?>

<a href="<?php echo $record['url'?>" target="_blank"><?php echo $record['link_text'?></a>
<br /><?php echo $record['content'?><hr align="center" color="#999999" /></td>
 </tr>

<?PHP $old_group $group// retain sub-group name before moving to new record. ?>
<?php endforeach; ?>

</table>


CREATING A LIST PAGE WITH NESTED INFORMATION FROM 2 MULTI RECORD EDITORS - Aug 2nd, 2010

The goal is to create a list page using information from 2 multi record editors. “Exhibitions”, and “Opening
Receptions”.

The “Exhibitions” editor has all of the permanent event information and the Opening Receptions” contains only the
opening reception information for each exhibition. (I’m using 2 editors so that the “Opening Reception”
information can automatically be hidden after the reception date has passed)

Here’s the code I thought would work, but it didn’t. Then you’ll see what did work, and understand why:

 Since I was going to access 2 section editors, I put both my getRecords() calls In the head of the document:



<?php
  
    require_once "/yourpathto/cmsAdmin/lib/viewer_functions.php";

  list(
$exhibitionsRecords$exhibitions metadata) = getRecords(array(
    
'Table name'   => 'exhibitions',
  ));
  
   list(
$opening_receptionsRecords$opening_receptionsMetaData) = getRecords(array(
    
'Table name'   => 'opening_receptions',
    
'where' => "exhibition LIKE '%" .mysql_real_escape_string(@$_REQUEST['sort_exhibition']). "%'",
  ));
  
?>



And then in the body:



<table>
  <?php foreach ($exhibitionsRecords as $record): ?>
  <tr>
    <td><a href="<?php echo $record['_link'?>">"<?php echo $record['exhibition'?>"</a>
 
  Show Dates: <?php echo date("D, M jS"strtotime($record['opening_date'])) ?> -  <?php echo date("D, M jS, Y ",
strtotime($record['closing_date'])) ?>
<hr color="#C70000" width="300" />

Opening Reception: <?php foreach ($opening_receptionsRecords as $record): ?><?php echo date("D, M jS",
strtotime($record['reception_date'])) ?><?php endforeach; ?>
</td>
</tr>
<?php endforeach; ?> 
</table>



Using this approach, no matter how I adjusted the code, all of the “Opening Reception” dates were showing in each
event listing.

I was out of ideas, when Dave suggested that my  initial approach didn’t work because you can't look up all the
opening dates without knowing all the exhibition titles in advance.  So one way to do it is to load the opening date as
you display each exhibition.  

Instead of putting both getRecords() calls in the head of the document, set up one getRecords() in the document head
that loads the exhibitions records.  Then create the foreach loop to display them.

Then, inside that foreach loop, insert the other getRecords() call that loads the opening dates for each particular
exhibition and a foreach loop displays those. Like this: 

In the head:



<?php
  
  require_once "/yourpathto/cmsAdmin/lib/viewer_functions.php";

  list(
$exhibitionsRecords$exhibitions metadata) = getRecords(array(
    
'Table name'   => 'exhibitions',
  ));

?>



Then in the Body:



<table>
<?php foreach ($exhibitionsRecords as $record): ?>
<tr>
<td>
<a href="<?php echo $record['_link'?>"> <class="style2">"<?php echo $record['exhibition'?>"</a>
<br />
Show Dates: <?php echo date("D, M jS"strtotime($record['opening_date'])) ?> -  <?php echo date("D, M jS, Y ",
strtotime($record['closing_date'])) ?><br />
<?php echo $record['days_and_times'?></p><hr color="#C70000" width="300" />

Opening Reception: <?php
     list($opening_receptionsRecords$opening_receptionsMetaData) = getRecords(array(
      
'Table name'   => 'opening_receptions',
      
'where' => "exhibition = '" .mysql_real_escape_string(@$record['exhibition']). "'",
    ));
  
?>

<?php foreach ($opening_receptionsRecords as $reception): ?>
<?php echo date("D, M jS"strtotime($reception['reception_date'])) ?>
<?php endforeach; ?>

</td>
</tr>
<?php endforeach; ?>
</table>



Dave’s approach worked like a charm, however, he pointed out that this adds MySQL queries, so if you’re displaying
lots and lots of records this might not be a very efficient approach.


USING DIFFERENT STYLES TO DISPLAY GROUPS OF RECORDS ON A LIST PAGE - Aug 2nd, 2010

NigelGordijk had a number of articles that he wanted to display on his list page in the following format:

- The latest article with a large photo and a intro description
- The next two articles with smaller photos and intro descriptions
- The next five articles as links only, without photos or intro descriptions 

Damon from Interactive Tools offered this solution.

He said, “The code consists of three IF statements:

The first: Top Story - if the counter is 1 then output top story code.

The second: Next Two Stories - if the stories are number 2 or number 3, use the code to output the small images etc.

The third: Last Five Articles - if the story number is greater than 3, use the code that outputs only the title (with
link) and date, no images.”

You can see how this works by setting up a multi-record editor called “News” with at least a “Title” field, a
“Content” field, and one Upload field called “Uploads” and then adding some test records.

Here are the settings Damon used for the thumbnails for this example:

Thumb 1 - 235px wide x 999px high
Thumb 2 - 480px wide x 999px high

Now create a list viewer with the following code before the <head>



<?php header('Content-type: text/html; charset=utf-8'); ?>
<?php
  
  require_once "/your_path_to/lib/viewer_functions.php";

  list(
$newsRecords$newsMetaData) = getRecords(array(
    
'tableName'   => 'news',
  ));

?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">



And in the body of your list viewer insert the following code:



<?php $counter 1?>
<?php foreach ($newsRecords as $record): ?>

<?php if(($counter == 1) || ($newsMetaData['page'] > 1)) : ?>
<!-- TOP STORY -->
<?php foreach ($record['uploads'] as $upload): ?>
<?php if ($upload['hasThumbnail']): ?>
<img src="<?php echo $upload['thumbUrlPath2'?>" width="<?php echo $upload['thumbWidth2'?>" height="<?php echo
$upload['thumbHeight2'?>" alt="" /><br />
<?php endif ?>
<strong><a href="<?php echo $record['_link'?>"><?php echo $record['title'?></a></strong><br />
<?php echo date("D, M jS, Y"strtotime($record['date'])) ?><br />
<?php echo $record['content'?><br />
<?php endforeach ?>
<hr />
<!-- END TOP STORY -->

<?php elseif(($counter 1) && ($counter 4)) : ?>
<!-- NEXT TWO STORIES -->
<a href="<?php echo $record['_link'?>"><?php echo $record['title'?></a><br />
<?php foreach ($record['uploads'] as $upload): ?>
<?php if ($upload['hasThumbnail']): ?>
<img src="<?php echo $upload['thumbUrlPath'?>" width="<?php echo $upload['thumbWidth'?>" height="<?php echo
$upload['thumbHeight'?>" alt="" align="left" /><br />
<?php endif ?>
<?php endforeach ?>
Date: <?php echo date("D, M jS, Y"strtotime($record['date'])) ?><br />
<?php echo $record['content'?>
<br clear="left" />
<hr />
<br clear="left" />
<!-- END NEXT TWO STORIES -->

<?php elseif($counter 3): ?>
<!-- REMAINING ARTICLES -->
<a href="<?php echo $record['_link'?>"><?php echo $record['title'?></a><br />
Date: <?php echo date("D, M jS, Y"strtotime($record['date'])) ?><br /><br />
<!-- END REMAINING ARTICLES -->

<?php endif; ?>
<?php $counter++ ?>
<?php endforeach; ?> 



If you want to limit the number of words displayed for the “Content” and link to a detail page, you can place the
following code before the code in the <body> of your page to define a “maxWords function:



<?PHP  
  function maxWords($textOrHtml$maxWords) { 
  
$text strip_tags($textOrHtml); 
  
$words preg_split("/\s+/"$text$maxWords+1); 
  if (
count($words) > $maxWords) { unset($words[$maxWords]); } 
  
$output join(' '$words); 
 
  return 
$output
  } 
?>



And replace the 



<?php echo $record['content'?> 



code, with something like this to utilize the “maxWords” function you inserted:



<?PHP  echo maxWords($record['content'], 25); 
?>...<a href="<?php echo $record['_link']; ?>">Read More</a>



The 25 can be any number and will define the number of words you want to display. Dragging the record to new positions
on the record list will change the way they are displayed on your list page. The record at the top of the list is #1,
and the second is # 2, etc. Note: the <br clear”left”/> makes sure that no floating elements are allowed on the left
side of the text.


PASSING VARIABLES AT THE END OF A URL FROM A DETAIL PAGE TO A LISTING PAGE - Mar 12th, 2017

Creating a link on a detail page that would show all off the records created by the author of that record on a new list
page. 

ON THE DETAIL PAGE
First, create a variable from the createdByUserNum value on the detail page


<?php $member1 $my_tableRecord['createdByUserNum'?>


Then append that variable's value, and the new variable to be passed along to the end of the Link URL


<a href="show-all-records-test1.php?member=<?php echo $member1 ?>">Show All records</a>


ON THE LISTING PAGE
Add this code before the list records call to define the value of the passed $member variable


<?php $member $_REQUEST['member']; ?>


Then in the list records call, add a 'where' to filter the results to only those records that were created by that
author:


  <?php // load records from 'my_table'

  list($my_tableRecords$my_tableMetaData) = getRecords(array(
    
'tableName'   => 'my_table',
    
'loadUploads' => true,
    
'allowSearch' => true,
    
'orderBy' => 'createdDate DESC',
    
'where' => 'createdByUserNum = "'$member .'"' ,
  ));
 
?>


The 'orderBy' => 'createdDate DESC', sorts the results with the newest at the top


UPDATING A TABLE VALUE WITHOUT RELOADING A WEB PAGE USING AN AJAXURL - Dec 12th, 2018

I needed to accept or reject submitted entries into an art exhibition so that exhibition managers could see which
entries were going to be in the show and which were not. I also needed to send  acceptance/rejection emails to the
entrants automatically.

The problem was that there were a great many submissions and I didn't want to re-load the page each time, the way it
seemed to work when submitting a form to update the entrant's record.  This would have made it extremely hard for a
manager to keep track of which submission they were working on. 

One more piece of background and then on to the code.

I'm using info5 to indicate which submission images are accepted/rejected. Since I'm also using info5 for price values,
there's some code that strips out any existing alphabetical characters from the existing info5 value, and appends either
'keep' or 'remove' to the remaining numbers in the field.

To follow this recipe you'll need a milti-record editor (exhibition_submissions) with at least one multi-image upload
field (submission_images) with all 5 info fields.

Your application will probably be different but the concepts should translate easily.

First, on the page that displays all of the submitted images there are 2 small Ajax scripts that pass 2 variables
'recNum' and 'newValue'. One to mark a submission as 'keep' and the other to mark it as  'remove':


<script type = "text/javascript">
      function keepSubmission(recNum, newValue ){
    ajaxUrl = "keep2017.php?submit=1&recNum=" + escape(recNum)+"&newValue=" + escape(newValue);
      
    $.ajax({
    url: ajaxUrl,
    }).done(function() {
        //add code here if anything needs to happen after the ajax call
       alert("Submission accepted");
     });   
      }
  </script>
  <script type = "text/javascript">
   function removeSubmission(recNum, newValueR ){
    ajaxUrl = "remove2017.php?submit=1&recNum=" + escape(recNum)+"&newValueR=" + escape(newValueR);
      
    $.ajax({
    url: ajaxUrl,
    }).done(function() {
        //add code here if anything needs to happen after the ajax call
       alert("Submission removed");
     });   
      }
  </script> 


Then, in the foreach loop that dis[plays each submitted image, the code to remove any existing text in the info5 field:


 <?php foreach($exhibition_submissionsRecords as $record) : ?>
<?php foreach ($record['submission_images'] as $upload): ?>
<?php // keep the price but replace any text with 'keep' ?>                
            <?php  $keep 'keep' ?> 
        <?php $oldValue $upload['info5'?>
       <?php $newValue preg_replace('/[^0-9 .]/',''$oldValue?>
         <?php $newValue = ($newValue$keep?> 
    <?php // keep the price but replace any text with 'remove' ?>         
            <?php  $remove 'remove' ?> 
        <?php $oldValue $upload['info5'?>
       <?php $newValueR preg_replace('/[^0-9 .]/',''$oldValue?>
         <?php $newValueR = ($newValueR$remove?> 
          <?php $recNum $upload['num'?>

<a href="<?php echo $upload['urlPath'?>"><img src="<?php echo $upload['thumbUrlPath2'?>" width="<?php echo
(
$upload['thumbWidth2']/2?>" height="<?php echo ($upload['thumbHeight2']/)?>" alt="" /></a>
                
                    
                <div id='ajaxlink' onclick="keepSubmission(<?php echo "'$recNum', '$newValue'" ?>)">CLICK TO ACCEPT THIS
SUBMISSION</div>
                <div id='ajaxlink' onclick="removeSubmission(<?php echo "'$recNum', '$newValueR'" ?>)">CLICK TO REMOVE
THIS SUBMISSION</div>
                
                
                 <?php if($upload['info1']):?>Title: <?php echo $upload['info1']?><?php else :?><span
style="color:#C30">No Title</span><?php endif ?>
                <?php if($upload['info2']):?>Medium: <?php echo $upload['info2']?><?php else :?><span
style="color:#C30">No Medium</span><?php endif ?>
               <?php if($upload['info3']):?>Dimensions: <?php echo $upload['info3']?><?php else :?><span
style="color:#C30">No Dimensions</span><?php endif ?>
                <?php if($upload['info4']):?>Price: <?php echo $upload['info4']?><?php else :?><span
style="color:#C30">No Price</span><?php endif ?>

 <?php endforeach ?>
 <?php endforeach ?>


You may have noticed that the Ajax scripts have a url attached to each. These pages are where the actual database
updating occurs. Here's the code for those.

A page with the update code for 'keep' called keep2017.php:

<?php 
 // load viewer library
  $libraryPath 'cmsAdmin/lib/viewer_functions.php';
  
$dirsToCheck = array('/your_servre_path/','','../','../../','../../../');
  foreach (
$dirsToCheck as $dir) { if (@include_once("$dir$libraryPath")) { break; }}
  if (!
function_exists('getRecords')) { die("Couldn't load viewer library, check filepath in sourcecode."); }

$newValue mysql_escape($_REQUEST['newValue']);
 
mysqlStrictMode(false);  
      
$query "UPDATE `{$TABLE_PREFIX}uploads` SET 
       info5  =  '$newValue'
                 WHERE num = '".mysql_escape$_REQUEST['recNum'] )."'";
      
mysql_query($query) or die("MySQL Error:\n"htmlspecialchars(mysql_error()) . "\n"); 
      
$userNum mysql_insert_id(); 
?> 
  
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
    <head>
            <meta name="robots" content="noindex,nofollow" />
</head>
</body>
</html>

And a page with the update code for 'remove' called remove2017.php:

<?php 
 // load viewer library
  $libraryPath 'cmsAdmin/lib/viewer_functions.php';
  
$dirsToCheck = array('/your_servre_path/','','../','../../','../../../');
  foreach (
$dirsToCheck as $dir) { if (@include_once("$dir$libraryPath")) { break; }}
  if (!
function_exists('getRecords')) { die("Couldn't load viewer library, check filepath in sourcecode."); }

$newValueR mysql_escape($_REQUEST['newValueR']);
 
mysqlStrictMode(false);  
      
$query "UPDATE `{$TABLE_PREFIX}uploads` SET 
       info5  =  '$newValueR'
                 WHERE num = '".mysql_escape$_REQUEST['recNum'] )."'";
      
mysql_query($query) or die("MySQL Error:\n"htmlspecialchars(mysql_error()) . "\n"); 
      
$userNum mysql_insert_id(); 
      
?> 
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
    <head>
            <meta name="robots" content="noindex,nofollow" />
</head>
</body>
</html>


To allow the exhibition manager to see just those images that were accepted I set up  keepviewer.php

With the following active code:

In the head:


<script type = "text/javascript">
      function removeSubmission(recNum, newValue ){
    ajaxUrl = "remove2017.php?submit=1&recNum=" + escape(recNum)+"&newValue=" + escape(newValue);
      
    $.ajax({
    url: ajaxUrl,
    }).done(function() {
        //add code here if anything needs to happen after the ajax call
       alert("Submission Removed");
     });   
      }
  </script>


And in the body:


<?php foreach($exhibition_submissionsRecords as $record) : ?>
              <?php foreach ($record['submission images'] as $upload): ?>
              <?php if (strpos($upload['info5'], 'eep')) : ?>
 <?php // keep the price but replace any text with 'remove' ?>         
            <?php  $remove 'remove' ?> 
        <?php $oldValue $upload['info5'?>
       <?php $newValueR preg_replace('/[^0-9 .]/',''$oldValue?>
         <?php $newValueR = ($newValueR$remove?> 
          <?php $recNum $upload['num'?>

followed by the code to display the images and info values

I also included ajax scripting on that page to allow for removal of the submission.
 

<div id='ajaxlink' onclick="removeSubmission(<?php echo "'$recNum', '$newValue'" ?>)">CLICK TO REMOVE SUBMISSION</div>

A few small changes will convert that to a remove viewer with scripting to allow the supmission to be re-accepted

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

Just for completeness, here's the code for remove.php with only one variable 'recNum' (I was not using info5 for
anything so I could just change it from '0' to '1' and back again.)


<?php mysqlStrictMode(false);  
      
$query "UPDATE `{$TABLE_PREFIX}uploads` SET 
       info5  = '1'
                 WHERE num = '".mysql_escape$_REQUEST['recNum'] )."'"
      
mysql_query($query) or die("MySQL Error:\n"htmlspecialchars(mysql_error()) . "\n"); 
      
$userNum mysql_insert_id(); 
      
   
?>   
  <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
    <head>
            <meta name="robots" content="noindex,nofollow" />
</head>
</body>
</html>


And the changed code in the ajax script:

<script type = "text/javascript"> 
      function removeSubmission( recordNum ) { 
    ajaxUrl = "remove.php?submit=1&recNum=" + escape(recordNum); 
      
    $.ajax({ 
    url: ajaxUrl, 
    }).done(function() { 
        //add code here if anything needs to happen after the ajax call 
       alert("Submission Removed");
     }); 
          } 
     </script>


Finally, to allow displaying the alert on the page, add the following to the head:


<script type="text/javascript">
<!--
function MM_nbGroup(event, grpName) { //v6.0
  var i,img,nbArr,args=MM_nbGroup.arguments;
  if (event == "init" && args.length > 2) {
    if ((img = MM_findObj(args[2])) != null && !img.MM_init) {
      img.MM_init = true; img.MM_up = args[3]; img.MM_dn = img.src;
      if ((nbArr = document[grpName]) == null) nbArr = document[grpName] = new Array();
      nbArr[nbArr.length] = img;
      for (i=4; i < args.length-1; i+=2) if ((img = MM_findObj(args[i])) != null) {
        if (!img.MM_up) img.MM_up = img.src;
        img.src = img.MM_dn = args[i+1];
        nbArr[nbArr.length] = img;
    } }
  } else if (event == "over") {
    document.MM_nbOver = nbArr = new Array();
    for (i=1; i < args.length-1; i+=3) if ((img = MM_findObj(args[i])) != null) {
      if (!img.MM_up) img.MM_up = img.src;
      img.src = (img.MM_dn && args[i+2]) ? args[i+2] : ((args[i+1])? args[i+1] : img.MM_up);
      nbArr[nbArr.length] = img;
    }
  } else if (event == "out" ) {
    for (i=0; i < document.MM_nbOver.length; i++) {
      img = document.MM_nbOver[i]; img.src = (img.MM_dn) ? img.MM_dn : img.MM_up; }
  } else if (event == "down") {
    nbArr = document[grpName];
    if (nbArr)
      for (i=0; i < nbArr.length; i++) { img=nbArr[i]; img.src = img.MM_up; img.MM_dn = 0; }
    document[grpName] = nbArr = new Array();
    for (i=2; i < args.length-1; i+=2) if ((img = MM_findObj(args[i])) != null) {
      if (!img.MM_up) img.MM_up = img.src;
      img.src = img.MM_dn = (args[i+1])? args[i+1] : img.MM_up;
      nbArr[nbArr.length] = img;
  } }
}
//-->
</script>






PRE-CHECKING ALL ENTRIES MARKED AS ‘DEFAULT’ IN A MULTI VALUE CHECK BOX LIST - Dec 11th, 2015

With a lot of help from Ross Fairbairn, Interactive Tools’ consulting Guru, we came up with this method, based on the
following assumptions.

1. You have a multi-record section with one text field for each check box entry. For this example I’ve called the
section “entries”. It has at least one text field. For this example the text field is called “list_entry”. 

 2. You have a another multi-record section with a multi-value check box list field.  For this example I’ve called the
section “listings” and the list field “my_list”.

3) the “my_list” field gets it’s options and values from the “entries” section using the 
Get Options From Database (advanced) method, with:
Section Table Name > entries
Option Values > num
Option Labels > list_entry 

The first step is to go to your "entries" section and make note of the records numbers for the list_entry records you
want to set as default in the multi-value"my_list” check box field when you create a new “listings” section
record.

Let's say you pick list_entry 1, 7 and 16.

The second step is to go to section editors under the admin menu and modify the “listings” Section Editor. 

Choose the “my_list” field and enter the record numbers you chose (1,7, and 16) into the "Default Values" field,
separated by tab characters.

The only method that worked for me was to open my notepad text editor, entering a tab character in a blank text document
by pressing the tab key on my keyboard, then highlighting and copying (CTL-C) the tab character to my clipboard. I could
then paste (CTL-V) that character after entering each record number.

The result was:

1(tab)7(tab)16(tab) 
NOTE:(you won’t actually see the (tab), but there should be a multi-character long space between the record numbers.

Save this field and the section details.
 
When you create a new record in the “Listings” editor, the “my_list” entries that you entered as defaults will
be pre-checked. (You can still uncheck those entries and/or check other entries in the list as required.)

Note that this will only affect new records, it will not change existing records.

BLOGS



CREATING A BLOG (UPDATED FOR MYSQLI) - Jul 22nd, 2019

I recently put together a blog on one of my client’s sites and I thought I’d put the recipe up just in case someone
could benefit from it. 

BACKGROUND:
The blog editor (called ‘blog’) has a title, date, contents and an image upload field. Image thumbnail2 is set for a
max width of 200 px and a max height of 300 px

The client didn’t want to allow comments from others so that’s not included in the code. If they had, I would have
used either the Website Comments plugin or a 3rd party solution like http://www.gentlesource.com/comment-script/ ,
http://www.phpjabbers.com/post-comment/ , 
http://www.adriantnt.com/products/comments_script/ , or
http://js-kit.com/comments/

The images are padded and wrapped in the contents by the following css:


.pad { border-right: 20px solid transparent; border-bottom: 10px solid transparent; border-top: 5px solid transparent;}
img { border:hidden;}
img.floatLeft {float: left;  margin: 10px;}
img.floatRight {float: right; margin: 4px; }


I’ve allowed the visitor to decide if they want to view the earliest or most current posts first and retaining their
choice for subsequent searches. 

The use of the letters a and b in the form for added security are explained in the post called: ALLOWING VISITORS TO SET
VIEWER ORDERBY OPTIONS

The number of records shown on the main viewer page is set in a single record editor called ‘common_information’.

A major key to creating the blog is the code that produces a listing of unique months and years for existing articles to
act as an archive of blog posts.

Another was being able to display the archive month and year once at the top of the page, next to the word Archives

Here’s the code for the main blog page (blog1.php):


<span class="heading_font">Have It Your Way</span>
<form method="POST" action="<?php echo $_SERVER['PHP_SELF'?>">

  <select name="order">
    <option value="b">earliest date at the top</option>
    <option value="a">most recent date at the top</option>
  </select>
  <input type="submit" name="submit" value="Click/Tap To Choose Display Order">
</form>
<?php $orderBy "date";
if (@
$FORM['order'] == 'b') { $orderBy "date"; }
if (@
$FORM['order'] == 'a') { $orderBy "date DESC"; }

?>
<?php $record_limit $common_informationRecord['record_limit'?>
<?php if (@!$record_limit ) { $record_limit1 ""; } ?>
<?php if (@$record_limit >=) { $record_limit1 $common_informationRecord['record_limit']; } ?>

<?php // load records from 'blog'
  list($blogRecords$blogMetaData) = getRecords(array(
    
'tableName'   => 'blog',
    
'loadUploads' => true,
    
'allowSearch' => false,
    
'orderBy' => $orderBy
    
'limit' => $record_limit1,
  ));
  
  
?>
<table width="50%" border="0" align="center" cellpadding="10">
  <tr>
    <td width="75%" align="left" valign="top"><table width="100%" border="0" align="left" cellpadding="2">
        <?php foreach ($blogRecords as $record): ?>
        <tr>
          <td align="left" ><hr />
            <span class="text_font_bold"><?php echo date("D, M jS, Y "strtotime($record['date'])) ?>
            <?php echo strtoupper($record['title']) ?></span>
            <div class=" pad" style="float:left">
              <div style="float:none">
                <?php foreach ($record['image'] as $upload): ?>
                <a target="_blank" href="<?php echo $upload['urlPath'?>"> <img src="<?php echo
$upload['thumbUrlPath2'?>" width="<?php echo $upload['thumbWidth2'?>" height="<?php echo $upload['thumbHeight2'?>"
alt="" /></a>
                <?php endforeach ?>
              </div>
            </div>
            <span class="text_font"><?php echo $record['content'?></span></td>
        </tr>
        <?php endforeach ?>
      </table></td>
    <td width="25%" align="left" valign="top"><p><span class="page_title_font">
        Or Select
        An Archive</span>
        <?php
// get list of unique months and years with articles
$query "SELECT DATE_FORMAT(date, '%M %Y') as dateAndYear, YEAR(date) as year, MONTH(date) as month FROM cms_blog GROUP
BY dateAndYear ORDER BY date";
$result mysqli()->query($query) or die("MySQL Error: "htmlspecialchars(mysqli()->error)   . "\n");
while (
$record $result->fetch_assoc()):
?>
        <a href="blog2.php?date_year=<?php echo $record['year'?>&date_month=<?php echo $record['month']
?>&orderBy=<?php echo $orderBy ?>"><?php echo $record['dateAndYear']; ?> </a>
        <?php endwhile ?>
    </td>
  </tr>
</table>


And here’s the code for the archive page (blog2.php)


<span class="heading_font">Have It Your Way</span>
<form method="POST" action="<?php echo $_SERVER['PHP_SELF'?>">
<input type="hidden" name="date_year" value="<?php echo @$_REQUEST['date_year']; ?>">
<input type="hidden" name="date_month" value="<?php echo @$_REQUEST['date_month']; ?>">
<input type="hidden" name="orderBy" value="<?php echo @$_REQUEST['orderBy']; ?>">
  <select name="order">
    <option value="b">earliest date at the top</option>
    <option value="a">most recent date at the top</option>
  </select>
  <input type="submit" name="submit" value="Click/Tap To Choose Display Order">
</form>
<?php $orderBy =$_REQUEST['orderBy']; ?>
<?php 
if (@$FORM['order'] == 'b') { $orderBy "date"; }
if (@
$FORM['order'] == 'a') { $orderBy "date DESC"; }

?>


<?php // load records from 'blog'
  list($blogRecords$blogMetaData) = getRecords(array(
    
'tableName'   => 'blog',
    
'loadUploads' => true,
    
'allowSearch' => false,
    
'orderBy' => $orderBy
    
  ));
  
  
?>
<table width="50%" border="0" align="center" cellpadding="10">
  <tr>
    <td width="75%" align="left" valign="top"><table width="100%" border="0" align="left" cellpadding="2">
        <tr>
         <td align="center"><?php $date_year $_REQUEST['date_year']; ?>
            <?php $date_month $_REQUEST['date_month']; ?>
            <?php
$date = @$_REQUEST['date_year']."-".@$_REQUEST['date_month']."-28";
$formattedDate date("F Y"strtotime($date));
?>
            <span class="page_title_font"><?php echo $formattedDate ?> Blog Posts</span>
          </td>
        </tr>
        <?php foreach ($blogRecords as $record): ?>
        <?php $record_year date("Y"strtotime($record['date'])) ?>
        <?php $record_month date("n"strtotime($record['date'])) ?>
        <?php if (($date_year == $record_year) && ($date_month == $record_month) ):?>
        <tr>
          <td align="left" ><hr />
            <span class="text_font_bold"><?php echo date("D, M jS, Y "strtotime($record['date'])) ?>
            <?php echo strtoupper($record['title']) ?></span>
            <?php if($record['image'] ):?>
            <div class=" pad" style="float:left">
              <div style="float:none">
                <?php foreach ($record['image'] as $upload): ?>
                <a target="_blank" href="<?php echo $upload['urlPath'?>"> <img src="<?php echo
$upload['thumbUrlPath2'?>" width="<?php echo $upload['thumbWidth2'?>" height="<?php echo $upload['thumbHeight2'?>"
alt="" /></a>
                <?php endforeach ?>
              </div>
            </div>
            <?php endif ?>
            <span class="text_font"><?php echo $record['content'?></span></td>
        </tr>
        <?php endif ?>
        <?php endforeach ?>
      </table></td>
    <td width="25%" align="left" valign="top"><span class="page_title_font">
      Or Select
      An Archive</span>
      <?php
// get list of unique months and years with articles
$query "SELECT DATE_FORMAT(date, '%M %Y') as dateAndYear, YEAR(date) as year, MONTH(date) as month FROM cms_blog GROUP
BY dateAndYear ORDER BY date";
$result mysqli()->query($query) or die("MySQL Error: "htmlspecialchars(mysqli()->error)  . "\n");
while (
$record $result->fetch_assoc()):
?>
      <a href="blog3.php?date_year=<?php echo $record['year'?>&date_month=<?php echo $record['month'?>&orderBy=<?php
echo $orderBy ?>"><?php echo $record['dateAndYear']; ?></a>
      <?php endwhile ?></td>
  </tr>
</table>


USING A PULL DOWN FORM FIELD TO SHOW AVAILABLE DATES - Jun 7th, 2014

Showing available dates issue got a bit more complicated when I wanted to use a pull down form field instead of a list
to show the available date options.

I've made this a separate example to keep the code as simple as possible.

For this example you’ll need to create a multi-record section with at least a Date field called ‘publication_date’
and a text field called ‘title’ and a checkbox field called ‘hidden’.

Note: change ‘your_table_name’ to the name of the table that you’ve created (in 2 places). Put a
viewer_functions.php call at the top of both viewers, and a list records call for your table at the top of your
available_date_test2.php viewer. 

With a bit of help from Chris Waddell from Interactive Tools, I came up with the following code for my
available_date_test.php viewer (the one with the form):


<?php
// get list of unique months and years with records
$query "SELECT DATE_FORMAT(publication_date, '%M %Y') as dateAndYear, YEAR(publication_date) as year,
MONTH(publication_date) as month FROM cms_your_table_name WHERE `hidden` = 0 GROUP BY dateAndYear ORDER BY
publication_date DESC";
$result mysql_query($query) or die("MySQL Error: "htmlspecialchars(mysql_error()) . "\n");
?>

<form method="POST" action="available_date_test2.php">
<select name="dateAndYear" style="width:350px;" >
<option value="">Choose A Date</option>
<?php while ($record mysql_fetch_assoc($result)):?>
<option class="body-text" value="<?php echo $record['dateAndYear'?>"><?php echo $record['dateAndYear'];
?></option><?php endwhile ?></select>
<input type="submit" name="submit" value="Go To Records" style="width:200px; "></form>

And this code for the available_date_test2.php page that show records filtered by the date chosen:

<?php
@list($date_month$date_year) = explode(' ', @$_REQUEST['dateAndYear']); // create the variables $date_month and
$date_year split on space
?>
<?php
$date = @$date_year."-".@$date_month."-28"

// According to Jason Sauchuk -  This creates a "date" variable based on the 2 variables in the string (we use 28 for
the "day" since all months will have at least 28 daysThis number isn't important, but it helps php decipher which
variables are months, days, and years).;

$formattedDate = date("F Y", strtotime($date)); 

// This formats that date into the form Month Year. You can then output $formattedDate where ever you want to.
?>
          Records with a date field of  <?php echo $formattedDate ?>
              <?php foreach ($your_tableRecords as $record): ?>
              <?php $record_year = date("Y", strtotime($record['publication_date'])) // format the $record_year variable
to show only the 4 digit year  ?>
              <?php $record_month = date("F", strtotime($record['publication_date']))  // format the $record_month
variable to show the full name of the month  ?>
              <?php if (($date_year == $record_year) && ($date_month == $record_month) ):?>
             <?php echo $record['title'] ?><hr>
            <?php endif ?>
              <?php endforeach; ?>


SORTING BLOG POSTS BY KEYWORDS IN MULTIPLE FIELDS AND AN AVAILABLE DATE FILTER ON ONE VIEWER - Jan 7th, 2019

The previous recipes addressed a basic approach to creating a Blog or Articles editor (really the same thing unless you
are allowing user posts on your blog).

Here’s an approach that puts the pieces together.

It uses a single viewer, and allows visitors to search by multiple field keyword criteria, or to filter their results
from a list that only contains available dates (Month and Year), and put all of this in a neat set of pull down form
fields. 

The approach uses 2 forms (one for the keyword searches and one for the available date filters) and sets a variable
called $archive depending on which one is used (I’m sure that there’s a more elegant way to do this, and welcome
anyone’s input).

The 2 fields that are set for keyword searches in this example are ‘article_type’ and ‘publication’, and the
date filter uses month and year.

You’ll need to set up 3 multi-record tables to follow this example:

A table called article_types with one text field called ‘title’
A table called publication_names with one text field called ‘title’
A table called Articles with the following minimum set of fields

A date field called publication_date
A text field called article_title
2 list fields, one called ‘article_type and the other called ‘publication’ (these get their option values and
option labels from the other 2 tables)

Here’s the code:

At the top of your page



<?php
  
  require_once "/path_to_your_server/html/cmsAdmin/lib/viewer_functions.php";

  list(
$article_typesRecords$article_typesMetaData) = getRecords(array(
    
'tableName'   => 'article_types',
   
    ));

list(
$publication_namesRecords$publication_namesMetaData) = getRecords(array(
    
'tableName'   => 'publication_names',
   
    ));

?>


And in the body:



<form method="POST" action="<?php echo $_SERVER['PHP_SELF'?>">
  <input type="hidden" name="archive" value="0">
  <table >
    <tr>
      <td><select name="article_type_keyword" style="width:350px; ">
          <option  value="">Choose An Article Type To Display</option>
          <?php foreach ($article_typesRecords as $record): ?>
          <option value="<?php echo $record['num'?>"><?php echo $record['type'?></option>
          <?php endforeach ?>
        </select></td>
      <td><input type="submit" name="submit" value="Filter Your Page"  style="width:200px; "></td>
    </tr>
    <tr>
      <td><select name="publication_keyword" style="width:350px; " >
          <option value="">And/Or Choose A Publication To Display</option>
          <?php foreach ($publication_namesRecords as $record): ?>
          <option value="<?php echo $record['num'?>"><?php echo $record['publication_name'?></option>
          <?php endforeach ?>
        </select></td>
      <td><INPUT TYPE="submit" VALUE="Cancel Search Filters"  style="width:200px; "></td>
    </tr>
  </table>
</FORM>
<br />
<?php // insert block 1 (Form) for displaying only current year's articles here ?> 
<?php  
list($articles_by_ericaRecords$articles_by_ericaMetaData) = getRecords(array(
    
'tableName'   => 'articles_by_erica',
    
'loadUploads' => true,
//'allowSearch' => false,
   'orderBy' => 'publication_date DESC',
    ));

?>
<?php // get list of unique months and years with articles ?>
<?php // remove this code for years only: %M and , MONTH(publication_date in the $query  ?>
<?php $query "SELECT DATE_FORMAT(publication_date, '%M %Y') as dateAndYear, YEAR(publication_date) as year,
MONTH(publication_date) as month FROM cms_articles_by_erica WHERE `hidden` = 0 GROUP BY dateAndYear ORDER BY
publication_date DESC";
$result mysql_query($query) or die("MySQL Error: "htmlspecialchars(mysql_error()) . "\n");
?>
<form  method="POST" action="<?php echo $_SERVER['PHP_SELF'?>">
  <table >
    <tr>
      <td ><input type="hidden" name="archive" value="1">
        <select name="dateAndYear" style="width:350px;" >
          <option value="">Or, Display Articles By Publication Date</option>
          <?php
 while ($record mysql_fetch_assoc($result)):?>
          <option value="<?php echo $record['dateAndYear'?>"><?php echo $record['dateAndYear']; ?></option>
          <?php endwhile ?>
        </select></td>
      <td><input type="submit" name="submit" value="Filter By Date"  style="width:200px; "></td>
    </tr>
  </table>
</form>
<?php @$archive $_REQUEST['archive']; ?>
<?php if(@$archive== || @$archive==""):?>
<?php // beginning of replaced code block 2 for displaying only current year's articles ?>
<br />
<table >
  <?php foreach ($articles_by_ericaRecords as $record): ?>
  <tr>
    <td ><?php echo $record['article_title'?>
      <?php if (($record['publication'] && !$record['publication'] =="") ||($record['publication_date'] &&
!
$record['publication_date'] =="") ||($record['article_type'] && !$record['article_type'] =="")):?>
<br />
      (
      <?php endif ?>
<?php // end of replaced code block 2 for displaying only current year's articles ?>
      <?php if ($record['publication'] && !$record['publication'] ==""):?>
      <?php echo $record['publication:label'?> -
      <?php endif ?>
      <?php if ($record['publication_date'] && !$record['publication_date'] ==""):?>
      <?php echo date("F Y "strtotime($record['publication_date'])) ?> -
      <?php endif ?>
      <?php if ($record['article_type'] && !$record['article_type'] ==""):?>
      <?php echo $record['article_type:label'?>
      <?php endif?>
      <?php if (($record['publication'] && !$record['publication'] =="") ||($record['publication_date'] &&
!
$record['publication_date'] =="") ||($record['article_type'] && !$record['article_type'] =="")):?>
      )
      <?php endif ?>
      <br /><hr /></td>
  </tr>
 
  <?php endforeach; ?>
</table>
<?php endif ?>
<?php  if(@$archive== 1):?>
<br />
<h3>Articles by Publication Date</h3>
 <?php // remove this code for years only:  $date_month, ?>
<?php @list($date_month$date_year) = explode(' ', @$_REQUEST['dateAndYear']); // split on space
?>
<hr />
<table >
  <?php foreach ($articles_by_ericaRecords as $record): ?>
  <?php $record_year date("Y"strtotime($record['publication_date'])) ?>
   <?php // remove this code for years only:  <?php $record_month = date("F", strtotime($record['publication_date'])) ?>

<?php $record_month date("F"strtotime($record['publication_date'])) ?>
 <?php // remove this code for years only: && ($date_month == $record_month) ?>
 <?php  if ((@$_REQUEST && $archive == 1) && ($date_year == $record_year) && ($date_month == $record_month) ):?>
  <tr>
    <td ><?php echo $record['article_title'?>
      <?php if (($record['publication'] && !$record['publication'] =="")||($record['publication_date'] &&
!
$record['publication_date'] =="") ||($record['article_type'] && !$record['article_type'] =="")):?>
      <br />
      (
      <?php endif ?>
      <?php if ($record['publication'] && !$record['publication'] ==""):?>
      <?php echo $record['publication:label'?> -
      <?php endif ?>
      <?php if ($record['publication_date'] && !$record['publication_date'] ==""):?>
      <?php echo date("F Y "strtotime($record['publication_date'])) ?> -
      <?php endif ?>
      <?php if ($record['article_type'] && !$record['article_type'] ==""):?>
      <?php echo $record['article_type:label'?>
      <?php endif?>
      <?php if (($record['publication'] && !$record['publication'] =="")||($record['publication_date'] &&
!
$record['publication_date'] =="") ||($record['article_type'] && !$record['article_type'] =="")):?>
      )
      <?php endif ?>
      <br />
      <hr /></td>
  </tr>
  <?php endif?>
  <?php endforeach; ?>
</table>
<?php endif?>


Two permutations of this approach is to show only the current years articles as default and to list 'sort by date'
archives by year instead of month and year.

A) To show only the current year's articles as default:
1)
Search for <?php // insert block 1 (Form) for displaying only current year's articles here ?> above and insert the
following code:

<form method="POST" action="<?php echo $_SERVER['PHP_SELF'?>">
                              <input type="hidden" name="submitted" value="0">
                              <table align="center"   width="100%" border="0" cellspacing="0" cellpadding="0">
                                <tr>
                                  <td align="left"><INPUT TYPE="submit" VALUE="Clear Other Filters And Show All <?php
echo date("Y")?> Articles"  style="width:92%; background-color:#7F0000;"></td>
                                </tr>
                              </table>
                            </FORM>
                            <br />


2)
Search for <?php // beginning of replaced code block 2 for displaying only current year's articles ?> and <?php // end
of replaced code block 2 for displaying only current year's articles ?> above and replace that code with the code below:

<?php // begin show this year's articles only code ?>
                            <?php if(@$submitted == '' || @$submitted == "0"):?>
                            <br />
                            <h3>All <?php echo date("Y")?> Articles Only</h3>
                            <table align="center" width="100%">
                              <?php $curYearCount '0' ?>
                              <?php foreach ($articles_by_ericaRecords as $record): ?>
                              <?php $pubYear1 date("Y"strtotime($record['publication_date']))?>
                              <?php $curYear1 date("Y")?>
                              <?php if ($pubYear1 == $curYear1):?>
                              <?php $curYearCount++ ; ?>
                              <?php endif ?>
                              <?php endforeach ?>
                              <?php foreach ($articles_by_ericaRecords as $record): ?>
                              <?php $pubYear date("Y"strtotime($record['publication_date']) )?>
                              <?php $curYear date("Y")?>
                              <?php if(($pubYear == $curYear) && $curYearCount >= ):?>
                              <tr>
                                <td colspan="2"><span class="Medium-Text-Bold"><?php echo $record['article_title']
?></span><span class="Medium-Text">
                                  <?php if (($record['publication'] && !$record['publication'] =="")
||(
$record['publication_date'] && !$record['publication_date'] =="") ||($record['article_type'] &&
!
$record['article_type'] =="")):?>
                                 <br />
                                  (
                                  <?php endif ?>
                                  <?php if ($record['publication'] && !$record['publication'] ==""):?>
                                  <?php echo $record['publication:label'?> -
                                  <?php endif ?>
                                  <?php if ($record['publication_date'] && !$record['publication_date'] ==""):?>
                                  <?php echo date("F Y "strtotime($record['publication_date'])) ?> -
                                  <?php endif ?>
                                  <?php if ($record['article_type'] && !$record['article_type'] ==""):?>
                                  <?php echo $record['article_type:label'?>
                                  <?php endif?>
                                  <?php if (($record['publication'] && !$record['publication'] =="")
||(
$record['publication_date'] && !$record['publication_date'] =="") ||($record['article_type'] &&
!
$record['article_type'] =="")):?>
                                  )
                                  <?php endif ?>
                                  </span><br /></td>
                              </tr>
                              <tr>
                                <?php if ($record['article_url']): ?>
                                <?php if(!preg_match("/^https:\/\//i"$record['article_url'] )):?>
                                <?PHP if (!preg_match("/^http:\/\//i"$record['article_url'])) {
$record['article_url'] = "http://" $record['article_url']; } ?>
                                <?php endif ?>
                                <?php endif ?>
                                <?php if ($record['image']):?>
                                <td style="padding-left:10px; padding-top:10px; text-align:left;" align="center" ><?php
$article_url $record['article_url'?>
                                  <?php foreach ($record['image'] as $index => $upload): ?>
                                  <a target="_blank" href="<?php echo $record['article_url'?>"><img style="
padding-right:10px; border:hidden;" src="<?php echo $upload['thumbUrlPath2'?>" width="<?php echo
$upload['thumbWidth2'?>" height="<?php echo $upload['thumbHeight2'?>" alt="" /></a><br />
                                  <?php if($upload['info1'] ):?>
                                  <span  class="Small-Text">Photo by:<<br />
                                  <?php echo htmlencode($upload['info1']) ?></span>
                                  <?php endif ?>
                                  <?php endforeach ?></td>
                                <?php else :?>
                                <td>&nbsp;</td>
                                <?php endif ?>
                                <td <?php if(!$record['image'] ):?>colspan="2" <?php endif?> valign="top"
class="Medium-Text"><?php echo $record['article_description'?><br />
                                  <a target="_blank" href="<?php echo $record['article_url'?>"><?php echo
$record['article_link_text'?>... (read more)</a>
                                  <?php $the_article $record['article_url'?>
                                 <br />
                                  <?php foreach ($on_line_article_commentsRecords as $record): ?>
                                  <?php  if ($record['link_url'] == $the_article ) :?>
                                  <br />
                                  <?php if(!preg_match("/^https:\/\//i"$record['link_url'] )):?>
                                  <?PHP if (!preg_match("/^http:\/\//i"$record['link_url'])) {
$record['link_url'] = "http://" $record['link_url']; } ?>
                                  <?php  endif  ?>
                                  <a href="http://www.ericaminer.com/comments.php?match_url=<?php echo
$record['link_url'?>"><?php echo $common_informationRecord['comments_link_text'?></a>
                                  <?php break ?>
                                  <?php endif ?>
                                  <?php endforeach ?>
                                  </td>
                              </tr>
                              <tr>
                                <td colspan="2"><hr /></td>
                              </tr>
                             <?php endif ?>
                              <?php endforeach; ?>
                            </table>
                            
                            <?php endif?>
                           <?php if (@$curYearCount == && (@$submitted == '' || @$submitted == "0") ): ?>     
                            <h3>So far, there are no articles published for <?php echo ($curYear?>.<br /> <br />
                              Please use the pulldown above to select <?php echo ($curYear-1?> and earlier
articles.</h3><?php endif ?> 
                            <?php // end show this year's articles only code?>


B) To modify the 'Search by Date' archive to display by year instead of month and date you'll need to remove all
references to 'month' in the code above.  (There are 4 )
Search for the 4 occurrences of  remove this code for years only:  in the code above and remove the indicated code. 
You can also change any notes to reflect your changes.

IF STATEMENTS



UNDERSTANDING AND USING IF STATEMENTS - Jun 19th, 2011

The if statement is one of the most powerful and valuable operators that you can use when designing your pages. You can
test for many different scenarios and have your page output (or not output) specific code, depending on the outcome of
your tests. 

The problem is that many times, if you’ve made a mistake in your coding, you’ll get a blank page displayed in your
browser and have no idea what your mistake was. 

So here’s a short primer on the correct way to use this powerful tool.

The first rule to remember is that every (series of) if statement(s) must be closed with an <?php endif ?> in order for
them to work correctly.

The second rule is that if you’re using a series of if statements, then the first one is always an “if”, the last
is always “else” and any in the middle are “elseif”.

 So, if you’re using only one if statement to test for a particular condition. Say to test whether the field
“your_field” is empty, then you’d use:

 In a single record viewer: 



<?php if ($your_tableRecord['your_field']): ?> ...your code... 
<?php endif ?>



Or in a multi record viewer:



<?php foreach ($your_tableRecords as $record): ?>
<?php if ($record['your_field']): ?> ...your code... 
<?php endif ?>
<?php endforeach; ?>



YOU CAN ALSO PUT AN IF STATEMENT AROUND A BLOCK OF TEXT TO KEEP IT FROM BEING RUN IF THERE ARE NO RECORDS AVAILABLE.



<?php if ($your_tableRecord['your_field']): ?> 
<?php foreach ($your_tableRecord['your_field'] as $upload): ?>  

... code to be run...

<?php endforeach ?>  
<?php endif ?>


 
IF YOU WANTED TO TEST FOR PARTICULAR CONTENTS IN “YOUR_FIELD”. 

Say, if the contents of “your_field” equaled “apples” then, you’d use the exactly equal to “==” operator:

In a single record viewer: 



<?php if ($your_tableRecord['your_field'] == 'apples'): ?> ...your code... 
<?php endif ?>



Or in a multi record viewer:



 <?php foreach ($your_tableRecords as $record): ?>
<?php if ($record['your_field'] == 'apples'): ?> ...your code... 
<?php endif ?>
<?php endforeach; ?>



IF YOU WANTED TO TEST FOR THE ABSENCE OF PARTICULAR CONTENTS IN “YOUR_FIELD”.

Say, if the contents of “your_field” was anything other than “apples” then, you can use the not operator “!”
:

In a single record viewer:


 
<?php if (!$your_tableRecord['your_field'] == 'apples'): ?> ...your code... 
<?php endif ?>



Or in a multi record viewer:



 <?php foreach ($your_tableRecords as $record): ?>
<?php if (!$record['your_field'] == 'apples'): ?> ...your code... 
<?php endif ?>
<?php endforeach; ?>



TO USE A SERIES OF IF STATEMENTS TO TEST FOR A SERIES OF CONDITION, THEN YOU’D USE:

in a detail viewer:



<?php if ($your_tableRecord['your_field'] == 'apples'): ?> ...your field contains apples... 
<?php elseif ($your_tableRecord['your_field'] == 'bananas'): ?> ...your field contains bananas... 
<?php else: ?> ...your code... 
<?php endif ?>



And in a list viewer:



<?php foreach ($your_tableRecords as $record): ?>
<?php if ($record['your_field'] == 'apples'): ?> ...your field contains apples... 
<?php elseif ($record['your_field'] == 'bananas'): ?> ...your field contains bananas... 
<?php else: ?> ...your code... 
<?php endif ?>
<?php endforeach; ?>



YOU’RE NOT LIMITED TO A SINGLE OR A SINGLE FIELD CONDITION. 

To test for multiple conditions, you can use:

(Note: The “!” works in this too)



<?php if ($your_tableRecord['field_a'] == 'apples' && $your_tableRecord['field_b'] == 'bananas' &&
$your_tableRecord['field_c'] == 'pears'): ?> ...Your record has all three... 
<?php endif ?>



Or 



<?php foreach ($testRecords as $record): ?>
<?php if ($record['field_a'] == 'apples' && $record['field_b'] == 'bananas' && $record['field_c'] == 'pears'): ?>
...Your record has all three...
<?php endif ?>
<?php endforeach; ?>



IF YOU WANTED TO TEST FOR THREE CONDITIONS IN A LIST VIEWER RECORD. 

Say, “field_a” empty, “field_b” empty and “field_c” not empty, you could use:



<?php if (!$record['field_a'] && !$record['field_b'] && $record['field_c']): ?> ...your code...
<?php endif ?>

IF YOU WANTED TO TEST FOR ANY OF A SERIES OF CONDITIONS. 

Say a or b or c, you could use:



<?php if ($your_table Record['your_field'] == "apples" || $your_tableRecord['your_field'] == "bananas" ||
$your_tableRecord['your_other_field'] == "pears"): ?> 
  Your code...
<?php else: ?> 
Your code...
<?php endif ?>



Or



<?php if ($record['your_field'] == "apples" || $record['your_field'] == "bananas" || $record['your_other_field'] ==
"pears"): ?> 
  Your code...
<?php else: ?> 
Your code...
<?php endif ?>



IF YOU WANT TO TEST TO SEE IF A FIELD CONTAINS A PARTICULAR ALPHANUMERIC PATTERN

Like seeing if apples is included in a multi item list, you can use the strpos function. Theoretically the strpos
function returns the numeric position of the first occurrence of the pattern that you’re searching for, however,
before it can do that it has to decide if the pattern exists in the string.

To test for the pattern “apples” in the string “bananas, apples, pears, raisins” you could use something like:



<?php if (strpos($your_table['your_field'], 'apples')): ?>
Your code...
<?php endif ?>



This is by far not a comprehensive list of the possibilities for using if statements, but it should be enough to get you
started.

Before leaving this topic, I was curious as to the rules for requiring an <?php endif; ?> to close an "if" statement.

Jason Sauchuk, a programmer with Interactive Tools cleared up the question:

"You only need <?php endif ?> if your "if" statement covers more than 1 <?php?> block.

Example:



<?php if($x==1?>  
 
 ----some code---  
 
<?php endif ?>



This could also be put into 1 <?php ?> block with no <?php endif ?>, as in:



<?php if($x==1){ 
 ---
some code--  
           }  
 
?>




IF STATEMENTS THAT MEET MORE THAN ONE CONDITION - Aug 3rd, 2010

If you’ve got a table called “people and two fields “price” and “description” and you need a specific result
depending on whether there’s information in one, the other or both fields, you can approach it this way. Remember that
the operator “!” means not so !$record['price'] means that there’s no information in the price field.



<!-- If there’s no price and no description, just show the title. –>
<?PHP foreach ($peopleRecords as $record): ?>
<?PHP if (!$record['price'] && !$record['l_description']): ?><?PHP echo $record['title'?><?PHP endif ?>

<!-- If there’s no price, just  a description , offer information only. –>
<?PHP if (!$record['print_price'] && $record['full_description']): ?><a href=”<?PHP echo $record['_link'?>”><?PHP
echo $record['title'?></a>Click the title for more information.<?PHP endif ?>">

<!-- If there’s a price and no description, ask for the sale. –>
<?PHP if ($record['price'] && !$record['description']): ?><a href=”<?PHP echo $record['_link'?>”><?PHP echo
$record['title'?></a>Click the title to buy.<?PHP endif ?>

<!-- If there’s a price and a description, offer information and ask for the sale. –>
<?PHP if ($record['price'] && $record[description']): ?><a href=”<?PHP echo $record['_link'] ?>”><?PHP echo
$record['title'] ?></a>Click the title the for more information or to buy.<?PHP endif ?>
<?PHP endforeach ?>


USING IF STATEMENTS TO DEFINE OUTPUT BASED ON A FILE EXTENSION - Aug 16th, 2011

If you need to publish specific code that’s based on the type of file that you’ve uploaded into an upload field,
like a .swf flash file, here’s the trick. Just use:



<?php if ($upload['extension'] == 'swf'): ?>



or for the second through next to last if parameter in the list (the last one gets an <?php else: ?> instead of a <?php
elseif...)



<?php elseif ($upload['extension'] == 'swf'): ?>



You can use this for non upload fields as well:



<?php if ($your_fieldRecord['extension'] == 'doc'): ?>




 or 



<?php elseif ($your_fieldRecord['extension'] == 'doc'): ?> 




Don’t forget that every if statement must be closed with an <?php endif ?>


SETTING UP "IF" STATEMENTS FOR NO RECORDS MATCHING A CRITERIA - Apr 1st, 2012

This one plagued me for a while until Jason Sauchuk, a programmer with Interactive Tools came to the rescue.

I had a checkbox field in a multi-record editor and I wanted to be able to output a “no records found with the
checkbox checked” message if there were no records with the checkbox checked.

Here’s what he offered:



<?php $ischeck0?>  
    <?php foreach ($e_blast_events_noticeRecords as $record): ?>  
        <?php if($record['apbc_event']==1$ischeck=1?> 
    <?php endforeach ?>  
     
 <?php if ($ischeck==0) echo "no records found."?> 



I could have also added:



 <?php if ($ischeck==1) echo "records were found."?> 



In the previous recipe, I was using a "where" statement to restrict which dates are displayed in a group. The code I
used to show only events that had a start date or a reception date in the next 7 days was:



<?php 
    list($my_tableRecords$my_tableMetaData) = getRecords(array( 
    
'Table name'   => 'my_table'
     
'where' => '(NOW() + INTERVAL 7 DAY) >= event_reception_date AND event_reception_date >= TIMESTAMP(CURDATE(),
"00:00:00") OR (NOW() + INTERVAL 7 DAY) >= event_start_date AND event_start_date >= TIMESTAMP(CURDATE(), "00:00:00")',  
  )); 
 
?>



If there were no records that met the criteria, I wanted to output the same kind of “no records found” notice.

This was one I should have been able to figure out but I just couldn’t come up with a solution. Again, Jason came to
the rescue.  He suggested a simple solution:



<?php if(!$e_blast_events_noticeRecords) echo "No events are opening or have a reception during the next 7 days.";  
?>



And 



<?php if($e_blast_events_noticeRecords) echo "These events are opening or have a reception during the next 7 days."?>



USING IF STATEMENTS AND RECORD COUNTS TO CHANGE PAGE CONTENTS - Nov 25th, 2010

I recently had an artist client who’s portfolios were set up to show a list page of portfolio categories, a thumbnail
page showing all of the images that belonged to a particular category, and a detail page that showed details about
specific images.

They wanted to be able to link visitors directly to a thumbnail page if there was only one category in their portfolio,
and to show the list page only if there was more than one category.

I was able to easily accomplish their goal by counting the visible records in the portfolio category section. (If
records were "hidden" they were not counted.)

The portfolio categories were listed in a section called, “portfolio_name_details”, and a thumbnail page was called,
"portfolio_album.php"

I inserted the following code in the "Portfolio" link of the navigation menu:



<a href="<?php if ($portfolio_name_detailsMetaData['totalRecords'] == 1): ?>portfolio_album.php<?php else:
?>portfolio.php<?php endif ?>">Portfolio</a>


You can use the same approach to change the page content and perform other tasks.  

NOTE: Don't forget to include the record count table in on each viewer's "get records" call. Mine looked like this:



list($portfolio_name_detailsRecords, $portfolio_name_detailsMetaData) = getRecords(array(
    'tableName'   => 'portfolio_name_details',
  ));



USING THE NUMBER OF RECORDS MEETING A MYSQL QUERY TO CHANGE WHAT A VIEWER SHOWS - Feb 1st, 2011

I had a page with a number of "Where" statements setting the criteria for which records wound appear on groups on a
page.

What my client wanted to do was hide all reference to any group of records where there were no records that met those
criteria.

Example. The heading HAPPENING THIS WEEK if there were no records with start dates falling within the next 7 days.

Here's the simple if statement code that worked for me.



<?php
    list($eventsRecords$eventsMetaData) = getRecords(array(
    
'tableName'   => 'events',
      
'where' => 'start_date > TIMESTAMP(NOW()+ INTERVAL 7 DAY)',
      
      
'orderBy'=> 'end_date ASC',
  ));
  
  
?> 

<?php if (!$eventsMetaData['totalRecords'] == 0): ?> 

Show this code....

 <?php else: ?>  

Show some other code...

<?php endif ?>



ALLOWING VISITORS TO SET VIEWER ORDERBY OPTIONS IN A VIEWER - Sep 5th, 2022

A while back user rjbathgate wanted to enable visitors to determine the order that results are displayed in a viewer
from a series of options.

Dave Edis from Interactive Tools offered:

First create a search form on the viewer, something like:


<form method="POST" action="<?php echo $_SERVER['PHP_SELF'?>">

<select name="order">
<option value="a">price (highest first)</option> 
<option value="b">price (lowest first)</option>
...etc...  
</select>

<input type="submit" name="submit" value="Search">
</form>


Then add some code to the list records call at the top of the viewer code


$orderBy = ""; 
if (@$FORM['order'] == 'a') { $orderBy = "price DESC"; } 
if (@$FORM['order'] == 'b') { $orderBy = "price"; } 
# ... etc ... 
 
 
list($your_tableRecords, $your_tableMetaData) = getRecords(array( 
  'tableName'          => 'your_table', 
  'orderBy'            => $orderBy, 
));


NOTE: According to Dave Edis from Interactive Tools, the reason to do it that way by passing a letter (or word or code,
it doesn't matter) and testing for that instead of just specifying the order by in the option value directly is because
you don't want users to be able to pass MySQL directly into your program or it's a security risk. 

You can expand this idea to create as complex a set of criteria as required.





ALLOWING VISITOR TO SET WHERE VALUES IN A VIEWER - Sep 5th, 2022

In this example you're trying to limit the records shown to those that match the value of a field called project_title,
so first you'll need to create a list field called project_title in your table.

Then, we'll assume that in any record the values for that field can be either Test event 1 or Test event 2.

At the top of your page in the load records calls use the code:



$where = "";  
if (@$FORM['where'] == 'a') { $where = 'Test Event 1'; }  
if (@$FORM['where'] == 'b') { $where = 'Test Event 2'; }    
       
 list($$your_tableRecords, $$your_tableMetaData) = getRecords(array(  
    'tableName'   => '$your_table',  
    'where'       => " project_title = '$where' ",   
  ));


And in the body, the form  code would be:


<form method="POST" action="<?php echo $_SERVER['PHP_SELF'?>">
<select name="where">
<option value="">Select</option>
<option value="a">Event 1</option>
<option value="b">Event 2</option>
</select>

<input type="submit" name="submit" value="Choose an exhibition to View">
</form>
 
And Here's Another example of limiting the records shown on a viewer page. 
This one has three possible options and incorporates check boxes in a single record editor to determine which options
are available to the visitor:

At the top of the viewer page in the list records calls 

$where = "";  
if (@$FORM['where'] == 'a') { $where = "want_this_book = '1'"; }  
if (@$FORM['where'] == 'b') { $where = "get_better_copy = '1'"; } 
if (@$FORM['where'] == 'c') { $where = "get_better_copy = '1' OR want_this_book = '1'"; } 
 
 list($booksRecords, $booksMetaData) = getRecords(array(
    'tableName'   => 'books',
    'loadUploads' => true,
    'allowSearch' => true,
    'where'       => $where,
      
    // 'debugSql' => true, 
  ));
   
?>

In The Body:

 <form method="POST" action="">
<select name="where">
<option value="">Make a selection from this pulldown menu </option>
<?php if ($common_informationRecord['sort_by_want_these_books'] == '1'):?><option value="a">Search For Books I Want But
Don't Have</option><?php endif ?>
<?php if ($common_informationRecord['sort_by_better_copy_of_these_books'] == '1'):?><option value="b">Search For Books I
Want A Better Copy Of</option><?php endif ?>
<?php if ($common_informationRecord['sort_by_want_these_books'] == '1' &&
$common_informationRecord['sort_by_better_copy_of_these_books'] == '1' ):?><option value="c">Search For All The Books I
Want To Add To My Collection</option><?php endif ?>
</select>

<input type="submit" name="submit" value="Sort By Your Selection">
</form>



NOTE: According to Dave Edis from Interactive Tools, the reason to do it that way by passing a letter (or word or code,
it doesn't matter) and testing for that instead of just specifying the order by in the option value directly is because
you don't want users to be able to pass MySQL directly into your program or it's a security risk. 

You can expand this idea to create as complex a set of criteria s required.

 ANOTHER NOTE: Jason Sauchuk offered this mini tutorial on the use of single and double quotes. He said:

In PHP, a string that is set with double quotes can have variables inserted directly into it without concatenation.

example:


<?php 
  $myName "Jason"
  
$greeting  "Hello, my name is $myName"
?>


The value of $greeting would be:
Hello, my name is Jason 

If we took this same piece of code and used single quotes:

    

<?php 
  $myName "Jason"
  
$greeting  'Hello, my name is $myName'
?>


The value of $greeting would be:
Hello, my name is $myName

PHP will not put the value of $myName into the string.



ALLOWING VISITOR TO SET WHERE VALUES FROM MASTER VALUES LIST - Feb 15th, 2013

For this example I wanted my client to be able to filter the Exhibition records displayed by choosing possible
project_title 'where' values from a pre-determined list of Exhibition titles. This would insure that all the requests
were consistent when filtering the records to be shown, and would keep all the possible values in a CMSB table, instead
of needing to hard code them into the viewer code.

I also wanted to make sure that I was not creating a security risk as described by Dave Edis from Interactive Tools, who
said:

"By passing a letter (or word or code, it doesn't matter) and testing for that instead of just specifying the order by
in the option value directly, you won't allow users to pass
MySQL directly into your program and create a security risk."

To accomplish this task, I created a multi-record editor called master_exhiibtion_list which has only one text field
called title. This way each allowed Title was in it's own record. 

Then in the head of my viewer, (with a lot of help from Jason Sauchuck from Interactive Tools), I inserted the following
code:


<?php
list($master_exhibition_listRecords$master_exhibition_listMetaData) = getRecords(array(
'tableName' => 'master_exhibition_list',

));
?>

<?php
$numToName = array(); 
foreach (
$master_exhibition_listRecords as $record){ 
  
$numToName[$record['num']] = $record['title']; 


}
?>

<?php 
 $where ""
  
?>

<?php foreach ($master_exhibition_listRecords as $record): ?>

<?php if (@$FORM['where'] == $record['num']) { $where $record['title'];}?>
<?php endforeach; ?>
   
  
<?php
 list($client_uploadsRecords$client_uploadsMetaData) = getRecords(array(  
    
'tableName'   => 'client_uploads',  
    
'where'       => " project_title = '$where'",  

  ));
?>


Then for the form that selects the values (again with a lot of help from Jason) I used:



<form method="POST" action="<?php echo $_SERVER['PHP_SELF'?>">

<select name="where"> 
<option value=""><span class="body-text-bold">Select</span></option> 
<?php foreach($numToName as $num => $name): ?> 
<option value="<?php echo $num;?>"><?php echo $name;?></option> 
<?php endforeach?> 
</select>

<input type="submit" name="submit" value="Select An Exhibition And Click To View">
</form>



ALLOWING ADMINS TO SET SORT ORDERBY VALUES BASED ON A LIST FIELD SELECTION - Sep 5th, 2022

I needed to let an site admin decide whether a viewer would show the data on a list page with the newest record in the
table 'studio_art_images' first, or show the oldest record first, show the records in a random order, or in the order
they appeared in the record list.

With a bit of help from Interactive Tools Guru Daryl Maximo, here's what we came up with.

1) set up a single value list field in a single record editor (in this example it's 'sort_order' and the section is
named 'common_information')

2) enter the possible list values as:

Oldest First
Newest First
Show in Random Order
None

At the top of the viewer use the code:


<?php
 list($common_informationRecords$common_informationMetaData) = getRecords(array(
    
'tableName'   => 'common_information',
  
//'debugSql' => true,
    'limit'       => '1',
  ));
  
$common_informationRecord = @$common_informationRecords[0]; // get first record
 ?> 
<?php $orderedBy ""?>
<?php  
 if (strpos($common_informationRecord['sort_order'], 'Oldest' )!== FALSE) { $orderedBy 'createdDate ASC'; };
 if (
strpos($common_informationRecord['sort_order'], 'Newest' )!== FALSE) { $orderedBy 'createdDate DESC'; };
 if (
strpos($common_informationRecord['sort_order'], 'Rand' )!== FALSE) { $orderedBy 'RAND()'; };
 if (
strpos($common_informationRecord['sort_order'], 'None' )!== FALSE) { $orderedBy ''; };

?>
 <?php list($studio_art_imagesRecords$studio_art_imagesMetaData) = getRecords(array(
    
'tableName'   => 'studio_art_images',
     
'orderBy' => $orderedBy,
  ));
?>


Then display the record data in your viewer using standard foreach loop code. 

According to Daryl,

Without the !== FALSE the strpos() function returns the position of the first occurrence of a substring in a string so
it could be because it's returning position "0", which is a boolean false, that causes none of your if statements to be
true.

You can use the !== or the === operator to check for "0" as a value and not a boolean false.

VIEWERS - DETAIL PAGES



SPLITTING DETAIL PAGES - Aug 3rd, 2010

Instead of a single detail page for a record I needed to have some of the detail information on one detail page and
other information on another detail page and link the 2 of them so I could navigate back and forth between them. 

I was lost until Dave from Interactive Tools showed just how easy that was. He said:
You just need to pass the record number along when you link back and forth between detail pages.
  
So if you're on detailpage1.PHP and you've loaded record 23, then you'd like to link to detailpage2.PHP, use this:



<a href="detailpage2.php?<?PHP echo $record['num'?>”>link text to detail page 2</a>



Another way to do it, if you just want to pass along whatever is after the ? in the url is like this:



<a href="detailpage2.php?<?PHP echo @$_SERVER['QUERY_STRING'?>”>link text to detail page 2</a>




USING MORE THAN ONE DETAILS PAGE DEPENDING ON CONDITIONS - Aug 3rd, 2010

Let’s say that you have a series of fields in a multi record editor and you’d like to be able to show certain
information for one type of record and other information for other types of records.
 
I was working with an author who needed to display one subset of information for books that had been published and
another subset for those that had  not yet been published.

Based on some direction from Dave at Interactive Tools, I was able to:

1) Create a multi record editor with a special checkbox field to indicate if the book was published or unpublished.

2) Set up 2 details pages, one for the published books and another for those that were not yet published.

3) Use the following if statements in my list page to determine which details page would be shown and the record number
that would be used: (styling information deleted for clarity). 



<table>
<tr>
<?PHP foreach ($booksRecords as $record): ?>

<?PHP if ($record['published'] == '1'): ?>
   <td>
<a href="bookdetail1.php?<?PHP echo $record['num'?>"> <?PHP foreach ($record['list_image'] as $upload): ?> <img
src="<?PHP echo $upload['thumbUrlPath'?>" width="<?PHP echo $upload['thumbWidth'?>" height="<?PHP echo
$upload['thumbHeight'?>" alt="" /></a>

</td>
<td>
<a href="bookdetail1.php?<?PHP echo $record['num'?>"><?PHP echo $record['title'?></a>
<?PHP endforeach ?>
<?PHP endif ?>
             
 <?PHP if ($record['published'] == '0'): ?>
<td">
<a href="bookdetail2.php?<?PHP echo $record['num'?>"> <?PHP foreach ($record['list_image'] as $upload): ?> <img
src="<?PHP echo $upload['thumbUrlPath'?>" width="<?PHP echo $upload['thumbWidth'?>" height="<?PHP echo
$upload['thumbHeight'?>" alt="" /></a>
</td>
<td>
<a href="bookdetail2.php?<?PHP echo $record['num'?>"><?PHP echo $record['title'?></a>

<?PHP endforeach ?>
<?PHP endif ?></div></td>

<?PHP $maxCols=2; if (@++$count $maxCols == 0): ?></tr><tr><?PHP endif; ?>  

 <?PHP endforeach ?>
</tr>
</table>




ITEMS FROM A SINGLE RECORD EDITOR NOT SHOWING ON MULTI RECORD DETAIL PAGES - Aug 3rd, 2010

I had an image that came from a single record that I wanted to appear on my details pages from a multi record viewer. It
showed up on the first record in the list but gave me an error saying that the argument supplied for foreach() was
invalid for other records.
Dave had an insight, He said:

Try removing this line from the single page call in the details page header :



'where' => whereRecordNumberInUrl(1),



It worked like a charm.


SUGGESTING “ARTICLES YOU MIGHT ALSO LIKE” BASED ON KEYWORDS - May 8th, 2011

User Illume Magazine wanted to be able to suggest articles on their article detail page that, based on matching
keywords, might be of interest to readers of a specific article.

This concept could also be used for showing products that share certain qualities to potential buyers.

Here’s what a collaboration between Chris Waddell from Interactive Tools and CMSB user Perchpole came up with:

For this recipe you’ll need to add a “your_keyword_field” text field to the multi-record editor that contains your
articles.

Here's the code that goes at the top of your detail viewer:

Insert the code between the lines INSERT THIS CODE and END OF INSERTED CODE into your viewer and change the path to your
viewer_functions.php and the names of your table and fields.



  <?php header('Content-type: text/html; charset=utf-8'); ?>
<?php
  

  // load viewer library
  $libraryPath 'cmsAdmin/lib/viewer_functions.php';
  
$dirsToCheck = array('/path_to_your_server/','','../','../../','../../../');
  foreach (
$dirsToCheck as $dir) { if (@include_once("$dir$libraryPath")) { break; }}
  if (!
function_exists('getRecords')) { die("Couldn't load viewer library, check filepath in sourcecode."); }

  
// load records
  list($your_products_tableRecords$your_products_tableMetaData) = getRecords(array(
    
'tableName'   => 'your_products_table',
    
'where'       => whereRecordNumberInUrl(1),
    
'limit'       => '1',
  ));
  
$your_products_tableRecord = @$your_products_tableRecords[0]; // get first record

  // show error message if no matching record is found
  if (!$your_products_tableRecord) {
    
header("HTTP/1.0 404 Not Found");
    print 
"Record not found!";
    exit;
  }
    
//INSERT THIS CODE
   // construct a where clause out of this record's keywords (e.g. "apples, oranges" -> "keywords LIKE '%apples%' OR
keywords LIKE '%oranges'")  

$keywords = preg_split('/,\s*/', $articlesRecord['your_keyword_field']);

$where = '0';  
foreach ($keywords as $keyword) {  
  $where .= ' OR ';  
  $where .= "your_keyword_field LIKE '%" . mysql_escape($keyword) . "%'";  
}  
  
// find the most recent records with at least one matching keyword (but not including the current record)  
list($similarRecords)= getRecords(array(  
  'tableName'   => 'your_products_table',  
  'where'       =>  "(" . $where . ") AND num != "  . $your_products_tableRecord['num'],  
  'limit'       => 5,   
));
// END OF INSERTED CODE
?>



Then, where you want to display the titles of, and links to similar articles on the detail page, use the following code:



<?php foreach ($similarRecords as $record): ?> 
<a href="<?php echo $record['_link'?>"><?php echo $record['title'?></a><br /><?php endforeach ?> 



The regular expression in the first line of code above will create keywords split on a comma followed by any number of
spaces. Here it is again for reference:



$keywords = preg_split('/,\s*/', $articlesRecord['keywords']);



For example, "a quick, brown fox" would turn into two keywords: "a quick" and "brown fox".

If you want to split on commas and/or spaces, you can use this regular expression instead:

    

$keywords = preg_split('/[,\s]+/', $articlesRecord['keywords']);



For example, "a quick, brown fox" would turn into four keywords: "a", "quick", "brown", and "fox". 

NOTE: You can also use this one to list keywords in a multi-value list field.  

** If you're going to use this in a multi value situation, you'll need to insure that there are no spaces between multi
word "keywords. You can use "_" or "-" to separate multiple words and then if you need to print out a keyword list for
some reason you can use another regular expression like the one below to replace each "_" or "-" with the required
spaces.

     

<?php foreach ($your_tableRecords as $record): ?> 

 <!-- Insert This Code -->

<?PHP $record['your_field'] = preg_replace("/[-_]/""&nbsp;"$record['your_field'] ); ?> 

<!-- End of Insert-->

<?php echo $record['your_field] ?> 
             
<?php endforeach; ?>




You can learn more about the power of regular expressions in the recipe called: “REPLACING ONE CHARACTER WITH ANOTHER
USING “REGULAR EXPRESSIONS”“

VIEWERS - DISPLAYING



FORCING ONLY TEXT TO BE DISPLAYED ON YOUR PAGES - Aug 3rd, 2010

If your editors are savvy they can force html code to be displayed in what you’ve set up as a text only field with
some potentially negative effects on your page layout.

Here’s a hidden gem from Dave at Interactive Tools that can help to guard against that.
According to Dave, you can force the entry to be displayed as text (even if they enter HTML) by adding
"htmlspecialchars" in the display tag like this:



<?PHP echo htmlspecialchars($record['content']); ?> 



That should dissuade any hot shot editors from bending the rules. 


ELIMINATE BLANK LINES OR HEADINGS WHEN THERE’S NO INFORMATION IN A FIELD - Aug 3rd, 2010

Dwelling asked the question and Dave came to the rescue.  He suggested to start by experimenting with the PHP “if”
tag.



<?PHP if ($newsRecord['title']): ?> 
  Title: <?PHP echo $newsRecord['title'?>  
<?PHP endif; ?>



Here’s one that was used in a multi record editor to eliminate the word “through” if there was no “end date”
and the heading :Reception” if that field was blank: 



<?PHP foreach ($exhibitionsRecords as $record): ?>
                        <tr valign="top" align="center">
    <td >
<span  class="heading-text-bold"><?PHP echo $record['title'?></span><br />
<span class="body-text"> <?PHP echo $record['start_date'?>&nbsp;</span>
<span class="body-text"> <?PHP if ($record['end_date']): ?>Through&nbsp;<?PHP echo $record['end_date'?></span><br />
<?PHP endif; ?>
<?PHP if ($record['reception_date_and_time']): ?> <span class="heading-text-bold">Reception</span><br /><span
class="body-text"> <?PHP echo $record['reception_date_and_time'?> </span> 
<?PHP endif; ?>                 
                       </td>
  </tr>
 <tr>
     <td align="left" height="125" valign="top"><div class="body-text"><?PHP echo $record['content'?></div></td>
  </tr> <tr>
    <td  align="center">
<!-- display image --> <?PHP foreach ($record['image'] as $upload): ?>
 <?PHP if ($upload['hasThumbnail']): ?>
<img src="<?PHP echo $upload['thumbUrlPath'?>" width="<?PHP echo $upload['thumbWidth'?>" height="<?PHP echo
$upload['thumbHeight'?>" alt="" />
<?PHP elseif ($upload['isImage']): ?>
 <img src="<?PHP echo $upload['urlPath'?>" width="<?PHP echo $upload['width'?>" height="<?PHP echo $upload['height']
?>" alt="" />
<?PHP else: ?>
 <a class="special" href="<?PHP echo $upload['urlPath'?>">DOWNLOAD<?PHP echo $upload['filename'?></a>
<?PHP endif ?>
<div class="body-text-bold">
<?PHP echo $upload['info1']; ?><br />
<?PHP echo $upload['info2']; ?> </div></td>
<?PHP endforeach ?>



Basically you just put the field you want to test in between the brackets. Note that this won't display for blank -or-
zero.


CHECKING FOR SPACES IN OTHERWISE BLANK FIELDS WHEN USING IF STATEMENTS - Mar 7th, 2023

When using “if” statements to suppress titles and other information in a viewer if a field is empty, it’s easy for
client’s to leave blank spaces in an otherwise empty field and defeat the “if” test. 

Now, thanks to a plugin (CMSB version 2.04 or above required)  written by Dave Edis of Interactive Tools, it’s even
easier to guard against this by checking to see if the content is text and not just a space and automatically deleting
the space before saving the record so that they can’t interfere with any “if” tests. It works in both text and
WYSIWYG fields

You can download this plugin from:

     http://www.thecmsbcookbook.com/downloads/removeExtraWhitespace3.zip

The plugins won’t delete errant spaces in otherwise blank fields that already exist in your saved records, but you can
still check for them in each if statement used on your page.

To a normal “if” statement:



 <?php if ($yourRecord['title']): ?> 
  Title: <?php echo $yourRecord['title'?> 
<?php endif ?>



Just add this code:



 && !ctype_space($yourRecord['title'])



And the result:



<?php if ($record['title'] && !ctype_space($record['title'])): ?> 
  <?php echo $record['title'?> 
<?php endif ?>



(Which means "if title has a value AND it's NOT (!) all spacing characters, then show it" will solve the problem.)

According to Dave Edis, This technique will not work if the content has PHP tags in it, but will for whitespace. 

Also note this will not strip leading or trailing spaces from the content. To do that gets a little more complex.


CHECKING FOR BLANK DATE FIELDS - Mar 7th, 2023

The second version of Dave Edis’ ExtraWhitespace plugin (CMSB version 2.05 or above required) adds the ability to
check for blank date fields to checking for white space in otherwise empty text fields, and eliminate the possibility of
returning a Jan 1, 1970 or similar date, if a blank date field is encountered.

You can download this version of the plugin from:

      http://www.thecmsbcookbook.com/downloads/removeExtraWhitespace3.zip 


MATCHING A PATERN OR REPLACING ONE CHARACTER WITH ANOTHER USING “REGULAR EXPRESSIONS” - Jun 3rd, 2017

REPLACING CHARACTERS
Sometimes you can run into problems when there’s a space, a dash or underscore between words in a field value. I ran
into this recently with an implementation of Lightbox. 

My client wanted to use a 2 word value for a field and Lightbox wanted to see a single word with no spaces. If I
wasn’t displaying this value in other places on the page I could have put a dash or underscore in the value instead of
a space.

I chose to use a dash to make Lightbox happy and to replace the dash with a space where it appeared on the page.

Here’s how for a multi record editor:(thanks to Chris Waddell at Interactive Tools for the code syntax)

Just insert the line below into your code.



<?php foreach ($your_tableRecords as $record): ?> 

 <!-- Insert This Code -->

<?PHP $record['your_field'] = preg_replace("/[-]/""&nbsp;"$record['your_field'] ); ?> 

<!-- End of Insert-->

<?php echo $record['your_field] ?> 
             
<?php endforeach; ?>



Chris explained:

/[-]/ means "match a single character in the set [-]". 
preg_replace() will repeat the match as many times as it can. 

It’s also possible to match more than one character in the set by adding the additional characters as shown below.
Changing the code above to this would replace any occurrence of a dash, an underscore, an r or an x with a space. The
replacement will be case sensitive.



<?PHP $record['your_field'] = preg_replace("/[-_rx]/”, "&nbsp;", $record['your_field'] ); ?> 



To make the replacement case insensitive, just ad an “i” to the end of the expression, like this:



<?PHP $record['your_field'] = preg_replace("/[-_rx]/i”, "&nbsp;", $record['your_field'] ); ?> 



If you wanted to replace 2 different characters with 2 different ones, you’d do it by adding a second line of
“preg_replace” code like this:



<?PHP $record['your_field'] = preg_replace("/[-]/", "&nbsp;", $record['your_field'] ); ?> 
<?PHP $record['your_field'] = preg_replace("/[_]/", "*", $record['your_field'] ); ?> 



The above code will replace any occurrence of a dash ("-") in the string with a space (" "). Then it will replace any
occurrence of an underscore ("_") with an asterisk ("*"). 

For a single record editor, the code syntax would be.



<?PHP $your_tableRecord['your_field'] = preg_replace("/[-_rx]/”, "&nbsp;", $your_tableRecord['your_field'] ); ?> 



If you want to replace a complete expression like rx with another character then remove the forward slashes around the
expression 

So This:



<?PHP $your_tableRecord['your_field'] = preg_replace("/[rx]/”, "&nbsp;", $your_tableRecord['your_field'] ); ?> 



Would become this:



<?PHP $your_tableRecord['your_field'] = preg_replace("[rx]”, "&nbsp;", $your_tableRecord['your_field'] ); ?> 



When using Lightbox and other utilities, an extra double quote can cause the code to become broken. If you are using an
uploads info fields you can't specify characters as allowedso I use this little trick to replace any inadvertent
double quotes with 2 single quoteswhich looks (almostthe same.



<?PHP $upload['info1'] = preg_replace("[\"]""''"$upload['info1'] ); ?>



In order to replace backslashes they must be escaped. Here's how to replace all backslashes with nothing:


<?PHP $your_tableRecord['your_field'] = preg_replace("[\\\\]”, "", $your_tableRecord['your_field'] ); ?>



or


<?PHP $your_variable = preg_replace("[\\\\]""$your_variable ); ?>

  
According to Dave Edis, there are many other uses for “regular expressions”. He commented: “Basically regular
expressions are ways of describing patterns.  They're used to matching, and replacing content.  So this is useful for
validating input (such as determining if an url, credit card number, phone number, or email is in the right format),
replacing content (stripping html tags, adding extra tags or attributes, etc, or parsing out data (matching all the
email addresses in a web page, parsing a CSV file and importing the data, or extracting some data (such as the current
temp from a weather web page to use somewhere else).

In PHP all the regular expression functions start with preg_ and in the CMS Builder code base we use those functions in
over 200 places.  So they come in quite handy.  If your web page editor software has a "Find in Files" or search
multiple files feature you can search the CMSB code for "preg_" and see some of the ways we use it.”

MATCHING A PATTERN

If you wanted anything other than between 11 to 14 characters that were either numbers or spaces in a phone number field
and throw an error message if the criteria was not met,  you could use something like:

  if(!preg_match("/^[ 0-9]{11,14}$/", @$phone )){
   $errorsAndAlerts .= "Phone numbers must be between 10 and 14 characters long and can not contain anything but numbers
and spaces \n";} 

You can see a list of the PHP functions here: 

       http://ca3.php.net/manual/en/ref.pcre.php 
 
Here’s a link to a regex tutorial:

http://www.regular-expressions.info/tutorialcnt.html

Here’s a link to a regex cheat sheet:

http://www.bitcetera.com/page_attachments/0000/0030/regex_in_a_nutshell.pdf

And a few on-line  regex testers/debuggers:

http://www.regexplanet.com/advanced/java/index.html

https://regex101.com/



MORE TRICKS WITH REGULAR EXPRESSIONS - Mar 12th, 2015

1) How to replace double quotes with 2 single quotes in a text field. (The field was a dimension field and the common
symbol for inches is a double quote.)

If your code doesn't like double quotes in the contents of a field, here's how to replace them with a set of 2 single
quotes that look pretty much the same.



 <?php  $your_table['your_field'] = preg_replace("[\"]""''"$your_table['your_field']); ?>

Or for a variable


<?php  $your_variable preg_replace("[\"]""''"$your_variable); ?>


Then you can echo your field as you normally would and any double quotes will be replaced.

2) I needed to populate both a navigation menu entry and a page header from the contents of a text field called
member_meetings. The field contents was: “Monthly Artists&nbsp;&nbsp;Sharing Meeting”

The navigation menu required the Artists&nbsp;&nbsp; for the text to line up appropriately, but the header required:

A &nbsp; R &nbsp; T &nbsp; I &nbsp;S &nbsp; T &nbsp; S &nbsp; &nbsp; S &nbsp; H &nbsp; A &nbsp; R &nbsp; I &nbsp; N
&nbsp; G &nbsp; &nbsp;M &nbsp; E &nbsp; E &nbsp; T &nbsp; I &nbsp; N &nbsp; G 



I knew that regular expressions could replace the characters for me as required, but couldn’t get the syntax to work
until Dave Edis from interactive Tools came to the rescue.

He offered the following solution:


<div align="center">
                      
<?php  $text strtoupper($common_informationRecord['member_meetings']) ;  
print 
expandText($text); 
// outputs: M &nbsp; O &nbsp; N &nbsp; T &nbsp; H &nbsp; L &nbsp; Y &nbsp; &nbsp; 
//     ...: A &nbsp; R &nbsp; T &nbsp; I &nbsp; S &nbsp; T &nbsp; S &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
//     ...: S &nbsp; H &nbsp; A &nbsp; R &nbsp; I &nbsp; N &nbsp; G 

 // 
function expandText($text) { 
  
$text preg_replace("/&nbsp;/i"''$text);     // replace &nbsp; with nothing 
  $text preg_replace("|<br\s*/?>|i""\n"$text); // replace breaks with nextlines 
  $text preg_replace("/ /"'     '$text);       // 5 spaces between works (or replace " " with "     "); 
  $text preg_replace("/(\w)\B/"'\1   '$text);  // 3 spaces between letters (or replace "**" with "*   *") 
  $text preg_replace("/  /"' &nbsp;'$text);    // replace "  " with " &nbsp;" 
  $text preg_replace("/\n/i""\n"$text);    // replace nextlines with breaks 
  return $text
}
?></div>


Capitalizing Hyphenated Names using str_replace and ucwords

To capitalize both halves of a hyphenated $last_name variable, you can use the following: 

 
<?php function ucwordsHyphen($last_name){
    return 
str_replace('- ','-',ucwords(str_replace('-','- ',$last_name)));
}
?>
<?php $last_name ucwordsHyphen(strtolower($record['last_name'])) ; ?>


Then where you want to display the last name:


<?php echo $last_name ?>




REGULAR EXPRESSION QUICK REFERENCE - Oct 15th, 2017

Here's a Regex quick reference to help you to customize your regular expressions to meet your needs

[abc]     A single character: a, b or c
[^abc]     Any single character but a, b, or c
[a-z]     Any single character in the range a-z
[a-zA-Z]     Any single character in the range a-z or A-Z
^     Start of line
$     End of line
\A     Start of string
\z     End of string
.     Any single character
\s     Any whitespace character
\S     Any non-whitespace character
\d     Any digit
\D     Any non-digit
\w     Any word character (letter, number, underscore)
\W     Any non-word character
\b     Any word boundary character
(...)     Capture everything enclosed
(a|b)     a or b
a?     Zero or one of a
a*     Zero or more of a
a+     One or more of a
a{3}     Exactly 3 of a
a{3,}     3 or more of a
a{3,6}     Between 3 and 6 of a
/[^0-9.]/ remove all non numerical characters and .
 

options: i case insensitive m make dot match newlines x ignore whitespace in regex o perform #{...} substitutions only
once


ONLY ECHO THE FIRST WORD OF A STRING - Mar 15th, 2012

I had a situation where I needed to strip all but the first word of a string and use that as part of a mailto:link but I
also wanted to use the entire string in the link text.

I found that I could echo a specific word from an array but I was starting with a multi-word text string.

So first I converted the string to an array with:



<?php $position mytableRecord['myfield'?>
<?php $myArray explode(" "$position);?>



Then I echoed the first element (which has an index of [0]) in the array with:



<?php echo strtolower($myArray[0]);?>



Then I placed all that in my mailto: link



<a class="special" href="mailto:<?php $myArray explode(" "$position);?><?php echo
strtolower($myArray[0]);?>@my_web_site.org">E-MAIL THE <?php echo strtoupper($position); ?></a>








SHOW RECORD BASED ON DRAG SORT ORDER - Aug 3rd, 2010

Sometimes you may want to show the information from a record based on drag sort order. User rjbathgate  did

Dave Edis says it’s really simple.

He suggests that if you add the code indicated, where n = the position of the record that you want to start with.
Don’t forget that the first position is position “0" Zero, so if you want to start with the 3rd position, you’d
enter 2 for the “n” value..



 list($your_tableRecords, $your_tableMetaData) = getRecords(array(
    'Table name'   => 'your_table',
// Insert This Code
'offset' => n,
<!-- End of Insert-->
  ));



SHOWING A LIST VIEWER AND A DETAIL VIEWER ON THE SAME WEB PAGE - Aug 3rd, 2010

My client wanted to create a newsletter detail page that displayed their current newsletter and also displayed a list of
links to past newsletters on the same page. The client did not want to have their visitors to have to navigate through a
list page. 

I knew that I could display the first record in a multi-record editor by leaving out any search modifiers at the end of
the detail page link. 



<a href=”newsletterdetail.php”>newsletter</a>



I also found that t I removed



'limit'       => '1', 



from the call for that table at the head of my detail page, I could show the first record (the current newsletter) and
at the same time insert a foreach loop on the page to display all of the past newsletters.

This may not be the most elegant approach, but it worked and may come in handy for some of you.

Here’s the code that I used:

In the head:



list($your_newsletter_tableRecords, $your_newsletter_tableMetaData) = getRecords(array(
    'Table name'   => 'your_newsletter_table',
      'where'       => whereRecordNumberInUrl(1),
  
  ));
  $your_newsletter_tableRecord = @$your_newsletter_tableRecords[0]; // get first record



In the body to display the current newsletter:



<?php echo $your_newsletter_tableRecord['your_field_1'?>
?php echo $your_newsletter_tableRecord['your_field_2'] ?>
?php echo $your_newsletter_tableRecord['your_field_3] ?>
etc...



And for the past newsletters menu:



<?php foreach ($your_newsletter_tableRecords as $record): ?>
               <a href="<?php echo $record['_link'?>"><?php echo $record['date'?></a>
<?php endforeach ?>



PULLING MATCHING RECORD INFORMATION FROM MORE THAN ONE MULTI-RECORD EDITOR - Aug 3rd, 2010

The same client in the above entry upped the ante by then wanting to be able to create as many news articles as desired
for any given issue of the newsletter. I had already created a multi-record editor, called “newsletters”,  for most
of the information that appeared in their newsletters (mastheads, segment titles, meeting information etc.) That meant
creating another multi-record editor for the “articles” and being able to have only the correct month’s articles
displayed on the detail page.

The first step was to create a simple Multi-Record editor called Newsletter Date with one text field for the Month and
Year of their newsletters. These records would be used to populate a pull down list which assigned articles to their
correct issue and to create the “past issues” list, and eliminated the chance of misspelling the entries.

The next step was to add a pull down list field to my existing multi-record editor “newsletters” that gets it’s
information from the Newsletter Date table, like this.

Field type: list
Display as: Pull-down
List Options: Get options from Database (advanced)
Section Table name : newsletter_date
Use this field for option values: month_and_year 
Use this field for option labels: month_and_year 

Then I created a multi record editor for the articles called “newsletter articles”, with the same pull-down list
field as above, plus any other fields required, like article headline, article contents, author.

After those editors were created, I created a few test records for an assortment of months, and  entered some dummy
information into them.  

Then it was time to set up a detail page that would display the correct sets of records. After rummaging around in the
forum, I found that inserting this where statement in the require once at the top of my page, 



'where' => "newsletter_date = '{$newsletter_templateRecord['newsletter_date']}'" 



I could restrict the article records displayed to only those that matched the “newsletter_date” field in the
original “newsletters” editor. 

So this is what the top of my page looked like.



<?php header('Content-type: text/html; charset=utf-8'); ?>
<?php
  
  require_once "/my_path/cmsAdmin/lib/viewer_functions.php";

 list(
$newsletterRecords$newsletterMetaData) = getRecords(array(
    
'tableName'   => 'newsletter',
      
'where'       => whereRecordNumberInUrl(1),
  
  ));
  
$newsletterRecord = @$newsletterRecords[0]; // get first record
  
 list($newsletter_articlesRecords$newsletter_articlesMetaData) = getRecords(array(
    
'Table name'   => 'newsletter_articles',
    
'where' => "newsletter_date = '{$newsletterRecord['newsletter_date']}'" 
    
  ));
  
$newsletter_articlesRecord = @$newsletter_articlesRecords[0]; // get first record
  
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">



The rest of the page was then pretty straight forward. 

Since I was always displaying the first record in the “newsletter” table, where I wanted the information from the
“newsletter” table to appear I used:



<?php echo $newsletterRecord['my_field_name'?>



Where I wanted the Articles to appear I used:



<?php foreach ($newsletter_articlesRecords as $record): ?>
<?php echo $record['article_headline'?><br />
<?php echo $record['article_contents'?><br />
<?php echo $record['author'?><br />
<?php endforeach; ?>



And the code for the list of past issues looked like this:



<?php foreach ($newsletterRecords as $record): ?>
 <a href="<?php echo $record['_link'?>"><?php echo $record['newsletter_date'?></a>
<?php endforeach ?>



I could have set up other multi record editors,  and as long as they followed the same pattern, they too would appear
correctly.  

Hope this approach comes in handy.


SHOWING IMAGES FROM ANOTHER TABLE ON YOUR PAGES - Aug 3rd, 2010

Aquaman had this interesting solution that he shared with us on the forum.

He had a series of banner images and he wanted to be able to specify which ones to display on a series of detail pages
without the need to upload the images each time a page was added.

Here’s how you can do the same:

First, create a section editor (called My Images” for this recipe) with two fields to act as your image “library”:

-Title
-Image (upload)

The upload field is set for a maximum of 1 upload and the extra info field labels are removed. I’d suggest setting
this as a required field. You can set the other values and thumbnail sizes to suite your needs.

In the section where you want to show the images, (Which I called “My Pages” for this recipe), you’d use the
following setup for a field (which I called Banner Image):

-Field Type: list
-Display As: pull-down
-Get options from database
-Section Table name: my_images
-Use this field for option values: num
-Use this field for option labels: title 

Before the <head> of the detail page where you want’s to show the images you’d used the following code:



<?php header('Content-type: text/html; charset=utf-8'); ?>
<?php
  
  require_once "/hsphere/local/home/gkornblu/thecmsbcookbook.com/cmsAdmin/lib/viewer_functions.php";

  list(
$my_pagesRecords$my_pagesMetaData) = getRecords(array(
    
'Table name'   => 'my_pages',
    
'where'       => whereRecordNumberInUrl(1),
    
'limit'       => '1',
  ));
  
$my_pagesRecord = @$my_pagesRecords[0]; // get first record

  // show error message if no matching record is found
  if (!$my_pagesRecord) {
    
header("HTTP/1.0 404 Not Found");
    print 
"Record not found!";
    exit;
  }

?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">



Then in the body of the page where you want’s to show the images you’d used the following code:



<?php 
  list($my_imagesRecords$my_imagesMetaData) = getRecords(array( 
    
'Table name'   => 'my_images'
    
'where'          => "num ='{$my_pagesRecord['banner_image']}'"
    
'limit'       => '1'
  )); 
  
$my_imagesRecord = @$my_imagesRecords[0]; // get first record 
?> 



And finally where the image is displayed (must be below the code above on your page)
 
<?php foreach ($my_imagesRecord['image'] as $upload): ?> 
 <img src="<?php echo $upload['thumbUrlPath'?>" width="<?php echo $upload['thumbWidth'?>" height="<?php echo
$upload['thumbHeight'?>" alt="" />
<?php endforeach ?>



If for some reason you wanted to display the images from the “banner library” on a list page, the code before the
<head> tag would change to:



<?php header('Content-type: text/html; charset=utf-8'); ?>
<?php
  
  require_once "/your_path_to/cmsAdmin/lib/viewer_functions.php";

  list(
$my_pagesRecords$my_pagesMetaData) = getRecords(array(
    
'Table name'   => 'my_pages',
  ));

?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">




And you’d need to include the code from the detail page in your foreach loop, and change the:



where'          => "num ='{$my_pagesRecord['banner_image']}'", 



to



'where'          => "num ='{$record['banner_image']}'",



Your code would then look like this:



<?php foreach ($my_pagesRecords as $record): ?>
     
         
 <?php 
  list($my_imagesRecords$my_imagesMetaData) = getRecords(array( 
    
'Table name'   => 'my_images'
    
'where'          => "num ='{$record['banner_image']}'"
    
'limit'       => '1'
  )); 
  
$my_imagesRecord = @$my_imagesRecords[0]; // get first record 
?> 
 
<?php foreach ($my_imagesRecord['image'] as $upload): ?> 
   <img src="<?php echo $upload['thumbUrlPath'?>" width="<?php echo $upload['thumbWidth'?>" height="<?php echo
$upload['thumbHeight'?>" alt="" />
<?php endforeach ?>   <hr/>
    <?php endforeach ?>




FORMATTING TEXT TO AUTOMATICALLY FLOW EVENLY INTO MULTIPLE COLUMNS - Aug 3rd, 2010

One of the things that I’ve wanted to be able to do was to be able to make long blocks of text automatically flow
evenly into multiple columns.

Here’s one approach, that uses a combination of CSS and Javascript to handle the discrepancies in the current
implementation of the “proposed” CSS3 multi column rules.

It’s based on the examples shown in these articles:

       http://www.alistapart.com/articles/css3multicolumn/

and

       http://www.cvwdesign.com/txp/article/360/

So that I could follow the examples in the articles, I created:

A .css style sheet called multi-column.css
A Javascript file called css3-multi-column.js
And a sample page called column-sample.php  
,
You can download the CSS, Javascript and sample files from

       http://www.thecmsbcookbook.com/downloads/css3-multi-column.zip

Every implementation will be slightly different so I’d recommend playing with the different possibilities listed in
the article and in the proposed spec to get your page looking like you want it to. 

Because of some conflict issues with the Javascript, you may get css conflicts if you use more than one set of column
classes in the same external css file, EVEN IF YOU NAME THE CLASSES DIFFERENTLY.

So either put the column formatting in the head your web page or create a totally different copy of the external .css
file for each pages that uses different parameters.

       http://www.w3.org/TR/css3-multicol

Note that in this example, the Javascript is called only for IE, and the css style sheet contains -moz and -webkit-
prefixes for Mozilla and Safari.

That said, Here’s the pretty simple code that I used:

In the head section of my 2 column viewer (column sample.php), I called the javascript and CSS file with:



<link href="multi-column.css" rel="stylesheet" type="text/css" /> 
<!--[if IE]>
<script type="text/javascript" src="css3-multi-column.js"></script> 
<![endif]-->



Then in the body, where I wanted to display the 2 column text, I enclosed it in a <div> tag. You can display multiple
fields in the same <div> and they’ll all flow into neat columns.



<div class="column" align="left">
<?php echo $yourRecord['your_field'?><br /><br /><hr width="200">
</div>

    

Since first writing this, I’ve been able to use the technique to create automatic flow in some pretty complex viewers,
so experiment and enjoy.


WHO STOLE MY ZEROS? - Aug 3rd, 2010

User Perchpole was concerned by how zeros were displayed on a viewer page. He said: “I'm using a record to perform a
calculation that generates a simple product shipping cost. The calculation is the product weight multiplied by a fixed
number. Something like this:



 <?php echo $record['weight'] * 5 ?>



Although this works fine in practice (and can be used in systems like Paypal, etc.) the figures don't look right.

For example, instead of £2.50 what you see is £2.5!

Instead of £7.00 all you get is £7.

Simple question: How do I rework the code to display the missing zeros?! 

Dave Edis from Interactive tools came to the rescue. He answered: 

“Try this: 



<?php echo number_format($record['weight'] * 52?>



The ", 2" tells it to _always_ show 2 decimals, even then they're 00. This will also add thousands separators. So one
million will display like this: 1,000,000.00

So simple when you know how...


ADDING UP THE TOTAL VALUE OF ALL 'COST' FIELDS - Oct 13th, 2011

User     s2smedia had a field called 'cost' and  wanted to add up all of the 'cost' fields and give a TOTAL

Jason Sauchuk from Interactive Tools offered the following for CMSB Version  2.08 and above:


<?php 
  $totalCost 0.00
  
$listings  mysql_select('listings'); 
   
  foreach (
$listings as $listing) { 
    
$totalCost += floatval($listing['cost']); 
  } 
   
?>


And this for previous CMSB versions:


<?php  
  $totalCost 0.00
   
  
$tableName "listings"
  
$listings  mysql_query_fetch_all_assoc("SELECT * FROM `{$TABLE_PREFIX}$tableName`"); 
    
  foreach (
$listings as $listing) {  
    
$totalCost += floatval($listing['cost']);  
  }  
    
?>
 


REMOVING DOLLAR SIGNS FROM NUMERICAL VALUES - Oct 26th, 2012

The easiest way to remove dollar signs from your data is not to allow them in the first place. But if you have to
include them, Greg Thomas, a developer at Interactive Tools offered this advice.

If you want to strip the dollar sign and comma from a number you could use these functions together:



$value = preg_replace('/[\$,]/', '', $value); 
$value = floatval($value);



The preg_replace function will strip out the dollar sign and any commas. The floatval function will convert the number
from a string to a floating value, ensuring it will behave like a number if you want to use it for addition,
multiplication, etc.

To convert the number back to a currency format I would use this:


 
echo '$ '.number_format($number, 2, '.', ',');



The number_format function will take a number and convert it into string format it. In this case it makes the number
have two decimal places and adds a comma for every 3 digits.


CALCULATOR USING PHP - Jan 5th, 2012

There are may approaches to creating calculators using JavaScript and other approaches. Here’s one that I put together
for a Gicleé printing company that uses fairly simple PHP programming.

Dimensional values in the pull down lists are taken from a multi-record list of standard print dimensions using the
getListOptions function where “dimensions” is the name of the table and “value2" is the name of the field holding
the numerical value.

Pricing values are taken from a multi-record list of information about each of the papers that the company utilizes.
Each record contains a field for manufacturer, media family, media weight and price.

The form displays pull down list of $label values made up of the values of the record’s fields: manufacturer, media
family and media weight. The value of the record’s price field is used for the option value.

The formula used is (standard length in inches x standard height in inches)/144) * price per square foot = $value.

A reset form is included at the end.



<form method="POST" action="<?php echo $_SERVER['PHP_SELF'?>" >
<select name = "price" >
<option value="">Choose A Paper Type From This List</option>
<?php foreach ($print_mediaRecords as $record): ?>
<?php $label = @$record['manufacturer:label'] . " ".   @$record['media_family'] . " ".  @$record['media_weight'] ." lbs"
?> 
<option value = "<?php echo(@$record['price']);?>" label="<?php echo($label?>">
<?php echo $label?></option>
<?php endforeach ?>
</select>
<br /> <br />
<span class="body-text-9">The dimensions listed are our standard printing dimensions. Your actual print dimensions can't
be larger than the ones you choose.</span>
<br /> <br />
<select name='input1'>
<option value="">Choose Your First Dimension (Length Or Width)</option>
<?php foreach (getListOptions('dimensions','value2') as $value => $label): ?> 
<option value = "<?php echo $value;?>" <?php selectedIf($value, @$_REQUEST['value2']);?>>
<?php echo $label?></option> 
<?php endforeach ?> 
</select> 
<br /> <br />
<select name='input2'>
<option value="">Choose Your Second Dimension</option>
<?php foreach (getListOptions('dimensions','value2') as $value => $label): ?> 
<option value = "<?php echo $value;?>" <?php selectedIf($value, @$_REQUEST['value2']);?>>
<?php echo $label?></option> 
<?php endforeach ?> 
</select> 
<br /> <br />
<input type="submit" name="submit" value="Click To Calculate The Cost Of Your Print" >
</form> 
<br />
<?PHP

//The calculation
$value = ((@$_POST['input1'] *  @$_POST['input2'])/144) * @$_POST['price'] ;
?>
<span class="body-text-bold-9">Cost For Each Print:</span> 
<span class="body-text-9">$
<?PHP
//Output result
echo number_format($value ,2?>
<br />
<span class=" body-text-bold-red-9">**</span> <span class="body-text-9">Print Prices are approximate, if adjustments of
more than 10% are required, we will notify you prior to proceeding.</span>
<br /> <br />
<FORM ACTION="">
<INPUT TYPE="submit" VALUE="Reset Form and Calculate Another Print Cost">
</FORM>


USING PHP VARIABLES IN JAVASCRIPT - Jan 22nd, 2012

I had a hard coded Javascript calculator (validate.js) that looks something like the code below, and I needed to be able
to pull the values for the calculation from a CMSB database called “Prices”. 

Here’s where I started (relevant code only):

[code]
// JavaScript Document

    if ($('#size1').is(':checked'))  total = total + 349;
    if ($('#size2').is(':checked'))  total = total + 249;
    if ($('#size3').is(':checked'))  total = total + 299;
    if ($('#size4').is(':checked'))  total = total + 399;    

    [/code]

Here’s what worked for me:

First, I created a single record editor called Pricing” with text fields for Size2, Size2, Size3 and Size 4. (I used
descriptive text for the field labels and the size1,size2,size3 and size4 for the field names.) 

After I entered in some sample prices, I changed my Javascript document validate.js to validate.js.php.
Then I put load viewer and load records code at the top of the Javascript file.
Then I added: <?php header("Content-type: text/css"); ?> to keep the code rendering happy.
Right below that I defined variables for each of the field names. 

IE:
<?php $size1$pricingRecord['size1'?>
<?php $size2$pricingRecord['size2'?>
<?php $size3$pricingRecord['size3'?>
<?php $size4$pricingRecord['size4'?>

Here’s how the relevant code in my final Javascript document  looked:

<?php
  

  // load viewer library
  $libraryPath 'cmsAdmin/lib/viewer_functions.php';
  
$dirsToCheck = array('/your_server_path/','','../','../../','../../../');
  foreach (
$dirsToCheck as $dir) { if (@include_once("$dir$libraryPath")) { break; }}
  if (!
function_exists('getRecords')) { die("Couldn't load viewer library, check filepath in sourcecode."); }

  
// load records
  list($pricingRecords$pricingMetaData) = getRecords(array(
    
'tableName'   => 'pricing',
    
'allowSearch' => '0',
    
'limit'       => '1',
  ));
  
$pricingRecord = @$pricingRecords[0]; // get first record
 
?>
<?php 
header("Content-type: text/css");
?>

<?php $size1$pricingRecord['size1'?>
<?php $size2$pricingRecord['size2'?>
<?php $size3$pricingRecord['size3'?>
<?php $size4$pricingRecord['size4'?>

// JavaScript Document

    if ($('#size1').is(':checked'))  total = total + <?= $size1 ?>;
    if ($('#size2').is(':checked'))  total = total + <?= $size2 ?>;
    if ($('#size3').is(':checked'))  total = total + <?= $size3 ?>;
    if ($('#size4').is(':checked'))  total = total + <?= $size4 ?>;


FORMATTING ROMAN NUMERALS AND TITLE CAPITALIZATIONS - Jul 9th, 2014

Formatting of Roman Numerals and title capitalization can be tricky, especially when you want most of the words in your
title to be capitalized.

This RemoveShouting function will capitalize all roman numerals between 1 and 10 in a string (which I called
$instruction) and also force all the words in the $lower_exception array to appear as lower case in that string. All
other words in the string will have their first letter capitalized. The code is based on an example that I found in the
PHP manual at:


http://www.php.net/manual/en/function.ucfirst.php


Just insert the function code in the body of your viewer before you need to call it, and outside of any foreach loops
(you can only declare a function once on a viewer).

Here’s the function code:


<?php function RemoveShouting($instruction)
{
 
$lower_exceptions = array(
        
"to" => "1""a" => "1""the" => "1""and" => "1""but" => "1""or" => "1""for" => "1""nor" => "1""of"
=> "1"
 );
                                     
 
$higher_exceptions = array(
        
"I" => "1""II" => "1""III" => "1""IV" => "1",
        
"V" => "1""VI" => "1""VII" => "1""VIII" => "1",
        
"IX" => "1""X" => "1"
 );

 
$words explode(" "$instruction);
 
$newwords = array();

 foreach (
$words as $word)
 {
       
$firstElement = ($word == reset($words));
       
$lastElement = ($word == end($words));
        
        if (!
$higher_exceptions[$word]) {$word strtolower($word);}
        if ((!
$lower_exceptions[$word]) || ($word == $firstElement) || ($word == $lastElement) ) {$word =
ucfirst($word);}
         
array_push($newwords$word);
 
 }
       
 return 
join(" "$newwords); 
}
?>


Then where you want to echo the re-formatted $instruction variable, insert:


 <?php echo RemoveShouting($instruction?>


You can add any words to the $lower_exceptions array to format your titles, but the rules for capitalization vary
between sources. Just pick one and be consistent. You can also add Roman Numerals to the $higher_exceptions array.

If you need to implement RemoveShouting  more than once, rename a copy of the function and insert your new variable.

WORKING WITH DATES



DISPLAYING A LIST OF MONTHS WITH STRTOTIME - CAVEAT - Nov 19th, 2012

When a user had issues with displaying months in a pull down menu in a form using:


<select name="month"> 
              <?php foreach(range(1,12) as $month): ?> 
              <option value="<?php echo $month;?> <?php selectedIf($month,@$_REQUEST['month']);?>"><?php echo
date("F",strtotime("0000-$month"));?></option> 
              <?php endforeach ?> 
            </select>

Greg Thomas, a programmer at Interactive Tools had this observation:

"In certain versions of PHP you can't leave any parts of a date string as 0 when using the strtotime function. Try
changing the code to look something like this:
 

<select name="month">  
  <?php foreach(range(1,12) as $month): ?>  
  <option value="<?php echo $month;?> <?php selectedIf($month,@$_REQUEST['month']);?>"><?php echo
date("F",strtotime("1985-$month-01 00:00:00"));?></option>  
  <?php endforeach ?>  
</select>"



DISPLAYING ONLY TODAY’S RECORD - Aug 3rd, 2010

User jtedescojam set up a number of records in a multi record editor and wanted to be able to be able to display a
record’s information only when the date in it’s date field matched the actual date. 

Dave Edis from Interactive Tools had a simple suggestion.

In the get records call for that editor at the top of the page add this code:



'where' => "YEAR(date) = YEAR(NOW()) AND DAYOFYEAR(date) = DAYOFYEAR(NOW())",



he noted that there’s a lot more information about date functions at:

       http://dev.mysql.com/doc/refman/5.1/en/date-and-time-functions.html

Thanks Dave.


COMPARING DATES - Apr 1st, 2018

COMPARING DATES
When combined with the “if” statement, this extremely useful concept can be used to show or hide fields based on a
particular date, automatically archive records after a specified time period and perform many other functions.

Here’s the basic idea of how to compare a date field to today’s date on a list page. This example uses a date field
called “opening_reception” in a multi record editor called “exhibitions”. The goal was to automatically hide the
opening reception date after the date had passed.

First you’ll need a “foreach” statement to display each record.



<?php foreach ($exhibitionsRecords as $record): ?>



Since dates are normally expressed as months, days and years are hard to compare mathematically, the dates are converted
to the number of seconds since the Unix Epoch (January 1 1970 00:00:00 GMT)

Note that dates are referenced to local time as set in the CMSB “General Settings” information and not server time. 

Dave suggested to first define a few variables and to use simple, descriptive names so that the process is easier to
follow. Then set some rules for the comparisons. You can use mathematical operators like  <, >, <= or >= between values
to compare them in different ways. 


 
<?php 
$eventUnixTime     strtotime$record['reception_date'] ); // seconds since 1970 
$eventDateString   date("l, F jS"$eventUnixTime);        // example format: Monday, June 1st 
$currentUnixTime   time(); 
$currentDateString date("l, F jS"$currentUnixTime); 


$isEventToday      = ($eventDateString == $currentDateString); // first comparison
$isEventOver       = !$isEventToday && ($eventUnixTime $currentUnixTime); // second comparison
$isFutureEvent     = !$isEventOver && !$isEventToday// third comparison
?>


Then it becomes a simple matter of comparing the variables



<?php if ($isFutureEvent): ?> 
Opening Reception: <?php echo $eventDateString ?> 
   
<?php elseif ($isEventToday): ?> 
The Opening reception is today. Don't miss it!! 
   
<?php else: ?> 
Sorry, you missed the Opening Reception. 
   
<?php endif; ?> 



and don’t forget the endforeach statement to close your loop.


 
<?php endforeach ?>



Or, Let’s say you want to group a set of meetings by future and past dates

You could use: 



 <?php foreach ($general_meetingsRecords as $record): ?>
<?php
$eventUnixTime     strtotime$record['date'] ); // seconds since 1970 
$eventDateString   date("l, F jS"$eventUnixTime);        // example format: Monday, June 1st 
$currentUnixTime   time(); 
$currentDateString date("l, F jS"$currentUnixTime);

$isEventToday      = ($eventDateString == $currentDateString); 
$isEventOver       = !$isEventToday && ($eventUnixTime $currentUnixTime); 
$isFutureEvent     = !$isEventOver && !$isEventToday
?> 

 <?php if ($isEventToday): ?> <p>                    

<div align="center"class="heading-text-bold">TONIGHT'S MEETING:</div>
</p>
<br /> <span class="body-text-bold"><?php echo date("D, M jS, Y g:i a"strtotime($record['date'])) ?>
</span> 
<br />                         
<div align="left" class="body-text"><?php echo $record['content'?></div>
<hr align="left" color="#A29DB2" width="100" /><br />
<?php endif; ?>  <?php endforeach ?>
</div>
<p>
                    
<div align="center"class="heading-text-bold">UPCOMING MEETINGS:</div>
 </p>
<?php foreach ($general_meetingsRecords as $record): ?>
                        
<?php
 $eventUnixTime     strtotime$record['date'] ); // seconds since 1970 
$eventDateString   date("l, F jS"$eventUnixTime);        // example format: Monday, June 1st 
$currentUnixTime   time(); 
$currentDateString date("l, F jS"$currentUnixTime);
                                            
$isEventToday      = ($eventDateString == $currentDateString); 
$isEventOver       = !$isEventToday && ($eventUnixTime $currentUnixTime); 
$isFutureEvent     = !$isEventOver && !$isEventToday
?>  

<?php if ($isFutureEvent): ?>
<br /> 
<span class="body-text-bold"><?php echo date("D, M jS, Y g:i a"strtotime($record['date'])) ?></span> <br />
                         
<div align="left" class="body-text"> <?php echo $record['content'?></div><hr align="left" color="#A29DB2" width="100"
/>
 <?php endif; ?>  <?php endforeach ?>
 <br />
<p><div align="center" class="heading-text-bold">PAST MEETINGS:</div></p>
                            
<?php foreach ($general_meetingsRecords as $record): ?>
                        
<?php
$eventUnixTime     strtotime$record['date'] ); // seconds since 1970 
$eventDateString   date("l, F jS"$eventUnixTime);        // example format: Monday, June 1st 
$currentUnixTime   time(); 
$currentDateString date("l, F jS"$currentUnixTime);
                                            
$isEventToday      = ($eventDateString == $currentDateString); 
$isEventOver       = !$isEventToday && ($eventUnixTime $currentUnixTime); 
$isFutureEvent     = !$isEventOver && !$isEventToday
?>  

<?php if ($isEventOver): ?>
<br /> 
<span class="body-text-bold"><?php echo date("D, M jS, Y g:i a"strtotime($record['date'])) ?></span> <br />

                         
 <div align="left" class="body-text"><?php echo $record['content'?></div><hr align="left" color="#A29DB2" width="100"
/>
<?php endif; ?>  <?php endforeach ?>
 </div>



Note that you have to redefine the variables for each "foreach" loop

If you want to compare other field values, just define more variables and compare those as well.

If you want to test for the values of the variable that you're using. Try something like this in your foreach loop:



<ul> 
    <li>Event date = <?php echo $eventDateString ?></li>
    <li>Current date = <?php echo $currentDateString ?></li> 
    <li>$isEventToday = <?php echo $isEventToday "true" "false" ?></li> 
    <li>$isEventOver = <?php echo $isEventOver "true" "false" ?></li> 
    <li>$isFutureEvent = <?php echo $isFutureEvent "true" "false" ?></li> 
    <li>$CurrentUnixTime extended = <?php echo date("l jS \of F Y h:i:s A"$currentUnixTime); ?></li> 

  </ul>



ADDING DAYS WEEKS OR MONTHS TO DATES - Dec 5th, 2012

When I was creating the on-line version of this cookbook, I wanted to be able to show NEW or REVISED recipes for a
period of 30 days.

Fortunately, using PHP  it's pretty easy to add (or subtract) days, weeks, or months, to a date. The trick is that you
have to convert your dates into UnixTime (seconds since 1970) before you can successfully add ore subtract the values.

Here's how I accomplished my goal.

First I created 2 checkboxes fields in my editor. One for New and one for REVISED.

Then I defined the date and time variables that I needed to compare. (Current UnixTime is shown for clarity of this
recipe) and placed the code within my foreach loop.



<?PHP
$newUnixTime  strtotime($record['createdDate']); // seconds since 1970 to createdDate 
$updateUnixTime  strtotime$record['updatedDate'] ); // seconds since 1970 to updateDate
$currentUnixTime strtotime(date('Y-m-d'));
$pastUnixTime strtotime(date('Y-m-d'strtotime("-30 day")));
$newdifference $newUnixTime $pastUnixTime ;
$updatedifference $updateUnixTime $pastUnixTime ;
?>



Once the variables were determined, it was a matter of comparing them and outputting the correct information based on
the result.



<!-- Check For New Or Revised Date -->
<?php if ($record['new'] == 1  && $newdifference  >= 0): ?><span class="Medium-Text-Bold-Red">NEW</span><?php endif ?>
<?php if ($record['revised'] == && $updatedifference  >= 0): ?><span class="Medium-Text-Bold-Red">REVISED</span><span
class="Medium-Text-Bold"> - </span><?php endif ?>
<!-- End Date Check -->



Some other examples of adding to and subtracting from dates.



$add_a_week = strtotime(date('Y-m-d', strtotime("+1 week")));
$subtract_2_months = strtotime(date('Y-m-d', strtotime("-2 month")));



LISTING RECORDS WITHIN A DATE RANGE - Aug 3rd, 2010

Here’s one that came in extremely handy. 

I had an organization that had events that spanned a period of time and a special reception somewhere within those
dates.

My client wanted to list all of the events on their list page, but wanted to make a special list at the top of the page
for those events with receptions that were going to occur within the upcoming week.

Thanks to Dave Edis of Interactive Tools (again) who freely admits that date math makes his head hurt, we came up with
the following. 

First I created a date field in my ‘date_compare_test’ multi record editor called ‘reception_date’.

Then came the task of creating a list viewer that would list only the events I wanted in the appropriate groups.

Dave came up with the idea of putting 2 viewers on my list viewer page and using 2 different ‘where’ statements in
the get records calls to separate the content that could show in each area.

Here's the 'where' statement that we used to show reception_ dates for the upcoming week, as you suggested.


    
 'where' => '(NOW() + INTERVAL 7 DAY) >= reception_date AND reception_date >= TIMESTAMP(CURDATE(), "00:00:00")',



And here's the 'where' statement that we used to show all the other events (and leave out those already listed)



 'where' => 'reception_date <= TIMESTAMP(NOW()) OR reception_date >= TIMESTAMP(NOW()+ INTERVAL 7 DAY)',



So here’s how the complete viewer code looked.



 <!– Start of first list viewer code–>

<?php header('Content-type: text/html; charset=utf-8'); ?>
<?php
  
  require_once "/hsphere/local/home/apbcweb/artistsofpalmbeachcounty.org/cmsAdmin/lib/viewer_functions.php";

  list(
$date_compare_testRecords$date_compare_testMetaData) = getRecords(array(
'Table name'   => 'date_compare_test',
'where' => '(NOW() + INTERVAL 7 DAY) >= reception_date AND reception_date >= TIMESTAMP(CURDATE(), "00:00:00")',
  ));
?>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
 <head>
  <title></title>
  <meta http-equiv="Content-Type" content="text/html;charset=UTF-8" />
 
<hr />

EVENTS WITH RECEPTIONS TODAY THROUGH 7 DAYS IN THE FUTURE<br /><br />
<?php foreach ($date_compare_testRecords as $record): ?> 
Title: <?php echo $record['title'?><br />
The Reception Date is <?php echo date("D, M jS, Y g:i a"strtotime($record['reception_date'])) ?> <br />
<?php endforeach ?> 
<hr />
  
 <!– Start of second list viewer code–>

<?php

  list($date_compare_testRecords$date_compare_testMetaData) = getRecords(array(
    
'Table name'   => 'date_compare_test',
  
'where' => 'reception_date < TIMESTAMP(CURDATE(), "00:00:00") OR reception_date > TIMESTAMP(NOW()+ INTERVAL 7 DAY)',
 ));
?>

 EVENTS WITH RECEPTIONS BEFORE TODAY OR MORE THAN 7 DAYS IN THE FUTURE<br /><br />
  <?php foreach ($date_compare_testRecords as $record): ?> 
      
      Title: <?php echo $record['title'?><br />
      The Reception Date is <?php echo date("D, M jS, Y g:i a"strtotime($record['reception_date'])) ?> <br />
<?php endforeach ?> 
  
</body>
</html>



Dave explained that for date queries, 

“The first step is to figure out if you need to do it in MySQL or PHP. If you are able to do it in MySQL it's often
simpler.

You can find a list of MySQL date/time functions here:

       http://dev.mysql.com/doc/refman/5.1/en/date-and-time-functions.html

And here's some examples:

NOW() - Gives you the current date and time: 2010-03-01 09:41:50
(NOW() + INTERVAL 7 DAY) - Get date/time 7 days from now: 2010-02-22 09:45:25
CURDATE() - Gives you the current date only: 2010-03-01
TIMESTAMP() - Format value as date/time, or if two values, add them together
TIMESTAMP(CURDATE()) - Get date/time at beginning of day: 2010-03-01 00:00:00
TIMESTAMP(CURDATE(), “23:59:59") - Get date/time at end of day 2010-03-01 23:59:59

And you can test all these with the MySQL Console Plugin by entering SELECT followed by the function. So: SELECT NOW()
in the Mysql Console returns: 2010-03-01 09:41:50

So the first step is to figure out the values you want to compare. My guess is you want:
The date 7 days from now: (NOW() + INTERVAL 7 DAY)
The reception date: reception_date
The date at the end of today: TIMESTAMP(CURDATE(), “23:59:59")

If you write it out in English first it's way easier:
- If the reception_date is 7 days or less from now
- AND the reception_date hasn't already passed

I like to arrange my code so if reads like a time range with the test date in the middle like this:



start_date >= test_date AND test_date >= end_date



So that would be:



(NOW() + INTERVAL 7 DAY) >= reception_date AND reception_date >= TIMESTAMP(CURDATE(), “00:00:00") 



Now I understand why Dave says date math makes his head hurt.


USING THE NEW “CUSTOM DATE OR STRTOTIME VALUE” DATE FUNCTION - Aug 3rd, 2010

Since we can now specify a custom date or strtotime value in date fields, (V2.01) it’s easy to specify a  publishDate
or  removeDate that is say 4 weeks from the current date. It’s just a bit counter intuitive. 

When you first choose “specify custom date (or strtotime value) below” from the default value pull-down menu the box
is filled with something like “2010-01-01 00:00:00" Which you can manually change to choose a custom date and time.

You can modify this date string to choose a custom date and time in the future. 

Or you can add a simple relative expression like “+4 weeks” after the modified date string (without the double
quotes) and the date will be set to your modified date + 4 weeks. 

To specify a value relative to the current date and time in the field (this is the counterintuitive part). Just delete
the 2010-01-01 00:00:00 and replace it with the “+4 weeks” expression (without the double quotes). 

You can use any number that you want to and change “weeks” to “years”, “days”, hours”, “minutes” or
even “seconds” to meet your needs. You should see the new date reflected in the “preview” just under the field. 

This only works on newly created records so of course it won't update existing records which must be revised manually.

Or Dave Edis from Interactive Tools suggested:

If you wanted to update the old records you could do that with this command in the MySQL Console (a free plugin):



UPDATE cms_your_table_name SET removeDate = NOW() + INTERVAL 4 week WHERE removeDate = "0000-00-00 00:00:00"


 
Your table name replaces the text "your_table_name" after the "cms_".

As always, when you’re making these kinds of changes to your on-line database, don’t forget to back up your original
data before you begin.


SHOW DATES ON YOUR PAGE - Jan 15th, 2015

Sometimes you’ll want to show the current year on your page, like when showing copyright information.
It’s easy to show the current year by adding 



<?php echo date("Y"?> 



to your page.

You can use any of the date modifiers to show date information as well, so:



<?php echo date("D, M jS, Y"?>



Would show the Day of the week, and the Month, Day and Year.

You can format a date string to display as Day Date, Month Year on a detail page by using:




 <?php echo date("D jS, M  Y "strtotime($your_tableRecord['your_date_ field'])) ?>



Or, on a list page:



 <?php echo date("D jS, M  Y "strtotime($record['your_date_field'])) ?>



If you thought, as I did, that you couldn’t format the displaying of the special fields createdDate and updatedDate,
you’d be wrong.

You can format these the same as any other date field.  

NOTE: The commas in the string determine where commas show in your display.

This is the complete list of Date formatting modifiers

A = Uppercase 12hr time syntax - Example: AM, PM
a = Lowercase 12hr time syntax - Example: am, pm
B = Swatch Internet time - Example: 000 through 999
c = ISO 8601 date (added in PHP 5) - Example: 2004-02-12T15:19:21+00:00
D = short textual representation of the Day - Example: Fri
d = Day of the month, 2 digits with leading zeros - Example: 03
e = Timezone identifier (added in PHP 5.1.0) - Example: UTC, GMT, Atlantic/Azores
F = Full Textual representation of the month - Example: January
G = Hours in 24 hour format without leading zeros - Example: 17
g = Hours in 12 hour format without leading zeros - Example: 7
H = Hours in 24 hour format with leading zeros - Example: 17
h = Hours in 12 hour format with leading zeros - Example: 07
I (capital i) = Whether or not the date is in daylight saving time - Example: 1 if Daylight Saving Time, 0 otherwise
i = Minutes with leading zeros - Example: 00 through 59
j = Day of the month without leading zeros - Example: 10, 25
L = Whether it's a leap year - Example: 1 if it is a leap year, 0 otherwise
l (lowercase 'L') =  full textual representation of the day of the week e.g. Monday, Tuesday
M = A short textual representation of a month, three letters - Example: Jan, Feb
m = Numeric representation of a month, with leading zeros - Example: 01
N = ISO-8601 numeric representation of the day of the week (added in PHP 5.1.0) - Example: 1 (for Monday) through 7 (for
Sunday)
n = Numeric representation of a month, without leading zeros - Example: 1 through 12
O = Difference to Greenwich time (GMT) in hours - Example: +0400
o = ISO-8601 year number. This has the same value as Y, except that if the ISO week number (W) belongs to the previous
or next year, that year is used instead. (added in PHP 5.1.0) e.g. 2010
P = Difference to Greenwich time (GMT) with colon between hours and minutes (added in PHP 5.1.3) - Example: +02:00
r = RFC 2822 formatted date - Example: Thu, 21 Dec 2000 16:01:07 +0200
S = English ordinal suffix for the day of month - Example: st, nd, rd, or th.
s = Seconds with leading zeros - Example: 28
T = Timezone abbreviation - Example: GMT, PST, EST, etc
t = Number of days in the given month - Example: 28, 31, etc.
U = Seconds since the UNIX epoch (January 1 1970 00:00:00 GMT) - Example: 1041604168
u = Microseconds (added in PHP 5.2.2) - Example: 654321
W = ISO-8601 week number of year, weeks starting on Monday (added in PHP 4.1.0) - Example: 42 (the 42nd week in the
year)
w = numeric representation of the day - Example: 0 (for Sunday) through 6 (for Saturday)
Y = 4 digit year - Example: 2003
y = 2 digit year - Example: 03
Z = Timezone offset in seconds. The offset for time zones west of UTC is always negative, and for those east of UTC is
always positive. - Example: 43200 through 50400 
z = Day of the year (starting from 0) - Example: 0 through 365
 
And here are some Date String examples and their output

date("F j, Y, g:i a"); - March 10, 2010, 5:16 pm
date("m.d.y"); - 03.10.10
date("j, n, Y") - 10, 3, 2010
date("D M j G:i:s T Y"); - Sat Mar 10 15:16:08 PST 2010
date("D M jS, Y"); - Sat Mar 10th, 2010
date("H:i:s"); - 17:16:17
date('\i\t \i\s \t\h\e jS \d\a\y.'); - It is the 10th day.

*** According to Dave Edis of Interactive Tools, in  V2.01+ if you have a blank date field, the date stored in your
database is actually 0000-00-00 00:00:00, which is the default format MySQL stores dates in. 

So, if you try to display the date, the technical reason you get 1969 or 1970 is because the way many servers record
time is as epoch time or "seconds since midnight 1970 GMT", so what strtotime() does is covert your date to epoch time,
but since your date is zeroed out you get 0 seconds since 1970, which the date() function interprets as 1970, and then
adjusts for your timezone (GMT -8 hours?)

If you try to test for a blank date with a simple:



<?php if ($your_tableRecord['your_date_field']): ?>



The test won’t work as you expect, since there is actually a date value stored in your database,  (0000-00-00
00:00:00)

Until this glitch is fixed, and you update to the “fixed” version, you’ll have to format your test like this:



<?php if ($your_tableRecord['your_date_field'] && $your_tableRecord['your_date_field'] != '0000-00-00 00:00:00'): ?> 



You can learn more about date formatting at:

      http://www.php.net/date


RELATIVE DATES - FACEBOOK STYLE - May 8th, 2011

Chris Waddell from Interactive Tools just wrote a function that turns a date into a more human-readable format such as
"37 seconds ago", "about an hour ago", "Wednesday at 6:07pm", or "February 17, 2007", depending on how far in the past
the date is. (It doesn't handle future-dates at all.)

This code can go anywhere before it's first used:



<?php 
function pretty_relative_time($time) { 
  if (
$time !== intval($time)) { $time strtotime($time); } 
  
$d time() - $time
  if (
$time strtotime(date('Y-m-d 00:00:00')) - 60*60*24*3) { 
    
$format 'F j'
    if (
date('Y') !== date('Y'$time)) { 
      
$format .= ", Y"
    } 
    return 
date($format$time); 
  } 
  if (
$d >= 60*60*24) { 
    
$day 'Yesterday'
    if (
date('l'time() - 60*60*24) !== date('l'$time)) { $day date('l'$time); } 
    return 
$day " at " date('g:ia'$time); 
  } 
  if (
$d >= 60*60*2) { return intval($d / (60*60)) . " hours ago"; } 
  if (
$d >= 60*60)   { return "about an hour ago"; } 
  if (
$d >= 60*2)    { return intval($d 60) . " minutes ago"; } 
  if (
$d >= 60)      { return "about a minute ago"; } 
  if (
$d >= 2)       { return intval($d) . " seconds ago"; } 
  return 
"a few seconds ago"

?> 
 



And here's how to call it in the body of your page: 



<?php echo pretty_relative_date($record['createdDate']); ?>






SHOWING THE DIFFERENCE BETWEEN 2 DATES IN DAYS, HOURS, AND MINUTES - May 15th, 2015

Here's an interesting idea that was posted by CMSB user mjftech.  


<?php
 $openDate strtotime($record['createdDate']);
 
$closeDate strtotime($record['closedDate']);
 
$dateDiff = ($closeDate $openDate);
 
  
$d = ($dateDiff/(60*60*24))%365;
  
$h = ($dateDiff/(60*60))%24;
  
$m = ($dateDiff/60)%60;
   
  echo 
$d." days\n";
  echo 
$h." hours\n";
  echo 
$m." minutes\n";
?>

VIEWERS - SEARCHING/SORTING/COMPARING



SETTING UP BASIC SEARCHES - Sep 4th, 2022

Here’s some basic code that you can use to set up a simple search box on the list page of a multi-record editor to
return only those records that match your search criteria. There's also a cancel search form. Style the forms to match
your needs.

***If  'allowSearch' =>  is in your load records call at the top of your viewer, make sure that its set to true *** 

 

<form action="" type="post">
        <input type="text" name="your_field_name_keyword" id="search" placeholder="Enter A Name To Search For - Partial
Names OK" value="<?php echo htmlencode(@$_REQUEST['your_field_name_keyword']); ?>" size="60" />
        <input type="submit" value="Start Your Search" />
    </form>

<form type="post" action="" >
                <input type="submit" value="Cancel Search Filters - Show All Records">
              </form>

Notice the “name” entry on the sample form above. It starts with the field in which you want to preform the search
(your_field_name) and is followed by a modifier that determines what type of search is to be performed (_your-criteria).

CMSB is set up to allow you to search by many criteria by changing ( _your-criteria) in the “name” entry to one of
these:

_match - an exact match    
_keyword - will look for specific words or partial words    
_prefix - starts with keyword (or letter)
_query - allows google-style query searches such as: +dog -cat "multi word phrase". Only records matching EVERY word or
quoted phrase are returned. Words or phrases that start with - mean "must not match". The +  means "must match" Its
optional and not required. 
_fieldname_empty     Matches blank fields.    Matches fields that are blank ("").
_min - A minimum value for numeric searches
_max - A maximum value for numeric searches
_year     year number for date searches
_month - month number for date searches
_day - Day of month for date searches

So, let’s say you’ve set up a field called “fruit” that can contain one or more keywords, like Apple, Banana,
Pear, Orange.

Here’s the basic code that you would use to search for one of those.



<form method="POST" action="<?php echo $_SERVER['PHP_SELF'?>">
  <input type="text" name="fruit_keyword" value="">
  <input type="submit" name="submit" value="Search">
</form>



If the visitor entered Apple, Banana, Pear, or Orange, only those records that contained the keyword would be listed.

You could also change the form to include a drop down menu of choices instead of the text box like this:



<form method="POST" action="<?php echo $_SERVER['PHP_SELF'?>">
 
<select name="fruit_keyword">
<option value="">Please Choose a Fruit</option>
<option value="Apple">Apple</option>
<option value="Banana">Banana</option>
<option value="Pear">Pear</option>
<option value="Orange">Orange</option>
</select>

 <input type="submit" name="submit" value="Search">
</form>



CANCELLING A SEARCH - Aug 27th, 2020

To cancel a search use this code:

<form method="post" action="" >
<INPUT TYPE="submit" VALUE="Cancel Search">
</form>


BASIC SEARCH FOR A KEYWORD IN MULTIPLE FIELDS IN A MULTI-RECORD EDITOR - Oct 2nd, 2022

At the top of your page:


<?php
// load records from 'your_table'
  list($your_tableRecords$your_tableMetaData) = getRecords(array(
    
'tableName'   => 'your_table',
    
'loadUploads' => true,
    
'allowSearch' => true,
 
'where'       => " field1 LIKE '%".mysql_escape($query)."%' OR field2 LIKE '%".mysql_escape($query)."%' OR field3 LIKE
'%".mysql_escape($query)."%'  " // Add as many fields as you need above, separating each by an OR
  ));

$query = @$_REQUEST['query']; // this will hold the keyword
?>

And in the Body

 <form action="search.php" method="post">
            <input type="text" name="query">
            <input type="submit" name="submit" value="Search">
</form>

You might also want to include a cancel search form:

<form method="post" action="" >
<INPUT TYPE="submit" VALUE="Cancel Search  - Start a New Search">
</form>


MORE COMPLEX SEARCH FOR A KEYWORD IN MULTIPLE FIELDS IN A MULTI-RECORD EDITOR - Oct 2nd, 2022

Searching Multiple fields at once 

I needed to be able to search 3 category fields (text fields) at once, all of which got their option values and option
names from a master catalog of categories. There were 3 fields because any of three books could be in more than one
category. They could be “Art” books that also fit the “animals” category. To complicate the issue, my client
wanted all of the Genre Categories to show in the pull down for admins but only a small subset of those would be shown
to the public (non-admins). 

Step 1 was to set up a master “Genre” catalog multi record editor. This editor was called “Genre Categories” and
it had only 2 fields. A text field called ‘Genre” and a checkbox called “Include In public search”. (If I wanted
to show a value to the public, then that box got checked). You should create 4 or 5 records for testing and check some
to be in the public list.

Step 2 was to set up the  multi record main books editor, called “Books”. This editor had many fields, but the 4
that are needed for the multi field search are list fields called, “ Genre’, Genre 2, Genre 3 and “Public
Genre”. In addition, I’d add a “title” field for testing.

All list fields get their list options from “MYSQL query (advanced)”

The query for “Genre”, “Genre 2", and “Genre 3" are:

SELECT num, genre_names
 FROM `<?php echo $TABLE_PREFIX ?>genre_categories`
ORDER BY genre_names ASC


And the query for “Public Genre” is:

SELECT num, genre_names
 FROM `<?php echo $TABLE_PREFIX ?>genre_categories`
WHERE include_in_public_search = 1
ORDER BY genre_names ASC 


Step 3 was to populate the books editor with some records and assign different values to Genre 1, Genre 2 and Genre 3
for testing.

Now for the list page viewer.

At the top of your page you’ll need to load your viewer library and load records from the books editor. Make sure that
your load records call has “allow search” set to true, like this



<?php header('Content-type: text/html; charset=utf-8'); ?>
<?php
  /* STEP 1: LOAD RECORDS - Copy this PHP code block near the TOP of your page */
  
  // load viewer library
  $libraryPath 'cmsAdmin/lib/viewer_functions.php';
  
$dirsToCheck = ['','../','../../','../../../','../../../../']; // add if needed: '/your/server/public_html/path/'
  foreach ($dirsToCheck as $dir) { if (@include_once("$dir$libraryPath")) { break; }}
  if (!
function_exists('getRecords')) { die("Couldn't load viewer library, check filepath in sourcecode."); }

  
// load records from 'books'
  list($booksRecords$booksMetaData) = getRecords(array(
    
'tableName'   => 'books',
    
'loadUploads' => true,
    
'allowSearch' => true,
  ));

?>


In the body of your page insert your search forms:


 <form method="post"  action= "search.php">
          <input type="hidden" name="save" value="1" />
<table align="left" width="90%" border="0" cellspacing="0" cellpadding="2">
            <tr>
              <td colspan="2">&nbsp;</td>
            </tr>
                         <tr>
              <td  align="right" class="text_font"><b>Book Search 1:</b></td>
              <td align="left" valign="bottom" colspan="2"><select name = "genre, genre_2, genre_3_match[]"  width="300"
class="text_font" style="width: 300px; max-height: 50px; "  multiple>
                  <?php foreach (getListOptions('books''genre') as $value => $label43): ?>
                  <option value = "<?php echo $value;?>" <?php selectedIf($value, @$_REQUEST['genre, genre_2,
genre_3_match[]']);?>> <?php echo $label43?></option>
                  <?php endforeach ?>
                </select></td>
            </tr>
            <tr>
              <td  align="right" class="text_font"><b>Book Search 2:</b></td>
              <td align="left" valign="bottom" colspan="2"><select name = "genre, genre_2, genre_3_match[]"  width="300"
class="text_font" style="width: 300px; max-height: 50px; "  multiple>
                  <?php foreach (getListOptions('books''public_genre') as $value => $label43): ?>
                  <option value = "<?php echo $value;?>" <?php selectedIf($value, @$_REQUEST['genre, genre_2,
genre_3_match[]']);?>> <?php echo $label43?></option>
                  <?php endforeach ?>
                </select></td>
            </tr>
            <tr>
              <td align="right" class="text_font"><b>&nbsp;</b></td>
              <td align="left" valign="bottom" colspan="2"><input type="submit" value="Submit Search Filters" ></td>
            </tr>
          </table>
        </form>
 <table align="left" width="90%" border="0" cellspacing="0" cellpadding="2">
          <tr> 
            <!--<td>&nbsp;</td>-->
            <td align="center" colspan="2"><form method="post" action="" >
                <INPUT TYPE="submit" VALUE="Cancel Search Filters - Start Another Search">
                
              </form></td>
          </tr>
</table>


Below that you’ll show the results of your search starting with a telltale that shows the number of records returned
that match your search criteria:


<!-- BEGIN RESULTS DISPLAY-->
 <table align="left" width="90%" border="0" cellspacing="0" cellpadding="2">
 <tr>
                <td colspan="2">
                <?php if(@$_REQUEST['save']):?>
                <?php $count 0 ?>
                  <?php foreach ($booksRecords as $record): ?>
                <?php $count++ ?>
                <?php endforeach?>
                
               There are <?php echo $count?> records that match your search criteria
             <?php endif ?></td>
          </tr>


Followed by:


<tr>
            <td span class="text_font" colspan="2"><hr  align="left" style="height:2px; width=90%"  />
<?php foreach ($booksRecords as $record): ?>
 
<b>Title:</b> <?php echo htmlencode($record['title']) ?>
         
              <?php if($record['genre']):?>
                <b>Genre 1:</b> <?php echo $record['genre:label'?>
              <?php endif ?>
      
              <?php if($record['genre_2']):?>
              <b>Genre 2:</b> <?php echo $record['genre_2:label'?>
              <?php endif ?>
      
                <?php if($record['genre_3']):?>
              <b>Genre 3:</b> <?php echo $record['genre_3:label'?>
              <?php endif ?>
<?php // Add any additional fields here to show them in the results ?>
<hr  align="left" style="height:2px;" width="90%"/>
<?php endforeach ?>


And that’s it. After you’ve tested your code, you can add <?php $CMS_USER getCurrentUserFromCMS();   ?> to see If
the current user is an admin, and surround the “Book Search 1" code with <?php if (@$CURRENT_USER['isAdmin']): ?> and
<?php endif ?>.

You can use that code to restrict the displaying of any fields you like to admins only, and style the page any way that
works for you.
 


SEARCH FORM THAT GETS LABEL VALUES FROM A LIST FIELD - Aug 17th, 2011

Instead of manually entering your search criteria, you can use this code to retrieve the label values from a list field
and automatically insert them into your search form. Possible values for your-criteria are listed below.


<form method="POST" action="<?php echo $_SERVER['PHP_SELF'?>">
<select name = "your_field_your-criteria" > 
<option value="">Please Choose a Category</option>
<?php foreach (getListOptions('your_table ''your_field') as $value => $label): ?> 
     
<option value = "<?php echo $value;?>" <?php selectedIf($value, @$_REQUEST['your_field']);?>>
    
<?php echo $label?></option> 
     
<?php endforeach ?> 
 
</select> 

<input type="submit" name="submit" value="Search" >
</form>
__


Search Criteria (your-criteria)

_match - an exact match    
_keyword - will look for specific words    
_prefix - starts with keyword (or letter)
_query - allows google-style query searches such as: +dog -cat "multi word phrase". Only records matching EVERY word or
quoted phrase are returned. Words or phrases that start with - mean "must not match". The + is optional and not
required. 
_fieldname_empty     Matches blank fields    Matches fields that are blank (""). Example: email_empty=1
_min - A minimum value for numeric searches
_max - A maximum value for numeric searches
_year     year number for date searches
_month - month number for date searches
_day - Day of month for date searches


CREATING A PRODUCT CATEGORY SEARCH FORM THAT POPULATES IT'S OPTIONS LIST FROM VALUES IN EXISTING RECORDS - Dec 27th, 2012

I’m a big fan of pulling list values from a multi-record master category table. That way, there are no misspellings to
throw off subsequent searches.

I had a fairly comprehensive list of categories that could be assigned to products, however, I wanted the viewer search
filter form to offer only those category choices that would render results and I didn’t want to have any duplicates in
the options list.

The multi-record master category section (mobility_aid_categories) has only one text field (category)

The multi-record section that contains the actual listings records (mobility_aids) has a required list field
(mobility_aid_category) that pulls it’s option labels from the category field and it’s option values from the record
number field of the mobility_aid_categories table. Each listing record must be assigned to a specific category. 

With a bit of help from Greg Thomas a programming guru at Interactive Tools, we came up with the following.

The load records call code (with a variable $categorygroup defined) in the viewer looks like this:

<?php
 // load records from 'mobility_aids'
  list($mobility_aidsRecords$mobility_aidsMetaData) = getRecords(array(
    
'tableName'   => 'mobility_aids',
    
'loadUploads' => true,
    
'allowSearch' => true,
  ));
  
   list(
$mobility_aid_categoryRecords$mobility_aid_categoryMetaData) = getRecords(array(
    
'tableName'   => 'mobility_aid_category',
  ));
    
  
$categorygroup array_filter(array_pluck($mobility_aid_categoryRecords'category'));

?>

The viewer search form code looks like this:



<form method="POST" action="<?php echo $_SERVER['PHP_SELF'?>">
<?php 
//Create an array to store the values that have been selected
$selectArray = array(); 
//foreach section item
foreach($mobility_aidsRecords as $record){ 
//If the record has something selected for mobility_aid_category 
if(@$record['mobility_aid_category:label']){
//If the selected value isn't already in the array
if(!in_array(@$record['mobility_aid_category:label'],$selectArray)){ 
//Add it to the $selectArray
$selectArray[$record['mobility_aid_category']] = $record['mobility_aid_category:label']; 
    }
  } 
}
?>
<div class="body-text">
<select name="mobility_aid_category" >
<option value= "">Choose The Category To Display And Click/Tap The Search Button</option>
<?php foreach ($selectArray as $value => $name): ?>
<option value="<?php echo $value?>"><?php echo $name?></option>
<?php endforeach; ?>
</select> 
</div>
<br /><input type="submit" name="submit" value="Search">
 &nbsp; &nbsp; &nbsp;&nbsp; &nbsp; &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp; &nbsp; &nbsp; &nbsp;
<input type="submit" value="Click/Tap To Cancel Search Filters" />
</form> 


and the viewer code for the listings themselves look like this:


<?PHP
function wordCount($textOrHtml) {
$text strip_tags($textOrHtml"<b></b><i></i>");
$words preg_split("/\s+/"$text);

return 
count($words);
}
?>
<table width="100%"  align="center"border="0" cellpadding="10">
        <tr>
                <td><?php foreach ($categorygroup as $group): ?>
                  <div class=" heading-text-12"><?php foreach ($mobility_aidsRecords as $record): ?><?php if
(
$record['mobility_aid_category:label'] == $group) : ?> <?php echo strtoupper($group); ?><?php break ?><?php endif;
?><?php endforeach ?>
                  </div>
                  <?php foreach ($mobility_aidsRecords as $record): ?>
                  <?php if ($record['mobility_aid_category:label'] == $group) : ?>
                <?php $name htmlspecialchars($record['name']); ?>
                 <table border="0">
  <tr>
    <td width="150" height="150" align="center" valign="middle"><?php foreach ($record['image'] as $index => $upload):
?>
    <a href="<?php echo $record['_link']; ?>"><img src="<?php echo $upload['thumbUrlPath'?>" width="<?php echo
$upload['thumbWidth'?>" height="<?php echo $upload['thumbHeight'?>" alt="" /></a>
    <?php endforeach ?>
    </td>
    <td>
                  <a  href="<?php echo $record['_link']; ?>"><span class="heading-text-12"><b><?php echo
strtoupper($name); ?></b></span></a><br /> <br />
                  <div align="left" class=" body-text-9"><?php echo maxWords($record['description'], 25); ?>
                    <?php $words 25 ?>
                    <?php if (wordCount($record['description']) > $words) : ?>
                    <span class=" body-text-9">...</span><a  href="<?php echo $record['_link']; ?>"><span class="
body-text-bold-italic-10"> READ MORE</span></a></td>
  </tr>
</table>


TAGGING CATEGORIES TO FIND SIMILAR CONTENT - Aug 3rd, 2010

In response to a post from Mickey that asks: Is it possible to add tags or categories to an article and then after the
article content have CMSB list those 'tags' in a format that allows a user to click on a tag and then retrieve similarly
tagged articles output to say 10 headline (summary contents) items per page?  

Jake at Interactive Tools came up with this idea:

To set this up, you'll want to create a textfield for the tags to be entered into - I called mine "Tags". You may also
want to specify some instructions for this field that each tag should be separated by a comma.
Now create a normal detail viewer page, or open up your existing one. The only thing we're going to do here is add this
code:



 <?PHP $tagList split(','$record['tags']); ?> 
<?PHP foreach ($tagList as $tagLink): ?> 
<a href="/path/to/your/listViewer.php?tags_keyword=<?PHP echo urlencode(trim($tagLink)); ?>">
<?PHP echo $tagLink ?></a> 
<?PHP endforeach ?>



That code will split up your tags each time it encounters a comma, and will also remove any spaces entered before or
after the comma, if present. It will also URL encode the text should some weird characters make it in there. Finally it
will generate a search link for each one of the entered tags.

The only thing you'll need to change in that code is this chunk of text:



/path/to/your/listViewer.php



This should point to the list viewer page for this section.

 If you'd like to limit the results to ten on this page, just enter "10" into the 'limit' option at the start of your
list viewer page:



 'limit'       => '10',



That should be it! 

Great idea, Jake...


PREVIOUS AND NEXT RECORD SEARCH ON DETAILS PAGE - Aug 3rd, 2010

Matrix created an image viewer that displays thumbnail images for a gallery of images as the list viewer page. Each
thumbnail is linked to the detail page where the larger image is displayed. The new idea was to add Next & Previous
links on each Detail Page for sequential navigation through the individual image pages if someone doesn't want to return
to the section list (thumbnail display) each time.

Interactive Tools’ Dave Edis came to the rescue once again. 
(This works in version 1.18 and above.)

He said: Add this code to the bottom of step 1 in your detail viewer (change the ‘Table name’ to the name of your
table):



list($prevRecord, $nextRecord) = getPrevAndNextRecords(array( 
  'Table name' => 'news', 
  'recordNum' => getNumberFromEndOfUrl(), 
));



Then add this where you want your prev/next links to go:



<?PHP if (@$prevRecord): ?> 
  <a href="<?PHP echo $prevRecord['_link'?>"> &lt;&lt; <?PHP echo $prevRecord['title'?></a> 
<?PHP endif; ?> 
 
<?PHP if (@$nextRecord): ?> 
  <a href="<?PHP echo $nextRecord['_link'?>"><?PHP echo $nextRecord['title'?> &gt;&gt;</a> 
<?PHP endif; ?>



Voila... 

After implementing this, Matrix commented: 

“It works like a charm for us. This client is very actively using 10 "albums" contained in a "gallery" all produced
through CMS Builder. How we did this was to simply present the thumbnails in a list page, each linked to a detail page
with the image and lots of content. With the addition of the next and previous links for each detail page, it's perfect.

So, the interface only requires uploading a new work for each new page, add details and content in a couple of
additional text box fields, and publish. It's especially helpful that the detail pages can be re-ordered by dragging.
Very nice features.”


USING THE MULTI-SELECT DROP DOWN LIST TO CREATE LINKS TO LISTINGS RELATED TO A MAIN CATEGORY - Dec 29th, 2018

Creating links from checked entries in a multi value list field

In a section called “salon_test”, I created a multi value list field (salons_presented_in), which gets it’s option
values from the record number (num) field in the table I want to access (salon_eblasts), and it’s option labels from
the title field in that table.

I’ve created a detail page for the “salon_test” section that gets its record number from the end of the URL.

On that detail page, I needed to create separate links to another detail page for each entry that’s checked in the
multi value list field (salons_presented_in).

Building on some related products posts in the forum, I came up with this:

At the top of the viewer

// load viewer library
  $libraryPath = 'cmsAdmin/lib/viewer_functions.php';
  $dirsToCheck = array('/path_to_your_server/','','../','../../','../../../');
  foreach ($dirsToCheck as $dir) { if (@include_once("$dir$libraryPath")) { break; }}
  if (!function_exists('getRecords')) { die("Couldn't load viewer library, check filepath in sourcecode."); }

// load record from 'salon_test'
 list($salon_testRecords, $salon_testMetaData) = getRecords(array(
   'tableName'   => 'salon_test',
   'where'       => whereRecordNumberInUrl(0),
   'loadUploads' => true,
   'allowSearch' => false,
  'limit'       => '1',
  ));
  $salon_testRecord = @$salon_testRecords[0]; // get first record
?>

And in the body where I wanted to show the links:

<?php 
$relatedSalons join(','explode("\t"trim($salon_testRecord['salons_presented_in'], "\t")));
$valueFieldName "num";

 if (!
$relatedSalons) { $relatedSalons 0; }

  
// load records from 'salon_e_blasts'
  list($salon_e_blastsRecords$salon_e_blastsMetaData) = getRecords(array(
    
'tableName'   => 'salon_e_blasts',
    
'loadUploads' => true,
    
'allowSearch' => false,
'where' => "`$valueFieldName` IN ($relatedSalons)",
));
      
?>
<?php foreach ($salon_e_blastsRecords as $record): ?>
<a href="your_second_detail_page.php?<?php echo $record['num']?>"><?php echo $record['presentation_title'?></a>
<?php endforeach; ?>

There’s also a long thread here, with lots of juicy information:

       http://www.interactivetools.com/forum/gforum.cgi?post=63505


SEARCHING FOR MULTIPLE WORDS IN MULTIPLE FIELDS - Mar 9th, 2012

In this example, a search would return all records where the field yourfield contains an exact mtch for a word or
phrase. If those words appeared in another order, or their was an extra space, or they were found in another field, they
would be ignored.

<form  action="results.php" method="post"> 
<table> 
 <tr> 
<td >enter search terms:</td> 
<td ><input type="text" name="yourfield_keyword"  /></td> 
</tr> 
<tr> 
 <td>&nbsp;</td> 
<td ><input type="submit" value="Search" /></td> 
</tr> 
</table> 
</form> 


If you want to search for all the words in a phrase in any order, change your keyword search to a query search.

<input type="text" name="yourfield_query"  />


And if you wanted to search in multiple fields, then you'd have to list all the fields you want it to search, separated
by commas, in the input field name.

<input type="text" name="yourfield,yourfield2,yourfield3_query"  />


SEARCHING FOR KEYWORDS SITEWIDE INCLUDING IMAGE INFO FIELDS (MULTISEARCH) - Jul 8th, 2020

Back in 2008 (ancient history...) Dave Edis announced that as of version 1.13 (more ancient history...) a multi search
function had been added to CMSB and he offered some example implementation.

I thought that it would be great if I could including upload info fields in the multisearch results.

With a lot of help from Daniel Loewe, a senior programmer at Interactive Tools, I was able to reach my goal.

Here's the code for both the MultiSearch page (called multisearch.php) and the Image Viewer page (called test.php) (no
styling):


<?php
$libraryPath 'cmsAdmin/lib/viewer_functions.php';
$dirsToCheck = array('/your_server_path/','','../','../../','../../../');
foreach (
$dirsToCheck as $dir) { if (@include_once("$dir$libraryPath")) { break; }}
if (!
function_exists('getRecords')) { die("Couldn't load viewer library, check filepath in sourcecode."); }

$searchOptions = array();
$searchOptions['keywords'] = @$FORM['q'];
$searchOptions['perPage'] = "100";
$searchOptions['debugSql'] = "0";

$searchTables = array();
$searchTables['family_stories'] = array(
'viewerUrl' => 'story_detail.php',
'titleField' => 'title',
'summaryField' => 'sub_title',
'searchFields' => array('title','sub_title','story'),
);

$searchTables['faq'] = array(
'viewerUrl' => 'faqdetail.php',
'titleField' => 'question',
'summaryField' => 'category',
'searchFields' => array('category','question','answer'),
);

$searchTables['uploads'] = array(
'viewerUrl' => 'test.php',
'titleField' => 'filePath',
'summaryField' => 'thumbFilePath',
'searchFields' => array('filePath''info1','info2''info3''info4''info5'),
);

list(
$searchRows$searchDetails) = searchMultipleTables($searchTables$searchOptions);
?>
<!-- /STEP1: Load Record List -->
<?php echo "<?xml version='1.0'?>\n"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8" />
<style type="text/css">
body {
font-family: arial;
font-size: 12px;
}
</style>
</head>
<body>
<h1>Search Viewer</h1>
<form method="GET" action="<?php echo $_SERVER['PHP_SELF']; ?>" >
<input type="text" name="q" value="<?php echo htmlspecialchars(@$FORM['q']); ?>" size="50">
<input type="submit" name="" value="Search">


<table border=0 cellpadding=2 cellspacing=0 width=100%>
<tr>
<td bgcolor="#EEEEEE"><font size=2 class="maintext">Search results for <b><?php echo htmlspecialchars(@$FORM['q']);
?></b></font> - <font size=2 class="maintext">This page shows results <b><?php echo
htmlspecialchars(@$searchDetails['pageResultsStart']); ?></b> to <b><?php echo
htmlspecialchars(@$searchDetails['pageResultsEnd']); ?></b> out of <b><?php echo
htmlspecialchars(@$searchDetails['totalRecords']); ?></b> found.</font></td>
</tr>
</table>

<!-- STEP3: Display Page Links (Paste anywhere below "Load Record List") -->
<?php if ($searchDetails['prevPage']): ?>
<a href="<?php echo $searchDetails['prevPageLink'?>">&lt;&lt; previous page</a>
<?php else: ?>
&lt;&lt; prev
<?php endif ?>
- page <?php echo $searchDetails['page'?> of <?php echo $searchDetails['totalPages'?> -
<?php if ($searchDetails['nextPage']): ?>
<a href="<?php echo $searchDetails['nextPageLink'?>">next page &gt;&gt;</a>
<?php else: ?>
next &gt;&gt;
<?php endif ?>
<!-- /STEP3: Display Page Links -->

<!-- show errors -->
<?php if ($searchDetails['invalidPageNum']): ?>
Results page '<?php echo $searchDetails['page']?>' not found, <a href="<?php echo $searchDetails['firstPageLink']
?>">start over &gt;&gt;</a>.
<?php elseif ($searchOptions['keywords'] && $searchDetails['noRecordsFound']): ?>
No records matched search query!

<?php elseif ($searchOptions['keywords'] == ""): ?>
Enter a keyword to search.

<?php endif ?>

<!-- STEP2: Display Record List -->
<hr />
<?php foreach ($searchRows as $record): ?>
<a href="<?php echo $record['_link'?>"><?php echo $record['_title'?></a>
<?php if ($record['_summary']): ?>
<?php echo $record['_summary'?>
<?php else: ?>
No description is available for this page.
<?php endif ?>
<a href="<?php echo $record['_link'?>" style="color: #008000"><?php echo $record['_link'?></a>
<hr/>
<?php endforeach ?>
<!-- /STEP2: Display Record List -->

</form>
</body>
</html>


And for the Image Viewer called test.php (no styling)

<?php
$libraryPath 'cmsAdmin/lib/viewer_functions.php';
$dirsToCheck = array('/your_server_path/','','../','../../','../../../');
foreach (
$dirsToCheck as $dir) { if (@include_once("$dir$libraryPath")) { break; }}
if (!
function_exists('getRecords')) { die("Couldn't load viewer library, check filepath in sourcecode."); }

list(
$uploadsRecords$uploadsMetaData) = getRecords(array(
'tableName' => 'uploads',
'where' => whereRecordNumberInUrl(0),
'loadUploads' => true,
'allowSearch' => false,
'limit' => '1',
));
$uploadsRecord = @$uploadsRecords[0]; // get first record

?>

<a href='/multisearch.php'><span class=" navigation_font" style="font-size:24px;">&lt; &lt; BACK TO SEARCH
PAGE</span></a>

<div > <img src="http://your_site.com/cmsAdmin/uploads/<?php echo $uploadsRecord['filePath'?>"/>  
<?PHP $uploadsRecord['info1'] = preg_replace("/[\"]/""''"$uploadsRecord['info1'] ); ?>
<?PHP $uploadsRecord['info2'] = preg_replace("/[\"]/""''"$uploadsRecord['info2'] ); ?>
<?PHP $uploadsRecord['info3'] = preg_replace("/[\"]/""''"$uploadsRecord['info3'] ); ?>
<?PHP $uploadsRecord['info4'] = preg_replace("/[\"]/""''"$uploadsRecord['info4'] ); ?>
<?PHP $uploadsRecord['info5'] = preg_replace("/[\"]/""''"$uploadsRecord['info5'] ); ?>
<div align='center' style='font-size:1.4em; vertical-align:top; text-align:left;' class='text_font' ><?php
echo($uploadsRecord['info1']) ?> <?php echo($uploadsRecord['info2']) ?> <?php echo($uploadsRecord['info3']) ?> <?php
echo($uploadsRecord['info4']) ?> <?php echo($uploadsRecord['info4']) ?></div>
The first CMS Builder reference book is now available on-line!


SEARCH RESULTS PAGE TELLTALES (THINK BREADCRUMBS) - Jan 3rd, 2014

I needed to create a telltale on a search results page to remind visitors of the search criteria they had entered in a
search form.

Everything in the telltale was pretty straight forward (I left the Status (a list field) search, and the all listings
search as an example) until I got to the listing age searches. 

I wanted to be able to filter the results to only those listings that were created less than either 1, 2, or 3 months
ago, and the telltale was returning long date/time indicators instead of just how many months old the listings shown
would be.

With a lot of help from Dave Edis, Senior Developer at Interactive Tools, Here’s the solution we came up with:

 <table width="40%" border="0" cellspacing="0" cellpadding="2">
    <form method="POST" class="arial_14" action="listings-s.php">
  <tr>
        <td class="arial_14"><font color="#FFFFFF"><b>Show Listings: </b></font></td>
        <td colspan="3"><select name = "status_match"width="300" class="arial_14" style="width: 300px" >
            <option value="">All Listings</option>
            <?php foreach (getListOptions('listing''status') as $value => $label4): ?>
            <option value = "<?php echo $value;?>" <?php selectedIf($value, @$_REQUEST['status']);?>> <?php echo
$label4?></option>
            <?php endforeach ?>
          </select></td>
      </tr>

<tr>
        <td class="arial_14"><font color="#FFFFFF"><b>Date Listed: </b></font></td>
        <td colspan="3">
            <select name="months_ago" width="300" class="arial_14" style="width: 300px">
  <option value="">All Dates</option>
  <option value="1">Listed less than 1 Month ago</option>
  <option value="2">Listed less than 2 Months ago</option>
  <option value="3">Listed less than 3 Months ago</option>
</select>
</td>
 </tr>  
<tr>
<td class="arial_14"><b>&nbsp;</b> </td>
<td align="left" colspan="3"><input type="submit" name="submit" value="Search" ></td>
</tr>
</form> 
</table>

And On The Results Page

<?php 

if (@$_REQUEST['months_ago']) { 
  
$monthsAgo $_REQUEST['months_ago'];
  
$_REQUEST['createdDate_min'] = mysql_datetime(strtotime("-$monthsAgo month"));
}
 
?>


<?php
  $searchCriteria '';
if (@
$_REQUEST['status_match']) { $searchCriteria .= "Status: {$_REQUEST['status_match']} - "; }


   if (@
$_REQUEST['months_ago']) { $searchCriteria .= "Listed Less Than: {$_REQUEST['months_ago']} months ago - "; }
  
$searchCriteria chop($searchCriteria', '); // remove trailing , or spaces
    $searchCriteria chop($searchCriteria'- '); // remove trailing - or spaces
  
?>

<div align="center"> <span class="arial_14"> There are <?php echo $listingMetaData['totalRecords']; ?> listings that
match your search for:
 <?php if ( @!$searchCriteria ):?>
 All Listings
 <?php else :?>
 
 <?php echo $searchCriteria ?>
 <?php endif ?>
</span>
              
 <a href="search.php"><span class="arial_14">
 &lt;&lt; <u>Return to the Advanced Search Page</u></span></a></div>


BREAKING E-MAIL LISTS INTO GROUPS OF "N" ADDRESSES EACH - Apr 4th, 2013

To limit abuse on their email server, some ISPs limit the number of recipients that can be included in a single email,
or the amount of emails that can be sent at one time.

I encountered this problem when using an email list generated from a multi-record section of publicity email contacts.

Here's the way I broke the email list generated from the email contact records into groups of acceptable numbers.

For this example, you'll need to create a multi-record editor called "publicity" with 2 text fields, "title" and "email"

I also added a max_emails text field in a single record editor called "Common Information" to determine the number of
emails allowed in each group.

If statements are used to set the criteria for different email lists. To refine which emails appear in each of your
lists, you can use as many criteria as you want to.  

In this example there's only one email list displayed on the page, but there are 2 counters used. "counta" to display
the total number of records containing email addresses in the list and  "countb" to limit the number of emails in each
group. Because I wanted the total number to appear above the email list it was associated with, a separate foreach loop,
with identical criteria was created to increment counta . You can name your counter variables anything you want, but
make sure that each counter variable is unique.

Then, if you're using divs to generate the groups, use the following code in the body of your list viewer

Where you want the Information header to appear:



NOTE: To help to conform to ISP reciepient limits, email addresses are separated into groups of no more than <?php echo
$common_informationRecord['max_emails'?> addresses each. System administrators can change this number in the "Common
Information" editor.


Where you want the list heading to appear:



<div align="left"> <?php $counta 0?>
<?php foreach ($publicityRecords as $record ): ?>
 <?php if ($record['email']): ?>
<?php $counta++; ?>
<?php endif ?>
<?php endforeach; ?>
<span class="your_class">There are currently <?php echo $counta ?> addresses in this list .</span>
</div>


And where you want the email list to appear:



<div align="left">
<?php $countb 0?>
<?php foreach ($publicityRecords as $record): ?>
<?php if ($record['email']): ?>
<?php $countb++; ?>
<?php echo $record['email'?>;&nbsp;
<?php endif ?>
<?PHP $maxemails$common_informationRecord['max_emails']; ?>
<?php if ($countb >= $maxemails): ?>
<?php $countb 0?>
</div>
<div align="left">
<br />
<hr >
<br />
<?php endif ?>
<?php endforeach ?>
</div>


If you're using tables, in place of the code:

   
<?php if ($countb >= $maxemails): ?>
<?php $countb 0?>
</div>
<div align="left">
<br />
 <hr >
<br />
<?php endif ?>
<?php endforeach ?>
</div>



substitute 



<?php if ($countb >= $maxemails): ?>
<?php $countb 0?>
</td></tr><tr><td>
<br />
<hr />
<br />
<?php endif ?>
<?php endforeach ?>
</td>
</tr>
</table>

CHAPTER 3 - WORKING WITH UPLOADS

IMAGES



CHANGING AN UPLOAD FIELD’S INFO1 TYPE FROM TEXT FIELD TO TEXT BOX - Oct 25th, 2021

When entering captions on photographs for project, I was continuously running out of room in the info text fields. As an
interim solution I used multiple info fields to hold any overflow text and displayed them all on the web page.

A better approach was to automatically convert info1 fields to a text box format, and Carl from Interactive Tools helped
me create these 2 plugins to accomplish that goal. He also wrote the code necessary to automatically combine the text
from any existing multiple info fields into the new text box so that I didn’t have to re-enter all the existing
information (use this one with caution).

The resulting set of plugins (you need to activate both) seems to convert both existing upload info1 fields and new
ones, but that shouldn’t change the way the fields are displayed on your web pages. The plugins also adds a character
and word count to all text box fields (which could be used independently).

After the text boxes are created and data is entered in them, deactivating the plugins didn't seem to erase any of the
info1 field information from the database, it just changed the info1 field type and how the data is displayed in the
back end. Re-activating them restored the new info1 text box format and the information.

There's an executable form accessible in the plugin listing that is used to combine existing info1, 2, 3, 4, and 5 text
entries into the new info1 text box.

Please do extensive testing to make sure that there are no adverse effects to your live site when using these plugins.

You can download the plugins from:

http://www.thecmsbcookbook.com/downloads/convertInfo1.zip


LIMITING THE AMOUNT OF COLUMNS IN A SINGLE ROW IMAGE DISPLAY - Aug 3rd, 2010

Here’s a suggestion from Interactive Tools’ Dave Edis:

The CSS way is to have each image in a div that has something like: style="float: left; width: 200px". This will make
them flow from left to right and wrap when they run out of space (you can put them inside a bigger fixed width
container).

Or with a table you can have one table cell per image and insert a closing and opening TR every X images. Here's a code
snippet that does that:



<?php $maxCols=2; if (@++$count $maxCols == 0): ?></tr><tr><?php endif; ?>



Which is just a way of saying "Every 2 images insert "</tr><tr>"". 



Here’s a single row example using the thumbnail2 image as a link to a detail page, separate Title and Subtitle fields,
a hidden border for IE  and a fixed height for the image cell so that everything lines up nicely.



<table width="100%" border="0" cellpadding="5">  
<tr>
<?php foreach ($your_sectionRecords as $record): ?><?php foreach ($record['image'] as $upload): ?>
<td align=”center” height="350" valign="bottom">
<a href="<?php echo $record['_link'?>"><img src="<?php echo $upload['thumbUrlPath2'?>" width="<?php echo
$upload['thumbWidth2'?>" height="<?php echo $upload['thumbHeight2'?>" alt="" style="border:hidden" /><br /><div
align="center" class="medium-title"><?php echo $record['title'?></div>
<div align="center" class="medium-text"><?php echo $record['sub_title'?></div></a>
</td>
<?php $maxCols=2; if (@++$count $maxCols == 0): ?></tr><tr><?php endif; ?>
<?php endforeach ?><?php endforeach ?>
</tr>
</table>



Similarly, if you’re not using images this code will output a </tr><tr> after every 2 <td>...your content...</td>
lines:



<table border="1">  
 <tr>  
  
<?PHP foreach ($yourrecords as $record): ?>  
<td>  
<?PHP echo $record['your_content'?><br />  
</td>  
 <?PHP $maxCols=2; if (@++$count $maxCols == 0): ?></tr><tr><?PHP endif; ?>  
 <?PHP endforeach; ?>  
</tr>  
</table>



Use your own variable names instead of $yourrecord, but the inserted code can stay the same.


LIMITING THE AMOUNT OF COLUMNS IN A MULTI ROW IMAGE DISPLAY - Feb 9th, 2013

The table example above is fine for a single row of images or information that you want to wrap every x columns. If you
want to set up sets of rows that you want to wrap, you’ll have to wrap your table inside a table. Here’s a 2 row
example using the thumbnail2 image as a link to a detail page, separate Title and Subtitle fields, a hidden border for
IE  and a fixed height for the image cell so that everything lines up nicely.



<table width="100%" border="0" cellpadding="5">
<tr>
<td align="center" >
<tr>
<?php foreach ($your_sectionRecords as $record): ?><?php foreach ($record['image'] as $upload): ?>
 <td align="center" width="50%">
<table>
<tr>
<td height="350" valign="bottom"><a href="<?php echo $record['_link'?>"><img src="<?php echo $upload['thumbUrlPath2']
?>" width="<?php echo $upload['thumbWidth2'?>" height="<?php echo $upload['thumbHeight2'?>" alt=""
style="border:hidden" /></a>
</td>
</tr>
<tr>
<td ><div align="center" class="medium-title"><?php echo $record['title'?></div>
<div align="center" class="medium-text"><?php echo $record['sub_title'?></div></td></tr></table>
</td>
<?php $maxCols=2; if (@++$count $maxCols == 0): ?></tr><tr><?php endif; ?>
<?php endforeach ?> <?php endforeach ?>
</tr>
</table>



Again, Use your own variable names instead of $your_sectionRecord, but the inserted code can stay the same.

*** If you’re going to use that little code snippet multiple times on the same page, just use a different variable
instead of $count or it will remember the count from the last section and increment that.

Instead of this:



<?php $maxCols=5; if (@++$count $maxCols == 0): ?></tr><tr><?php endif; ?>



Use these:



<?php $maxCols=5; if (@++$count1 $maxCols == 0): ?></tr><tr><?php endif; ?>

<?php $maxCols=5; if (@++$count2 $maxCols == 0): ?></tr><tr><?php endif; ?>

<?php $maxCols=5; if (@++$count3 $maxCols == 0): ?></tr><tr><?php endif; ?>



LIMITING THE AMOUNT OF COLUMNS IN A MULTI COLUMN IMAGE DISPLAY - Aug 3rd, 2010

If you want to set up sets of columns that you want to wrap, you’ll have to wrap your table of columns inside a table
also.

Here’s a similar example to the above that lists sets of contact information next to images.



<table width="10%" border="0" align="left" cellpadding="0">
<tr>
<td align="left" >
<tr>
<?php foreach ($your_sectionRecords as $record): ?><?php foreach ($record['image'] as $upload): ?>
<td align="center" width="50%">


<table border="0" align="left" cellpadding="5">
<tr>
<td align="left" valign="top">

<table width="100%" border="0" cellpadding="5">
<tr>
<td align="left" valign="top"><a href="<?php echo $record['_link'?>"><img src="<?php echo $upload['thumbUrlPath'?>"
width="<?php echo $upload['thumbWidth'?>" height="<?php echo $upload['thumbHeight'?>" alt="" style="border:hidden"
/></a></td>
<td align="left" valign="top"><div class="yourclass"><?php echo $record['title'?></div>
<div class="yourotherclass"><?php echo $record['name'?><br />
<?php echo $record['tel_no'?><br /><?php echo $record['fax_no'?><br /><?php echo $record['email'?></div></td>
</tr>
</table>

</td>
</tr>
</table>

</td>
<?php $maxCols=4; if (@++$count $maxCols == 0): ?></tr><tr><?php endif; ?>
<?php endforeach ?> <?php endforeach ?>
</tr>
</table> 




LETTING YOUR CLIENT DECIDE HOW MANY COLUMNS TO DISPLAY - Aug 3rd, 2010

If you want to allow your client to enter the number of columns that they’d like to display, you can do it like this.

In a single record editor (I have one for all of the common information on my site) add a field called
thumbnail_column_limit

Then create a variable to replace the maxcol number:



<?PHP $col = ($common_informationRecord['thumbnail_column_limit']); ?>



And replace the number in your maxcol code with that variable, like this:



<?php $maxCols=$col; if (@++$count $maxCols == 0): ?> </tr> <tr>
<?php endif; ?>



SHOWING ONLY THE FIRST IMAGE OR GROUP OF IMAGES ON A PAGE - Oct 13th, 2011

User Perchpole asked: On a List page, how do I limit the number of (image) uploads displayed with each record to one? 

Dave answered: 

If you want to show just one image you can use <?PHP break ?> like this to stop after just one:



<?PHP foreach ($record['gallery_images'] as $upload): ?>  
  <img src="<?PHP echo $upload['thumbUrlPath'?>" width="<?PHP echo $upload['thumbWidth'?>" height="<?PHP echo
$upload['thumbHeight'?>" alt="" /> 
  <?PHP break ?> 
<?PHP endforeach ?>



Jason Sauchuk from Interactive Tools suggested that another option would be to limit your query to only one result and
then put the first record in it's own variable.

For example:
    


// load records  
  list($gallery_imagesRecords, $gallery_imagesMetaData) = getRecords(array(  
    'tableName'   => 'gallery_images', 
    'limit' => 1, 
)); 
 
$gallery_imagesRecord = $gallery_imagesRecords[0]; // get first record.


You can then use the variable $gallery_imageRecord to show various fields in the first record without needing a foreach
loop.




A variation on this is the ability to show a specified number of images from a series of records before a <?PHP break ?>


Jason Sauchuk offered the following approach:



<?php $count 0?> 

<?php foreach ($record['gallery_images'] as $upload): ?> 
  
<img src="<?PHP echo $upload['thumbUrlPath'?>" width="<?PHP echo $upload['thumbWidth'?>" height="<?PHP echo
$upload['thumbHeight'?>" alt="" />

 <?php if (++$count 3) { break; } ?>

<?php endforeach ?>


To randomize the images shown:



<?php $count 0?> 

<?php shuffle($record['gallery_images']); ?>

<?php foreach ($record['gallery_images'] as $upload): ?> 
  
<img src="<?PHP echo $upload['thumbUrlPath'?>" width="<?PHP echo $upload['thumbWidth'?>" height="<?PHP echo
$upload['thumbHeight'?>" alt="" />

 <?php if (++$count 3) { break; } ?>

<?php endforeach ?>





LIMITING THE NUMBER OF IMAGES ON A PAGE AND SHOWING CODE IF EXCEEDED - Aug 3rd, 2010

I wanted to show only a certain number of thumbnails on a page (20) and display a link to a second page if that number
was exceeded. The limit part was easy. In the get records code at the top of the page I entered 



 'limit'       => '20', 



To display the code if the number was exceeded, (in this case a link to a second viewer) I used the following: ( I was
already using a counter on this page so I called this counter count2)



<?php $count2 0?> 
<?php foreach ($portfolio_imagesRecords as $record): ?>     
 <?php $count2++; ?>  
<?php endforeach; ?> 

<?php if ($count2 >19): ?><a href=”http://www.your_site.com/your_viewer2.php”>Click for more images.><?php endif ?>



You could also use this approach to display different messages:


<?php $count2 0?> 
<?php foreach ($portfolio_imagesRecords as $record): ?>     
 <?php $count2++; ?>  
<?php endforeach; ?> 
 
<?php echo $count2>19 "There are over the maximum number of thumbnails" "You cannot add more thumbnails" ?>



COUNTING IMAGES IN A MULTI-IMAGE UPLOAD FIELD - Jan 18th, 2012

I needed to count the number of images that existed in a multi-image upload field that was the only record in my editor,
so that if there was only one image I could display it as an image and if there was more than one image I could display
them as a slide show.

I started out with another recipe in the Cookbook called: LIMITING THE NUMBER OF IMAGES ON A PAGE AND SHOWING CODE IF
EXCEEDED (just above this one)

The basic approach in that recipe is:


<?php foreach ($home_page_slidesRecords as $record): ?>  
<?php $count 0?>  
<?php foreach ($record['image'] as $upload): ?>  
 <?php $count++; ?> 
<?php endforeach; ?> 
<?php echo $count>"There is more than one thumbnail" "There is only one thumbnail" ?>  
<?php endforeach; ?>


I modified the code so that instead of messages, the required code was inserted into the page. I also added a checkbox
field to allow the client to decide if they wanted to use this as a slide show regardless of how many images were
uploaded. I also added <?php shuffle($record['images']) ?> to the code to randomize the image displayed when there was
no slide show.


<?php foreach ($home_page_slidesRecords as $record): ?>  
<?php $count 0?>  
<?php foreach ($record['image'] as $upload): ?>  
 <?php $count++; ?> 
<?php endforeach; ?> 
<?php if ($count && $home_page_slidesRecord['slideshow'] == 1): ?>
<div  id="show" class="slideshow"></div>
<?php else: ?>
<?php shuffle($record['images']) ?><?php foreach ($record['image'] as $upload): ?><img src="<?PHP echo
$upload['thumbUrlPath'?>" width="<?PHP echo $upload['thumbWidth'?>" height="<?PHP echo $upload['thumbHeight']
?>" alt="" />
<?PHP endforeach ?>
<?php endif ?>
<?php endforeach; ?>


DISPLAY IMAGES RANDOMLY - Aug 3rd, 2010

If you want to randomize how your images are displayed each time your page is reloaded you can shuffle your array first:



<?PHP shuffle($record['gallery_images']) ?> 
<?PHP foreach ($record['gallery_images'] as $upload): ?>  
  <img src="<?PHP echo $upload['thumbUrlPath'?>" width="<?PHP echo $upload['thumbWidth'?>" height="<?PHP echo
$upload['thumbHeight'?>" alt="" /> 
 <!-- Optional Code -->
 <?PHP break ?>
 <!-- End Optional Code --> 
<?PHP endforeach ?>



The   <?PHP break ?> stops the foreach loop so that only a single image is displayed.

Cool huh!!


DISPLAY A BACKGROUND IMAGE USING ONLY THE URL OF THE IMAGE OR THUMBNAIL - Aug 3rd, 2010

If you’re using an image as a background image for a page, you can use the code below in the CSS to specify the
location of the image:



<?PHP foreach ($record['gallery_images'] as $upload): ?>  
<?PHP echo $upload['urlPath'?>
<?PHP endforeach ?>



Or



<?PHP foreach ($record['gallery_images'] as $upload): ?>  
<?PHP echo $upload['thumbUrlPath'?>"
<?PHP endforeach ?>



If you want to specify a particular thumbnail’s location, just put a 2, 3 or 4 after thumbUrlPath


DISPLAYING THUMBNAIL 2, THUMBNAIL3, THUMBNAIL4 - Aug 3rd, 2010

If you’ve created these thumbnails, you can use them by adding a 2, 3, etc. at the end of the “thumbUrlPath”,
“thumbWidth" and “thumbHeight" statements.

Dave added:
To test for the existence of the alternate thumbnails, just add the number (2, 3, or 4) on the end and test for
thumbUrlPathX instead of “hasThumbnail”. If there's no thumbnail, thumbUrlPathX will be blank and test as false. 



<?php if ($upload['thumbUrlPath2']): ?> 
  <a href="<?php echo $upload['urlPath2'?>" rel= "lightbox[model]"> 
  <img border="0" src="<?php echo $upload['thumbUrlPath2'?>"  
       width="<?php echo $upload['thumbWidth2'?>" 
       height="<?php echo $upload['thumbHeight2'?>" alt="" /> 
<?php endif ?>



DISPLAYING A GENERIC IMAGE OR SPECIAL TEXT IF NO IMAGE IS UPLOADED - Aug 3rd, 2010

A number of users have asked how to display a generic image (or specific text) if no image has been uploaded to a
record. Well there are a number of ways. One offered by Dave Edis from Interactive Tools is:



<?php foreach ($your_tableRecords as $record): ?> 
       
      <?php if (sizeof($record['your_image_field']) >= 1): ?>  
      
      <?php foreach ($record[‘your_image_field'] as $upload): ?>   
        <?php if ($upload['hasThumbnail']): ?>   
        <a href="<?php echo $record['_link'] ?>"><img src="<?php echo $upload['thumbUrlPath'] ?>" alt="" width="<?php
echo $upload['thumbWidth'] ?>" height="<?php echo $upload['thumbHeight'] ?>" align="center" /></a>  
        <?php elseif ($upload['isImage']): ?>   
        <img src="<?php echo $upload['urlPath'] ?>" alt="" width="<?php echo $upload['width'] ?>" height="<?php echo
$upload['height'] ?>" align="right" />  
        <?php endif ?>   
      <?php endforeach ?>  
        
    <?php else: ?>  
    <img src="http://www.your_site.com/path_to_your_generic_image/generic_image.jpg" width="your_image_width_in_pixels"
height="your_image_height_in_pixels" alt="your_alt_text" />  
    <?php endif ?>  
         <?php endforeach; ?>



ROLLOVER IMAGES AND CMSB - Aug 3rd, 2010

Creating Rollover images is pretty simple, here's how Dreamweaver does it. 

When a rollover scenario is created, this script gets inserted in the head 



<script type="text/javascript">
<!--
function MM_swapImgRestore() { //v3.0
  var i,x,a=document.MM_sr; for(i=0;a&&i<a.length&&(x=a[i])&&x.oSrc;i++) x.src=x.oSrc;
}
function MM_preloadImages() { //v3.0
  var d=document; if(d.images){ if(!d.MM_p) d.MM_p=new Array();
    var i,j=d.MM_p.length,a=MM_preloadImages.arguments; for(i=0; i<a.length; i++)
    if (a[i].indexOf("#")!=0){ d.MM_p[j]=new Image; d.MM_p[j++].src=a[i];}}
}

function MM_findObj(n, d) { //v4.01
  var p,i,x;  if(!d) d=document; if((p=n.indexOf("?"))>0&&parent.frames.length) {
    d=parent.frames[n.substring(p+1)].document; n=n.substring(0,p);}
  if(!(x=d[n])&&d.all) x=d.all[n]; for (i=0;!x&&i<d.forms.length;i++) x=d.forms[i][n];
  for(i=0;!x&&d.layers&&i<d.layers.length;i++) x=MM_findObj(n,d.layers[i].document);
  if(!x && d.getElementById) x=d.getElementById(n); return x;
}

function MM_swapImage() { //v3.0
  var i,j=0,x,a=MM_swapImage.arguments; document.MM_sr=new Array; for(i=0;i<(a.length-2);i+=3)
   if ((x=MM_findObj(a[i]))!=null){document.MM_sr[j++]=x; if(!x.oSrc) x.oSrc=x.src; x.src=a[i+2];}
}
//-->
</script>

To preload the rollover image(s)  this code gets inserted in the opening <body> tag code :



<body onload="MM_preloadImages('path_to/your_normal_image.jpg','path_to/your_rollover_image.jpg')">



And where the rollover image is to appear, this code is inserted:



<a href="http://www.your_site.com" onmouseout="MM_swapImgRestore()"
onmouseover="MM_swapImage('image_name','',’path_to/your_rollover_image.jpg',1)"><img
src=”path_to/your_normal_image.jpg" alt="your_alt_text" name="image_name" width="296" height="90" border="0"
id="Image1" /></a>



To integrate this into CMSB:

Add the javascript above to the head section of your page, then to preload your images:

For a multi record editor on a list page, you’d insert this into the opening <body> tag



<body onload="MM_preloadImages('<?php foreach ($your_tableRecords as $record): ?><?php foreach
(
$record['your_roll_over_image_field'] as $upload): ?>’<?php echo $upload['thumbUrlPath'?>’, <?php endforeach;
?><?php endforeach; ?>’blank.jpg’)">



(The call to blank.jpg adjusts the syntax of the commas between entries.)

For a single record editor or a detail page the code would be: 



<body onload="MM_preloadImages(<?php foreach ($your_tableRecord['your_rollover_image_field'] as $upload): ?>’<?php
echo $upload['thumbUrlPath'?>’, <?php endforeach; ?>’blank.jpg’)">



(Note: If you’re adding a series of roll over images to some you already have, just add their calls to the end of the
existing preload list)

Then in the Body, you’d insert the following where you wanted the rollovers to be displayed.

For a multi record editor on a list page, you’d insert:



<?php foreach ($your_tableRecords as $record): ?>
          
<a href="http://www.your_site.com" 

onmouseout="MM_swapImgRestore()" 

onmouseover="MM_swapImage('<?php echo $record['num'?>','','<?php foreach ($record['your_roll_over_image_field'] as
$upload): ?><?php echo $upload['thumbUrlPath'?> <?php endforeach; ?>',1)">

<img src="<?php foreach ($record['your_normal_image_field'] as $upload): ?><?php echo $upload['thumbUrlPath'?>"
name="portfolio"  width="<?php echo $upload['thumbWidth'?>" height="<?php echo $upload['thumbHeight'?>" border="0" 
<?php endforeach; ?>id="<?php echo $record['num'?>" /></a> 

<?php endforeach; ?>



The names and ids of your images must be unique. I’ve used the record number to accomplish this, but you can use any
unique field value that is associated with the record.        

For a single record editor or detail page with multiple single image fields you’d use:



<?php foreach ($your_tableRecord['your_normal_image_field'] as $upload): ?>

<a href="http://www.your_site.com" 
onmouseout="MM_swapImgRestore()" 

onmouseover="MM_swapImage(<?php echo $upload['info1'?>','',’<?php echo $upload['thumbUrlPath']',1)">

<img src=”<?php echo $upload['thumbUrlPath'] ?>" alt="your_alt_text" name="<?php echo $upload['info1'] ?>"
width="<?php echo $upload['thumbWidth'] ?>" height="<?php echo $upload['thumbHeight'] ?>border="0" id="<?php echo
$upload['info1'] ?>"" /></a>

<?php endforeach; ?> 



The names and ids of your images must be unique. I’ve used info2 to accomplish this, but you can use any info field
value that is associated with the image. 

<a href="http://www.your_site.com

onmouseout="MM_swapImgRestore()"

onmouseover="MM_swapImage('<?php foreach ($your_tableRecord['your_normal_image_field'] as $upload): ?><?php echo
$upload['info2'?><?php endforeach ?>','','<?php foreach ($your_tableRecord['your_roll_over_image_field'] as $upload):
?>

<?php echo $upload['thumbUrlPath'?><?php endforeach ?>',1)">

<img src=”<?php foreach ($your_tableRecord['your_normal_image_field'] as $upload): ?><?php echo
$upload['thumbUrlPath'?><?php endforeach ?>" 
alt="<?php foreach ($your_tableRecord['your_normal_image_field'] as $upload): ?><?php echo $upload['info2'?><?php
endforeach ?>” 
name="<?php foreach ($your_tableRecord['your_normal_image_field'] as $upload): ?><?php echo $upload['info2'?><?php
endforeach ?>" 
width="<?php foreach ($your_tableRecord['your_normal_image_field'] as $upload): ?><?php echo $upload['thumbWidth']
?><?php endforeach ?>" 
height="<?php foreach ($your_tableRecord['your_normal_image_field'] as $upload): ?><?php echo $upload['thumbHeight']
?><?php endforeach ?>" 
border="0" 
id=<?php foreach ($your_tableRecord['your_normal_image_field'] as $upload): ?><?php echo $upload['info2'?>
<?php endforeach ?>" />
</a>
<?php endforeach ?>



There are many ways to slice and dice the rollover scenario, but if you mock it up in HTML first, and then replace the
values as needed with your PHP calls, keeping in mind the unique identifier requirement, you should be fine. 


CREATING A DETAILS PAGE TO DISPLAY A SINGLE IMAGE FROM THE MULTI IMAGE UPLOAD AND USING INFO FIELDS - Aug 3rd, 2010

The standard code block still goes at the top of the page:



<?php
  
  require_once "/path_to_your/cmsAdmin/lib/viewer_functions.php";
  list(
$photographyRecords$photographyMetaData) = getRecords(array(
    
'Table name'   => 'photography',
    
'where'       => whereRecordNumberInUrl(1),
    
'limit'       => '1',
  ));
  
$photographyRecord = @$photographyRecords[0]; // get first record?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>



Then for example, if you want the 5th image (counting starts with “0") in the series that was the size specified in
thumbnail 2 to be displayed on the page you would call it with:



<img src="<?PHP echo $photographyRecord['images'][4]['thumbUrlPath2']?>" border="0" width="<?PHP echo
$upload['thumbWidth2'?>" height="<?PHP echo $upload['thumbHeight2']?>" style="margin-bottom: 5px" />



And if you wanted to use the info fields associated with that specific image you would call them with (substituting the
info number for “X”):



<?PHP echo $photographyRecord['images'][4]['infoX']?>



If you want to test for an image first you can use the code: 



<?PHP if ($photographyRecord['images'][4]): ?>
...
<?PHP endif ?> 



Pretty simple when you know how isn’t it.


LINK THUMBNAILS TO FULL-SIZED VERSION OF IMAGES - Aug 3rd, 2010

By default, this is the code used to generate the image tags for thumbnails is:



<?PHP if ($upload['hasThumbnail']): ?> 
            <img src="<?PHP echo $upload['thumbUrlPath'?>" width="<?PHP echo $upload['thumbWidth'?>" height="<?PHP
echo $upload['thumbHeight'?>" alt="" /><br />



If you want your thumbnails to link to the full-sized version of the image, you can substitute that code with this:



<?PHP if ($upload['hasThumbnail']): ?> 
            <a href="<?PHP echo $upload['urlPath'?>"><img src="<?PHP echo $upload['thumbUrlPath'?>" width="<?PHP
echo $upload['thumbWidth'?>" height="<?PHP echo $upload['thumbHeight'?>" alt="" /></a><br />




MAKE A TITLE AND CAPTION APPEAR UNDER OR ABOVE AN IMAGE - Aug 3rd, 2010

Uploads can have up to 5 additional "info" fields. By default Title and Caption are defined. If you are displaying your
images with a variable called $upload you can define values for the title and caption by adding fields named info1,
info2,...info5 to your section editor and then displaying them on your web page like this:



Title: <?PHP echo $upload['info1']; ?>
Caption: <?PHP echo $upload['info2']; ?>
Your Definition: <?PHP echo $upload['info3']; ?>



CREATING PHOTO GALLERY DETAIL PAGES - Aug 3rd, 2010

You can create a series of detailed pages that can be found on a listing page by using the approach below.

We’ll use the example of a simple image gallery that an organization can use to display images from it’s past
events.

We’ll use galleryList.php as a template for a gallery list page

galleryList.php is set up to show the “title” for all past event records available, with links to that particular
event’s record.

We’ll use galleryDetail.php as a template for the image collections from the individual events held that year

Each Gallery record is in a multi record format with fields for a “title”, an “event description”, and up to 30
individual “images”. The images are set to limit uploads to a maximum size of 1000KB, to resize any images larger
than 800 x600 pixels, and to create thumbnails of no more than 275 pixels in their larger dimension. 

If you want to keep the events of a single year (or group) on separate list pages, then you’ll want to create a
separate section editor for each year. You can set up the links to the list pages on a master links page or on a
navigation menu.

If you’re using this approach for a different application other than a straight ahead photo gallery, you can create
your first section editor and then make it a template in the section editor. Don’t forget to change the Search URL and
field names to match your new template’s values. And to change any image limitations as necessary.

Copying a sectioneditor.ini.php file from your on-line cmsAdmin/data/schema/ folder to your cmsAdmin/data/schemaPresets/
folder and it will show up in the “add section” pull down menu.

After creating a section editor for your project, you can make this section editor into a template, by:

Going to your on-line cmsAdmin/data/schema/ folder and downloading the new sectioneditor.ini.php file.

Opening the downloaded file in your web page editor.

To change the name that appears in the pull down list, change the “menuName” value in the first section of the new
sectioneditor.ini.php file. 

Change any other “label” values to generic values if necessary. You’ll be changing these individually to suite
when you create your new section editors.

Upload your modified new sectioneditor.ini.php file to your cmsAdmin/data/schemaPresets/ folder and it will show up in
the “add section” pull down menu

To add a set of image galleries for a new year:

Create new section editor from the Gallery preset menu
change the Menu Name and Table Name as appropriate.
Modify Viewer URLS as appropriate.

Add a hidden field if appropriate and add neverRemove and hidden to show in the editor menu.

Create a new years list and details pages by replacing all the template year references with the new year.


CONTROLLING THE TYPES OF FILES THAT CAN BE UPLOADED - Aug 3rd, 2010

To modify one of your existing upload fields to accept .wav or mp3 files, go to Admin -> Section Editors and click the
“modify” link for the section this field is being used in. Now click the "modify" link for this specific field, and
then click on the "Show All" link for the Input Validation section. Here, there's a field called "File extensions
allowed:", which you can add "wav" to. This field acts as a comma-separated list. WITHOUT THE PERIODS.  Here's an
example of the format to follow when adding in file type extensions here:

wav,wmv,wma,aif

Just save your setting adjustments and you'll be able to upload specific types of files for that field. 


ALIGNING IMAGES SO THAT TEXT WRAPS AROUND THEM - Aug 3rd, 2010

Here’s a suggestion that was offered by CMSB user equinox69. He said:

If you don't want to put the data into separate table fields then a simple html trick to make it wrap for any browser is
the following:

If you want the image to show on the right and have text wrap on the left and below the image automatically adjusting do
this: <img src="pic.jpg" align="right"> text here text here text here text here, etc.

If you want the image to show on the left and have the text wrap on the right and below the image automatically
adjusting, then do this:



text here, text here text here text here, etc. <img src="pic.jpg" align="left">



It's worked in every version of IE since at least 3.0 and netscape, mozilla, etc. The browser want to put the text on
the side the graphic is listed but the graphic is forced to the other side so the browser forces the graphic over and
wraps the text appropriately. 


DISPLAYING NEWEST IMAGES FIRST - Aug 3rd, 2010

NigelGordijk asked if there was a way to display images as newest at the top of a gallery instead of the oldest images
first.

As usual, Dave Edis from Interactive Tools had the perfect solution. He said:

You can reverse the order with array_reverse().

If your current code looks something like this:

<?php foreach ($record['uploads'] as $upload): ?>

Just add "array_reverse"  to your code:



<?php foreach (array_reverse($record['uploads']) as $upload): ?>



Thanks Dave.


INSERTING RANDOM ROTATING IMAGES ON A WEB PAGE - Aug 3rd, 2010

Inserting images or banners on a web page and having them change each time the page is reloaded is really easy. 

For this example we’ll display one image at random from a simple multi record section editor. The fields in the editor
are Title, an image upload field limited to one upload per record and a link URL that’s entered into the first extra
information field (info1) in the upload advanced options. 

The code that displays the images and links on the web page is identical to any other image display code:



<?php foreach ($your_tableRecords as $record): ?>
 <?php foreach ($record['image'] as $upload): ?>
 <a href="<?php echo $upload['info1'?>"> <img src="<?php echo $upload['thumbUrlPath2'?>" width="<?php echo
$upload['thumbWidth2'?>" height="<?php echo $upload['thumbHeight2'?>" style="border:hidden" alt="" /></a>
<?php endforeach ?>
 <?php endforeach ?>


   
The secret is using various options to determine how the image records in your_table will be displayed.
For our situation we want to randomize the order of the images and then display only one image. We’ll use the option
'orderBy'     => 'RAND()', to randomize the order and 'limit'       => '1', to limit the number of images that can be
displayed at a time.
Here’s the code that would appear at the top of your PHP web page:



<?php header('Content-type: text/html; charset=utf-8'); ?>
<?php
  
  require_once "/your_path_to/cmsAdmin/lib/viewer_functions.php";

  list(
$your_tableRecords$your_tableMetaData) = getRecords(array(
    
'Table name'   => 'your_table',
    
'orderBy'     => 'RAND()',
    
'limit'       => '1',
  ));



If you wanted to have more than one group of images or banners display on your pagejust create a second multi record
editor (your_second_table) and insert the following at the top of your PHP page:



<?php header('Content-type: text/html; charset=utf-8'); ?>
<?php
  
  require_once "/your_path_to/cmsAdmin/lib/viewer_functions.php";

  list(
$your_tableRecords$your_tableMetaData) = getRecords(array(
    
'Table name'   => 'your_table',
    
'orderBy'     => 'RAND()',
    
'limit'       => '1',
  ));

list(
$your_second_tableRecords$your_second_tableMetaData) = getRecords(array(
    
'Table name'   => 'your_second_table',
    
'orderBy'     => 'RAND()',
    
'limit'       => '1',
  ));



and display the images from that table like this:



<?php foreach ($your_second_tableRecords as $record): ?>
<?php foreach ($record['image'] as $upload): ?>
<a href="<?php echo $upload['info1'?>"> <img src="<?php echo $upload['thumbUrlPath2'?>" width="<?php echo
$upload['thumbWidth2'?>" height="<?php echo $upload['thumbHeight2'?>" style="border:hidden" alt="" /></a>
<?php endforeach ?>
<?php endforeach ?>



INSERTING RANDOM ROTATING IMAGES ON A WEB PAGE FROM RANDOM RECORDS - May 19th, 2013

User SkidderChains wanted to provide images for a rotating image header with an interesting twist. The images had to be
pulled from a random record in a multi-record editor and then from a random image in that record’s image upload field.

Greg Thomas from Interactive Tools offered this solution:


  // load records from 'gallery_2' 
  list($gallery, $gallery_2MetaData) = getRecords(array( 
    'tableName'   => 'gallery_2', 
    'orderBy'     => 'RAND()', 
    'loadUploads' => true, 
    'allowSearch' => false, 
  )); 
   
  //shuffle all of the images for each record 
  foreach($gallery as $images){ 
    shuffle($images['image']); 
  } 
   
  //counter 
  $n = 0; 
  //max number of images you want to pull from each section; 
  $max = 5; 
  while($n <= $max): ?> 
     
    <?php foreach($gallery as $key => $images): ?> 
       
      <?php //If there is a picture in the image array with a key of $n, assign it to the $image varible, else return
false 
      if($image = @$images['image'][$n]): ?> 
         
        <img src="<?php echo $image['urlPath']; ?>" alt="<?php echo $image['info1']; ?>" /> 
      <?php endif; ?> 
       
    <?php endforeach; ?> 
    <?php $n++; ?> 
  <?php endwhile; ?>


Greg said:

This is just an example, and you will have to modify the code to work with your setup. 

“I'm using the getRecords function to randomly sort the sections, then cycling through them and displaying each
individual image using the $n variable to select a single image from the image array. So first it will display all of
the images from each section with a key of 0, then 1, 2 etc.


DISPLAYING IMAGES BLOCKS IN A RANDOM ORDER - Sep 19th, 2010

A client of mine wanted to have 4 groups of images and a navigation menu display in a single row, 5 column table on
their home page. They wanted the images to change in a random fashion and the navigation menu to appear in a different
cell each time the page was visited. 

I was able to help them by combining the random display concept above with a simple javascript program that I found on
the vBulletin support forum.

       http://forum.vbulletinsetup.com/f62/display-content-random-order-xhtml-valid-2355.html

I created 4 section editors like the ones above, and then created a web page with the following code:
The class=”group1" is used by the randomizing script to swap the contents of the cells.



<?php header('Content-type: text/html; charset=utf-8'); ?>
<?php
  
  require_once "/path_to_your/cmsAdmin/lib/viewer_functions.php";

  list(
$images_1Records$images_1MetaData) = getRecords(array(
    
'Table name'   => 'images_1',
    
'orderBy'     => 'RAND()',
    
'limit'       => '1',
    
    ));

list(
$images_2Records$_images_2MetaData) = getRecords(array(
    
'Table name'   => 'images_2',
    
'orderBy'     => 'RAND()',
    
'limit'       => '1',
    
    ));

list(
$images_3Records$images_3MetaData) = getRecords(array(
    
'Table name'   => 'images_3',
    
'orderBy'     => 'RAND()',
    
'limit'       => '1',
    
    ));

list(
$images_4Records$images_4MetaData) = getRecords(array(
    
'Table name'   => 'images_4',
    
'orderBy'     => 'RAND()',
    
'limit'       => '1',

    
));

?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<script type="text/javascript" src="scripts/random.js"></script>

<title>Random Blocks</title>
</head>

<body >
<table  border="0" align="center" cellpadding="0">
  
 <tr>
    <td valign="top">
    <?php foreach ($images_1Records as $record): ?>
      <div class="group1">
      <?php foreach ($record['image'] as $upload): ?>
      <a href="<?php echo $upload['info1'?>"> <img src="<?php echo $upload['thumbUrlPath2'?>" width="<?php echo
$upload['thumbWidth2'?>" height="<?php echo $upload['thumbHeight2'?>" style="border:hidden" alt="" /></a>
      <?php endforeach ?>
      <?php endforeach ?>
    </div></td>

    <td valign="top">
    <?php foreach ($images_2Records as $record): ?>
    <div class="group1">
      <?php foreach ($record['image'] as $upload): ?>
     <a href="<?php echo $upload['info1'?>"> <img src="<?php echo $upload['thumbUrlPath2'?>" width="<?php echo
$upload['thumbWidth2'?>" height="<?php echo $upload['thumbHeight2'?>" style="border:hidden" alt="" /></a>
      <?php endforeach ?>
      <?php endforeach ?>
    </div></td>

    <td valign="top">
    <?php foreach ($images_3Records as $record): ?>
    <div class="group1">
      <?php foreach ($record['image'] as $upload): ?>
     <a href="<?php echo $upload['info1'?>"><img src="<?php echo $upload['thumbUrlPath2'?>" width="<?php echo
$upload['thumbWidth2'?>" height="<?php echo $upload['thumbHeight2'?>" style="border:hidden" alt="" /></a>
      <?php endforeach ?>
      <?php endforeach ?>
    </div></td>

    <td valign="top">
    <?php foreach ($images_4Records as $record): ?>
    <div class="group1">
      <?php foreach ($record['image'] as $upload): ?>
      <a href="<?php echo $upload['info1'?>"><img src="<?php echo $upload['thumbUrlPath2'?>" width="<?php echo
$upload['thumbWidth2'?>" height="<?php echo $upload['thumbHeight2'?>" style="border:hidden" alt="" /></a>
      <?php endforeach ?>
      <?php endforeach ?>
    </div></td>

 <td valign="top"><div class="group1">
      <table align="center"  width="90%" border="0" cellpadding="0">
  <tr>
    <td valign="top" ><a class="your-class" href="your_index.php">Home</a></td>
  </tr>
  <tr>
    <td valign="top" ><a class="your-class" href="page_1.php">Menu Item 1</a></td>
  </tr>
  <tr>
    <td ><a class="your-class" href="page_2.php">Menu Item 2</a></td>
  </tr>
  <tr>
    <td ><a class="your-class" href="page_3.php">Menu Item 3</a></td>
  </tr>
  <tr>
    <td ><a class="your-class" href="page_4.php">Menu Item 4</a></td>
  </tr>
</table>

    </div></td>

  </tr>
  
</table>

    <script type="text/javascript">
//randomize order of contents with DIV class="group1"
randomizeContent("group1")
  </script>


</body>
</html>



SUPERIMPOSING TEXT ON RANDOM IMAGES - Jan 6th, 2015

Taking the above concept one step further, my client wanted to superimpose the title of the image over each of the
random images. Using the “title” field from each record, nested div tags and CSS to adjust style and position made
easy work of the task.
The basic format for the code is:



<div style="position: relative; background: url(path to image); width: (width)px; height: (height)px;">
 <div style="position: absolute; bottom: 0; left: 0.5em; width: (text line width)px; font-weight: bold; color:
#000000;"> <p>(text to appear at the bottom left of the image)</p> </div> </div>


Replacing the code in each cell with the code below (change the table names in each cell as appropriate) will
superimpose the contents of the title field of each record in 9 point black Verdana text at the bottom of each image and
indent it .5 em from the left. The text width is set to the width of the image so that it will wrap and not extend past
the edge of the image. 



<div class="group1" ><div style="position: relative; background: url(<?php foreach ($image_1Records as $record): ?>
     
      <?php foreach ($record['image'] as $upload): ?><?php echo $upload['thumbUrlPath2'?>); width: <?php echo
$upload['thumbWidth2'?>px; height: <?php echo $upload['thumbHeight2'?>px;"> 
      <div style="position: absolute; bottom: 0; left: 0.5em; width: <?php echo $upload['thumbWidth2'?>px;
font-family:Verdana, Tahoma, Arial, Sans-Serif; font-size:9pt; font-weight:normal; text-align:left; font-style: normal;
color: #000000;"><?php echo $record['title'?></div><?php endforeach ?>
      <?php endforeach ?></div> </div>




UPDATE
In a multi-recod situation where there were a large number of records but only some of them had images, I didn't want to
increase the server load by loading all the records and images before I chose one, but I was having some difficulty
adding the name of the artist in the overlay.

Greg Thomas from Interactive tools reminded me that a parent record number of an upload can be found with
$upload['recordNum'].

Here's the code we ultimately came up with:



<?php $uploadRecords mysql_select('uploads'" tableName = 'your_table' AND fieldName = 'images'"); ?>
<table align="center" width="80%" border="0" cellpadding="5">
  <tr>
    <?php shuffle($uploadRecords?>
    <?php foreach ($uploadRecords as $upload): ?>
    <td align="center">
      <div onclick='window.location="#"' class="round-corner" style="position: relative; background:
url(http://your_site.com/cmsAdmin/uploads/<?php echo
$upload['thumbUrlPath2'?>); width: <?php echo $upload['thumbWidth2'?>px; height: <?php echo $upload['thumbHeight2']
?>px;">
      <?php
      
        $upload['urlPath'] = preg_replace('/\.\w+$/'''$upload['urlPath']); 
        
$upload['urlPath'] = preg_replace("/[-_]/"" "$upload['urlPath'] ); 
        
$upload['urlPath'] = ucwords($upload['urlPath'] ); 

      
?>
        <div style="position: absolute; top: .9em; left: 1.2em; width: 200px; font-family:Verdana, Tahoma, Arial,
Sans-Serif; font-size:1.2em; font-weight:bold; text-align:left; font-style: normal; color: #00F;" class="your_class">
          <?php echo $upload['urlPath']; ?>
        </div>
        <?php  
          list($accountsRecords$accountsMetaData) = getRecords(array(
          
'tableName'   => your_table',
          'where'       => "`num` = '{$upload['recordNum']}'",
          'loadUploads' => false,
          'allowSearch' => false,
          'limit'       => '1',
        ));
        ?>
        <?php foreach ($accountsRecords as $record2): ?>
          <div style="position: absolute; bottom: 1.2em; left: 1.2em; width: 200px; font-family:Verdana, Tahoma, Arial,
Sans-Serif; font-size:1.2em; font-weight:bold; text-align:left; font-style: normal; color: #00F;" class="your_class">
            <?php  echo $record2['first_name']; ?> <?php echo $record2['last_name']; ?>
          </div>
        <?php endforeach ?>
      </div>
    </td>
  </tr>
</table>
<?php break?>
<?php endforeach ?>


CREATING A ROTATING DISPLAY OF ARTIST’S IMAGES WITH SUPERIMPOSED NAME AND TITLE - Jan 6th, 2015

I was working a site for a photo exhibition which used a simple multi record editor called "jack".

The client wanted to be able to easily add photographers as they were juried in to the exhibition. 

The idea was that there would be a table of thumbnails which would display one image per photographer. They wanted each
photograph to have the photographer’s name and the name of the image automatically superimposed over the image.  The
images would need to be live links to the photographers detail page. Further, they wanted each photographer’s image to
randomly rotate through their available images and also wanted the photographers cell to randomly appear in a different
place in the table each time the page was reloaded.

Here’s how I did it...

I set each artist up with a separate user account which was created with "author" rights to "jack". Artists were only
allowed  create one record each, but could upload up to 25 of their images to that record.

The editor records contained an upload box for the images which used the “info1" field for the name of the image, a
text field called “title” for the photographers name and a text field called “url” to hold the link information.

On the list page, I displayed the images in a table with a limited number of columns. 

I superimposed  the photographers names and image titles the images with Divs to position the text, using the basic
format:



<div style="position: relative; background: url(path to image); width: (width)px; height: (height)px;">
<div style="position: absolute; bottom: 0; left: 0.5em; width: 400px; font-weight: bold; color: #fff;">(text to appear
at the bottom left of the image)</div>
<div style="position: absolute; top: 0; left: 0.5em; width: 400px; font-weight: bold; color: #fff;">(text to appear at
the top left of the image)</div>
</div>



RANDOMIZING THE IMAGES IN THE CELLS

In the viewer I used



'orderBy' => 'RAND()', 



to randomize the images that appear in the various cells.

MAKING THE BACKGROUND IMAGES INTO LIVE LINKS
Dave Edis reminded that you can use the URL field and add:



onclick='window.location="<?php echo $record['url'?>"' 



to the Div

SHOWING ONLY ONE IMAGE PER AUTHOR

The code above accomplished almost all of the criteria for this display, however, it still displayed all of the images
that were uploaded to all the records in the table and this needed to be limited to one image per author.

Dave’s solution was simple. He said, “Just add thees lines to your code”:



<?php foreach ($jackRecords as $record): ?> 
<!-- Insert This Code -->
<?php shuffle($record['images']) ?> 
<!-- End of Insert-->
<?php foreach ($record['images'] as $upload): ?>
<!-- Insert This Code --> 
<?php if (@$alreadySeen$record['title'] ]++) { continue; } ?> 
<!-- End of Insert-->



The finished viewer code looks like this:

In the require once area:



list($jackRecords, $jackMetaData) = getRecords(array( 
    'Table name'   => 'jack', 
        'orderBy'     => 'RAND()',



And in the body:



<table width="80%" border="0" cellpadding="5">
 <tr><?php foreach ($jackRecords as $record): ?><?php shuffle($record['images']) ?> <?php foreach ($record['images'] as
$upload): ?><?php if (@$alreadySeen$record['title'] ]++) { continue; } ?> 

<td align="center">

<div onclick='window.location="<?php echo $record['url'?>"' style="position: relative; background: url(<?php echo
$upload['thumbUrlPath2'?>); width: <?php echo $upload['thumbWidth2'?>px; height: <?php echo $upload['thumbHeight2']
?>px;">
    
<div style="position: absolute; top: 0; left: 0.5em; width: 200px; font-family:Verdana, Tahoma, Arial, Sans-Serif;
font-size:10pt; font-weight:bold; text-align:left; font-style: normal; color: #fff;"><?php echo $record['title']
?></div>
    
<div style="position: absolute; bottom: 0; left: 0.5em; width: 200px; font-family:Verdana, Tahoma, Arial, Sans-Serif;
font-size:10pt; font-weight:bold; text-align:left; font-style: normal; color: #fff;"><?php echo $upload['info1']
?></div>

</div></td><?php $maxCols=3; if (@++$count $maxCols == 0): ?></tr><tr><?php endif; ?>

<?php endforeach ?><?php endforeach ?>

 </tr>
</table>


 
User perchpole added that you can also add things like the following to your div code for other effects:



<div style="cursor: pointer;" onmouseover="this.style.backgroundColor='red';"
onmouseout="this.style.backgroundColor='blue';" onmouseup="location.href='<?php echo $record['url'?>'">your content
goes here</div>  



UPDATE
In a multi record situation where there were a large number of records but only some of them had images, I didn't want
to increase the server load by loading all the records and images before I chose one, but I was having some difficulty
adding the name of the artist in the overlay.

Greg Thomas from Interactive tools reminded me that a parent record number of an upload can be found with
$upload['recordNum'].

Here's the code we ultimately came up with:



<?php $uploadRecords mysql_select('uploads'" tableName = 'your_table' AND fieldName = 'images'"); ?>
<table align="center" width="80%" border="0" cellpadding="5">
  <tr>
    <?php shuffle($uploadRecords?>
    <?php foreach ($uploadRecords as $upload): ?>
    <td align="center">
      <div onclick='window.location="#"' class="round-corner" style="position: relative; background:
url(http://your_site.com/cmsAdmin/uploads/<?php echo
$upload['thumbUrlPath2'?>); width: <?php echo $upload['thumbWidth2'?>px; height: <?php echo $upload['thumbHeight2']
?>px;">
      <?php
      
        $upload['urlPath'] = preg_replace('/\.\w+$/'''$upload['urlPath']); 
        
$upload['urlPath'] = preg_replace("/[-_]/"" "$upload['urlPath'] ); 
        
$upload['urlPath'] = ucwords($upload['urlPath'] ); 

      
?>
        <div style="position: absolute; top: .9em; left: 1.2em; width: 200px; font-family:Verdana, Tahoma, Arial,
Sans-Serif; font-size:1.2em; font-weight:bold; text-align:left; font-style: normal; color: #00F;" class="your_class">
          <?php echo $upload['urlPath']; ?>
        </div>
        <?php  
          list($accountsRecords$accountsMetaData) = getRecords(array(
          
'tableName'   => your_table',
          'where'       => "`num` = '{$upload['recordNum']}'",
          'loadUploads' => false,
          'allowSearch' => false,
          'limit'       => '1',
        ));
        ?>
        <?php foreach ($accountsRecords as $record2): ?>
          <div style="position: absolute; bottom: 1.2em; left: 1.2em; width: 200px; font-family:Verdana, Tahoma, Arial,
Sans-Serif; font-size:1.2em; font-weight:bold; text-align:left; font-style: normal; color: #00F;" class="your_class">
            <?php  echo $record2['first_name']; ?> <?php echo $record2['last_name']; ?>
          </div>
        <?php endforeach ?>
      </div>
    </td>
  </tr>
</table>
<?php break?>
<?php endforeach ?>

Of course you’d style the code to suit your needs.


DISPLAY A SPECIFIC IMAGE FROM A SPECIFIC RECORD - Jun 14th, 2013

My client wanted to be able to display a specific image from a group of records and multi image uploads.

I couldn’t get this to work until Chris Waddell from Interactive Tools unlocked the secret.

He said: 

If $affiliationsRecords is a list of all your records, then $affiliationsRecords[0] will give you the first record,
$affiliationsRecords[1] will give you the second, etc. It's a little confusing that the counting starts at 0 instead of
1.

$affiliationsRecords[3]['logo'][2] will grab the fourth record ([3]), its list of images (['logo']), and select its
third image ([2]). 

Since $affiliationsRecords[1]['logo'] refers to the list of images. You need to use $affiliationsRecords[1]['logo'][0]
if you want to refer to a specific image ([0] gives you the first one, etc.) 

You’ll need to define a variable for the specific image. To call the 3rd image from the 4th record I used 



<?php $myImage $affiliationsRecords[3]['logo'][2]; ?> 



The code that you’d use in the body of your viewer page where you want the image to display would be:



<?php $myImage $affiliationsRecords[3]['logo'][2]; ?> 
<img src="<?php echo $myImage['thumbUrlPath'?>" width="<?php echo $myImage['thumbWidth'?>" height="<?php echo
$myImage['thumbHeight'?>" alt="" />



You can also define other variables, like this one for the link to a detail page relating to the same record:



<?php @$mylink $affiliationsRecords[4]['_link']; ?>



Then the code including a linked image might look like this:



<?php @$myImage $affiliationsRecords[3]['logo'][2]; ?> 
<?php @$mylink $affiliationsRecords[3]['_link']; ?> 
<a href="<?php echo $mylink ?>"><img src="<?php echo $myImage['thumbUrlPath'?>" width="<?php echo
$myImage['thumbWidth'?>" height="<?php echo $myImage['thumbHeight'?>" alt="" border="0"/></a>



That approach works for working with Nths uploads belonging to Nths records in a list of records. According to Chris
Waddel form Interactive Tools, if you're FOREACHing over your list of records and want to display the Nth upload of each
one, you can use $record['uploadFieldName'][4].

 Personally, he likes to assign an upload to a variable (like $upload) so I don't have to repeat the
['uploadFieldName'][4] part, like this:




<?php foreach( $affiliationsRecords as $affiliationsRecord ): ?>
  Title: <?php echo htmlencode($affiliationsRecord['title']) ?>

  2nd Upload:
  <?php $upload = @$affiliationsRecord['uploadFieldName'][4?>
  <?php if ($upload): ?>
    <img src="<?php echo $upload['thumbUrlPath'?>" width="<?php echo $upload['thumbWidth'?>" height="<?php echo
$upload['thumbHeight'?>" />
  <?php else: ?>
    None!
  <?php endif ?>  

<?php endforeach ?>


MAKE AN IMAGE AN ACTIVE DOWNLOAD LINK - Aug 3rd, 2010

This example uses a multi record editor called test3 that contains 2 upload fields, “pdf” and “image”

The code on the PHP viewer page to display the image as an active link is below. 

The basic structure of the code you want to appear in the client’s browser  is. 



<a href="path to your pdf upload"><img src="path to the thumbnail" width="thumbnail width" height=”thumbnail height"
alt="alternate description of link" /> any other text that you want under the image</a>



It’s also a good idea to put a link on your  page that lets the visitor know that they’ll need at least the free
Adobe PDF reader to open the PDF document.  The link to the free reader is: http://get.adobe.com/reader/

In the PHP code to accomplish this there is one main foreach loop that says look at each record in the test3 table. This
loop is closed with an endforeach at the end of the code block.

Then there’s a nested foreach loop for the pdf uploads that says look for the pdf upload path in each of the test3
records and display the path. This loop is closed at the end of the pdf code.

There’s a 3rd nested foreach loop to look for image uploads and display them as images. This loop is closed at the end
of the image display code.

These loops are extracted from the full code in the CMS Admin code generator. As long as you know what type of uploads
you’re working with, you don’t have to use all of the “if” tests in the code. This makes your page much cleaner
and easier to understand. 



<?php foreach ($test3Records as $record): ?>
<?php foreach ($record['pdf'] as $upload): ?>
<a href="<?php echo $upload['urlPath'?>"> <?php endforeach ?>

<?php foreach ($record['image'] as $upload): ?>

<img src="<?php echo $upload['thumbUrlPath'?>" width="<?php echo $upload['thumbWidth'?>" height="<?php echo
$upload['thumbHeight'?>" alt="" />Click On the Image for the PDF</a>
<?php endforeach ?><?php endforeach ?>



BATCH UPLOADING OF IMAGES - Aug 3rd, 2010

The new multi-file upload facility built in to CMSB Version 2.02 has kind of eliminated the need for this recipe, but
I’m leaving it here because it explains a number of things about manipulating MySQL and Excel data.

That said...
  
I had a project that required the uploading of over 800 images and thumbnails overnight and displaying the thumbnails in
a table. In my case there were no titles or captions for the images, just the images.

There were a some images that needed to be deleted after the upload was finished and some that needed to be re-ordered.
These tasks were accomplished in CMSB after all the images were uploaded via FTP and the database was modified to show
the images.

In CMSB, all uploads are referenced in a single database table called “yourprefix_uploads”. There are many columns
of information but the data in them is pretty straightforward. By manipulating the data in the columns and uploading all
of the images to upload folders I was able to accomplish the task in a few hours with a minimum of effort. 

The instructions look complex, but they are much simpler to implement than they look.

TOOLS REQUIRED

Navicat for MySQL http://navicat.com/ to access the on-line databases and export and import information.
EXCEL to manipulate the data
Photoshop (or the free image editor Irfanview http://www.irfanview.com ) to batch create the thumbnails and full sized
images to be uploaded.
A free batch file renaming utility like http://www.bulkrenameutility.co.uk/

THE PROCESS
IMAGES
Rename all the project images sequentially with the bulk rename utility. (I used 1.jpg through 850.jpg but the names can
be more complex)
Batch resize the images to create both the thumbnail and full sized images
Upload all images to the appropriate sub folders in the cmsAdmin folder on your server 

EDITOR AND VIEWER
Set up a multi record editor with an upload box with an unlimited amount of images per record. Modify the size
information to match the size of the thumbnails and images that you created.
Set up a list page viewer to display the thumbnails
Upload a few test images and make sure that your viewer displays correctly. (This will also be helpful when you add data
to the database table

DATABASE
Use Navicat to download the “yourprefix_upload” table in excel format
Create a backup copy of the original “yourprefix_upload” table ***IMPORTANT***
Add the appropriate series of information to the columns in the table
Upload the revised “yourprefix_upload” table to overwrite the old table
If any errors messages appear in Navicat, go back to your spreadsheet and correct them (usually typos or missing
information) and upload again. 
If you were careful, you’re done, and the images will appear in your viewer.

THE DETAILS
In the spreadsheet you’ll find columns for:

num (find the highest number in this column and add sequential numbers in ascending order to rows below the last row
beginning with the highest existing number plus 1. Create the exact number of rows to match the amount of images that
you’ll be adding.)
order find the highest number for the Table name you are adding to and add sequential numbers in ascending order to rows
below the last row beginning with the highest existing number plus 1)
createdTime just extend the values down to fill the required rows from the last value in this column. This is not a
critical value.
Table name this row contains the name of the table where you will be adding the images. Add the table name to all rows
that will contain images
fieldName this is the name of the field in the table where you will be adding the images. Add the field name to all rows
that will contain images
record number since there is only one record in your gallery, the value in each one of these rows is 1
preSaveTempId not used
filePath this is the full path to the full sized images. Each row contains the full path and file name for one image. 
Find the sample image entry and use this format as a model.
Since the increasing value is not at the end of the final cell contents, you’ll have to create the path information
with sequential numbers at the end and then add (concatenate) the .jpg to the end of the values. (SEE WORKING WITH
SERIES IN EXCEL, BELOW)
urlPath this is the folder, sub folder and file name location of each full sized image. Each row contains the location
information for one image. Find the sample image entry and use this format as a model. Create the entries using the
concatenation instructions below.
width and height the size of your full sized images in pixels (the values in each column will be identical)
thumbFilePath, thumbUrlPath, thumbWidth, thumbHeight the information for thumbnail 1. (see filepath above)
thumbFilePath2,3,4 etc. (as required) If you are not using a particular thumbnail, enter a value of 0 for each row in
their width and height columns.

WORKING WITH COMPLEX SERIES IN EXCEL
In those columns where the increasing value is the last characters in the cell (simple numbers, repeating values)
working with series is extremely simple using step 1 and 2 below.

To create a series of entries in an Excel spreadsheet where the increasing value is not last characters in the cell.
(Image1.jpg, image2.jpg) You’ll have to perform the operation in 2 steps. 1) create the series of increasing values.
(Image1, image2, etc.) and 2) add (concatenate) the .jpg to all values.

Here’s how:

1) In the target column, where the resultant series is to be “Image1.jpg, image2.jpg” remove the .jpg so that the
number is the last value (image1, image2) 
2) To fill the cell range in the column with a series of increasing numbered contents highlight the range then (edit>
fill> series)
3) If there is more than one column that requires a series, repeat step 1 and 2 as necessary.
4) Create a column where all cells in the series range of rows have the contents “.jpg”
5) in a blank column in the first row to contain the concatenated contents, enter the formula =CONCATENATE(A1,A2) where
A1 is the cell that contains the contents “image1" and B1 is the cell in the same row that contains the contents
“.jpg" 
6) Highlight the cell in the blank column and copy (CTRL-C)
7) Click in the cell in the row directly below that one and drag to the end cell in the series
8) Paste using CTRL-V
9) To replace the contents of the target column in step 2 with the concatenated version:
    A) highlight and copy all the concatenated cells in the new column.
    B) place the cursor in the first cell of the range in the target column (step 2)
    C) choose Paste - Values Only from the paste menu

10) Change the formula in the cell referred to in step 4 to represent the new target row and repeat step 6 through 9

It’s a bit cumbersome, but it works. If anyone has a simpler approach, please share it and I’ll include it in the
Cookbook.


SHOWING A LINK TO UPLOADS ONLY IF THEY EXIST - Aug 3rd, 2010

chassa2556 asked about showing a link to a gallery on a page only when pictures are uploaded to the page on the back
end.

Dave came to the rescue again...

First you need to determine the name of your uploads variable. Usually it will look something like this:



$your_field_nameRecord['uploads'] 



Use your own field name in the example code below.

Next, you can use some code like this to test for uploads:



<?PHP if ($your_field_nameRecord['uploads']): ?>
... this HTML or PHP will be shown if there are uploads ...
<?PHP endif ?>



Just wrap those tags around the content you want to show. Make sure the content can be removed without breaking the page
design. For example if you put that around half a table and there was no uploads it would only show the bottom html for
the table.

Typically you'd put it around a entire table, tr, td, or block of code.

Along the same lines, you can add a ! for "not" to test if there are "not" uploads. Like this:



<?PHP if (!$your_field_nameRecord['uploads']): ?>
Sorry, there are no uploads.
<?PHP endif ?>



Dave went on to remind us that the best approach, as always is to create the working HTML pages first. Then get the link
working to open in a new page. Then wrap it in the if tags above so it only shows when there are actually images.


SIMPLE IMAGE ROTATOR - CMSB + JQUERY + CSS - Dec 29th, 2018

User Zickey offered this simple image rotator built with jQuery, CSS and pulling records from a Category Menu, using top
level categories. 

He says it’s a stripped down version of http://www.gmarwaha.com/jquery/jcarousellite/ if you need a more full feature
version of this jQuery image rotator.

The rotator pauses on hover and once clicked will delivery you to the category menu record.

You can download the required files and a sample viewer from

http://www.thecmsbcookbook.com/downloads/simple_image_rotator.zip

To implement this, the following code goes at the top and in the head section of your viewer:
(Don’t forget to change the path to your viewer_functions.php)


<?php header('Content-type: text/html; charset=utf-8'); ?> 
<?php 
   
  require_once "/path_to_your/lib/viewer_functions.php"
 
  list(
$rotate_thisRecords$rotate_thisMetaData) = getRecords(array( 
    
'tableName'   => 'rotate_this'
  )); 
 
?> 
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 
<html xmlns="http://www.w3.org/1999/xhtml"> 
<head> 
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> 
<title>Simple Logo Rotator</title> 
 
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script> 
<script type="text/javascript" src="js/1-jcarousellite_1.0.1.js"></script> 
<script type="text/javascript" src="js/1-jcarousellite_button.js"></script> 
<link rel="stylesheet" type="text/css" href="css/simplelogorotator.css"> 
</head>  


And in the body where you want the images to be displayed.


<body> 
<div id="rotate_this"> 
      <div id="slide-container"> 
          <div id="slides-boxed"> 
            <ul> 
         <?php foreach ($rotate_thisRecords as $record): ?> 
         <?php foreach ($record['logo'] as $upload): ?> 
              <!-- logos --> 
              <li> 
         <?php if ($upload['hasThumbnail']): ?> 
         <div class="slide-images" style="background:url(<?php echo $upload['thumbUrlPath'?>) no-repeat 0 0;"
onmouseover="this.style.cursor='pointer';" onclick="document.location.href='<?php echo $record['_link'?>'"></div> 
         <?php endif ?> 
              </li> 
              <!-- /logo --> 
        <?php endforeach ?> 
        <?php endforeach ?> 
            </ul> 
          <!-- /slides-boxed --></div> 
      <!-- /slide-container --></div> 
<!-- /rotate_this --></div> 
</body> 
</html>


You can modify the css file to make the container any size that you want it to be.

Zickey said he stripped out the html effecting the buttons, but the (1-jcarousellite_button.js) file is required for
this to run even though there are no prev/next buttons in the php file. You could probably edit it out the need for it
in the 1-jcarousellite_1.0.1.js.


FORCING BROWSERS TO DOWNLOAD IMAGES (OR OTHER FILES) - Nov 27th, 2011

There are times that you'd like a visitor to be able to download images (and other files, like PDFs) instead of just
displaying them in line.

Here's a suggestion that originally came from Dave Edis at Interactive Tools.

He suggested using an .htaccess file to do the job.

Here's my version which presents a download dialog box when clicked on for all jpg,psd and tif or tiff files. This only
affects files when you're linking to them, as in the code:



<?php foreach ($your_tableRecord['your_upload_field'] as $upload): ?>
            
<a href="<?php echo $upload['urlPath'?>"><img src="<?php echo $upload['thumbUrlPath'?>" width="<?php echo
$upload['thumbWidth'?>" height="<?php echo $upload['thumbHeight'?>" alt="" /></a><br />

 <?PHP endforeach; ?>


Here's the .htaccess code



<FilesMatch "\.(jpg|psd|tif|tiff)$">
ForceType application/octet-stream
</FilesMatch>


According to Shi Chuan on his blog at
http://www.blog.highub.com/apache/http-server/force-files-like-pdf-download-using-htaccess/ "If you view the files in a
web page, they will display, but if linked directly to them they will show you the save box. (Files don’t seem to
download if they are saved in cache Ctrl+F5 to fix that)."


INSERTING AN IMAGE FROM A SERVER INTO A CMSB DATABASE RECORD - Dec 17th, 2011

This one's probably not going to be extremely useful, but you never know...

I had a client who was already allowing subscribers to uploading an image to a directory on their server. I took over
the project because they wanted to convert to a content managed site. 

One of the things I was stuck on was how to save the uploaded image into a CMSB database record.

First I’ll describe how they were uploading their image to a folder on their server in case it comes in handy for
someone.

The subscriber was presented a form like this, in a file called purchase.php:


             
<form name="ordernow" enctype="multipart/form-data" action="ordernow.php" method="post" >
<table width="100%" border="0" cellspacing="0" cellpadding="0" class="text_field">
<tr>
<td valign="top" class="title" height="30">First Name
</td>
<td>
<input type="text" name="first_name" id="first_name">
</td>
 </tr>
<tr>
<td valign="top" class="title" height="30">Last Name
</td>
<td>
<input type="text" name="last_name" id="last_name">
</td>
 </tr>
<tr>
<td valign="top">
Select your image: <br /> (upload time will depend on the speed of your internet connection)
</td>
<td> <input type="file" name="photo"></td>
</tr>
<tr>
<td>&nbsp;</td>
<td align="left" valign="top">
<input type="submit" value="Submit" name="submit">
</td>
</tr>
 </table>
</form>


The orginal ordernow.php file that did the processing looked like this:


<?php session_start();
{
if(
$_POST){
 
$name_of_uploaded_file =basename($_FILES['photo']['name']);
//get the file extension of the file
        $type_of_uploaded_file =substr($name_of_uploaded_file,strrpos($name_of_uploaded_file'.') + 1);
         
        
$size_of_uploaded_file =$_FILES["photo"]["size"]/1024;//size in KBs
            
        //copy the temp. uploaded file to attachdoc folder
        $upload_folder='photo/';
        
$path_of_uploaded_file $upload_folder $name_of_uploaded_file;
        
//echo 'path_of_uploaded_file'.$path_of_uploaded_file;
        $tmp_path $_FILES["photo"]["tmp_name"];
         
        if(
is_uploaded_file($tmp_path))
        {
        
copy($tmp_path,$path_of_uploaded_file);
          if(!
copy($tmp_path,$path_of_uploaded_file))
          {
            
$errors .= '\n error while copying the uploaded file';
          }
        }    
        
$photo=$path_of_uploaded_file;
$first_name1=$_POST['first_name'];
$last_name1=$_POST['last_name'];
$errors='Order placed.';
}

?>


With the changes below I was able to get all of the text fields to get added to the CMSB database but couldn’t figure
out how to get the image to upload. Jason Shautuch from interactive tools came to the rescue.

Here’s what I changed and added to the ordernow.php file

At the top of the page:


<?php header('Content-type: text/html; charset=utf-8'); ?>
<?php
$libraryPath 'cmsAdmin/lib/viewer_functions.php';
  
$dirsToCheck = array('','../','../../','../../../','../../../../');
  foreach (
$dirsToCheck as $dir) { if (@include_once("$dir$libraryPath")) { break; }}
  if (!
function_exists('getRecords')) { die("Couldn't load viewer library, check filepath in sourcecode."); }
  
// load records
  list($common_informationRecords$common_informationMetaData) = getRecords(array(
    
'tableName'   => 'common_information',
    
'allowSearch' => '0',
    
'limit'       => '1',
  ));
  
$common_informationRecord = @$common_informationRecords[0]; // get first record
   // load records

if($_POST){
 
// turn off strict mysql error checking for: STRICT_ALL_TABLES
 mysqlStrictMode(false); // disable Mysql strict errors for when a field isn't defined below (can be caused when fields
are added later)

 
$name_of_uploaded_file =basename($_FILES['photo']['name']);

        
//get the file extension of the file
        $type_of_uploaded_file =substr($name_of_uploaded_file,strrpos($name_of_uploaded_file'.') + 1);
         
        
$size_of_uploaded_file =$_FILES["photo"]["size"]/1024;//size in KBs
            
        //copy the temp. uploaded file to attachdoc folder
        $upload_folder='photo/';
        
$path_of_uploaded_file '/home/users/web/b1587/ipw.peteralanportraits/public_html/' .$upload_folder .
$name_of_uploaded_file;
        
        
$tmp_path $_FILES["photo"]["tmp_name"];
        
// echo $path_of_uploaded_file; 
        if(is_uploaded_file($tmp_path))
        {
        
copy($tmp_path,$path_of_uploaded_file);
          if(!
copy($tmp_path,$path_of_uploaded_file))
          {
            
$errors .= '\n error while copying the uploaded file';
          }
        }    
        

$photo=$path_of_uploaded_file;
$first_name1=$_POST['first_name'];
$last_name1=$_POST['last_name'];

}


Then to create the new record in the CMSB Table customer_uploads I added:


 mysql_query("INSERT INTO `{$TABLE_PREFIX}customer_uploads` SET
                                     
                      first_name                 = '".$first_name."',
                      last_name                 = '".$last_name1."',
                                                                   createdDate       = NOW(),
                                                                   updatedDate       = NOW(),
                                                                   createdByUserNum  = '0',
                                                                   updatedByUserNum  = '0'")
      or die(
"MySQL Error Creating Record:<br />\n"htmlspecialchars(mysql_error()) . "\n");
      
$userNum mysql_insert_id();
// image upload code will go here
$errors='Thank You <br /> Your Order Has Been Successfully Placed.';
}

?>
To get the image to upload Jason suggested that I add the following :

 or die("MySQL Error Creating Record: <br /> \n". htmlspecialchars(mysql_error()) . "\n");
      $userNum = mysql_insert_id();
// upload the image file
     @saveUploadFromFilepath('customer_uploads', 'uploads', $userNum, '123456789' , $path_of_uploaded_file); 
$errors='Thank You <br /> Your Order Has Been Successfully Placed.';
}

?>


When I tried  to use the same code to insert an image into an already existing record where an order code had been
entered by the system admin, it didn’t work.

Jason suggested the following:

In an update form add a field for an order_code


<form name="ordernow" enctype="multipart/form-data" action="ordernow2.php" method="post" >
<table width="100%" border="0" cellspacing="0" cellpadding="0" class="text_field">
<tr>
<td valign="top" class="title">Upload</td>
<td> <input type="file" name="photo"></td>
</tr>
<tr>
<td valign="top" class="title" height="30">Order Code:</td>
<td><input type="text" name="order_code">
</td>
</tr>
<tr>
<td>&nbsp;</td>
<td align="left" valign="top">
<input type="submit" value="Submit" name="submit"></td>
</tr>
</table>
</form>


Then in the update file that does the processing, now called ordernow2.php change the mysql_query("INSERT INTO code to:
 


//first load the customer_upload records
 list($customer_uploadsRecords, $customer_uploadsMetaData) = getRecords(array(
    'tableName'   => 'customer_uploads',
 ));

//Then update the records (limited by the WHERE clause further down)
mysql_query("UPDATE `{$TABLE_PREFIX}customer_uploads` SET
    
                                     first_name                 = '".$first_name."',
                      last_name                 = '".$last_name1."',
                      updatedDate       = NOW(),
                                                                   updatedByUserNum  = '0'

// limits records to only those where the order code entered matches the order code in the record
WHERE order_code = '".mysql_escape($order_code)."'")  

      or die("MySQL Error updating Record: <br /> \n". htmlspecialchars(mysql_error()) . "\n");
 
   $customerUploadsRecords = mysql_select("customer_uploads", "order_code = '".mysql_escape($order_code)."'"); 
   
  foreach ($customerUploadsRecords as $record) { 
    @saveUploadFromFilepath('customer_uploads', 'uploads', $record['num'], '123456789' , $path_of_uploaded_file); 
  }
$errors='Thank You <br /> Your Order Has Been Successfully Updated.';

}

?>


That worked just fine.


DISPLAYING IMAGE(S) FROM A MASTER IMAGE LIBRARY IN YOUR VIEWERS - Dec 29th, 2018

Here’s a way to use a master image library and not have to re-upload your images each time you want to use them.

It developed because user Deborah wanted to be able to select the specific logo(s) that were displayed on a set of
detail pages from a database of previously uploaded logo images.

After a little help from Chris Waddell from Interactive Tools, here’s what Deborah came up with. I hope you find it
useful.

SECTION EDITORS
For this example you’ll need to create a multi-record editor called “Logo Uploads” with a “Title” text field
for the name of the logo and an “Upload” field called “logos” for the logo images. 

You’ll also have to create a multi-record editor called “Programs” where you’ll create your “products”
records and decide which logos are to be shown on each product detail page.

In the “Programs” editor you’ll also need to create a multi value pull down (or checkbox) list field called
“logos”. 

For the “List Options” for this field, choose “Get options from database (advanced)”, Section Tablename:
“logo_uploads”, Option Values: “num”, and Option Labels “title”.

For the Viewer URLs > detail page URL, enter /logotestdetail.php

After you’ve uploaded some “logos” and created some “product” records it’s time create your test list and
detail viewers. 

VIEWERS
The list viewer will be called “logotestlist.php” and the detail viewer will be called “logotestdetail.php”

The list viewer is a standard viewer which you can create from the code generator for the “Programs” editor.

The detail viewer has a few modifications, but start with the standard detail viewer that’s generated by the code
generator, and then at the top of the viewer, add the following code after the get records call:



<?php //display logos selected from list in other table, note both tables have same field name of 'logo'
$numsTabbedList $programsRecord['logo'];
$numsTabbedList trim($numsTabbedList);
$numsCommaList str_replace("\t"","$numsTabbedList);
if (
$numsCommaList) {
list(
$logo_uploadsRecords,) = getRecords(array(
'tableName' => 'logo_uploads',
'where' => "num IN ($numsCommaList)",
'allowSearch' => '0',
));
}
else { 
$logo_uploadsRecords = array(); // empty array
}
?> 



And in the body of the viewer where you want your logos to appear, add this code (page styling is up to you):



<?php foreach($logo_uploadsRecords as $logo_uploadsRecord): ?> 
  <?php foreach ($logo_uploadsRecord['logo'] as $upload): //display logos selected from list in logo_uploads table ?> 
 <img src="<?php echo $upload['thumbUrlPath'?>" width="<?php echo $upload['thumbWidth'?>" height="<?php echo
$upload['thumbHeight'?>" alt="" /><?php endforeach ?>  
<?php endforeach ?>



You can download a set of ini.php files and a list and detail viewer from:

http://www.thecmsbcookbook.com/downloads/logos.zip

Sorry, you'll have to supply your own images.

** DON’T FORGET TO CHANGE THE “LOAD VIEWER LIBRARY - $dirsToCheck PATH IN THE VIEWERS TO YOUR SERVER PATH.

After you’ve got the example working, you can add any required fields and style the pages the way you’d like.  


DISPLAYING 'AVAILABLE COLORS" IMAGES ON A DETAIL PAGE IF COLOR IS CHECKED IN A LIST FIELD - Jul 25th, 2013

In a simple store scenario was trying to display images on a detail page for all colors that are checked as being
available for a particular item.

This was my approach:
 
First I created an array ($colors1) which contains a comma separated list of all of the colors that are available for
the item.

Next I uploaded images representing all of the available colors in a multi-image upload field and entered the color of
the image in the info1 field for each image.

Then in a foreach loop, I used the strpos function to determine if the info1 value exists in the $colors1 variable and
if it does, display that thumbnail image.

Problem was, the image corresponding to the first color in the $colors1 variable was always skipped, and I couldn’t
figure out why.

Greg Thomas from Interactive Tools postulated that it might be because the first item in the array doesn't have a
leading comma and space, and so as far as the strpos function is concerned it’s not an exact match. 

He suggested using the in_array function instead to detect if the color has been selected (was in the array) or not.
Yes, the order of the variables in the 2 functions are opposite from each other, but that’s just the way they need to
be to work.

Here’s the recipe that came out of the discussion:

<table align= “center”>
 <tr>
<td colspan="2">
<?php $colors1 join(', '$store_inventoryRecord['colors:labels']); ?>
<span class="navigation_font">Available Colors: <?php echo $colors1 ?></span><br />
</td>
</tr>
<tr>
<?php foreach ($store_inventoryRecord['image'] as $upload): ?>
<?php $info1 $upload['info1'?>
<?php // if(strpos($colors1, $info1)): // this doesn’t work?>
<?php if(in_array($info1,$store_inventoryRecord['colors:labels'])): // This works ?>
 <td align="center">
<span class="sub_heading_font"><?php echo $upload['info1'?></span><br />
<img src="<?php echo $upload['thumbUrlPath'?>" width="<?php echo $upload['thumbWidth'?>" height="<?php echo
$upload['thumbHeight'?>" alt="" />
</td>
<?php endif ?>
<?PHP $maxCols=2; if (@++$count $maxCols == 0): ?></tr><tr><?PHP endif; ?>
<?php endforeach ?>
</tr>
</table>


KEEP LEGAL CHARACTERS IN AN UPLOAD FILE NAME FROM BREAKING PAGE CODE - Mar 12th, 2015

I was using a background URL to show an image in a <div>

background: url(<?php echo $masterurl ?>/cmsAdmin/uploads/<?php echo $upload['thumbUrlPath2'?>);

and there were certain legal file name characters, like parentheses, that would break this code.

Claire Ryan, Interactive Tools programming Guru came to the rescue with this simple fix.

She said: 

Try single quoting the URL in the CSS, like this:

background: url('<?php echo $masterurl ?>/cmsAdmin/uploads/<?php echo $upload['thumbUrlPath2'?>');








SHOWING WHEN AN UPLOAD WAS CREATED - Mar 12th, 2015

Thanks to Damon Edis from Interactive Tools who reminded me that you can use:



 <?php echo date("M jS,  Y H:i T"strtotime($upload['createdTime'])) ?> 
\
to show the time and date when an upload was uploaded.

You can also use:


<?php showme($your_tableRecord['your_upload_field']); ?> 

to show all available variable and values in arrays.

WORKING WITH AUDIO



PLAYING AUDIO FILES ON A PAGE - Nov 18th, 2010

There are a number of choices here, just as with visual uploads. 

The simplest way to include mp3 or wave audio on your page is to place a tag on your page, like this.

A multi record example:



<?php foreach ($your_sectionRecords as $record): ?>
<?php foreach ($record['audio_1'] as $upload): ?>
<a href="<?php echo $upload['urlPath'?>" target="_blank">Hear my song</a>
 <?php endforeach ?>  <?php endforeach ?>



And a single record editor example:



<?php foreach ($your_sectionRecord['audio_1'] as $upload): ?>
<a href="<?php echo $upload['urlPath'?>" target="_blank">Hear my song</a>
<?php endforeach ?>



If you want an image to appear instead of, or along with your text, you would insert the appropriate image display code
like this:



<a href="<?php echo $upload['urlPath'?>" target="_blank">
<?php foreach ($record['your_image'] as $upload): ?>
<img src="<?php echo $upload['thumbUrlPath'?>" width="<?php echo $upload['thumbWidth'?>" height="<?php echo
$upload['thumbHeight'?>" alt="" /><br />
 <?php endforeach ?><br /><Hear my song</a>



If you wanted to  have an audio file play automatically  when the page loads and also embed a player control, you could
embed the following, with the autoplay values set to “true”. If you don’t want it to play automatically, set the
autoplay values to “false”.

For a multi record editor:



<?php foreach ($your_sectionRecords as $record): ?>
<?php foreach ($record['audio_1'] as $upload): ?>

<object width="300" height="42">
    <param name="src" value="<?php echo $upload['urlPath'?>">
    <param name="autoplay" value="true">
    <param name="controller" value="true">
    <param name="bgcolor" value="#FF9900">
  <embed src="<?php echo $upload['urlPath'?>" autostart="true" loop="false" width="300" height="42"  
controller="true" bgcolor="#FF9900"></embed>
</object>
<?php endforeach ?>
<?php endforeach ?>



For a single record editor:



<?php foreach ($your_editorRecord['audio_1'] as $upload): ?>

<object width="300" height="42">
    <param name="src" value="<?php echo $upload['urlPath'?>">
    <param name="autoplay" value="true">
    <param name="controller" value="true">
    <param name="bgcolor" value="#FF9900">
    <embed src="<?php echo $upload['urlPath'?>" autostart="true" loop="false" width="300" height="42"
controller="true" bgcolor="#FF9900"></embed>
    </object>
    <?php endforeach ?>



PLAYING A SOUND FILE ONCE A SESSION - Dec 31st, 2012

I recently had a request to play a greeting message only the first time a visitor accessed my client’s site. My client
was OK with the idea that the “first time” could be defined by opening the visitor’s browser. I wanted to avoid
using Javascript and stick with PHP, so here’s what I found. Hope it proves useful.

This code gets placed a the very head of your page, before the “require once” code.



<?php setcookie("firsttime","no"?>



And this code gets placed right after your <body> tag



<?php
$firsttime $_COOKIE[firsttime];
if (@
$firsttime != "no") { echo "<embed src='http://www.yoursite.com/yourgreeting.mp3' width=0 height=0 />"; }
?>



The width and height are set to “0" so that the player is invisible on the page.

But what if you wanted to play the sound once a day instead of once a session? The answer is in the next recipe.


PLAYING A SOUND FILE NO MORE THAN ONCE IN A SPECIFIED TIME PERIOD - Aug 3rd, 2010

This code gets placed a the very head of your page, before the “require once” code.



<?php 
$expire=time()+60*60*24;
setcookie("firsttime","no"$expire ); ?> 



In this example the duration is set to 1 Day  (60 seconds * 60 Minutes * 24 hours)

And this code gets placed right after your <body> tag:



<?php
$firsttime $_COOKIE[firsttime];
if (@
$firsttime != "no") { echo "<embed src='http://www.yoursite.com/yourgreeting.mp3' width=0 height=0 />"; }
?>


The width and height are set to “0" so that the player is invisible on the page.

IMAGES - THIRD PARTY INTEGRATION



A BETTER JQUERY SLIDE SHOW - Jul 19th, 2015

My client wanted to have more flexibility in their slide show, like the ability to display a second image along with the
main image. (In this case an image that compares the size of their artwork to the size of a person.

Greg Thomas from Interactive Tools suggested looking at the JQuery plugin Cycle2 at:

http://jquery.malsup.com/cycle2/ 

It proved to be a very versatile solution that was really easy to implement, is extremely well documented, and has
plenty of options and plugins on the downloads page.

There are only 2 files that need to be loaded in the head of your viewer (although plugin files are also needed for
advanced functions).
jquery.cycle2.min.js and http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.js 

Here’s my implementation for a captioned, fade in slide show with a second image called size_image that’s displayed
to the left of the caption information. Each image is linked to a detail page for that record called
‘homepageslidedetail.php’. NOTE: the preg_replace makes sure that there are no extra double quotes hanging around to
muck up the works.

I’m using a multi-record editor called ‘portfolio_images’ with 2 upload fields called ‘image’ and ‘size
image’ along with text fields for ‘title’ and ‘size_of_work and other information.

At the very top of the viewer

<?php

 require_once "/path_to_your_server/cmsAdmin/lib/viewer_functions.php";
 
  
// load records from 'portfolio_images'
  list($portfolio_imagesRecords$portfolio_imagesMetaData) = getRecords(array(
    
'tableName'   => 'portfolio_images',
   
'loadUploads' => true,
   
'loadUploads' => true,
    
'allowSearch' => false
  ));

In the Head section of the viewer: (my .js files are in a folder called Scripts)

<script src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.js"></script>
<
script src="Scripts/jquery.cycle2.min.js"></script>
 

And in the Body of the  viewer:

 <?php foreach ($portfolio_imagesRecords as $record): ?>
    <div align="center">
       <a href="homepageslidedetail.php?<?php echo $record['num']?>">
       <?php foreach ($record['image'] as $index => $upload): ?> <img src="<?php echo
htmlencode($upload['thumbUrlPath2']) ?><?php endforeach ?>" >
    </a>
        <div class="cycle-caption">
        
        <table align="center" width="60%" border="0" cellspacing="0" style="border:hidden">
  <tr>
   <td class="text_font" align="left" valign="middle" style="padding-left:15px;">
  <?php foreach ($record['size_image'] as $index => $upload2): ?><img src="<?php echo
htmlencode($upload2['thumbUrlPath']) ?>" /><?php endforeach ?></td>
   <td class="text_font" align="left" valign="top"><?php $record['title'] = preg_replace("/['\"]/","&acute;",
htmlencode($record['title']) ) ; 
 
$record['size_of_work'] = preg_replace("/['\"]/","&acute;"htmlencode($record['size_of_work']) ); ?>
 <?php echo strtoupper($record['title']) ?>
 <?php echo strtoupper($record['size_of_work']) ?>
 </td>
 </tr>
</table></div></div>
<?php endforeach ?>
    
    
</div>

      </td>
  </tr>
  <tr>
    <td width="60%" valign="top"  align="center" style="text-align:center;">
      <span class="heading_font" ><b>
     
      YOUR SHOW TITLE GOES HERE</b></span>
     </td>
  </tr>
  <tr>
    <td align="center" > <span class="text_font" >Click/Tap An Image To Learn More
      </span><div class="text_font" align="center" style="text-align:left; width:60%;"> 
        <?php echo $curated_show_description ?></div>
      
      </td>
  </tr>
</table>



 


JQUERY SLIDE SHOW PLUGIN - Dec 29th, 2018

Cross Slide is a jQuery plugin that, in only 2kB of Javascript code, creates some common slide-show animations
traditionally only available via Adobe Flash™ or other proprietary plugins. 

CrossSlide builds upon jQuery's animation facility, so it is as portable across browsers as jQuery itself.

Just a note, I've switched to using SLIDESHOW II - A JAVASCRIPT CLASS FOR MOOTOOLS, in another recipe in the Cookbook,
because I had issues with occasional flashing on initial page loading and found the order of JavaScript files to be a
bit finicky. It's possible that a later version of jquery has fixed these issues, but I found it easier to go with the
other approach.

The following is a recipe for implementing the simplest form of this slide show a static cross fade, but once you’ve
got that going, the more complex implementations are really easy and instructions can be found at:

http://tobia.github.com/CrossSlide/ 

You can download the required scripts, a sample viewer, and a copy of the Cross Slide web page from:

http://www.thecmsbcookbook.com/downloads/cross_slide.zip

In the head of your viewer, you’ll need the following:


<SCRIPT type="text/javascript" src="Scripts/jquery-1.5.min.js"></SCRIPT>
<SCRIPT type="text/javascript" src="Scripts/jquery.cross-slide.min.js"></SCRIPT>


For the demonstration viewer, I’ve added the only required css code (you must specify a fixed size for the window),
but you can add it to your external style sheet.


<style type="text/css">
    
#slideshow {
  width: 600px;
  height: 200px;
}

    </style>


Here’s the javascript that’s required for the html version (this also goes in the head of your viewer):

<script>
  $(function() {
    $('#slideshow').crossSlide({
      sleep: 2,
      fade: 1
    }, [
      { src: 'picture1.jpg' },
      { src: 'picture2.jpg' },
      { src: 'picture3.jpg' },
      { src: 'picture4.jpg' }
    ])
  });
</script>


And here’s the php implementation of that code for CMS Builder using an upload field called “images” with multiple
uploads allowed, in a single record editor called home_page_slide_show. You can learn more about removing trailing
commas in the recipe called “REMOVE-THE-TRAILING-COMMA-AFTER-LAST-IMAGE-IN-A-SERIES”


<script type="text/javascript">
     $(function() {
    $('#slideshow').crossSlide({
      sleep: 5,
      fade: .4
    }, [

<?php 
    $output ''
    foreach (
$home_page_slide_showRecord['images'] as $upload) { 
      
$output .= '{ src: ' '"' .$upload['thumbUrlPath']. '"' '}'','
    } 
    
$output rtrim($output,','); // remove trailing comma 
    print $output
?>

    ])
  });
</script>


Then in the body, where you want the viewer to be displayed, you’d insert the following with an optional loading image
or text to be displayed while the images are loading (I use the first image in the database as the loading image):


<div align="center" id="slideshow"><img src=”your_loading_image.jpg" /></div>


SLIDESHOW II - A JAVASCRIPT CLASS FOR MOOTOOLS - Dec 29th, 2018

Slideshow II, by Aeron Glemann, is a javascript class for Mootools 1.3..2  to stream and animate the presentation of
images on your website. Slideshow II is the result of many trials in code attempting to create a javascript class that
was lightweight, unobtrusive, a snap to setup (but also highly configurable), extendable and - built using the
javascript framework with the best effects - visually very impressive. Slideshow is open-source with an MIT-style
license.

I've used it on a number of my web sites to replace Flash shows, and it's proved pretty easy to implement and very
flexible.

NOTE: The code below has been updated to handle some (IE 8+) browser issues and updated Mootools files.

After you've looked over some of the examples at:

http://www.electricprism.com/aeron/slideshow/

You can download the required scripts (probably not the latest versions), and a sample viewer from:

http://www.thecmsbcookbook.com/downloads/glemann-slideshow.zip

(Once you've got the basic installation working, you can download the latest files from the link below.)

To set up a slide show:

Create a single record editor called "Slides" with one upload field called images, and set the number of images to the
maximum you'd like to allow for your slide show.

For this example, We'll be setting up a simple cross fade show with images with an aspect ration of 1:2

Set thumbnail2 size to px 250 wide and 500 px high

** Download the latest version of SlideShow II  from:

http://code.google.com/p/slideshow/

Upload some sample images to your upload field and save the record. best if they are in the aspect ratio of the
thumbnail size, but you can use the sample images in the image folder of the zipped file.

Unzip the Slideshow files.

In the slideshow.css file, change the width and height in the .slideshow and .slideshow-images entries to match the
(maximum) size of your slideshow images. (use 250 and 500 for this example)

Upload the slideshow.css file to your css folder (the code below assumes a folder in your site root called css)

Upload the javascripts to your Scripts folder (the code below assumes a folder in your site root called Scripts)

For your test viewer use the following code at the top of your page.

You'll need to create an editor (single record, or multi record limited to one record) called "slides" with an image
upload field that accommodates between 6 and 12 slides 


<?php header('Content-type: text/html; charset=utf-8'); ?>
<?php
  

  // load viewer library
  $libraryPath 'cmsAdmin/lib/viewer_functions.php';
  
$dirsToCheck = array('/hsphere/local/home/c323748/your_site.com/','','../','../../','../../../');
  foreach (
$dirsToCheck as $dir) { if (@include_once("$dir$libraryPath")) { break; }}
  if (!
function_exists('getRecords')) { die("Couldn't load viewer library, check filepath in sourcecode."); }

  
// load records
  list($slidesRecords$slidesMetaData) = getRecords(array(
    
'tableName'   => 'slides',
  
'where'       => whereRecordNumberInUrl(1),
    
'limit'       => '1',

  ));
  
$slidesRecord = @$slidesRecords[0]; // get first record

  ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">


And this code in the head section of your page. 

NOTE: Most of the variables are now handled in the code in the head section with numbers and true/false entries. 

Duration is the cross fade duration
Delay is the amount of time the slide stays up before the cross fade
If your images are not exactly the same size and aspect ratio, setting the overlap to overlap: false, This will prevent
the slide edges from lingering outside of the new slide during the fade.
If you want to caption the slides in the show, you can use one of the upload info fields for the caption (n this example
I used info1), and change the captions variable to captions: true, 
The hu variable is left blank so that the path to your uploads is correctly parsed.
There's more in the wiki docs for this slideshow.

** Note that the use of an apostrophe or a quote mark in a caption will break the slide show javascript $output
variable.

The workaround is to insert the preg_replace in the foreach loop to change these characters to an acute accent mark.
That looks similar enough to pass and won't break the code.


<head>

<title>Slide Show Test</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />

<link rel="stylesheet" href="css/slideshow.css">
    <script src="Scripts/mootools-1.3.2-core.js"></script>
    <script src="Scripts/mootools-1.3.2.1-more.js"></script>
    <script src="Scripts/slideshow.js"></script>
       <script>
        window.addEvent('domready', function(){
                
            var data = { 
<?php 
    $output ''

//  shuffle is optional
//  shuffle($slidesRecord['images']) ;

    foreach ($slidesRecord['images'] as $upload)

     { 
 
$upload['info1'] = preg_replace("/['\"]/","&acute;"$upload['info1'] ); 
     
$info1 $upload['info1'];
      
$output .= "'"$upload['thumbUrlPath2']. "'"": { caption: " ."'$info1'"." }" ","
    } 
    
$output rtrim($output,","); // remove trailing comma 
    print $output
?>
};
            
new Slideshow('show', data, { captions: false, controller: false, thumbnails: false, overlap: true, delay: 5000,
duration: 200, height: 250, hu: '', width: 500 });
            
        });
    </script> 

</head>


Then in the body where you'd like the slide show to appear, insert this code:



 <div  id="show" class="slideshow"></div> 


If there's only one image uploaded and you'd rather show that as a still image, use this code in the body instead:



?php if (@$slidesRecord['images'][1]): ?>  
    
    <div  id="show" class="slideshow"></div> 
    <?php else : ?>
    <?php foreach ($slidesRecord['images'] as $upload): ?>
        
            <img src="<?php echo $upload['thumbUrlPath2'?>" width="<?php echo $upload['thumbWidth2'?>" height="<?php
echo $upload['thumbHeight2'?>" alt="" /><br />

        <?php endforeach ?>
    <?php endif ?>


After you've got this simple example working, you can experiment with some of the more complex implementations in the
sample index.html file.

If you're trying to get attributes like target="blank" to work, you'll need to add an href tag in your slideshow <div>
like this:



<div  align="center"  id="top" class="slideshow"><a href="#" target="_blank"></a> </div>


Note that the html attribute 'target="_blank"' for links is no longer valid code as of XHTML Strict 1.0. 

The code below, thanks to a post on http://snipplr.com/view/30551/ when added to your viewer page above the existing 
window.addEvent code, will run using mootools on domready to send all clicks to new windows when going to an external
domain.


<script>
   window.addEvent('domready', function() {
    // modified from http://joesong.com/2009/11/external-links-in-new-window-passive-and-with-mootools/
    // to avoid using the 'target' attribute, which is not part of xhtml 1.0 strict
    var currentDomain = window.location.host;
    $(document.body).addEvent('click', function(evt) {
    var target = $(evt.target);
    if (target.get('tag') !== 'a') {
    target = target.getParent();
    }
     
    if (target && target.get('tag') === 'a' && target.get('href').test('http') &&
!target.get('href').test(currentDomain)) {
    window.open(target.get('href'), '_blank');
    return false;
    }
    });
    });
</script>


And here’s an example of a slide show, with links and captions, that uses the image and name fields in the existing
records of a multi-record section.

In the head:


<script type="text/javascript">
        window.addEvent('domready', function(){
            
            
            var data = { <?php $blank "'" ?>
            <?php 
    $output ''
    foreach (
$servicesRecords as $record) {  foreach ($record['image'] as $upload){
         
$record['title'] = preg_replace("/['\"]/","&acute;"htmlencode($record['title']) );
          
     
$output .= "'"$common_informationRecord['master_url'] . $upload['thumbUrlPath3']. "'"": {caption: "
."'".strtoupper($record['title']) ."'" .","."href:"."'" ."servicesdetail.php?".$record['num']."'" ." }"  ",";  
    } }
    
$output rtrim($output,","); // remove trailing comma 
    print $output
?>
};

            new Slideshow('show', data, { captions: true, resize: false, controller: false, thumbnails: false, overlap: true,
delay: 5500, duration: 700, height: 360, hu: '', width: 480 });
            
        });
    </script>


And in the body where you want the slide show to display:
Note the fixed height of the div to allow for the caption and the styling of the caption text.
 

<div style="height:375px;" id="show" class="slideshow text_font"></div> 


USING SLIDESHOW II WHILE REDUCING SERVER LOAD - Dec 29th, 2018

I was using Slideshow II to display 2 independently shuffled banner rotators per page (1 at the top and 1 at the bottom)
on a site that had thousands of accounts, and looping through all of those records (twice) was slowing page loading time
down considerably.

Dave Edis, Senior Developer from Interactive Tools suggested using a MySQL call to pull the images from the table
instead and we came up with the following solution:

NOTE: There are 3 fields 'top_rotator_duration',  'bottom_rotator_duration'  and 'cross_fade_duration'  in a single
record editor (common_information) that control the length of time that each banner appears on the screen and the length
of the cross fade between images.

I'm using the image's info3 field to hold the URL for the site that the images are linked to. 

AT THE TOP OF YOUR PAGE:

<?php // load uploads from accounts
   $uploadRecords mysql_select('uploads'" tableName = 'accounts' AND fieldName = 'large_banners' ");
?>


IN THE HEAD SECTION OF YOUR PAGE

<script>
        
        window.addEvent('domready', function() {
    // modified from http://joesong.com/2009/11/external-links-in-new-window-passive-and-with-mootools/
    // to avoid using the 'target' attribute, which is not part of xhtml 1.0 strict
    var currentDomain = window.location.host;
    $(document.body).addEvent('click', function(evt) {
    var target = $(evt.target);
    if (target.get('tag') !== 'a') {
    target = target.getParent();
    }
     
    if (target && target.get('tag') === 'a' && target.get('href').test('http') &&
!target.get('href').test(currentDomain)) {
    window.open(target.get('href'), '_blank');
    return false;
    }
    });
    });    
        window.addEvent('domready', function(){
        var data = { <?php $blank "'" ?><?php $output ''
 
?>

<?php shuffle ($uploadRecords?>
<?php foreach ($uploadRecords as $upload): ?>
 <?php $output .= "'""/cmsAdmin/uploads/".$upload['urlPath']. "'"": { caption: " ."'4'".  ",""href:" "'" .
$upload['info3'] . "'" " }" ","?>

<?php endforeach ?>
<?php $output rtrim($output,","); // remove trailing comma 
print $output
?>
};
new Slideshow('top', data, { captions: false, controller: false, thumbnails: false, overlap: false, delay: <?php echo
$common_informationRecord['top_rotator_duration']?>, duration: <?php echo
$common_informationRecord['cross_fade_duration']?>, height: 100, hu: '', width: 668 });
            
});
    </script>

<script>
        window.addEvent('domready', function(){
            var data2 = { <?php $blank "'" ?><?php $output '';
 
?>

<?php shuffle ($uploadRecords?>
<?php foreach ($uploadRecords as $upload): ?>
 <?php $output .= "'""/cmsAdmin/uploads/".$upload['urlPath']. "'"": { caption: " ."'4'".  ",""href:" "'" .
$upload['info3'] . "'" " }" ","?>

<?php endforeach ?>
<?php $output rtrim($output,","); // remove trailing comma
print $output;
?>
};
new Slideshow('bottom', data2, { captions: false, controller: false, thumbnails: false, overlap: false, delay: <?php
echo $common_informationRecord['bottom_rotator_duration']?>, duration: <?php echo
$common_informationRecord['cross_fade_duration']?>, height: 100, hu: '', width: 668 });
            
});
    </script>

WHERE YOU WANT YOUR BANNER ROTATORS TO APPEAR ON YOUR PAGE:

Top rotator

 <div align="center" id="top" class="slideshow"></div>

Bottom Rotator

 <div align="center" id="bottom" class="slideshow"></div>
 


REMOVE THE TRAILING COMMA AFTER LAST IMAGE IN A SERIES - Feb 5th, 2011

When re-purposing code for various image display applications to work with CMS Builder, you’ll need to remove the
trailing comma from the last entry in your image list so the application will work in IE.

Here’s one way to do it, using the rtrim function.

Let’s say that your code requires the following syntax for your list:

{ src: 'cmsAdmin/uploads/thumb/1_Detail_10.jpg' },
{ src: 'cmsAdmin/uploads/thumb/2_Detail_4.jpg'   },
{ src: 'cmsAdmin/uploads/thumb/3_Detail_3.jpg'  },
{ src: 'cmsAdmin/uploads/thumb/4_Detail_5.jpg' }


Just using the following would put a comma at the end of every entry, including the last one.


 <?php foreach ($home_page_slide_showRecord['images'] as $upload): ?>
                  { src: '<?php echo $upload['thumbUrlPath'?> ' },<?php endforeach ?> 


Instead, try something like this:


<?php  $output ''?>

<?php foreach ($home_page_slide_showRecord['images'] as $upload)  { 
      
$output .= '{ src: ' '"' .$upload['thumbUrlPath']. '"' '}'',' 
    } 
?>

   <?php  $output rtrim($output,','); // remove trailing comma ?>

   <?php print $output?>


It looks complicated, but it’s not. Here’s how it works, so you can modify the code to suit your particular needs.

The <?php  $output ''?> code clears the variable $output.

Then the $output variable  $output .= '{ src: ' . '"' .$upload['thumbUrlPath']. '"' . '}'. ',' ;  work like this.

The “.” (period) and the ‘ (single quote) before any character or group of characters, followed by another ‘
(single quote) says, “append these characters to the string you’re creating”. 

So in this example, the $output variable would take the {scr: and the space following it,  append a ” (double quote)
to it, then append an image url path to that, then append another “ (double quote) to that, then append a } (right
brace) and finally append a ‘ (comma) to complete the string. This would loop through the images and add the above to
the string for each one.

The next line:


<?php  $output rtrim($output,','); ?>


Is where the magic happens. It says, look at the string created for the $output variable and if the very last character
is a comma, remove it.

Then  

<?php print $output?>


prints the final code to your page.


 





IMAGEMENU IMAGE SLIDERS AND CMSB - Dec 29th, 2018

The sliding menus created with one of the many free versions of Image Menu can create an interesting list page for
displaying albums of images.

The phatfusion version requires at least mootools V 1.1

You can find the latest phatfusion version of the script and all the required plugins at:

http://phatfusion.net/imagemenu/

And there’s a jquery version available at:

http://www.alohatechsupport.net/webdesignmaui/maui-web-site-design/create_image_menu_with_jquery.html

You can read more about how they work and the options available on the respective sites.

This recipe will focus on how to adapt these scripts to work with CMS Builder I’m using the Phatfusion version, but
the changes are pretty much the same for the jquery version.

You can download a copy of all the files used in the recipe, including a copy of the layered Photoshop document used to
create the labeled sliding images, from:

http://www.thecmsbcookbook.com/downloads/imagemenu.zip

You can see a working example of sliding menus at:

The first step in adapting Image Menu to CMSB is to create 6 image upload fields in a single record editor (the editor
in this example is called “sliders” and the fields are called slider_1 through slider_6. The thumbnail size is set
for 320px wide and 300 px high.)

You can create your “sliders” editor using the sliders.ini.php file included in the zip file. Just copy it to your
cmsAdmin>data>schemaPresets folder and then select it from the “select presets” menu when creating your new editor. 


Here’s the code required in the head of your sliding_images.php viewer:


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Welcome to My Photography Web Site</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<link rel="stylesheet" type="text/css" href="_common/css/imageMenu.php" />

<script type="text/javascript" src="_common/scripts/mootools.js"></script>
<script type="text/javascript" src="_common/scripts/imageMenu.js"></script>

</head>
<body>
    
<div id="container">
<div id="imageMenu">
<ul>
        
        <li class="slider_1"><a href="gallery1.php"></a></li>
        <li class="slider_2"><a href="gallery2.php"></a></li>
        <li class="slider_3"><a href="gallery3.php"></a></li>
        <li class="slider_4"><a href="gallery4.php"></a></li>
        <li class="slider_5"><a href="gallery5.php"></a></li>   
        <li class="slider_6"><a href="gallery6.php"></a></li>
</ul>
</div>
<script type="text/javascript">
        
        window.addEvent('domready', function(){
            var myMenu = new ImageMenu($$('#imageMenu a'),{openWidth:310, border:2, onOpen:function(e,i){alert(e);}});
        });
    </script>
<div class="clear"></div>
    
     
</div></body></html>


The other customized file is the _common/css/imageMenu.php file (originally the imageMenu.css file) (Don’t forget to
change the path to your viewer_functions.php to match your server).

Here’s the code:


<?php header('Content-type: text/html; charset=utf-8'); ?>
<?php
  
  require_once "/path_to_your/cmsAdmin/lib/viewer_functions.php";

  list(
$slidersRecords$slidersMetaData) = getRecords(array(
    
'tableName'   => 'sliders',
    
'where'       => whereRecordNumberInUrl(1),
    
'limit'       => '1',
  ));
  
$slidersRecord = @$slidersRecords[0]; // get first record

  // show error message if no matching record is found
  if (!$slidersRecord) {
    print 
"Record not found!";
    exit;
  }

?>
<?php 
header("Content-type: text/css");
?>

#imageMenu {
    position: relative;
    left:50px; 
    width: 800px; 
    height: 300px;
    overflow: hidden;
}

#imageMenu ul {
    list-style: none;
    margin: 0px;
    display: block;
    height: 300px;
    width: 1000px;
    
    }

#imageMenu ul li {
    float: left;
}

#imageMenu ul li a {
    text-indent: -1000px;
    background:#FFFFFF none repeat scroll 0%;
    border-right: 2px solid #fff;
    cursor:pointer;
    display:block;
    overflow:hidden;
    width:135px;
    height: 300px;
}

#imageMenu ul li.slider_1 a {
    background: url(<?php foreach ($slidersRecord['slider_1'] as $upload): ?>
<?php echo $upload['thumbUrlPath2'?><?php endforeach ?>) repeat scroll 0%; 
}

#imageMenu ul li.slider_2 a {
    background: url(<?php foreach ($slidersRecord['slider_2'] as $upload): ?>
<?php echo $upload['thumbUrlPath2'?><?php endforeach ?>
) repeat scroll 0%;
}
#imageMenu ul li.slider_3 a {
    background: url(<?php foreach ($slidersRecord['slider_3'] as $upload): ?>
<?php echo $upload['thumbUrlPath2'?><?php endforeach ?>) repeat scroll 0%;
}
#imageMenu ul li.slider_4 a {
    background: url(<?php foreach ($slidersRecord['slider_4'] as $upload): ?>
<?php echo $upload['thumbUrlPath2'?><?php endforeach ?>) repeat scroll 0%;
}

#imageMenu ul li.slider_5 a {
    background: url(<?php foreach ($slidersRecord['slider_5'] as $upload): ?>
<?php echo $upload['thumbUrlPath2'?><?php endforeach ?>) repeat scroll 0%;
}

#imageMenu ul li.slider_6 a {
    background: url(<?php foreach ($slidersRecord['slider_6'] as $upload): ?>
<?php echo $upload['thumbUrlPath2'?><?php endforeach ?>) repeat scroll 0%;
        border-right: 2px solid #fff;

}

.clear {
    clear: both;
}


USING DYNAMIC DRIVE IMAGE THUMBNAIL VIEWER II - Dec 29th, 2018

NOTE:
Since the concept of rollovers doesn't exist on tablets and smart phones, you might want to look at the recipe called 
DETECTING-MOBILE-PHONES-AND-TABLETS at: 
http://www.thecmsbcookbook.com/recipedetail.php?354
to switch to another image viewer format when those are detected.

Thanks to John Scheuer, the author of Dynamic Drive’s Thumbnail Viewer II, there’s a totally revised version for
2010 including the use of jquery.

You can learn more about the new version at: 

http://www.dynamicdrive.com/dynamicindex4/thumbnail2.htm

and learn about some of the revisions that have been made at:

http://www.dynamicdrive.com/forums/showthread.php?t=32036

This recipe revision focuses on implementation of Thumbnail Viewer II in CMS Builder viewers.

You can download a version of the script including all revisions to date, and a sample viewer at:

http://www.thecmsbcookbook.com/downloads/thumbnailviewer2-2010.zip

As part of the revision process, John also included a number of “switches” that make implementation much easier.  
They include: 
trigger:'mouseover', (click) show enlarged image by clicking on a thumbnail
preload:'yes', (no) don't preload enlarged images
fx:'fade', (reveal (top to bottom) or none) changes the animation 
fxduration:500, (animation time in ms)
enabletitle:'yes', (no) hides titles
descriptiontop:'no', (yes) displays titles above enlarged image
hidetitle:'no'}, (yes) hides individual title
link: (optional) Adds links to your images (followed by the URL) Note: use complete URL’s

You can add a link to the enlarged image by adding it to the targetdiv code in you viewer. Just change this:


rev="targetdiv:loadarea">


to this:


rev="targetdiv:loadarea,link:http://www.your_site.com/artworkadetail.php?<php echo $record['num'] ?>">
or
rev="targetdiv:loadarea,link:http://www.yoursite.com<?php echo $record['_link'?>">


You can also override any of the default settings by adding the new value to the targetdiv.


rev="targetdiv:loadarea, descriptiontop:no'">


Which would show the titles at the bottom of the enlarged image no matter what the default was set to.

NOTE: The script doesn’t deal with linking the thumbnails, but there’s a fix below.

THE RECIPE
The example below assumes that you’ve created a multi-record editor with an upload field for the image called
“image” (thumbnail is the thumbnail image and thumbnail3 is the enlarged image) and a text field called “title”
for the image title, and that you’ve uploaded some sample records. The approach is similar for a single record editor
with multiple images in an upload field.  

The sample viewer is coded to show six columns of thumbnails, to show the image titles at the top of the enlarged image,
to hide the title “tooltips” that appear over the thumbnails, to preload the images that will be utilized, and to
show the first image as the enlarged image on page load.

Once you’ve got the basic setup working, you can expand the fields to include any other information that you’d like
to appear in the title area or on the viewer.

THE CODE
At the top of your viewer, you’ll need to load the viewer_functions.php and load the records for your table, like this
(don’t forget to change the path and table name).

IF ALL OF YOUR IMAGES ARE IN UPLOAD FIELDS IN SEPARATE RECORDS (ONE UPLOADED IMAGE PER RECORD):


<?php header('Content-type: text/html; charset=utf-8'); ?>
<?php
  
$libraryPath 'cmsAdmin/lib/viewer_functions.php';
  
$dirsToCheck = array('/path_to_your_server/','','../','../../','../../../');
  foreach (
$dirsToCheck as $dir) { if (@include_once("$dir$libraryPath")) { break; }}
  if (!
function_exists('getRecords')) { die("Couldn't load viewer library, check filepath in sourcecode."); }
   
  
// load records
  list($your_tableRecords$your_tableMetaData) = getRecords(array(
    
'tableName'   => 'your_table',
?>

 ));

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">


AND IF ALL OF YOUR IMAGES ARE IN A SINGLE UPLOAD FIELD IN A SINGLE RECORD EDITOR:

<?php header('Content-type: text/html; charset=utf-8'); ?>
<?php
  
  require_once "path_to_your/cmsAdmin/lib/viewer_functions.php";
   
  
// load records
  list($your_tableRecords$your_tableMetaData) = getRecords(array(
    
'tableName'   => 'your_table',
     
'where'       => whereRecordNumberInUrl(1),
    
'limit'       => '1',
    
  ));
  
$your_tableRecord = @$your_tableRecords[0]; // get first record
  ?>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">


In the head of your viewers you’ll need to call the jquery and the js, like this:


<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>

<script type="text/javascript" src="Scripts/thumbnailviewer2010-1.js"></script>


If you want to make the thumbnails clickable, you’ll also need to add this script to the head of your viewer below the
thumbnailviewer2010-1.js call:


<script type="text/javascript">
jQuery(function($){
    $('a[rel=enlargeimage]').click(function(e){
        e.preventDefault();
        var rev = $(this).attr('rev').split(','), link;
        $.each(rev, function(){
            if($.trim(this).indexOf('link') === 0){
                location.href = this.substring(5);
            }
        });
    });
});
</script>


Then in the body of the viewer where you want the thumbnails to be displayed:

(Note the use of &quot; to replace quotation marks). 

John suggested that you might want to use a full URL in the targetdiv link (ie:
rev="targetdiv:loadarea,link:http://www.yoursite.com<?php echo $record['_link'?>">)  for some browsers, but I didn’t
need it for IE 7 & 8, FireFox 3+, Safari, or Chrome.

IF ALL OF YOUR IMAGES ARE IN UPLOAD FIELDS IN SEPARATE RECORDS (ONE UPLOADED IMAGE PER RECORD):


<table cellpadding="5">
<tr>
<?php foreach ($your_tableRecords as $record): ?>
<?php foreach ($record['image'] as $upload): ?>
<td width="50%" align="center" valign="top"  >
<a title="<span class=&quot;your_css_class&quot;><?php echo $record['title']; ?><br /></span>"href="<?php echo
$upload['thumbUrlPath3'?>"rel="enlargeimage" rev="targetdiv:loadarea,link:<?php echo $record['_link'?>">
<img src="<?php echo $upload['thumbUrlPath'?>" alt="" width="<?php echo $upload['thumbWidth'?>" height="<?php echo
$upload['thumbHeight']?>" border="0" style="margin-bottom: 5px" /> </a>
                      
<br />

<div valign="bottom" align="left" ></div>
 <?php $maxCols=6; if (@++$count $maxCols == 0): ?>
</tr>
 <tr>
 <?php endif; ?>
 <?php endforeach ?>
<?php endforeach; ?>

</tr>
</table>

        
And where you want to display the enlarged image:


<div align="center" id="loadarea"  style="width: 450px; height: 600px;">

 <!– this code displays the first image on page load –> 
<?php foreach ($your_tableRecords as $record): ?>
<?php foreach ($record['image'] as $upload): ?>
<span class="your_css_class"><?php echo $record['your_title_field'?></span>
<br /> <br /> <a href="<?php echo $record['_link'?>"><img src="<?php echo $upload['thumbUrlPath3'?>" alt=""
border="0" /></a><?php break; ?><?php endforeach ?><?php break; ?>
            
<?php endforeach ?></div> 


AND IF ALL OF YOUR IMAGES ARE IN A SINGLE UPLOAD FIELD IN A MULTI RECORD EDITOR:

In this part of the recipe, I've added the ability to define the maximum amount of columns and images per page of
thumbnails.

So, at the top of your viewer:


<?php header('Content-type: text/html; charset=utf-8'); ?>
<?php

  $libraryPath 'cmsAdmin/lib/viewer_functions.php';
  
$dirsToCheck = array('/path_to_your_server/','','../','../../','../../../');
  foreach (
$dirsToCheck as $dir) { if (@include_once("$dir$libraryPath")) { break; }}
  if (!
function_exists('getRecords')) { die("Couldn't load viewer library, check filepath in sourcecode."); }
 
 list(
$photo_galleriesRecords$photo_galleriesMetaData) = getRecords(array(
    
'tableName'   => 'photo_galleries',
    
'where'       => whereRecordNumberInUrl(0),
    
'loadUploads' => true,
    
'allowSearch' => false,
    
'limit'       => '1',
  ));
  
$photo_galleriesRecord = @$photo_galleriesRecords[0]; // get first record

?>


Then in the body of the viewer where you want the thumbnails to be displayed:

(Note the use of &quot; to replace quotation marks). 

John suggested that you might want to use a full URL in the targetdiv link (ie:
rev="targetdiv:loadarea,link:http://www.yoursite.com<?php echo $record['_link'?>">)  for some browsers, but I didn’t
need it for IE 7 & 8, FireFox 3+, Safari, or Chrome.


<table align="center" width="60%" border="0" cellpadding="5">
  <tr>
    <td colspan="2"><br /> 
      <p align="center" ><span class="page_title_font"><?php echo strtoupper($photo_galleriesRecord['title'])
?></span></p>
      <p align="center"><span class="page_sub_title_font"><?php echo $photo_galleriesRecord['sub_title'?></span></p>
      <p align="left"><span class="text_font"><?php echo $photo_galleriesRecord['description'?></span></p></td>
  </tr>
</table>
<table width="975" border="0" align="center" cellpadding="5">
  <tr>
    <td width="474" valign="top"><div align="center"><br /> 
        <?php if ($photo_galleriesRecord['images']): ?>
        <span class="page_sub_title_font"> Click/Tap for a Larger Image</span>
        <?php endif ?>
        <br /> 
        <br /> 
      </div></td>
  </tr>
</table>
<table width="950" border="0" align="center">
  <tr>
    <td valign="top"><?php
$photosPerPage 15// Change this to change the number of images per page
$photoPage = @$_REQUEST['photoPage'] ? $_REQUEST['photoPage'] - 0;
$firstIndex $photoPage $photosPerPage;
$lastIndex min($firstIndex $photosPerPagesizeof($photo_galleriesRecord['images'])) - 1;
?>
      <?php if ((ceil(sizeof($photo_galleriesRecord['images']) / $photosPerPage)) > 1): ?>
      <form action="?num=<?php echo $photo_galleriesRecord['num'?>" method="post">
        <?php if ($firstIndex 0): ?>
        <a href="?photoPage=<?php echo $photoPage?>&=<?php echo $photo_galleriesRecord['num'?>"><span
class="text_font">Click/Tap For Previous Page</span></a>&nbsp; &nbsp;
        <?php endif ?>
        <span class="text_font">Page
        <input type="text" name="photoPage" value="<?php echo($photoPage 1); ?>" class="text_font" style="width:
10px;" />
        of <?php echo(ceil(sizeof($photo_galleriesRecord['images']) / $photosPerPage)); ?></span>
        <?php if ($lastIndex sizeof($photo_galleriesRecord['images']) - 1): ?>
        <input type="submit" value="" />
        <a href="?photoPage=<?php echo $photoPage+2?>&=<?php echo $photo_galleriesRecord['num'?>"><span
class="text_font">Click/Tap For Next Page</span></a><br /> 
        <?php endif ?>
      </form>
      <?php endif ?>
      <br /> 
      <table border="0" cellspacing="3" cellpadding="3">
        <tr>
          <?php

if ($firstIndex sizeof($photo_galleriesRecord['images'])-|| $firstIndex 0) { $firstIndex 0$photoPage 0; }

foreach (
range($firstIndex$lastIndex) as $photoIndex):
@
$upload $photo_galleriesRecord['images'][$photoIndex]
?>
          <td align="center" valign="middle"><a title="<span class=&quot;text_font&quot;><br /> <?php echo
htmlspecialchars($upload['info1']); ?> <?php echo htmlspecialchars($upload['info2']); ?> <?php echo
htmlspecialchars($upload['info3']); ?><br /> </span>" href="<?php echo $upload['thumbUrlPath4'?>"

rel="enlargeimage" rev="targetdiv:loadarea," > <img src="<?php echo $upload['thumbUrlPath'?>" alt="" width="<?php echo
$upload['thumbWidth'?>" height="<?php echo $upload['thumbHeight']?>" border="0" style="margin: 5px" /> </a></td>
          <?PHP $col 4 // change this to change the number of thumbnail columns ?>
          <?php $maxCols=$col; if (@++$count $maxCols == 0): ?>
        </tr>
        <tr>
          <?php endif; ?>
          <?php endforeach ?>
        </tr>
      </table>
</td>
        </tr>
      </table>

        
And where you want to display the enlarged image:


<table border="0" cellpadding="5" cellspacing="0" width="25%">
        <tr>
          <td align="left" valign="top" ><div align="left">
            <div align="center" id="loadarea" style="width: 650px; height: 500px;">
              <?php $upload = @$photo_galleriesRecord['images'][$firstIndex?>
              <span class="text_font"><br /> 
              <?php echo htmlspecialchars($upload['info1']); ?> <?php echo htmlspecialchars($upload['info2']); ?> <?php
echo htmlspecialchars($upload['info3']); ?></span> <br /> 
              
              <img src="<?php echo $upload['thumbUrlPath4'?>" alt="" border="0" /> <br /> 
            </div></td>
        </tr>
      </table>


THE INFORMATION BELOW RELATES TO OLDER VERSIONS OF THE THUMBNAIL VIEWER

This interesting viewer’s instruction and example page is at:

       http://www.dynamicdrive.com/dynamicindex4/thumbnail2.htm

Their forum is at: 

        http://www.dynamicdrive.com/forums/forumdisplay.php?f=2

Just search for Thumbnail Viewer II

And you can download both the original and a revised version of the thumbnailviewer2.js here:

        http://www.thecmsbcookbook.com/downloads/thumbnailviewer2.zip

You can easily integrate the viewer with CMSB by using a single record - multi image upload ( I called the record
“photography” and the upload field “images”

I set up the input validation parameters as follows:

file extensions allowed (I limited mine to jpg for this application)
maximum uploads: 27 ( In my page layout I wanted to limit thumbnails to 9 rows of 3 columns)
Max upload size (Up to you, I limit images to 1000 KB)
Image resize: (My choice 600 pixels  wide x 800 pixels high)
Thumbnail 1: (100 x 100 to fit the format of my thumbnail page)
Thumbnail 2: (500 x 450 to fit the format of my single image details page)
Thumbnail 3: (I didn’t use it but you can)

CMSB allows for 5 “info” fields for each uploaded  image and I decided to use info1 as the Title of the image, and
info2" as the border style for the image (using the complete Border: style="border: 1px solid #000000" code)

Here’s the basic html version of the code needed for the Image Thumbnail Viewer, which would be inserted in each table
cell where a thumbnail was to appear.

(The enlarged images and thumbnails would have to be created externally and then uploaded to specific locations on your
server)


 <div align="center">                                        
<a href="http://www.yoursite.com/images/325/TR_orig_002.jpg" class="Image-Labels" 
title="the optional title you want to appear under the enlarged image"
rel="enlargeimage::mouseover" 
rev="loadarea" ><img border="0" src="http://www.yoursite.com/images/75/TR_orig_002.jpg" 
width="75" height="115" style="margin-bottom: 5px" /></a>
<br />
<span class="Small-Text"><a href="Bodyprint2.php">Learn More</a></span></div>


Where you want the enlarged image to appear, you’d use the following code:


 <div id="loadarea" class="Image-Labels" style="width: 500px; height: 450px;"> </div>


HERE’S THE CMSB IMPLEMENTATION OF THE SAME CODE:

(The $maxCols=3 is what determines the number of columns before a new row is created.)


<table>
<tr>
<?php foreach ($photographyRecords as $record): ?>
              <?php foreach ($record['image'] as $upload): ?>

<td align="center" valign="bottom" <?PHP echo $record['title'?> width="25%">
<a href="<?PHP echo $upload['thumbUrlPath2'?>"  class="Image-Labels" title=”<?PHP echo $upload['info1'?>”

rel="enlargeimage::mouseover" 

rev="loadarea" >

<img src="<?PHP echo $upload['thumbUrlPath'?>" border="0" width="<?PHP echo $upload['thumbWidth'?>" height="<?PHP
echo $upload['thumbHeight']?>" style="margin-bottom: 5px" />

</a>

<br /><div valign="bottom" align="center"><a href="<?PHP echo $upload['_link'?>">Learn More</a></div>

 <?PHP $maxCols=3; if (@++$count $maxCols == 0): ?></tr><tr><?PHP endif; ?> 
 <?PHP endforeach ?>    <?PHP endforeach ?>                      
</table>


Where you want the enlarged image to appear, you’d use the following code:


<div align="center" id="loadarea" class="Image-Labels"  style="width: 500px; height: 450px;">
   
  <?php foreach ($photographyRecords as $record): ?>
              <?php foreach ($record['image'] as $upload): ?>
              <img src="<?php echo $upload['thumbUrlPath2'?>" alt="" border="0" /><br /><br /><span
class="medium-bold"><?php echo $record['title'?></span><br /><span class="medium"><?php echo $record['size_of_work']
?></span><br />
           <br /><?php break; ?><?php endforeach ?><?php break; ?>
            
    <?php endforeach ?></div> 


KEEPING TOOLTIPS FROM POPPING UP AND OBSCURING YOUR THUMBNAILS

The problem with the above code is that when you hover over a thumbnail, a tooltip will pop up with all of your title
code, obscuring some of your thumbnails and confusing the visitor.

The tooltip gets it’s value from the image tag title=”your_title”, so in order to not see the tooltip, you’ll
have to use another attribute to pass your label information. 

To allow this, in the thumbnailviewer2.js file, search for: 


var description=(thumbnailviewer2.enableTitle && linkobj.getAttribute("title"))? linkobj.getAttribute("title") : ""
//Get title attr


And replace that line with:


var description=linkobj.rel.split('::')[2]? linkobj.rel.split('::')[2] : "";


Or you can download the revised script from the link at the top of this recipe.

This revision will allow you to piggyback onto the rel attribute and skip using the “title” tag.


<table cellpadding="5">
                <tr>
                  <?php foreach ($photographyRecords as $record): ?>
                  <?php foreach ($record['image'] as $upload): ?>
                  <td width="50%" align="left" valign="top"  >
                    <a href="#" name="<?php echo $upload['thumbUrlPath2'?>" 

rel="enlargeimage::mouseover::<br /><?php $title htmlspecialchars($record['title']); ?><span
class=&quot;medium-bold&quot;><?php echo ($title); ?></span><br /><?php $size_of_work =
htmlspecialchars($record['size_of_work']); ?><span class=&quot;medium&quot;><?php echo ($size_of_work);
?></span></span>"

rev="loadarea" > <img src="<?php echo $upload['thumbUrlPath'?>" alt=""  width="<?php echo $upload['thumbWidth'?>"
height="<?php echo $upload['thumbHeight']?>" border="0" style="margin-bottom: 5px" /> </a>
                    
                    <br />
                    
                    <div valign="bottom" align="left" ></div>
                  <?php $maxCols=3; if (@++$count $maxCols == 0): ?>                </tr>
                <tr>
                  <?php endif; ?>
                  <?PHP break ?><?php endforeach ?>
                  <?php endforeach; ?>
                  </tr>
              </table>


The same loadarea Div code would still be used.

The htmlspecialchar operator allows you to use double quotes and probably other special characters in your titles
without breaking the code. So text like 12" x 14"  would not cause problems. 

Note that you still have to use escape characters &quot; for other tags like class=&quot;your_class&quot;

IF YOU WANT THE FIRST ENLARGED IMAGE TO AUTOMATICALLY DISPLAY IN THE LOADAREA, JUST USE THIS CODE IN THE ENLARGED IMAGE
DISPLAY DIV


<div id="loadarea" class="Image-Labels" style="width: 400px; height: 500px;">

 <?php foreach ($photographyRecord['images'] as $upload): ?>

<img src="<?php echo $upload['urlPath'?>" alt="" border="0" />

<?php break; ?><?php endforeach ?></div> 


IF YOU’RE USING A MULTI RECORD EDITOR YOU’D HAVE TO CHANGE THE FOREACH LOOPS TO ACCOMMODATE THAT IN BOTH CODE BLOCKS


<table>
<tr>
<?php foreach ($photographyRecords as $record): ?><?php foreach ($record['images'] as $upload): ?>
<td align="center" valign="bottom" <?php echo $upload['info2'?>  width="25%">
<a href="<?php echo $upload['thumbUrlPath2'?>"  class="Image-Labels" 
rel="enlargeimage::mouseover:::&lt;br&gt;<?php echo $upload['info1'?>" rev="loadarea" >

<img src="<?php echo $upload['thumbUrlPath'?>" border="0" width="<?php echo 
$upload['thumbWidth'?>" height="<?php echo $upload['thumbHeight']?>" style="margin-bottom: 5px" /></a>
<br /><div valign="bottom" align="center" ><a href="<?php echo $record['_link'?>">Learn<br />More</a></div>

    <?php $maxCols=3; if (@++$count $maxCols == 0): ?></tr><tr><?php endif; ?> 

   <?php endforeach ?>    <?php endforeach; ?>

</table>


AND


<div id="loadarea" class="Image-Labels" style="width: 400px; height: 500px;">
 <?php foreach ($photographyRecords as $record): ?>
<?php foreach ($record['images'] as $upload): ?>

<img src="<?php echo $upload['urlPath'?>" alt="" border="0" />


<?php endforeach ?><?php break; ?><?php endforeach ?>
</div> 


MAKING THE LOADAREA IMAGE A LIVE LINK TO YOUR DETAIL PAGE

If you want your loadarea image (the large image) to be a live link to your detail page, it’s pretty simple.

In the loadarea <div> Replace the indicated code:


<?php foreach ($photographyRecords as $record): ?>
              <?php foreach ($record['image'] as $upload): ?>
<!-- Replace This Code -->
              <img src="<?php echo $upload['urlPath'?>" alt="" border="0" />
<!-- End Code Replace -->
<?php endforeach ?><?php break; ?><?php endforeach ?></div>


With this:


<?php foreach ($photographyRecords as $record): ?>
<?php foreach ($record['image'] as $upload): ?>
<!-- New Code -->
<a href="http://www.your_site.com/Your_detail_page.php?<?php echo $record['num'?>"><img src="<?php echo
$upload['urlPath'?>" alt="" border="0" /></a>
<!-- End New Code -->
<?php endforeach ?><?php break; ?><?php endforeach ?></div>



MAKING THE THUMBNAILS CLICKABLE AS WELL
This one was a little harder to figure out. It turns out that it required some changes to the thumbnailviewer2.js file.
You can download it from:

http://www.thecmsbcookbook.com/downloads/thumbnailviewer2revised.zip

Once I found the revised thumbnailviewer2.js it was again pretty simple to implement the changes.

In the Thumbnail code:


<?php foreach ($photographyRecords as $record): ?>
                  <?php foreach ($record['image'] as $upload): ?>
                  <td width="50%" align="center" valign="top" <?php echo $upload['info2'?>
                   <!-- Replace This Code -->
<a href="<?php echo $upload['thumbUrlPath2'?>"
<!-- End Code Replace -->
rel="enlargeimage::mouseover::  


Replace the indicated code with this code:


<?php foreach ($photographyRecords as $record): ?>
                  <?php foreach ($record['image'] as $upload): ?>
                  <td width="50%" align="center" valign="top"  >
                    <!-- New Code -->
<a href="<?php echo $record['_link'?>" name="<?php echo $upload['thumbUrlPath2'?>""
<!-- End New Code -->
rel="enlargeimage::mouseover:: 


That’s it, let your clients click away. 

ADDING A SECOND THUMBNAIL VIEWER TO YOUR DETAIL PAGE
I had a client who wanted to add alternate views of their artwork to the detail page for that work.

Adding the thumbnail viewer to the detail page was pretty straightforward. The biggest issue was to add <?php break; ?>
code to the list page and the detail page to keep from seeing a number of extra images.  Here’s how I did it.

On the first thumbnail viewer page I added a second <?php break; ?> to the loadarea just before the <?php endforeach ?>
like this:


<div id="loadarea" class="Image-Labels" style="width: 400px; height: 500px;">
 <?php foreach ($photographyRecords as $record): ?<
<?
php foreach ($record['images'] as $upload): ?>

<img src="<?php echo $upload['urlPath'?>" alt="" border="0" />


<?php break; ?><?php endforeach ?><?php break; ?><?php endforeach ?>
</div> 


and I added one <?php break; ?> in the loadarea of the second thumbnail viewer like this:


<div id="loadarea" class="Image-Labels" style="width: 400px; height: 500px;">
 <?php foreach ($photographyRecords as $record): ?<
<?
php foreach ($record['images'] as $upload): ?>

<img src="<?php echo $upload['urlPath'?>" alt="" border="0" />

<?php break; ?><?php endforeach ?><?php endforeach ?>
</div> 


INTEGRATING FANCY ZOOM SLIDE SHOW WITH CMSB - Aug 3rd, 2010

Thanks to Saginetic all his hard work

This tutorial will show you how to integrate a Gallery using FancyZoom 

        http://www.cabel.name/2008/02/fancyzoom-10.html  

to display thumbnails and transition to full size images when clicked.

You can use many different galleries of similar nature such as lightbox, highslide, etc... It's all pretty much the
same. Take the info you learn here and apply accordingly.

In CMSB:

1.Create a Multiple Entry Section Editor called Photos (database = photos)

2.Make the following fields
Album Name textfield album
Date date date
Upload upload pictures (place “Caption" in the info1 box)

3.Make some albums and upload some pictures

In HTML:

1.Download the FancyZoom package, right here:


       http://www.cabel.name/2008/02/fancyzoom-10.html

2.Using Transmit (or your favorite FTP client), upload the two folders inside the package to the root of your webserver.

3.Add the following two lines of code to the <head> section at the top of your web page(s):



<script src="/js-global/FancyZoom.js" type="text/javascript"></script> 
 
<script src="/js-global/FancyZoomHTML.js" type="text/javascript"></script>



4. Add onload="setupZoom()" inside your page's existing <body> tag. For example:



<body onload="setupZoom()">



5. Create a list viewer page (index.php) linking to each album.

6. Created links to images in your Details page (photos.php) will automatically zoom the images. For example:



<!-- STEP 2a: Display Uploads for field 'pictures'  --> 
    
 <?PHP foreach ($photosRecord['pictures'] as $upload): ?> 
           
<?PHP if ($upload['hasThumbnail']): ?> 
           
<a href="<?PHP echo $upload['urlPath'?>" title="<?PHP echo $upload['info1'?>"/> 
 
<img src="<?PHP echo $upload['urlPath'?>" width="100" border="0"/></a> 
 
<?PHP elseif ($upload['isImage']): ?> 
 
<a href="<?PHP echo $upload['urlPath'?>" title="<?PHP echo $upload['info1'?>"/> 
 
<img src="<?PHP echo $upload['urlPath'?>" width="100" border="0"/></a> 
 
<?PHP endif ?> 
<?PHP endforeach ?> 


The title tag in your href links to your caption (info1). The href for the thumbnails and full size image is the same
because you are not creating thumbnails. You are just setting the width of the preview image to 100 and it will zoom to
full size. You can use CMSB's thumbnail creation as well, just make sure you link the full-size image =
$upload['urlPath'] and not $upload['thumbUrlPath']

FancyZoom will use the size of the first element in the href to determine the initial size and location of the zoom.

FancyZoom works best if you wrap your href around a thumbnail, but also works from text-only links to images.

FancyZoom will attach itself to any jpg, gif, png, bmp, or tiff link in your page.

If you're a Javascript hacker, FancyZoom's flexible fadeIn and fadeOut functions can be used for all sorts of fun stuff.

If you explicitly don't want an image to zoom, add a rel="nozoom" tag to your href.

The complete post can be found here:

       http://www.interactivetools.com/forum/Products_C2/CMS_Builder_F35/gforum.cgi?post=64222


INTEGRATING LIGHTBOX WITH CMSB - Aug 3rd, 2010

Here’s the basic code that lightbox requires:


<?xml version="1.0" encoding="UTF-8" ?> 
<gallery> 
<album lgPath="album1/images/" tnPath="album1/thumb/" title="Album title" description="Album description"
tn="album1/preview.jpg"> 
<img src="1.jpg" title="" caption="" link="" target="_blank" pause="" /> 
<img src="1.jpg" title="" caption="" link="" target="_blank" pause="" /> 
<img src="1.jpg" title="" caption="" link="" target="_blank" pause="" /> 
<img src="1.jpg" title="" caption="" link="" target="_blank" pause="" /> 
<img src="1.jpg" title="" caption="" link="" target="_blank" pause="" /> 
</album> 
</gallery>



Where:



lgPath = path to the large version of the photos 
tnPath = path to the thumbnails (or preview photo in case of video) 
caption = this would show on mouseover of the image 
link = if the image is clicked it would follow this link 



The one thing CMS Builder doesn't do is put it's thumbnails in a different directory. It puts them in the same directory
and adds a _thumb extension. I've just used the main upload dir for both lgPath and tnPath. 

Here's some code for a page viewer. Ideally you'd have a list viewer that would list albums and then you'd click through
to this one to display an album. You could also direct link to a specific album. This one assumes you have some fields
defined called: title, and description. Also you'll need to change the Table name and the require_once path to match
your server. 



<?PHP   echo "<?xml version='1.0'  encoding='UTF-8'?>\n";
require_once 
"../lib/viewer_functions.php";
$options = array();   $options['Table name'] = 'news';
$options['recordNum'] = ''
$options['where']     = ''
$record getRecord($options); 
?>
<gallery>
<album lgPath="<?PHP echo $SETTINGS['uploadUrl']; ?>
    tnPath="<?PHP echo $SETTINGS['uploadUrl']; ?>"
    title="<?PHP echo htmlspecialchars($record['title']); ?>"
    description="<?PHP echo htmlspecialchars($record['description']); ?>"        tn="album1/preview.jpg">

<?PHP if ($record): ?>
<?PHP foreach (getUploads($options['Table name'], 'uploads'$record['num']) as $upload): ?>
<img src="<?PHP echo htmlspecialchars($upload['filename']); ?>" 
title="<?PHP echo htmlspecialchars($upload['info1']); ?>" 
caption="<?PHP echo htmlspecialchars($upload['info2']); ?>"
link="<?PHP echo $upload['urlPath'?>"
target="_blank"
pause="" />
<?PHP endforeach ?>
<?PHP endif ?>
</album>
</gallery>



SETTING UP LIGHTBOX OR MILKBOX TO SHOW IMAGES ON A DETAIL PAGE - Dec 29th, 2018

Revised for Milkbox version 3.0.3  http://reghellin.com/milkbox/ 
This new version works with IE 9 and keeps the titles from popping up as as tooltips when you rollover the thumbnails.
It also automatically re-sizes image to fir the browser window
 
The first thing that you’ll need to set up is a multi record editor with a single upload field and some text fields
for the information required for both the list page and details page. Mine was called “people”. I also wanted to
display the thumbnails in rows of 7 columns each that automatically extended to accommodate additional images.

On my list page there were the normal references to the Mootools and Milkbox files in the folder milkbox3.0.3.


<script src="milkbox3.0.3/js/mootools-core.js"></script>
     <script src="milkbox3.0.3/js/mootools-more.js"></script> 
    <script src="milkbox3.0.3/js/milkbox-yc.js"></script>
        <link rel="stylesheet" href="milkbox3.0.3/css/default.css" />
    <link rel="stylesheet" href="milkbox3.0.3/css/main.css" />
    <link rel="stylesheet" href="milkbox3.0.3/css/milkbox/milkbox.css.php" />


In the body, If there was a title, I included a link to a detail page in the title that appears on the full sized image
page that comes up after you click on the thumbnail. 

You can’t use actual "quotes ( " ) within the title code, but you can use the HTML entity &quot; in it’s place. With
that little fix, the titles are clickable and lead you to a detail page for that image.


<table border="0" id="gallery" cellspacing="0" cellpadding="10" align="center">
<tr valign="middle">
<?php foreach (peopleRecords as $record): ?><?php foreach ($record['image'] as $upload): ?>
                         <td align="center" valign="middle" style="border: 1px solid #ffffff"  width="15%">
<div align="center">

<a href="<?php echo $upload['thumbUrlPath3'?>"data-milkbox="milkbox:g1" title=" 
<?php if ($record['title']): ?><a href=&quot;#&quot;><h2><?php echo $record['title'?><br /><?php echo
$record['sub_title'?></h2></a><br /><?php endif ?>

<?php if (!$record['title']): ?><a href=&quot;<?php echo $record['_link'?>&quot;><h2>Click for more.</h2></a><?php
endif ?>

<img src="<?php echo $upload['thumbUrlPath'?>" width="<?php echo $upload['thumbWidth'?>" height="<?php echo
$upload['thumbHeight']?>" /></a>                                  

</div></td>
<?php $maxCols=7; if (@++$count $maxCols == 0): ?></tr><tr><?php endif; ?> 
<?php endforeach ?>
<?php endforeach ?>
</tr>
</table>


You can use other “if” statements to set the types of caption that appears and combinations of them on the details
page to show PayPal information if there’s a price entered for the item.

In main.css you'll find the values for:

Title Height and Color Properties
Thumbnail Border Size and Color Properties

To left justify the title text in Firefox 6, I added the following code to the h1, h2 properties in main.css:

alignment-adjust:auto;


NOTE: To make the enlarged image overlay on a blank or black page, at bottom of page, in the domready function, make
sure that overlayOpacity:1 is included. 

If there are other options, add that separated by a comma.

window.addEvent('domready', function(){ 
    milkbox = new Milkbox({ centered:true, overlayOpacity:1  });



NOTE: if you're going to change the enlarged image background color from white to something else (the property is in the
milkbox/css/milkbox.css.php file under #mbox-mainbox) you'll need to make the white background of the gifs transparent.

I've done that for play-pause.gif close.gif, prev.gif and next.gif and you can download them here:

http://www.thecmsbcookbook/downloads/milkbox_gifs.zip


SHOWING THE FIRST LIGHTBOX OR MILKBOX GALLERY IMAGE ON A LIST PAGE - Aug 3rd, 2010

CMSB users Nigel Gordijk and northernpenguin wanted to be able to show the first image from a lightbox gallery on a list
page of galleries.

I had suggested that  they add a single list page image upload field to their gallery record.

This would allow them to choose the "PR" image for their list page separate from the gallery images, so when a visitor
clicks on the gallery they wouldn’t immediately see the same image.

Flamerz suggested inserting the following inside the main foreach loop on the list page.



<?php if ($record['uploads'][0]['hasThumbnail']): ?>
<img src="<?php echo $record['uploads'][0]['thumbUrlPath'?>" width="<?php echo $record['uploads'][0]['thumbWidth'?>"
height="<?php echo $record['uploads'][0]['thumbHeight'?>" alt="" /><br />
<?php endif ?>



INTEGRATING SLIDE SHOW PRO WITH CMSB - Aug 3rd, 2010

Using Slide Show Pro with CMS Builder is relatively easy, but to utilize it effectively, you really need to have Adobe
Flash or access to the program. 

There are three components needed to integrate a SSP slide show into CMSB. A multi record section editor that allows
multiple images to be uploaded and that creates thumbnails of appropriates size for display in your slide show, an
image.xml.php file to control access to your CMSB on-line database, and an swf file with the ssp slide show and all your
required parameters set, and of course, the appropriate code to display the slide show on your web page.


THE SSP EXTENSION IN FLASH
SSP Installs as an extension in Flash. When creating a new Flash document, an instance of SSP can be embedded in the
document. At that point the look of your SSP slide show is controlled by the setting of a large number of variable
parameters. You can adjust these parameters in the Flash component inspector.

If you haven’t worked with Slide Show Pro before or you aren’t quite sure how to install the SSP extension, 
there’s a good “Quick Start” guide at

       http://wiki.slideshowpro.net/SSPfl/QuickStart 

that’s worth looking at. 

A few of the more important things that they mention are:

1) After launching Flash, select File > New. If you’re using Flash MX 2004 or Flash 8, select "Flash Document". If
you’re using Flash CS3 or CS4, select "Flash File (ActionScript 3.0)". If you’re using the ActionScript 3 version,
or "Flash File (ActionScript 2.0)" if you installed the ActionScript 2 version.. Click on Okay.

The next thing you should do is select Window > Properties and increase the frame rate of your movie to 31fps (the
default 12fps is not recommended). 

Next, click on the "Window" menu item at the top of Flash, and select "Development Panels > Components" if using Flash
MX 2004, or "Components" if using Flash 8 / Flash CS3/CS4. The "Components" panel will appear. Toggle the "SlideShowPro"
item open, then click, hold, and drag the component to the Stage (the large area underneath the timeline). An instance
of SlideShowPro for Flash will appear.

Click on the component instance on the Stage (the black area that holds your slides) to select it. Return to the
Properties panel. Change both the X and Y fields to zero to align the component with the upper-left corner of the Stage.
While you’re there, for this tutorial example change the width to 525 pixels and the height to 325 pixels.

Now click outside the instance in the grey area (the stage) and change the width and height to the same dimensions. 

Next, open the Component Inspector window and change the XML File Path entry to images.xml.php (For this example you
should name your xml.php file: images.xml.php)

Then save your FLA file. This example uses the filename yourslideshow.fla

The file that gets uploaded to your server is a compressed version of your FLA file called an SWF file (ShockWave File).


To create (publish) your .swf file correctly you’ll have to set up your publish settings (first time only).
Select "File > Publish Settings". Click on the "Flash" tab. If using the ActionScript 2 version of SlideShowPro, ensure
that "Version" is set to "Flash Player 7" (or higher) and "ActionScript version" is set to "ActionScript 2.0." If using
the ActionScript 3 version, "Version" should be set to "Flash Player 9" and "ActionScript version" set to "ActionScript
3.0". Finally, click "Publish." 

Flash will export two files to the same folder that contains your FLA file — yourslideshow.html and yourslideshow.swf.
You can ignore or discard the html file.

Now it’s time to add content to your slide show.

THE MULTI RECORD SECTION EDITOR

You’ll create a multi record section editor to upload your images. The one used in this example is called
index_slides. It is set to allow only one record to be created but it’s a multi record editor so that the images can
be dragged to re-order their appearance in the show.

In this example, set the first thumbnail at 525 pixels wide and 325 pixels high, and resize if over 800 pixels wide  and
500 pixels high. 

After you’ve created your section editor, upload a few test slides.

THE XML.PHP FILE
Since SSP uses an xml file to hold image location information, you’ll need to create an xml.php file to pull your
images from your database and feed them to your SSP slide show. 

HINT: In this example, the xml.php file is named images.xml.php but you can call it anything that you want to, just
remember to change the file name in the <object> on your viewer page, and in the Flash component inspector XML File Path
entry.

***NOTE: There are no other tags on this page, no <head>, no <body>, no <html>, just the code above. Upload the file to
the same folder as your .swf file and your viewer file.:



<?php header('Content-type: application/xml; charset=utf-8'); ?><?php echo '<?xml version="1.0" encoding="UTF-8"?>'?>

<?php
  
  require_once "/hsphere/local/home/yoursite.com/cmsAdmin/lib/viewer_functions.php";

  list(
$index_slidesRecords$index_slidesMetaData) = getRecords(array(
    
'tableName'   => 'index_slides',
    
'where'       => whereRecordNumberInUrl(1),
    
'limit'       => '1',
  ));
  
$index_slidesRecord = @$index_slidesRecords[0]; // get first record

?>

<gallery>
<album title="Slide Show" description="Slide Show" lgpath="/cmsAdmin/uploads/" tnpath="/cmsAdmin/uploads/thumb/">
<?php foreach ($index_slidesRecord['images'] as $upload): ?>
<img src="<?PHP echo $upload['filename'?>" />
<?PHP endforeach ?>
</album>
</gallery>



NOTE: If you want to start your slide show with a different image each time the page is reloaded, insert:



<?PHP shuffle($index_slidesRecord['images']) ?>



Before the line:



<?php foreach ($index_slidesRecord['images'] as $upload): ?>



DISPLAYING THE SSP SLIDE SHOW
The code to display the slide show on your web page is pretty straight forward, mine was set to render a transparent
background. If you’re are concerned about active content and Explorer 8 compatibility using Dreamweaver CS3, you
should automatically be warned about any existing incompatibility when you open your document.  Either the code can be
updated then, or you can do it manually by going to File > Convert > Active Content.

This code goes at the top of your page:



<?PHP
  
  require_once "/hsphere/local/home/yoursite.com/cmsAdmin/lib/viewer_functions.php";

  list(
$index_slidesRecords$index_slidesMetaData) = getRecords(array(
    
'Table name'   => 'index_slides',
    
'where'       => whereRecordNumberInUrl(1),
    
'limit'       => '1',
  ));
  
$index_slidesRecord = @$index_slidesRecords[0]; // get first record

?>
<?PHP echo "<?xml version='1.0'?>\n"?>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">



and this code goes where you want the slide show to appear:



<div id="flashcontent">

<object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000"
codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,0,0" width="525" height="325"
id="loader" align="middle">
                                <param name="allowScriptAccess" value="sameDomain" />
                                <param name="movie" value="yourslideshow.swf" />
                                <param name="quality" value="high" />
                                <param name="wmode" value="transparent" />
                                <param name="FlashVars" value="xmlfile=images.xml.php?<?php echo
$index_slidesRecord['num'?>" />
<embed flashvars="xmlfile=images.xml.php?<?php echo $index_slidesRecord['num'?>" src="yourslideshow.swf"
quality="best" wmode="transparent" width="525" height="325" name="loader" align="middle" allowscriptaccess="sameDomain"
type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" />                    
</object>
</div> 



After you’ve created your section editor, uploaded some sample images, created your viewer, your xml.php and your .swf
files and uploaded them to your server, your slide show should be operational. If it is, make a protection copy of your
FLA file and go ahead and play with the various SSP parameters and stage and instance sizes and colors. You’ll
discover that the combination of SSP and CMSB is exceptionally flexible.

HINTS
Along the way, here are a few specific things that I learned...

1) DO NOT USE AN ABSOLUTE PATH (http://www.yoursite/images.xml.php) anywhere in your object tag. 

2) The xmlFileType in the Flash Component Inspector should be set to: Default

3) If you create your CMSB image section editor as a Multi record editor set with a maximum of 1 record allowable, you
can drag your images to change their display order in the slide show.

4) Your slide show will look better if your images all have the same aspect ratio, or if you group the various sizes
creatively.

UNDERSTANDING SSP PARAMETERS
Setting parameters is probably the most difficult and confusing task in setting up a show. As I mentioned, parameters
are changed in the Flash Component Inspector panel. 

Here’s a link to a document that will explain the functions of all of the variable parameters that can be used with an
instance of SSP. It was compiled by Kenneth Huey.:

      http://www.thecmsbcookbook.com/ssp/SlideShowPro parameters.doc

A SHORT XML DICTIONARY

DEFINITION OF TERMS

<?xml version="1.0" encoding="utf-8"?> 
<gallery> opens the document node
 <album> opens the album node
<img /> creates a content element node, this always self closes with the final /
</album> closes the album node
</gallery> closes the document node

ACCEPTABLE VALUES FOR EACH NODE
gallery
- holds no values

album
- id="" Sets an id value when using actionscript methods
- title="" Title for the gallery screen.
- description="" Displayed in the gallery screen.
- lgpath="" Path where the large images reside, if a / is added to the begriming it will trace the path to the root
level, if no previous / is present paths will be relative to the xml file.
- tnpath="" Same as lgpath but for the thumbnail images.
- tn="" Thumbnail image displayed in the gallery screen.

img
- src="" filename relative to the lgpath
- tn="" set this is your tnpath image has a different name. if you avoid this the tn will be searched in the tnpath  by
the same name in the src value.
- caption="" caption of the image.
- link="" link called when the image is clicked.
- target="" target for the link value can be _self, _blank, or _parent and _top if working with frames.
- pause="" this will override the pause value set on the component inspector for this specific image. Must be a number
that will represent seconds.

MAKING YOUR SLIDE SHOW BACKGROUNDS TRANSPARENT
When it comes to setting all of the variables and other commands necessary, it’s not exactly a transparent process.
(pun)

Seriously, I’m pretty new to SSP and I fought with this for quite a long time before I was able to get it straightened
out, now it seems pretty easy. 

My first mistake was trying to use the stand alone version of the .swf that came with SSP. No I don’t quite understand
all of the finer points of their instance either, but is seems that some of the parameters are part of the .swf and no
matter what I did to the param.xml file I could not get rid of the dark background that showed up when a slide wasn’t
the full size of the frame. 

I finally bit the bullet and created my own .fla and .swf and instantly the issue disappeared. 

Oops... Only to be replaced by another issue.  There was this annoying background that sat over my web page and under
the slide show. No matter what value I put into the <object> bgcolor parameter, the background would change to that
color or default to white. 

I couldn’t make the background disappear, until I discovered the wmode parameter. 

Replacing the bgcolor parameter with the wmode parameter and setting it’s value to transparent:



<param name="wmode" value="transparent" />



and 



wmode=”transparent” 



does the trick.

“It’s always easy once you know how”


IMPLEMENTING ROYAL SLIDER FOR IMAGES ONLY AND FOR A COMBINATION OF IMAGES AND VIDEO - May 27th, 2020

Royal Slider (not a free program) is an easy to use jQuery image gallery and content slider plugin with animated
captions, responsive layout and touch support for mobile devices. As navigation you can use thumbnails, tabs or bullets.
Use it as image slider, slideshow, HTML content slider, gallery, banner rotator, video gallery, carousel or even
presentation. Developed in best practices of HTML5, CSS3 transitions are used for all animations (with fallback).

The on-line documentation is pretty complete and the plugin supports both normal eager load and lazy load**
implementations. I found that the lazy load implementation resulted in much faster load of the page with the image
slider already formatted.

Although Royal Slider is not a free program, and you’ll need to purchase a license for each site, they’re not
expensive. Dmitry, the author is very willing to help with customization and the forum is very helpful. The plugin also
comes in a Wordpress ready version.

Once you’ve uploaded the js and css files that come with the program to your site (mine are in a folder called
royalslider), and examined the demo files provided, it’s time to set up your CMSB implementation. There are many skins
and many options, but these are the ones that I chose for my site since it integrated well with my designs. 

The code below demonstrates 2 implementations. One which utilizes the info1 field for the image title between the large
image and the thumbnails and another that utilize separate text fields.

You’ll need to create either a multi-record section with at least a single image upload field and upload some test
records and images. or a single record section with at least one multi image upload field.

My section also included some text fields for information related to the specific image. (use the info fields for
specific image information in a multi image upload field)

Images should be around 1920 px wide to allow them to fill the screen in full screen mode. At the least, you’ll need
the first default thumbnail (which I set to  a max dimension of100px x 100px ) and you should un-check the resize image
check box. 

In the head section your viewer you’ll need to load the Royal Slider js and css files, the css overide and  Javascript
function below,. (the Javascript function can be loaded at the bottom of your page before the closing body tag if you
prefer)

NOTE: There are some modifications to the default skin and css files that you can download from this link and upload to
the appropriate subfolders in the royalslider folder on your site.

HTML5 VIDEO IMPLEMENTATION AT END OF RECIPE

http://www.thecmsbcookbook.com/downloads/royal-slider-mods.zip


<link rel="stylesheet" href="royalslider/royalslider.css">
<link rel="stylesheet" href="royalslider/skins/default/rs-default.css.php">
<script src='royalslider/jquery-1.8.3.min.js'></script>
<script src="royalslider/jquery.royalslider.min.js"></script>

<style type='text/css'>
.royalSlider .rsNav, .royalSlider .rsGCaption {
position: relative;
float: left;
left: 0 !important;
bottom: 0 !important;
right: 0 !important;
width: 100%;
text-align: center;
padding: 0;
    
}
.royalSlider {
text-align: left;
}
</style>

<script type='text/javascript'>
$(window).load(function(){
$('.royalSlider').royalSlider({
//autoScaleSlider:false,
controlNavigation: 'thumbnails',
autoScaleSlider: true,
autoScaleSliderWidth: 960,     
autoScaleSliderHeight: 880,
arrowsNav:true,
arrowsNavAutoHide: false,
arrowsNavHideOnTouch: true,
    // imageScaleMode: 'fit-if-smaller',
fitInViewport:true,
loop: true,
fullscreen: {
enabled: true,
nativeFS: false
        },
globalCaption: true,
numImagesToPreload:2,
fadeinLoadedSlide: true,
keyboardNavEnabled: true,
navigateByClick: true,
thumbs: {
appendSpan: true,
firstMargin: true,
paddingBottom: 40
      
    }
});
$('.rsGCaption').after( $('.rsNav') );
});  
</script>


Then For a multi record table with a single image upload field, in the body where you want to display the Slider, insert
the following. (Note: The regular expression, “preg_replace” removes any double quotes in your info field that would
break the code with 2 single quotes)

To display only a title using the image’s info1 field:


<div class="royalSlider rsDefault">
<?php foreach ($your_sectionRecords as $record): ?>
<div >
<?php foreach ($record['your_upload_field'] as $index => $upload): ?>
<a class="rsImg " href="<?php echo $upload['urlPath'?>">
<?PHP $upload['info1'] = preg_replace("/[\"]/""''"$upload['info1'] ); ?>
<div align='center' style='font-size:1.4em;' class='text_font ' >
<?php echo ucwords($upload['info1']) ?>
</div>
</a>

<div class="rsTmb" >
<img class="rsTmb" src="<?php echo $upload['thumbUrlPath'?>" /></div>
</div>
<?php endforeach; ?>
</div>
<?php endforeach; ?>


To use text fields for information relating to the image (multi-record table, single image upload field):


<div class="royalSlider rsDefault">
<?php foreach ($your_sectionRecords as $record): ?>
<div>
<?php foreach ($record['image'] as $upload): ?>
<a class="rsImg " href="<?php echo $upload['urlPath'?>" />
<?PHP $record['description'] = preg_replace("/[\"]/""''"$record['description'] ); ?>
<?PHP $record['title'] = preg_replace("/[\"]/""''"$record['title'] ); ?>
<?PHP $record['medium'] = preg_replace("/[\"]/""''"$record['medium'] ); ?>
<?PHP $record['size_of_work'] = preg_replace("/[\"]/""''"$record['size_of_work'] ); ?>
<?PHP $record['edition_size'] = preg_replace("/[\"]/""''"$record['edition_size'] ); ?>
<?PHP $record['year_created'] = preg_replace("/[\"]/""''"$record['year_created'] ); ?>
<?php // putting the link text in a div and styling the div allowed for fixed height adjustment for captions (reduced
css .royalSlider max-height85%; to accomodate?>
<div align='center' style='font-size:1.4em; height:150px;' class='text_font' >
<?php if ($record['title']): ?>
<b><?php echo strtoupper($record['title']) ?></b>
<?php endif; ?>
<?php if ($record['size_of_work']): ?>
<?php echo $record['size_of_work'?> 
<?php endif; ?>
<?php if ($record['medium']): ?>
<?php echo $record['medium'?>
<?php endif; ?>
<?php if ($record['edition_size']): ?>
<b>Edition of: </b><?php echo $record['edition_size'?>
<?php endif; ?>
<?php if ($record['year_created']): ?>
<b>Created in: </b><?php echo $record['year_created'?>
<?php endif; ?>
<?php echo $record['description'?>
</div>
</a>
<?php endforeach; ?>

<div class="rsTmb">
<?php foreach ($record['your_upload_field'] as $upload): ?>
<img class="rsTmb" src="<?php echo $upload['thumbUrlPath'?>"  />
<?php endforeach; ?>
</div>
</div>
<?php endforeach; ?>
</div> 


For a multi image upload field the code in the body is slightly different:
NOTE: if you're getting strange ghost thumbnail images, you may have to remove the $('.rsGCaption').after( $('.rsNav')
); from the Royal Slider Javascript on your page.


<div class="royalSlider rsDefault">
<?php foreach ($your_sectionRecords as $record): ?>
<?php foreach ($record['your_upload_field'] as $index => $upload): ?>
<div > <a class="rsImg " href="<?php echo $upload['urlPath'?>">
<?PHP $upload['info1'] = preg_replace("/[\"]/""''"$upload['info1'] ); ?>
<div align='center' style='font-size:1.4em;' class='text_font ' >
<?php echo ucwords($upload['info1']) ?>
</div>
</a>

<div class="rsTmb" >
<img class="rsTmb" src="<?php echo $upload['thumbUrlPath'?>" /></div>
</div>
<?php endforeach; ?><?php endforeach; ?>
</div>


To use text fields for information relating to the image use the info fields as above

**Lazy loading, also known as dynamic function loading, is a mode that allows a developer to specify what components of
a program should not be loaded into storage by default when a program is started. Ordinarily, the system loader
automatically loads the initial program and all of its dependent components at the same time (eager loading). In lazy
loading, dependents are only loaded as they are specifically requested. Lazy loading can be used to improve the
performance of a program if most of the dependent components are never actually used. 

HTML5 VIDEO IMPLEMENTATION

The only 3 changes (and one caution) are:

CAUTION
You will probably have to increase the upload_max_filesize and post_max_size values in your php.ini files (there are 3
places where these values appear) See the recipe "FILE UPLOAD ISSUES WITH THE NEW HTML UPLOADER AND FILE SIZE ISSUES IN
V3.15 AND OTHER VERSIONS" for more information on how to do this. ( http://www.thecmsbcookbook.com/recipedetail.php?586
)

CHANGES
1) Make sure that you're using the latest build of the royalslider JS file from http://dimsemenov.com/private/home.php?a
and have included the video module (You'll have to own RoyalSlider and have log in credentials to access this page)
2) Change the javascript on your page (below the css in the head) to:


<link rel="stylesheet" href="royalslider/royalslider.css">
<link rel="stylesheet" href="royalslider/skins/default/rs-default.css.php">
<script src='royalslider/jquery-1.8.3.min.js'></script>
<script src="royalslider/jquery.royalslider.min.js"></script>

<style type='text/css'>
.royalSlider .rsNav, .royalSlider .rsGCaption {
position: relative;
float: left;
left: 0 !important;
bottom: 0 !important;
right: 0 !important;
width: 100%;
text-align: center;
padding: 0;
    
}
.royalSlider {
text-align: left;
}
</style>
<script type='text/javascript'>

$(window).load(function() {
  
  $('.royalSlider').royalSlider({
    //autoScaleSlider:false,
    controlNavigation: 'thumbnails',
    autoScaleSlider: true,
    arrowsNavHideOnTouch: true,
    fitInViewport: true,
    loop: true,
    fullscreen: {
      enabled: true,
      nativeFS: false
    },
    globalCaption: true,
    numImagesToPreload: 2,
    thumbs: {

    }
  });
  
  $('.rsGCaption').after($('.rsNav'));
      
  var slider = $('.royalSlider').data('royalSlider');
  slider.ev.on('rsOnCreateVideoElement', function(e, url) {
    slider.videoObj = $('<video controls src=" ' + url + '"></video>');
  });

});

</script>


3) In the body where you're showing the slider (assuming its a single record with a single upload field that allows
multiple uploads):


<div class="royalSlider rsDefault">
                      <?php foreach ($your_tableRecords as $record): ?>
          
<?php foreach ($record['your_upload_field'] as $index => $upload): ?>              
                
<?php if ($upload['extension'] == 'mp4'): ?><div class="rsContent" >
<a class="rsImg" href="http://www.your_site.com/images/video_placeholder_image.jpg"
data-rsVideo="http://your_site.com<?php echo $upload['urlPath'?>">

<?php else:?> <div><a class="rsImg " href="<?php echo $upload['urlPath'?>">
<?PHP $upload['info1'] = preg_replace("/[\"]/""''"$upload['info1'] ); ?><?php endif ?>
<div align='center' style='font-size:1.4em; color:#FFF' class='sub_heading_font' >
 <?php if($upload['info1']):?><?php echo $upload['info1']?><?php endif ?>
               
</div>
</a>

<div class="rsTmb" >
<img class="rsTmb" src="<?php if ($upload['extension'] == 'mp4'):
?>http://www.your_site.com/images/video_placeholder_image.jpg<?php else :?><?php echo $upload['thumbUrlPath'?><?php
endif ?>" /></div>
</div>
<?php endforeach; ?><?php endforeach; ?>
</div>


You'll need to modify the above if you're using  separate records each with a single upload, but either way, the key is
to be able to differentiate between video files and images (you might be able to use<?php if (!$upload['isImage'] ): ?>
instead of <?php if ($upload['extension'] == 'mp4'): ?>)

USING FLASH



THE DIFFERENCE BETWEEN DOCUMENT, INSTANCE, AND VIEWER SIZE - Aug 3rd, 2010

If you’re finding it difficult to understand why the space allotted for your slide show on your web page (Viewer
Size), the space of the stage your slide show sits on (Document Size), the size of the images  that appear on that stage
(Instance Size), and the “x” and “y” values next to the instance sizes, you’re not alone.
Here, in layperson’s terms is what they all mean:
The “Viewer Size” which is the space allotted for your slide show on your web page is determined by the sizes in the
<object> on your web page.
The “Stage” is the area in a Flash Movie that will become visible in the final published Movie. The Stage can be any
size and is represented by a white (or another color if you’ve changed your background color rectangle. Think of it as
the area that your slide show sits on.
The “Document Size” is the size of that “stage”. If you click in the grey area that surrounds your slide show
instance, you’ll see the dimensions of the stage.

The” Instance” is the actual slide show that you are creating. If you click inside the instance, you’ll see the
dimensions of the slide show that will appear on that stage.

The “x” and “y” values indicate where the instance will appear on the stage.

That said, here’s a simple approach to make a slide show appear in the size and place you want it on your web page.

1) Set the Viewer size, the Document size and the Instance size to the same dimensions, which should be the dimensions
allocated for the show on your web page.
2) Set the “x” and “y” values to zero.

3) If you want to change how your slides fit the space defined on your web page, change the “content scale”
parameter. Other content settings will change the alignment of your images, whether there is a frame around your images,
etc. Try different settings to see which best  fits your needs. 

This will insure that your stage does not take up more room then you want it to, and that the images will display
centered on the stage and in the size defined in the <object> area of your web page.

Another one of those “it’s easy when you know how” ideas.


MAKING AN SWF DISPLAY LIKE AN IMAGE - Dec 29th, 2018

Dave Edis from Interactive Tools says that the first step is to figure out what HTML you want inserted for swf files.
You can find details about that here: 
http://kb.adobe.com/selfservice/viewContent.do?externalId=tn_4150 .

Next, If you know you're _only_ going to be uploading swf files in that upload field you can hard code it to always show
uploads with your swf html like this:


<?PHP foreach ($newsRecord['uploads'] as $upload): ?> 
  <embed src="<?PHP echo $upload['urlPath?>" ... /><br /> 
<?PHP endforeach ?>


Or if you're going to have images and swf in the same field you can test the file extension to figure out what html to
insert for each upload:


<?PHP foreach ($newsRecord['uploads'] as $upload): ?> 
  <?PHP if ($upload['extension'] == 'swf'): ?> 
    ... your html code for swf files ...  
  <?PHP elseif ($upload['hasThumbnail']): ?> 
    ... html code for thumbnails ... 
  <?PHP elseif ($upload['isImage']): ?> 
    ...your  html codes for images (if there is no thumbnail) ... 
  <?PHP else: ?> 
   ... your html code for other file types (this could be a download link) ... 
  <?PHP endif; ?> 
<?PHP endforeach ?>


If you’re using a Java Run Content script you’ll need to pull in the url link or flash name into the Java script
code without the .swf extension.

Here how:


<?PHP foreach ($your_sectionRecord['your_field_name'] as $upload): ?>
<?PHP $upload['urlPathNoExt'] = preg_replace('/\.\w+$/'''$upload['urlPath']); ?> <script type="text/javascript">
AC_FL_RunContent(
'codebase','http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,28,0','width','960','height','362','src','<?PHP
echo $upload['urlPathNoExt'?>
','quality','high','pluginspage','http://www.adobe.com/shockwave/download/download.cgi?P1_Prod_Version=ShockwaveFlash','bgcolor','#181818','movie','<?PHP
echo $upload['urlPathNoExt'?> ' ); //end AC code
</script>


How does it work?

The <?PHP $upload['urlPathNoExt'] = preg_replace('/\.\w+$/'''$upload['urlPath']); ?> code is what strips the .swf
extension from the field results. Dave says that you need to put that inside the foreach loop, but above where you want
to use it. You’d then display the new url path  without the extension with <?PHP echo $upload['urlPathNoExt'?> 


AUTOMATICALLY PUBLISH THE CORRECT CODE TO PLAY VARIOUS TYPES OF FLASH FILES - Aug 3rd, 2010

Einslistir wanted to know how to automatically publish the appropriate code for playing various flash and other media 
files. Dave Edis, from Interactive tools offered. 

When you generate a page viewer, the regular upload display code looks something like this. I've added some code in red.
(dougdrury added the code in blue to expand the options. See his note at the end of this entry) There’s also an mp3
option (with an orange background) below that.

FOR A LIST VIEWER



<!-- Display Uploads for field 'your_upload_field' (Paste this anywhere inside STEP2 to display uploads) --> 

  <?php foreach ($your_tableRecords as $record): ?>  <?php foreach ($record['your_upload_field'] as $upload): ?>
   
    <?php if ($upload['hasThumbnail']): ?> 
      <img src="<?php echo $upload['thumbUrlPath'?>" width="<?php echo $upload['thumbWidth'?>" height="<?php echo
$upload['thumbHeight'?>" /><br /> 
 
    <?php elseif ($upload['isImage']): ?> 
      <img src="<?php echo $upload['urlPath'?>" width="<?php echo $upload['width'?>" height="<?php echo
$upload['height'?>" /><br /> 
 
    <?php elseif ($upload['extension'] == 'swf'): ?> 
    <object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" 
    codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,29,0" width="350"
height="220"> 
      <param name="movie" value="<?php echo $upload['urlPath'?>"> 
      <param name="quality" value="high"> 
      <embed src="<?php echo $upload['urlPath'?>" quality="high" 
      pluginspage="http://www.macromedia.com/go/getflashplayer" 
      type="application/x-shockwave-flash" width="350" height="220"></embed> 
    </object> 
 
    <?php elseif ($upload['extension'] == 'wmv'): ?> 
    <object classid="CLSID:22d6f312-b0f6-11d0-94ab-0080c74c7e95" width="320" height="290"> 
    <param name="FileName" value="<?php echo $upload['urlPath'?>"> 
    <param name="AutoStart" value="True"> 
    <param name="ShowControls" value="True"> 
    <param name="ShowStatusBar" value="False"> 
    <param name="ShowDisplay" value="False"> 
    <param name="AutoRewind" value="True"> 
      <embed type="application/x-mplayer2" 
      pluginspage="http://www.microsoft.com/Windows/Downloads/Contents/MediaPlayer/" 
      autostart="True" filename="<?php echo $upload['urlPath'?>" 
      src="<?php echo $upload['urlPath'?>" 
      width="320" height="290" showcontrols="True" showstatusbar="False" showdisplay="False" autorewind="True"> 
      </embed> 
    </object> 

<?php elseif ($upload['extension'] == 'flv'):
$fileURL=$upload['urlPath'];
?>
<OBJECT classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"
codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0"
WIDTH="320" HEIGHT="240" id="myMovieName"><PARAM NAME=movie VALUE="player.swf">
<PARAM NAME=quality VALUE=high><PARAM NAME=bgcolor VALUE=#FFFFFF>
<PARAM NAME=flashvars VALUE=movie=<?=$fileURL?>>
<EMBED src="player.swf?movie=<?=$fileURL?>" quality=high bgcolor=#FFFFFF WIDTH="320" HEIGHT="240"
NAME="myMovieName" ALIGN="" TYPE="application/x-shockwave-flash"
PLUGINSPAGE="http://www.macromedia.com/go/getflashplayer"></EMBED></OBJECT>
<?php elseif ($upload['extension'] == 'mp3'):
$fileURL=$upload['urlPath'];
?>
  <object width="300" height="42">
    <param name="src" value="<?php echo $upload['urlPath'?>">
    <param name="autoplay" value="false">
    <param name="controller" value="true">
    <param name="bgcolor" value="#FF9900">
    <embed src="<?php echo $upload['urlPath'?>" autostart="false" loop="false" width="300" height="42"
controller="true" bgcolor="#FF9900"></embed>
    </object>
    
    <?php else: ?> 
      <a href="<?php echo $upload['urlPath'?>">Download <?php echo $upload['filename'?></a><br /> 
 
    <?php endif ?> 
  <?php endforeach ?>  <?php endforeach ?> 



FOR A DETAIL VIEWER

Just replace the first 2 lines of code:



<?php foreach ($your_tableRecords as $record): ?>  <?php foreach ($record['your_upload_field'] as $upload): ?>


 
with:



<?php foreach ($your_tableRecord['your_upload_field'] as $upload): ?> 



(And don’t forget to remove one of the <?php endforeach ?> lines at the end.)

Note: To play flv files you’ll have to have an flv player stored on your site.  An open source flv player is available
here,

http://www.osflv.com/  

dougdrury says: It seems a bit old (since it is saying codebase 6,0,40...) but whatever the latest version you have
loaded on you machine will run anyways, or you could write your own. 

If you want to use other types of media files, like .mov, just do a Google search for mov+embed+code and you’ll find
what you need.


REMOVE THE EXTENSION FROM THE .SWF FILE NAME IF YOU”RE USING THE “JAVA RUN CONTENT” SCRIPT - Aug 3rd, 2010

User The Seen needed to be able to pull in the url link or flash name into the Java script code but need to remove the
extension (ie: .swf) form the end. 

Here’s the embedded code that The Seen was using:



<?php foreach ($your_tableRecord['your_field'] as $upload): ?>
<script type="text/javascript">
AC_FL_RunContent(
'codebase','http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,28,0','width','960','height','362','src','<?php
echo
$upload['urlPath']?>','quality','high','pluginspage','http://www.adobe.com/shockwave/download/download.cgi?P1_Prod_Version=ShockwaveFlash','bgcolor','#181818','movie','<?php
echo $upload['urlPath']?>' );
</script> 
 <?php endforeach ?> 



Here’s the solution provided by Dave from Interactive Tools. He said:

 Just put:



<?php $upload['urlPathNoExt'] = preg_replace('/\.\w+$/'''$upload['urlPath']); ?>



inside the foreach loop but above where you want to use it. 

Then display it like this:



<?php echo $upload['urlPathNoExt'?>



So the result would be:



<?php foreach ($your_tableRecord['your_field'] as $upload): ?>
<?php $upload['urlPathNoExt'] = preg_replace('/\.\w+$/'''$upload['urlPath']); ?>
<script type="text/javascript">
AC_FL_RunContent(
'codebase','http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,28,0','width','960','height','362','src','<?php
echo
$upload['urlPathNoExt']?>','quality','high','pluginspage','http://www.adobe.com/shockwave/download/download.cgi?P1_Prod_Version=ShockwaveFlash','bgcolor','#181818','movie','<?php
echo $upload['urlPathNoExt']?>' );
</script> 
 <?php endforeach ?> 



FREE JWPLAYER HANDLES BOTH HTML5 AND FLASH - Dec 29th, 2018

I've begun using the free JWPlayer which can handle playing both Flash & HTML5 files to play videos on my sites.

http://www.longtailvideo.com 

Longtail also has a paid version of the player that you can re-brand, and offers some other interesting add-ons.

Together, HTML5 & Flash support media on both the iPod/iPad platform as well as more conventional platforms.

The implementation is pretty straight forward and you can read more on their site and forum.

Longtail recommends that you convert your( existing) videos to an .mp4 format so that you don’t have many different
media file types to deal with.

To convert flv videos to .mp4 I found that the free HandBrake converter, with versions for both Mac and Windows did the
trick right out of the box (don’t forget to check the “web optimized” box.

http://handbrake.fr/downloads.php

I used the Regular > Normal settings with the container set for MP4 and the “web optimized” check box checked. 

The video files were small and loaded quickly

Back to the JWPlayer...

First I needed to download the (free) player and other necessary files from their site at:

http://www.longtailvideo.com/players/jw-flv-player/

NOTE: If you don’t want a “share this video” page to come up at the end of the video, don’t forget to un-check
the “include Viral, a video sharing plugin” check box.

After uploading the jwplayer folder containing the latest swfobject.js, jwplayer.js, and player.swf to the root
directory of your site.

Then, in the body where you want the player to appear:


 <div id="containera">&nbsp;</div>
                <script type="text/javascript" src="../jwplayer/jwplayer.js" ></script> 
                <script type="text/javascript">
    jwplayer("containera").setup({
        'controlbar': 'bottom',
         'file': 'http://www.your_site.com<?php foreach ($your_videosRecord['your_mp4_video_file'] as $upload): ?><?php echo
$upload['urlPath'?><?php endforeach ?>',
       'height': '480',
       'width': '720',
        'image': 'http://www.your_site.com<?php foreach ($your_videosRecord['still_image_to_display_before _the_video_plays']
as 
$upload): ?><?php echo $upload['thumbUrlPath2'?><?php endforeach ?>',
        'autostart': 'false'
        
        

    });
        </script>


IMPORTANT: *** Make sure that you don't have a CSS id called containera elsewhere in the style sheets called for this
page or in your page code, or you'll get very strange results.***


Adjust the height and width to suit your needs

autostart can be either true or false

I deleted the default text  in the<div id="container">loading the player...</div> and replaced it with an &nbsp;

NOTE: Version 5x of the free JWPlayer had a logo that disappeared after a few seconds. Starting with V6+, the logo stays
annoyingly visible. Don't say I didn't warn you...

A FURTHER NOTE:

If your video doesn't play in Internet Explorer (9) you might try adding this line to your .htaccess file in the root
directory of your site. 

(If you don't have an .htaccess file, you can create one in any text editor. Just make sure it's saved as plain text and
that there's no other extension on the file you create. Then upload it to the root directory.)


AddType video/mp4 .mp4


IF there's a "Compatibility Mode" issue (Thank you Microsoft), you could try adding this to your video viewer as the
first metatag in the head section:


<meta http-equiv="X-UA-Compatible" content="IE=EmulateIE8" />


Here’s the gist of using the “compatible” metatag:  <meta http-equiv="X-UA-Compatible" content=" _______ " />

    The Standard User Agent modes (the non-emulate ones) ignore <!DOCTYPE> directives in your page and render based on
the standards supported by that version of IE (e.g., IE=8 will better obey table border spacing and some pseudo
selectors than IE=7).

    Whereas, the Emulate modes tell IE to follow any <!DOCTYPE> directives in your page, rendering standards mode based
the version you choose and quirks mode based on IE=5

    Possible values for the content attribute are:

    content="IE=5"
    content="IE=7"
    content="IE=EmulateIE7"
    content="IE=8"
    content="IE=EmulateIE8"
    content="IE=9"
    content="IE=EmulateIE9"
    content="IE=edge"

You can learn more on the Microsoft “Defining document compatibility” doc at:

http://msdn.microsoft.com/en-us/library/cc288325

INSTALLING V5+

After uploading the swfobject.js, jwplayer.js, and player.swf to the root directory of your site.

Then insert this code in the head of your page:


<script type="text/javascript" src="jwplayer.js"></script>


Then, in the body where you want the player to appear:


<div id="containera">&nbsp;</div>
<script type="text/javascript">
jwplayer("containera").setup({

file: "http://www.your_site.com<?php foreach ($your_videosRecord['your_mp4_video_file'] as $upload): ?><?php echo
$upload['urlPath'?><?php endforeach ?>",
height: 480,
width: 720,
image: "http://www.your_site.com<?php foreach ($your_videosRecord['still_image_to_display_before _the_video_plays'] as
$upload): ?><?php echo $upload['thumbUrlPath2'?>"<?php endforeach ?>,
autostart: true,
modes: [
{ type: "html5" },
{ type: "flash", src: "player.swf" }
]

});
</script>


IMPORTANT: *** Make sure that you don't have a CSS id called containera elsewhere in the style sheets called for this
page or in your page code, or you'll get very strange results.***

The modes: code makes sure that html5 is tried first and if it’s not compatible with the particular platform, then
flash is tried.

Adjust the height and width to suit your needs

autostart can be either true or false

I deleted the default text in the<div id="container">loading the player...</div> and replaced it with an &nbsp;


DISPLAYING .FLV VIDEO ON A PAGE - Mar 7th, 2015

If you’ve already got your .FLV file then skip this step. If you’re downloading a video from YouTube, then, first
download the free YouTube video downloader from

       http://www.xilisoft.com/download-youtube-video.html

Create an upload field that will accept one .flv file, a video_title text field, and if necessary, a video_image upload
field.

Then, to render a blank video screen in your viewer:

    

 <embed src="http://freevideocoding.com/flvplayer.swf?file=http://your_site.com/ 
<?php foreach ($common_informationRecord['video'] as $upload): ?><?php echo $upload['urlPath'?><?php endforeach ?> 
&autoStart=false" width="432" height="240" quality="high" type="application/x-shockwave-flash"
pluginspage="http://www.macromedia.com/go/getflashplayer"></embed> 
</p><br /><?php echo $common_informationRecord['video_title'?>



Or, to render an embedded still image in your viewer to click on:



<?php foreach ($common_informationRecord['video_image'] as $upload): ?> 
<img src="<?php echo $upload['thumbUrlPath'?>" width="<?php echo $upload['thumbWidth'?>" height="<?php echo
$upload['thumbHeight'?>" alt=""  <?php endforeach ?>style="cursor:pointer;" onclick="this.parentNode.innerHTML =
'\x3Cembed src=\'http://freevideocoding.com/flvplayer.swf?file=http://your_site.com 
<?php foreach ($common_informationRecord['video'] as $upload): ?><?php echo $upload['urlPath'?><?php endforeach ?> 
&autoStart=true\' width=\'325\' height=\'250\' quality=\'high\' type=\'application/x-shockwave-flash\'
pluginspage=\'http://www.macromedia.com/go/getflashplayer\'\x3E\x3C/embed\x3E';" /> 
              
   <br /><?php echo $common_informationRecord['video_title'?><br />CLICK THE IMAGE TO PLAY THE VIDEO



The line breaks are only here for clarity. There should be no extra spaces. Change the sizes to fit your application and
the URL to match your web site URL, style the text to suit your needs.

There's more information at 

       http://'http://freevideocoding.com

Hope that solves some of the mystery.


DISPLAYING YOUTUBE VIDEOS ON YOUR WEB PAGE - Mar 7th, 2015

At least for now, you can take advantage of YouTube’s amazing, free cross browser, cross platform streaming
technology, and not have any suggestions offered to your visitors after they’ve viewed your video.

Here’s my simple approach:

1) Set up a free YouTube account. (Make it a private channel if you want to, that’s up to you)
2) Upload your video and copy the portion of the video link that appears after the YouTube TLD (.com, .be, etc)
3) Create a text field in your multi record section called ‘YouTube Shortcode’,
4) Paste the copied shortcode in the ‘YouTube Shortcode’ field for the appropriate record.
4) Set up a viewer with the following code:
NOTE: My viewer had a 500 x 300 space allocated for the video frame and I chose to have a double line border around it.

 <iframe width="500" height="300" style="border:medium double #333333" src="https://www.youtube.com/embed/<?php echo
$your_multi_record_sectionRecord['youtube _shortcode '?>?rel=0&controls=1&showinfo=0" frameborder="0"
allowfullscreen></iframe>

That’s it, when your video has finished uploading and converting, it will play in the iframe when the play button is
clicked, or full frame when the visitor chooses that option, and there will be no suggestions after it has finished
playing. 

GOOGLE



GOOGLE'S NEW MAPS API BUSINESS MODEL BREAKS SOME EXISTING GEOCODER INSTALLS - May 3rd, 2019

If you're having trouble with a GeoCoding application that worked perfectly in the past, Daniel Louwe at Interactive
Tools may have the answer. He said:

Due to some recent changes (August 2018) by Google (https://developers.google.com/maps/billing/important-updates), the
info in the plugin readme.txt is no longer 100% accurate. We've been looking into the changes and will be updating our
documentation with the next plugin release, but for now, I can share some of this new information with you here.

Google now provides a $200/month credit towards their API services. This translates to 40,000 geocoding requests per
month (https://cloud.google.com/maps-platform/pricing/sheet/), which should be sufficient for most projects. Accessing
the API does now require setting up a billing account with a CC number, but the credit will be applied automatically
each month.

If you are at all concerned about exceeding the credit, it's possible to set a hard cap on API usage using quota limits
(https://cloud.google.com/apis/docs/capping-api-usage). These limits are per-day, so for the Geocoding API that would
work out to roughly a "1300 requests per day" quota limit to stay under $200. Budget alerts are also available
(https://cloud.google.com/billing/docs/how-to/budgets) to get notifications about overall billing account usage.

To get an existing API key up and running with the new system, you can try checking out this link:
https://cloud.google.com/maps-platform/user-guide/ and clicking on the Transition Tool. This tool will help identify any
steps you'll need to take to reactivate the API. Alternately, here are updated instructions for setting up a new key:

    Visit https://developers.google.com/maps/documentation/geocoding/get-api-key
    Click 'Get Started'
    Select 'Maps' and click 'Continue'
    Click the drop-down to either select an existing project or create a new one, 'Next'
    Follow the steps to create/enable a billing account for this project.
    Accept the prompt to enable the Maps API.
    Copy the API key and paste it into the plugin in the field for $GLOBALS['GEOCODER_GOOGLE_API_KEY'], and click 'Done'
    (Optional) Click 'Secure Credentials'
        Under Application Restrictions, select HTTP Referrers
        Add the domain for the website(s) this API key will be used on (for example localhost, example.com, 8.8.8.8)
        Click API restrictions
        Select the Geocoding API from the dropdown
        Click Save


GOOGLE MAPS AND CMSB - Aug 3rd, 2010

Here’s the gist of an excellent tutorial posted by Jake at Interactive Tools. Way to go Jake!!!

A user said: I have a business directory and would like a map at the bottom of the page. Can you head me in the right
direction? 

And Jake replied:
To set this up, You’ll need to sign up for a Google Maps API key at:

    http://code.google.com/apis/maps/signup.html
  

Then you'll want to create a field called "latitude" and a field called "longitude" for the section that you'd like to
display maps for, since coordinates for your markers will need to be entered as longitude and latitude values. This site
can be used to obtain coordinates for any location:

      http://mapki.com/getLonLat.php

Now, use CMS Builder's Code Generator to create list viewer code for your section. Copy the "STEP 1" piece of code and
paste it at the beginning of a plain text file. Below that, paste the following code:



<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" 
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> 
<html xmlns="http://www.w3.org/1999/xhtml"> 
  <head> 
    <meta http-equiv="content-type" content="text/html; charset=utf-8"/> 
    <title>Google Maps Example</title> 
    <script src="http://maps.google.com/maps?file=api&amp;v=2&amp;key=YOUR_GOOGLE_MAPS_API_KEY_HERE" 
      type="text/javascript"></script> 
    <script type="text/javascript"> 
 
    //<![CDATA[ 
 
    function load() { 
      if (GBrowserIsCompatible()) { 
 
      function createMarker(point,html) { 
        var marker = new GMarker(point); 
        GEvent.addListener(marker, "click", function() { 
          marker.openInfoWindowHtml(html); 
        }); 
        return marker; 
      } 
              
        var map = new GMap2(document.getElementById("map")); 
        map.setCenter(new GLatLng(YOUR_HOME_LATITUDE, YOUR_HOME_LONGITUDE), 13); 
 
      } 
            
<?PHP foreach ($your_section_nameRecords as $record): ?> 
 
      var point = new GLatLng(<?PHP echo $record['latitude'?>,<?PHP echo $record['longitude'?>); 
      var marker = createMarker(point,'<?PHP echo $record['address'?>, <?PHP echo $record['city'?>') 
      map.addOverlay(marker); 
       
<?PHP endforeach; ?> 
 

     
 
    //]]> 
    </script> 
  </head> 
  <body onload="load()" onunload="GUnload()"> 
    <div id="map" style="width: 500px; height: 300px"></div> 
          
  </body> 
</html>



We'll need to make some adjustments to this code. 

Replace the "YOUR_GOOGLE_MAPS_API_KEY_HERE" text with your Google Maps API key. You'll also want to replace the
"YOUR_HOME_LATITUDE" and "YOUR_HOME_LONGITUDE" text will the coordinates of the location where you'd like the map to be
centered by default. You can use the site http://mapki.com/getLonLat.php to obtain those coordinates. Adjust this line
of code so that it matches up with your section name, and this page will be done:



<?PHP foreach ($your_section_nameRecords as $record): ?>



The last step is adding an <iframe> tag on the page where you'd like the map to be displayed. If you save this file as
"map.php", then your <iframe> tag would look like this:



<iframe src="/path/to/map.php" width="550" height="350"></iframe>



And that should be it! 

Note that in my example above, when a user clicks on the map marker the location's address is displayed, followed by its
city. You can adjust the CMS Builder code used in this line if you'd like to change the information displayed there:



var marker = createMarker(point,'<?PHP echo $record['address'?>, <?PHP echo $record['city'?>')



Here’s a link to the entire post where you can find some web sites that incorporate Google maps:

      http://www.interactivetools.com/forum/gforum.cgi?post=64304;


GOOGLE FONTS AND CMSB - Dec 29th, 2018

NOTE: This recipe has been totally revised to fix some issues with the way Google expects to receive font information.

Thanks to Google, you’re not stuck with designing your site with only a standard font set and creating images for any
other fonts that you need to utilize. They’ve developed an API that allows you to download fonts for your sites on the
fly. At this writing Google lists over 400 web fonts to choose from. You can see the complete list at:

http://www.google.com/webfonts

A sample set of files, including a fonts.ini.php schema preset file, a css.php file and a viewer file can be downloaded
from:

http://www.thecmsbcookbook.com/downloads/googlefonts.zip

NEW EDITOR

To implement the font.api, first create a multi-record editor called “Google Font Names” with only one text field
called “Name”. 
***NOTE: Make sure that you check "Don't allow removing records" under the "advanced"  tab.

Then create a single record editor called “fonts” with sets fields for each css font class that you want to create.
Each set has
1) A pull down list field for the font name that gets its options from the database Google Font Names, (option values
from the “num” field, option labels from the “name” field) 
2) A text field for color
3) A test field for size
4) A list field for style with the pull down values normal, bold, and italic.

NOTE: When entering the Google font names in the editor, if there's a space between some of the words, leave those
space, and if some of the words in the font name are capitalized, make sure you enter them as capitals. Specific font
styles may have a colon and 3 digits following the name (Merriweather Sans:300 is the lighter weight version of the 
Merriweather Sans font.). Include that as well or the normal font weight for that font will be used.

CSS.PHP CODE
Now create a font_sample.css.php file with your css classes. Here’s the class “text” as an example. It uses Arial,
Black, .9em, normal as the fallback where a specific font is not defined.

The preg_replace code strips any font weight designations from the end of font names, to conform with Google's coding
requirements for the style sheet:

NOTE: If you’re not sure how to create a css stylesheet that pulls it’s data from a CMSB section, take a look at the
recipe called: “USING CMSB TO POPULATE A CSS STYLESHEET”


<?php $fontsRecord['text_font:label'] = preg_replace("/[0-9:]/"""$fontsRecord['text_font:label']); ?>

..text_font {
  font-family: "<?php if ($fontsRecord['text_font']): ?><?php echo $fontsRecord['text_font:label'?>", san-serif;<?php
else: ?>Arial", san-serif;<?php endif ?> <?php if ($fontsRecord['text_font_size']): ?>font-size:<?php echo
$fontsRecord['text_font_size'?>em;<?php else: ?>font-size: .9em;<?php endif ?> <?php if
(
$fontsRecord['text_font_color']): ?>color:#<?php echo $fontsRecord['text_font_color'?>;<?php else:
?>color:#000000;<?php endif ?> <?php if ($fontsRecord['text_font_style']): ?>font-style:<?php echo
$fontsRecord['text_font_style'?>;<?php else: ?>font-style: normal;<?php endif ?>
}


You can add or change the classes to suite your needs or working style.

VIEWER CODE
At the top of your viewer, add the load records call for the new fonts editor.


<?php
  
// load viewer library
  $libraryPath 'cmsAdmin/lib/viewer_functions.php';
  
$dirsToCheck = array('/your_server_path/','','../','../../','../../../');
  foreach (
$dirsToCheck as $dir) { if (@include_once("$dir$libraryPath")) { break; }}
  if (!
function_exists('getRecords')) { die("Couldn't load viewer library, check filepath in sourcecode."); }
    
   
// load records
  list($google_font_namesRecords$google_font_namesMetaData) = getRecords(array(
    
'tableName'   => 'google_font_names',
  ));
?>


Then in the head section, add:


<link rel="stylesheet" type="text/css" href="font_sample.css.php" />
<link rel="stylesheet" type="text/css" href="http://fonts.googleapis.com/css?family=<?php 
    $output ''
    foreach (
$google_font_namesRecords as $record) { $record['name'] = preg_replace("/[, ]/""+"$record['name']);
      
$output .= $record['name'] . "|"
    } 
    
$output rtrim($output,"|"); // remove trailing pipe 
    print $output
?>">


Now, just call your CSS classes the way you normally would, and the content should appear in the selected fonts, sizes
and styles.

Here's some simple code that you can put in the body of your test viewer, or you can use your own.

<div align="center" class="masthead_font">Masthead Font Sample</div>
    
<div align="center" class="navigation_font">Navigation Font Sample</div>
        
<div  align="center" class="heading_font">Heading Font Sample</div>

<div align="center" class="sub_heading_font">Sub heading Font Sample</div>

<div  align="center" class="text_font">Text Font Sample</div>

<div align="center" class="small_text_font">Small Text Font Sample</div>

<div align="center" class="tiny_text_font">Tiny Text Font Sample</div>


If you want to implement any of the new CSS3/HTML5 text effects, you can add them to the CSS style sheet.

Here's an example using drop shadow glow around a masthead font. using a combination of 4 text shadow styles.
NOTE: You'll have to add a field called masthead_font_shadow_color to your "Fonts" editor

.masthead_font {
  font-family: "<?php if ($fontsRecord['masthead_font']): ?><?php $fontsRecord['masthead_font:label'] =
preg_replace("/[0-9:]/"""$fontsRecord['masthead_font:label']); ?><?php echo $fontsRecord['masthead_font:label']
?>";<?php else: ?>Verdana", san-serif;<?php endif ?> text-shadow: -1px -1px 1px #<?php echo
$fontsRecord['masthead_font_shadow_color'?>, 1px -1px 1px #<?php echo $fontsRecord['masthead_font_shadow_color'?>,
-1px 1px 1px #<?php echo $fontsRecord['masthead_font_shadow_color'?>, 1px 1px 1px #<?php echo
$fontsRecord['masthead_font_shadow_color'?>; letter-spacing:3.5px; <?php if ($fontsRecord['masthead_font_size']):
?>font-size:<?php echo $fontsRecord['masthead_font_size'?>em;<?php else: ?>font-size: 1.5em;<?php endif ?> <?php if
(
$fontsRecord['masthead_font_color']): ?>color:#<?php echo $fontsRecord['masthead_font_color'?>;<?php else:
?>color:#000000;<?php endif ?> <?php if ($fontsRecord['masthead_font_style']): ?>font-style:<?php echo
$fontsRecord['masthead_font_style'?>;<?php else: ?>font-style: normal;<?php endif ?> 
}

Here's an implementation of a rollover effect using the text shadow effect.

NOTE: In addition to the font name, font size, and any other fields that you're using, you'll have to add 3 specific
fields to your "Fonts" editor. navigation_font_normal_color, navigation_link_shadow_color and
navigation_font_rollover_color


a.navigation:link {font-family: "<?php if ($fontsRecord['navigation_link_font']): ?><?php echo
$fontsRecord['navigation_link_font:label'?>", san-serif;<?php else: ?>Verdana", san-serif;<?php endif ?> text-shadow:
-1px -1px 1px #<?php echo $fontsRecord['navigation_link_shadow_color'?>, 1px -1px 1px #<?php echo
$fontsRecord['navigation_link_shadow_color'?>, -1px 1px 1px #<?php echo $fontsRecord['navigation_link_shadow_color']
?>, 1px 1px 1px #<?php echo $fontsRecord['navigation_link_shadow_color'?>;  text-decoration: none; border: none; <?php
if ($fontsRecord['navigation_link_font_size']): ?>font-size:<?php echo $fontsRecord['navigation_link_font_size']
?>em;<?php else: ?>font-size: 1.0em;<?php endif ?>; color:#<?php if ($fontsRecord['navigation_font_normal_color']):
?><?php echo $fontsRecord['navigation_font_normal_color'?><?php else: ?>000000<?php endif ?>;}

a.navigation:visited {font-family: "<?php if ($fontsRecord['navigation_link_font']): ?><?php echo
$fontsRecord['navigation_link_font:label'?>", san-serif;<?php else: ?>Verdana", san-serif;<?php endif ?> text-shadow:
3px 3px 2px #<?php echo $fontsRecord['navigation_link_shadow_color'?>; text-decoration: none; border: none; <?php if
(
$fontsRecord['navigation_link_font_size']): ?>font-size:<?php echo $fontsRecord['navigation_link_font_size'?>em;<?php
else: ?>font-size: 1.0em;<?php endif ?>; color:#<?php if ($fontsRecord['navigation_font_normal_color']): ?><?php echo
$fontsRecord['navigation_font_normal_color'?><?php else: ?>000000<?php endif ?>;}

a.navigation:hover {font-family: "<?php if ($fontsRecord['navigation_link_font']): ?><?php echo
$fontsRecord['navigation_link_font:label'?>", san-serif;<?php else: ?>Verdana", san-serif;<?php endif ?> text-shadow:
-1px -1px 1px #<?php echo $fontsRecord['navigation_link_shadow_color'?>, 1px -1px 1px #<?php echo
$fontsRecord['navigation_link_shadow_color'?>, -1px 1px 1px #<?php echo $fontsRecord['navigation_link_shadow_color']
?>, 1px 1px 1px #<?php echo $fontsRecord['navigation_link_shadow_color'?>; text-decoration: none; border: none; <?php
if ($fontsRecord['navigation_link_font_size']): ?>font-size:<?php echo $fontsRecord['navigation_link_font_size']
?>em;<?php else: ?>font-size: 1.0em;<?php endif ?>; color:#<?php if ($fontsRecord['navigation_font_rollover_color']):
?><?php echo $fontsRecord['navigation_font_rollover_color'?><?php else: ?>000000<?php endif ?>;}

a.navigation:active {font-family: "<?php if ($fontsRecord['navigation_link_font']): ?><?php echo
$fontsRecord['navigation_link_font:label'?>", san-serif;<?php else: ?>Verdana", san-serif;<?php endif ?> text-shadow:
3px 3px 2px #<?php echo $fontsRecord['navigation_link_shadow_color'?>; text-decoration: none; border: none; <?php if
(
$fontsRecord['navigation_link_font_size']): ?>font-size:<?php echo $fontsRecord['navigation_link_font_size'?>em;<?php
else: ?>font-size: 1.0em;<?php endif ?> color:#<?php if ($fontsRecord['navigation_font_normal_color']): ?><?php echo
$fontsRecord['navigation_font_normal_color'?><?php else: ?>000000<?php endif ?>;}


You can find some other cool effects in the article by Daniels Mekšs at
http://www.1stwebdesigner.com/css/css3-text-effects-typography/


USING @FONT-FACE TO REPLACE GOOGLE FONTS THAT DON’T WORK IN GOOGLE CHROME (OH, THE IRONY!) - Dec 29th, 2018

Using @font-face to replace Google Fonts that don’t work in Google Chrome (Irony)

Surprising as it may seem, there are some fonts (Bubbler One for example) that don’t display correctly in either
Google Chrome or Safari on a windows platform. (The bottom half of the font is cut off)

I assume the folks at Google will fix this embarrassment at some point, but until then, you can fix the issue using the
new CSS3 @font-face tag.

A little background... (If you know this already, you can skip this paragraph.) 
When you use a specific font on a web site, if that font is not installed on the viewer’s device, that font will be
replaced by a default font that is installed on that device, and your page will not render as you intended. That’s one
of the reasons why Google developed Google Fonts in the first place. 

Although I needed to implement the @font-face solution because a specific Google Font had a problem, you can use this
approach to make sure that any (non-standard) font will render properly 

Here’s how:

1) Download the non working font(s) from http://www.google.com/webfonts to a temporary folder (on your desktop).

2) Upload your font(s) to the  FONT SQUIRREL WEBFONT GENERATOR at http://www.fontsquirrel.com/fontface/generator . Leave
 the radio button at “Optimal”, then check the Agreement checkbox, and download your “Kit”. NOTE: This can take
a while depending on the size of your kit and what’s going on at Font Squirrel.

3) UnZip the kit. You’ll see a stylesheet.css file, and a series of font files that may be in .eot, svg, ttf, and woff
formats. Upload the font files to a directory on your server. (I created a ‘fonts’ directory in the site root.)

Open the stylesheet.css file and change the url paths to relative paths that are appropriate for your site. (Note:
Firefox accepts only relative URLS as a security precaution)
Since I set up a ‘fonts’ directory in the site root, for this example, I would change:

@font-face {
    font-family: 'my-font-family';
 src: url('my-webfont.eot');
    src: url('my-webfont.eot?#iefix') format('embedded-opentype'),
         url('my-webfont.woff') format('woff'),
         url('my-webfont.ttf') format('truetype'),
         url('my-webfont.svg#my-font-family') format('svg');
}


To:


@font-face {
    font-family: 'my-font-family';
src: url(../fonts/my-webfont.eot');
    src: url(../fonts/my-webfont.eot?#iefix') format('embedded-opentype'),
         url('../fonts/my-webfont.woff') format('woff'),
         url('../fonts/my-webfont.ttf') format('truetype'),
         url('../fonts/my-webfont.svg#my-font-family') format('svg');
}

Remove the font-weight: normal; and font-style: normal; from the @font-face code, since you’ll probably want to
include these in your individual classes where appropriate.

Then add your classes to the stylesheet.css code:

For this example we’ll set up a masthead, heading, and text class using the font family ‘my-font-family’ (don’t
repeat any class names that already exist) . NOTE: I added !important to the color: declaration, so that links using
these classes would render in the declared color in some browsers. 

I.E.:

.masthead {font-family:my-font-family;
font-size:1.6em;
color: #72433a; !important 
font-weight: bold;
font-style: normal;
}

.heading {font-family:my-font-family;
font-size:1.5em;
color: #72433a; !important
font-weight: normal;
font-style: italic;
 }

.text {font-family:my-font-family;
font-size:1.1em;
color: #72433a; !important
font-weight: normal;
font-style: normal;
}

4) If you already have a .css file attached to your web pages, you can copy all of the stylesheet.css code into that
file, or you can attach the stylesheet.css file to your page. 

That’s it, now you can use these new classes in your code and they should render correctly in all CSS3 enabled
browsers.

GOING FURTHER:

If you want to give your client the ability to change CSS parameters, take a look at the recipe called USING CMSB TO
POPULATE A CSS STYLESHEET at: http://www.thecmsbcookbook.com/recipedetail.php?335 

If you are using Google Fonts and @font-face, and you want to give your client the ability to change CSS parameters for
both, take a look at the recipe called GOOGLE FONTS AND CMSB at: <http://www.thecmsbcookbook.com/recipedetail.php?418

CHAPTER 4 - PAYPAL



POPULATING A PAYPAL FORM WITH CMSB - Aug 3rd, 2010

You can set up text fields in a single record section editor to handle any of the PayPal variables in much the same way
that you set up any text field. So in a PayPal form you could set up the following. (CMSB PHP variables replacing the
hard coded variables) 



<form action="https://www.paypal.com/cgi-bin/webscr" method="post">
<input type="hidden" name="cmd" value="_xclick">
<input type="hidden" name="business" value="<?PHP echo $paypalRecord['business'?>">
<input type="hidden" name="item_name" value="<?PHP echo $paypalRecord['name'?>"/>
<input type="hidden" name="amount" value="<?PHP echo $paypalRecord['price'?>"/>
<input type="hidden" name="no_shipping" value="0"> 
<input type="hidden" name="logo_custom" value="http://mysite/<?PHP echo $paypalRecord['pplogo'?> ">
<input type="hidden" name="no_note" value="1">
<input type="hidden" name="shopping_url” value=”<?PHP echo $paypalRecord['return_page_URL'?>“>
<input type="hidden" name="rm" value="2">
<input type="hidden" name="return" value="<?PHP echo $paypalRecord['thankyou_page_URL'?>">
<input type="hidden" name="currency_code" value="USD">
<input type="hidden" name="lc" value="US"> 
<input type="hidden" name="bn" value="PP-BuyNowBF">
<input name="submit" type="image" src="MyclientButton" alt="PayPal - The safer, easier way to pay online!"
align="absmiddle" border="0">
<img alt="" border="0" src="https://www.paypal.com/es_XC/i/scr/pixel.gif" width="1" height="1">
</form>



Or, if you’re using the link format:



<a class="special" href="https://www.paypal.com/cgi-bin/webscr?amount=<?PHP echo $paypalRecord['price']
?>&item_name=<?PHP echo $paypalRecord['name'?>&business=<?PHP echo $paypalRecord['business']
?>&cpp_header_image=http://mysite/<?PHP echo $paypalRecord['pplogo'?>
&currency_code=USD&lc=US&add=1&cmd=_cart&no_shipping=0&no_note=1&shopping_url=<?PHP echo
$paypalRecord['return_page_URL'?>&rm=2&return=<?PHP echo $paypalRecord['thankyou_page_URL'?>&bn
value=PP-ShopCartBF&">CLICK TO ORDER</a>



IF you want to shoppers to return to the same page that they came from to continue shopping you can replace 



<?PHP echo $paypalRecord['return_page_URL'?>



 with



<?PHP echo thisPageUrl() ?>



CONTINUE SHOPPING BUTTON
If you want your customers to only buy one item at a time you can disable the “continue shopping”  button by
removing the line from cart button that sets "add" to "1", and change your "cmd" value to "_xclick" instead of "_cart". 

AUTOMATIC FORWARDING (RETURN) FUNCTION
 Caveats regarding the automatic forwarding (return)  option:

If you are using the return option to send your customers to a thank you page that increments a counter of performs some
other function, there are a few issues to be considered.

 If the buyer pays with their PayPal account, the return option automatically takes the buyer to the URL in the
“return” option.

If the buyer pays with the Credit Card Option, they are first taken to the receipt page where PayPal gives them the
chance to print out a receipt. This is a legal requirement.

After that, the buyer must click on the "Return to Merchant" link in order to return to the site. Even if you use a
notify_url call to load a page in the background, that call will not be executed until the buyer clicks on the return to
merchant link.

You can  replace the default message in the “Return to Merchant” button on the credit card receipt page with a
custom message of up to 65 characters. You might use it to tell credit card  buyers that their transaction will not be
complete until they click on the return to merchant link. Just use the cbt function:



 <input type="hidden" name="cbt" value="<?PHP echo $paypalRecord['your_message_field'?>">



or



&cbt=<?PHP echo $paypalRecord['your_message_field'?>



From my understanding the only way to get a query string working is to add the variable rm=2.

Rm sets the FORM  METHOD used to send data to the URL specified by the return variable after payment completion.
Allowable rm values:

      0 – all shopping cart transactions use the GET method
      1 – the payer’s browser is redirected to the return URL by the GET method, and no transaction variables are
sent
      2 – the payer’s browser is redirected to the return URL by the POST method, and all transaction variables are
also posted

The default is 0.

Note: The rm variable takes effect only if the return variable is also set.


OPTIONS IN A BUY NOW FORM POPULATED FROM CHECKED VALUES IN PRODUCT RECORD - Jul 23rd, 2013

I needed to set up a form field on a detail page’s PayPal “Buy Now” form that would allow a buyer to choose the
color of an item from only those colors available for that particular item.

The colors available were supposed to be limited those that were checked in a multi-value check box list field in that
item's record in the store_inventory section. (Possible values for the multi-value check box list field were pulled from
another database using the "record number" for the value and a" title" field value for the label.)

I had done something similar many times, allowing a visitor choose from a complete list of possible choices, but I
couldn’t seem to figure out how to limit their options to only those colors that were checked as being available for a
particular item (record).

That is, until Greg Thomas from Interactive Tools came to the rescue. He said:

You can use a getRecords function to retrieve the details for the product.  The values and labels selected for that
product are included in the getRecords array, and you can select the values and labels like this:

  <?php  // load record from 'store_inventory'
  list($colors$store_inventoryMetaData) = getRecords(array(
    
'tableName'   => 'store_inventory',
   
'where'       => whereRecordNumberInUrl(1),
    
'loadUploads' => true,
    
'allowSearch' => false,
    
'limit'       => '1',
  ));
   
?>                    
  <?php $available_colors $colors['0'];

  
//Create a drop down list by combining the values and labels into one array
  $dropList array_combine($available_colors['colors:values'], $available_colors['colors:labels']);

  
?>
  <select name="os1">
    <option value="">Please Choose a Color</option>
    <!-- cycle through drop list to create options -->
    <?php foreach($dropList as $value => $label): ?>
      <option value = " <?php echo $label?>" <?php selectedIf($value, @$_REQUEST['colors']);?>> <?php echo $label;
?></option>
    <?php endforeach; ?>
  </select>

Based on Greg’s suggestion, in this example, there’s a multi value check box list field in my store_inventory
section called ‘colors’.

Since the ‘colors’ list field is being populated with all it’s possible values from another database, it uses the
record number from the other database as the value and the title from that database for the label. 

In the code above I'm getting the data for a store_inventory record using the getRecords function. 

Then the code creates a variable called $dropList that stores an array of the available_colors values and labels in this
record using the array_combine function. 

Finally the code cycles through this list using a foreach loop to create a select input.

PAYPAL NOTES:
In order to allow a buyer to choose values for optional criteria like style, size, color, etc., you need 2 fields in
your buy now button.

on0 and os0, where on0 is the label for the criteria, I.E.: <input type="hidden" name="on1" value="Color">, and os0 is
the value for that criteria, IE:  <input type="hidden" name="os1" value="Blue"> (or a list of options to choose from as
in this recipe)


The   <?php selectedIf($value, @$_REQUEST['colors']);?> includes the color chosen in the submission to PayPal.

You can learn more about integrating PayPal HTML variables at:
https://developer.paypal.com/webapps/developer/docs/classic/paypal-payments-standard/integration-guide/Appx_websitestandard_htmlvariables/


FORCING THE BUYER TO CHOOSE BETWEEN STYLES, COLORS, OR PRICE - Aug 2nd, 2013

Taking the example above one step further, let’s say you wanted to let buyers choose between sizes and have the price
for that size automatically populate your PayPal cart.

Here’s an approach that I used with a multi record section editor, that also let’s you force the buyer to make
required choice(es) before they can submit their order to PayPal.

The fields in the  multi record section editor used in the form were category, stock number, 8 ounce price, 16 ounce
price and 64 ounce price. If statements were used to allow a size option to not appear on the web page if the price is
not entered in the editor.

The onsubmit in the second line tests whether the choice has been made. 

Continue shopping returns the buyer back to the page where they placed the order from.



 <form name='cart101' action="https://www.paypal.com/cgi-bin/webscr"  method="post" 
 onsubmit="return PackageForm(this);">

<input type="hidden" name="business" value="payments@yourdomain.com">
<input type="hidden" name="cmd" value="_cart">
<input type="hidden" name="shopping_url" value="<?php echo thisPageUrl() ?>">
<input type="hidden" name="add" value="1">
<input type="hidden" name="no_note" value="1">
<input type="hidden" name="cpp_header_image" value="http://mysite/<?PHP echo $paypalRecord['pplogo'?>">
<input type="hidden" name="currency_code" value="USD">
<input type="hidden" name="lc" value="US">
<input type="hidden" name="bn value" value="PP-ShopCartBF">
<input type="hidden" name="alt" value=”<?php echo $record['category'?><?php echo $record['stock_ number'?>">
<input type="hidden" name="item_name" value="<?php echo $record['category'?> <?php echo $record['stock_number'?>">

<input type="hidden" name="xxx"> 
<select name="amount" style="width:150px;font:9px;margin-top:4px">
<option selected>Select a Size</option>
<?php if ($record['8_oz_price']): ?><option value="<?php echo $record['8_oz_price'?>">8 Ounce Size - <?php echo
$record['8_oz_price'?></option><?php endif ?>
<?php if ($record['16_oz_price']): ?><option value="<?php echo $record['16_oz_price'?>">16 Ounce Size - <?php echo
$record['16_oz_price'?></option><?php endif ?>
<?php if ($record['64_oz_price']): ?><option value="<?php echo $record['64_oz_price'?>">64 Ounce Size - <?php echo
$record['64_oz_price'?></option><?php endif ?>
</select><br />

<input type="image" src="https://www.paypal.com/en_US/i/btn/x-click-but10.gif" 
 name="submit" 
 alt="Make payments with PayPal - it's fast, free and secure!">
</form>



Here’s the javascript that goes into the body of the page to execute the validation tests.  

The “select name” variable “amount” is the one that is passed to PayPal if a selection is made.

 If you need more tests, add more unique variables. 

Remember that the “select name” variable is the one that gets sent to PayPal and the “name” variable is used to
test the condition. They can not be identical. in this case the names are os1 and os2



<script type="text/javascript" language="JavaScript">
function PackageForm(frm) {
 var emsg = '';

 //Review all your form fields that you want to require.
 //add your error message to the "emsg" variable.

//fires required field (os1)
 var i = frm.os1.selectedIndex;
 if (i == 0) {
  emsg += "Please tell us where you heard about us.\n";
 }

 
//second required field (os2)
 var i2 = frm.os2.selectedIndex;
 if (i2 == 0) {
  emsg += "Please select a color\n";
 }
 
 //if emsg is empty, the Order is OK, so proceed to PayPal. ELSE
 //if emsg is NOT empty, show error message(s).
 if (emsg == '') {
  return true;
 }else {
  alert(emsg);
  return false;
 }
}
</script>



HIDING THE CONTENTS OF PAYPAL FORM FIELDS FROM PRYING EYES (GOOD) - Aug 26th, 2012

FOR AN EVEN BETTER SOLUTION, LOOK AT THE NEXT RECIPE "HIDING THE CONTENTS OF FORM FIELDS (BEST)"

I had a number of PayPal buttons that returned visitors to signup and submission forms after payment. The trouble was
that a quick look at the source code would allow anyone to circumvent the payment page and spambots to capture the
payment email address. 

It didn't seem that I could use PayPal encrypted buttons because the data in the button was dynamic so the solution I
came up with was to use javascript to encrypt the real field values.

Here's a 2 part solution that you can use for any form. 

It's not a perfect solution, but it's better than none at all.

First create the cloaking  generator which I call cloak1.php. In this example it's set up to generate the code for 3
fields, (1 payment email address and up to 2 return URLs (return and return2)). The generator doesn't differentiate
between field types, so you can mix and match the type of data that's cloaked.  Note that the ret1, ret2 and ret3 code
is where the form field names are assigned. change them to match your form


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Cloaking Generator</title>
</head>

<body>
<h3>Cloaking Script Generating Form</h3>
<form
onsubmit="this.__enc_result.value=__enc_code(this.__enc_request.value,this.__enc2_request.value,this.__enc_email_request.value);
return false;" style="text-align:left">
<p>Enter the Return-URL you&#8217;d like to encode:
<input name="__enc_request" size="60" style="width:100%">
Enter the Second Return-URL you&#8217;d like to encode:
<input name="__enc2_request" size="60" style="width:100%">
Enter your PayPal &#8216;business&#8217; email to encode:
<input name="__enc_email_request" size="40" style="width:50%"></p>
<p><input type="submit" value="Click Here to Encode"></p>
<p>Then copy the code from the box below and paste it into your page between the <code>&lt;head&gt;</code> and
<code>&lt;/head&gt;</code> tags.</p>
<p><textarea name="__enc_result" cols="70" rows="20" readOnly="true" style="width:100%"
onclick="this.select()"></textarea>
</form>
<script>
<!--
var s1 = '<'+'script type="text/javascript">\n<!--\n';
var s2 = '\nfunction checkForm(theForm) {\n';
var s3 = '}\n//-->\n</'+'script>';
function __enc_code(str1,str2,str3) {
  var len1 = str1.length;
  var len2 = str2.length;
  var len3 = str3.length;
  var ret1 = "";
  var ret2 = "";
  var ret3 = "";
  var jscode1 = "";
  var jscode2 = "";
  var jscode3 = "";
  for (var i = 0; len1 > i; ++i) {
    ret1 += "&#"+str1.charCodeAt(i)+";"+((4 == i % 5 )? '"\n+"': '') ;
  }
  for (var i = 0; len2 > i; ++i) {
    ret2 += "&#"+str2.charCodeAt(i)+";"+((4 == i % 5 )? '"\n+"': '') ;
  }
   for (var i = 0; len3 > i; ++i) {
    ret3 += "&#"+str3.charCodeAt(i)+";"+((4 == i % 5 )? '"\n+"': '') ;
  }
  if ("" != ret1) {
    ret1 = 'var temp = "' + ret1 + '";\n';
    jscode1 = 'if (theForm.return)\n  theForm.return.value = temp;\n';
  }
  if ("" != ret2) {
    ret2 = 'var temp2 = "' + ret2 + '";\n';
    jscode2 = 'if (theForm.return2)\n  theForm.return2.value = temp2;\n';
  }
   if ("" != ret3) {
    ret3 = 'var temp3 = "' + ret3 + '";\n';
    jscode3 = 'if (theForm.business)\n  theForm.business.value = temp3;\n';
  }
  return s1+ret1+ret2+ret3+s2+jscode1+jscode2+jscode3+s3;
}
//-->
</script>
</body>
</html>


Here's how you'd implement this solution.

Paste the generated cloaking code in the head of your viewer.

Replace the email addresses and the URLs that are to be changed with temporary entries as in the PayPal button
membership example below.


<form onSubmit="checkForm(this)"
action= "https://www.paypal.com/cgi-bin/webscr">
  <input type="hidden" name="cmd" value="_xclick">
  <input type="hidden" name="lc" value="US">
  <input type="hidden" name="cbt" value="click to fill out your membership application">
  <input type="hidden" name="currency_code" value="USD">
  <input type="hidden" name="cpp_header_image" value="http://www.your_site.com/images/pplogo.jpg">
   <input type="hidden" name="item_name" value="$<?php echo $become_a_memberRecord['regular_dues_amount'?> - 1st Year
Dues Payment">
  <input type="hidden" name="amount" value="<?php echo $become_a_memberRecord['regular_dues_amount'?>">
  <input type="hidden" name="no_shipping" value="1">
  <input type="hidden" name="no_note" value="1">
  <input type="hidden" name="rm" value="1">
<input type="hidden" name="business" value="">
  <input type="hidden" name="return" value="">
  <input type="submit" value="CLICK HERE TO PAY $<?php echo $become_a_memberRecord['regular_dues_amount'?>">
</form>




HIDING THE CONTENTS OF PAYPAL FORM FIELDS FROM PRYING EYES (BEST) - Sep 14th, 2012

I had a number of PayPal buttons that returned visitors to signup and submission forms after payment. The trouble was
that a quick look at the source code would allow anyone to circumvent the payment page and spambots to capture the
payment email address. 

As I mentioned in the previous recipe, a JavaScript cloaking solution was only an interim measure until a real solution
could be found.

Thanks to Jason Sauchuk, from Interactive Tools, there is a  better solution.

He suggested the use of an intermediate PHP page that would do all of the heavy lifting of URL replacement on the server
side and never allow the hidden values to be seen in a viewer's source code.

Here's his basic idea.


 In place of the link that you'd like to hide, use something like:



 <a href  = "intermediate.php?url=1">Click Here!</a>



On the intermediate.php page, you use the values passed in to figure out which URL to redirect to:



<?php 
  $value = @$_REQUEST['url']; 
   
  
$url ""
   
  if (
$value == 1) { 
    
$url "http://www.myfirstoption.com"
  } 
  elseif (
$value == 2) { 
    
$url "http://www.mysecondoption.com"
  } 
  elseif (
$value == 3) { 
    
$url "http://www.mythirdoption.com"
  } 
 
  if (
$url) { 
    
redirectBrowserToURL($url); 
  } 
  exit; 
 
?>


USING THE CONCEPT FOR PAYPAL PAYMENT LINKS

PULLING DATA FROM FIELDS IN A SINGLE RECORD EDITOR (See below for pulling data from a multi-record editor)

On the viewer where I wanted the PayPal Payment links to go I added one of the following links:

NOTE: I found that I had to use text instead of numbers for the URL values to get the scheme to work.



<a href="intermediate1.php?url=one">Click Here for link 1!</a>

<a href="intermediate1.php?url=two">Click Here for link 2!</a>

<a href="intermediate1.php?url=three">Click Here for link 3!</a>

<a href="intermediate1.php?url=four">Click Here for link 4!</a>

<a href="intermediate1.php?url=five">Click Here for link 5!</a>


Then on your intermediate page insert the following code in the body, Adding your own ifelse sets as required:


<!-- create the required variables -->

<?php $var1 $your_tableRecord['amount_field_1']; ?> 
<?php $var2 $your_tableRecord['paypal_payment_e_mail_address_1']; ?> 
<?php $var3 $your_tableRecord['amouint_field_2']; ?>  
<?php $var4 $your_tableRecord['paypal_payment_e_mail_address_2']; ?>
<?php $ret1 "http://www.your_site.com/hidden_page1.php']; ?> 
<?php $ret2 = "http://www.your_site.com/hidden_page2.php"; ?> 

 <?php 


  $value = @$_REQUEST['url']; 
   
  
$url ""
// build the url with a value of 'one' from it's component parts

 if ($value == 'one') {   
      
      
$url  "https://www.paypal.com/cgi-bin/webscr?";      
      
$url .= "cmd=_xclick&";  
      
$url .= "amount=".urlencode($var1)."&";
      
$url .= "business=".urlencode($var2)."&";
      
$url .= "lc=US&";  
      
$url .= "cbt=item 1 button title&"
      
$url .= "currency_code=USD&";  
      
$url .= "rm=1&";  
      
$url .= "cpp_header_image=".urlencode("http://www.your_site.com/images/paypal_header.jpg")."&"
      
$url .= "item_name=item 1 name&";  
      
$url .= "no_shipping=1&";  
      
$url .= "no_note=1&"
      
$url .= "return=".urlencode($ret1)."&"
  } 
// build the url with a value of 'two' from it's component parts

 elseif ($value == 'two') {   
      
      
$url  "https://www.paypal.com/cgi-bin/webscr?";      
      
$url .= "cmd=_xclick&";  
      
$url .= "amount=".urlencode($var3)."&";
      
$url .= "business=".urlencode($var4)."&";
      
$url .= "lc=US&";  
     
$url .= "cbt=item 2 button title&";  
      
$url .= "currency_code=USD&";  
      
$url .= "rm=1&";  
      
$url .= "cpp_header_image=".urlencode("http://www.your_site.com/images/paypal_header.jpg")."&"
      
$url .= "item_name=item 2 name&";   
      
$url .= "no_shipping=1&";  
      
$url .= "no_note=1&"
      
$url .= "return=".urlencode($ret2)."&";
      
  } 
 
// the rest are simple URLs
 
    elseif ($value == 'three') { 
    
$url "http://www.site1.com"
  } 
 elseif (
$value == 'four') { 
    
$url "http://www.site2.com"
  } 
  
  elseif (
$value == 'five') { 
    
$url "site3"
  } 
  
 if (
$url) { 
    
redirectBrowserToURL($url); 
  } 
  exit; 
?>


PULLING VARIABLE DATA FROM A FIELD IN A SPECIFIC RECORD IN A MULTI-RECORD EDITOR

I had one situation where the entry fee varied depending on the particular exhibition record and the links to
intermediate.php was on a detail page for that record.

On the page that contained the link I added the record number by changing the link code to



<a href="intermediate1.php?url=one&num=<?php echo $your_tableRecord['num'?>">Click Here for link 1!</a>



Then on the intermediate page I added a load records call for the table with the detail page and added a where statement
to limit the records to ones matching the record number that was appended to the URL that called the intermediate.php
page.

So if the table was your_table_one, the code would be:


 list($your_table_oneRecords, $your_table_oneMetaData) = getRecords(array(
    'tableName'   => your_table_one',
    'where'       => whereRecordNumberInUrl('num'),
    'limit'       => '1',
  ));
$your_table_oneRecord = @$your_table_oneRecords[0]; // get first record


Then I added a variable for that entry fee data:



<?php $var5 $your_table_oneRecord['entry_fee']; ?> 


NOTE: Since I was pulling other variables from another single record table I had to remove
 


  'where'       => whereRecordNumberInUrl(1),
    'limit'       => '1',


from the list records call for that table and add


'allowSearch' => false,


DISPLAYING A BLOCK OF TEXT THAT TELLS THE BUYER WHICH SIZES ARE AVAILABLE FOR WHICH ITEMS - Aug 3rd, 2010

By using a “!” in the PHP code to indicate a “not” condition (red for visibility only) or blank field, we can
create the following matrix to print the 7 possible conditions for the 3 size choices :



<?php if ($record['8_oz_price'] && !$record['16_oz_price'] && !$record['64_oz_price']): ?>Available in the 8 ounce size
only.<?php endif ?>

<?php if (!$record['8_oz_price'] && $record['16_oz_price'] && !$record['64_oz_price']): ?>Available in the 16 ounce
(Pint) size only.<?php endif ?>

<?php if (!$record['8_oz_price'] && !$record['16_oz_price'] && $record['64_oz_price']): ?>Available in the 64 ounce
(Gallon) size only.<?php endif ?>

<?php if ($record['8_oz_price'] && $record['16_oz_price'] && !$record['64_oz_price']): ?>Available in the 8 ounce and 16
ounce (Pint) size only.<?php endif ?>

<?php if ($record['8_oz_price'] && !$record['16_oz_price'] && $record['64_oz_price']): ?>Available in the 8 ounce and 64
ounce (Gallon) size.<?php endif ?>

<?php if (!$record['8_oz_price'] && $record['16_oz_price'] && $record['64_oz_price']): ?>Available in the 16 ounce
(Pint) and 64 ounce (Gallon) size.<?php endif ?>

<?php if ($record['8_oz_price'] && $record['16_oz_price'] && $record['64_oz_price']): ?>Available in the 8 ounce, 16
ounce (Pint) and 64 ounce (Gallon) size.<?php endif ?>



SETTING UP LINKLOK PAYPAL AND CREATING A CMSB PRODUCT FILE - Dec 29th, 2018

Linklok is an inexpensive program that I've used for quite a while to handle PayPal transaction verification and supply
secure links for downloads (or web pages).

Although the documentation outlines many ways to customize yourLinklok installation, for a basic implementation there
are only two files required
for linklok to operate: linklokipn.php and linklokipnret.php

linklokipn.php is the only file that you’ll need to customize, and all the user defined parameters appear at the top
of the file:

The information included in the $Products variable is: "unique_product_id,product name,product currency and minimum
price accepted,path to
your hidden directory and the URL of your "success" page,leave this one set to “0",the number of minutes until the
link sent to the
purchaser expires";

NOTE: soyerveortmvobd is the folder where you keep your downloadable files
NOTE: Do not use any special characters in your Your_Product_ID or Your Product name, and no Dollar Sign in your price,
or PayPal may not accept your transaction.


$Products[] = "Your_Product_ID,Your Product Name,USD=99.95,soyerveortmvobd/your_success_page.php,0,1440";

// Setup admin and security variables
$LinklokURL = "http://www.your_site.com/linklokipn.php"; // URL of the linlok.php page on your site
$PaypalEmail = "payments@your_site.com"; // PRIMARY Paypal email address
$SellerCompany = "Your Company"; // Your company name
$SellerEmail = "info@your_site.com"; // Your email address for order inquiries
$SellerURL = "http://www.your_site.com"; // Your website URL
$LinkKey = "orange23"; // Encryption key for download links
$PDTtoken="12876Vdwerwrol_dmYMHfewrwurjdE3QsupkkvTo4Fv39-7y"; // Auto Return PDT token from paypal-profile-website
payment preferences

$DelayEchecks = "Y"; // Set to Y to delay eCheck orders until cleared.
$EmailTemplate = ""; // Optional Email Template in either .txt or .html format
$DownloadTemplate = ""; // Optional download page template
$ErrorTemplate = ""; // Optional Error page template
$CopyEmail = "any_email@your_provider.net"; // Receive copy of order emails. email address or ""
$ManualPassword = "kookamonga"; // Password for manual order entry. "" to disable
$HTMLEmail = "Y"; // Set to Y to use HTML formatted emails or N to send in plain-text
$Txnid = ""; // Optional to stop possible multiple calls from IPN
$WarningTemplate = ""; // Check payment warning email template
$WarningTemplatePage= ""; // Check payment warning thankyou page template


To use a dynamic product file that pulls it's data from your CMSB tables: 

Change the Products[] line to:


// Setup your products
$ProductCSV = "http://www.your_domain.com/your_folder/your_product_file.php";


The code for our_product_file.php file follows the format:

NOTE: Don not use head or body tags in your_products_file.php, just the code below modified to fit your needs

NOTE: You'll need to set up an editor called "Your Table" with at least a Product_ID, Product Name, and Price text
field, and an upload field that contains your downloadable product 

NOTE: Do not use any special characters in your Your_Product_ID or Your Product Name fields, and no Dollar Sign in the
Price field or PayPal may not accept your transaction.


<?php
  $libraryPath 'cmsAdmin/lib/viewer_functions.php';
    
$dirsToCheck = array('/path_to_your_server/','','../','../../','../../../');
  foreach (
$dirsToCheck as $dir) { if (@include_once("$dir$libraryPath")) { break; }}
  if (!
function_exists('getRecords')) { die("Couldn't load viewer library, check filepath in sourcecode."); }
  list($
$templatesRecords, $$templatesMetaData) = getRecords(array(
   
  ));
 
?>

<?php header("Content-type: text/css");?>
Your_First_Product_ID,Your First Product name,USD=99.95,soyerveortmvobd/your_success_page.php,0,1440
Your_Second_Product_ID,Your Second Product name,USD=99.95,soyerveortmvobd/your_second_product.zip,0,2880
<?php foreach ($your_tableRecords as $record): ?><?php echo $record['product_id'?>,<?php echo $record['product_name']
?>,USD=<?php echo $record['price'?>,<?php foreach ($record['upload'] as $upload): ?>http://www.your_domain.com<?php
echo $upload['urlPath'?><?php endforeach; ?>,0,1440
<?php endif ?>
<?php endforeach; ?>


If you should decide to purchase linklok, or any other Vibralogic programs, I'd appreciate your using my affiliate link:

http://www.shareasale.com/r.cfm?B=12671&U=520135&M=3826


SHOW A PAYPAL FORM ONLY IF CERTAIN CRITERIA ARE MET - Jan 31st, 2023

This recipe, which is part of a wholesale download ordering system, forces a customer to enter at least one email
address before they can order downloads (see the recipe: "MODIFYING LINKLOK PAYPAL TO SEND DOWNLOAD EMAILS TO MULTIPLE
RECIPIENTS").

It requires the Website Membership plugin and can be used without Linklok, but some minor modifications in the paypal
button will be required.

Linklok is an inexpensive program that I've used for quite a while to handle PayPal transaction verification and supply
secure links for downloads (or web pages).

If you should decide to purchase linklok, or any other Vibralogic programs, I'd appreciate your using my affiliate link:

http://www.shareasale.com/r.cfm?B=12671&U=520135&M=3826

If at least one email address is not entered, an error message is triggered. Once there’s at least one email address,
the PayPal ordering link is shown. 

The script creates a variable called $custom which contains the comma separated email addresses entered and appends that
to the PayPal link. PayPal uses the custom variable too pass the email addresses trough with the completed transaction.
Since PayPal will only pass a maximum of 256 characters in the custom variable, there are only 5 email address fields to
insure that the limit is not exceeded. 

At the top of the page insert this error message code:


<?php $errorsAndAlerts  "You Must Enter At Least 1 Email Address To Continue"?> 


and the login redirect: 


 if (!$CURRENT_USER) { websiteLogin_redirectToLogin(); }


Then in the body:


<?php // determine if at least 1 email has been entered in the form before submitting and display error messages ?>
<?php $showForm 1 ?>
<?php $showPayPalButton 0 ?>
<?php if(@$_REQUEST['submitForm'] && (@$_REQUEST['email_1'] || @$_REQUEST['email_2']|| @$_REQUEST['email_3'] ||
@
$_REQUEST['email_4'] || @$_REQUEST['email_5'])):?>
<?php $showForm 0 ?>
<?php $showPayPalButton 1 ?>
<?php endif ?>
<?php if(@$_REQUEST['submitForm'] && (@!$_REQUEST['email_1'] && @!$_REQUEST['email_2'] && @!$_REQUEST['email_3'] &&
@!
$_REQUEST['email_4'] && @!$_REQUEST['email_5'])):?>
<div align="center" ><h2 style="color:#F00;"><?php echo $errorsAndAlerts?></h2></div>
<?php endif ?>
<?php if ( $showForm == 1  ) :?>
<div align="center">
  <h2>Thanks for choosing a 5 pack.
    
    Enter the  email addresses of the 5 clients to whom you'd like to send download links, 
  then click the "Pay and Send Links" Button. </h2>
  
  
  <form method="post" action="">
<?php // NOTE: if you use action="<?php echo $_SERVER['PHP_SELF'] ?>" the custom variable will not be passed correctly
?>
    <table border="0" cellspacing="0" cellpadding="2">
      <tr>
        <td>Email 1</td>
        <td><input type="text" name="email_1" value="<?php echo htmlencode(@$_REQUEST['email_1']); ?>" size="50" /></td>
      </tr>
      <tr>
        <td>Email 2</td>
        <td><input type="text" name="email_2" value="<?php echo htmlencode(@$_REQUEST['email_2']); ?>" size="50" /></td>
      </tr>
      <tr>
        <td>Email 3</td>
        <td><input type="text" name="email_3" value="<?php echo htmlencode(@$_REQUEST['email_3']); ?>" size="50" /></td>
      </tr>
      <tr>
        <td>Email 4</td>
        <td><input type="text" name="email_4" value="<?php echo htmlencode(@$_REQUEST['email_4']); ?>" size="50" /></td>
      </tr>
      <tr>
        <td>Email 5</td>
        <td><input type="text" name="email_5" value="<?php echo htmlencode(@$_REQUEST['email_5']); ?>" size="50" /></td>
      </tr>
      <tr>
        <td colspan="2" align="center"><input class="button" type="submit" name="submitForm" value="Pay and Send Links
&gt;&gt;" /></td>
      </tr>
    </table>
  </form>
</div>
<?php endif ?>

<?php if ( @$_REQUEST['email_1']) :?>
<?php $email_1 = @$_REQUEST['email_1']. ', ' ?>
<?php endif ?>
<?php if ( @$_REQUEST['email_2']) :?>
<?php $email_2 = @$_REQUEST['email_2']. ', ' ?>
<?php endif ?>
<?php if ( @$_REQUEST['email_3']) :?>
<?php $email_3 = @$_REQUEST['email_3']. ', ' ?>
<?php endif ?>
<?php if ( @$_REQUEST['email_4']) :?>
<?php $email_4 = @$_REQUEST['email_4']. ', ' ?>
<?php endif ?>
<?php if ( @$_REQUEST['email_5']) :?>
<?php $email_5 = @$_REQUEST['email_5']. ', ' ?>
<?php endif ?>
<?php $custom = @$email_1 .@$email_2 .@$email_3 .@$email_4 .@$email_5 ?>

<?php $custom rtrim($custom,', '); // remove trailing comma ?>
<?php $custom=urlencode($custom); // urlencode the output ?>
<br />
<br />
<br />
<br />
<br />
<?php if ($showPayPalButton == ) :?>
<table align="center" width="40%" border="0" cellpadding="5">
  <tr>
    <td colspan="3" align="center"><h2>Click the "Buy Now" button to pay for your 5 pack through PayPal.</h2>
      (PayPal Also Accepts Credit Cards)
      You'll have a chance to review your order before it's final.
</td>
  </tr>
  <tr>
    <td  valign="top"><div align="center"> <img src="http://www.your_site.com/images/paypal-logo.jpg" alt="" height="99"
width="100" border="0" /></div></td>
    <td valign="top">
      <div align="center"><a class="special" href="https://www.paypal.com/cgi-bin/webscr?amount=24.95
&amp;item_number=5-pack
&amp;item_name=Five Pack
&amp;business=payments@your_site.com
&amp;no_note=1
&amp;cpp_header_image=http://your_site.com/images/pp_header.png&amp;currency_code=USD
&amp;lc=US&amp;add=1&amp;cmd=_cart
&amp;no_shipping=0&amp;return=http://www.your_site.com/linklokipnret.php
&amp;notify_url=http://www.your_site.com/linklokipn.php
&amp;bn value=PP-ShopCartBF
&amp;custom1=<?php echo $custom?>">
<img src="http://www.your_site.com/images/buy_now_button.gif" /></a> 

      </div>
</td>
    <td valign="top"><img src="http://www.your_site.com/images/paypal-logo.jpg" alt="" height="99" width="100"
border="0" />
</td>
  </tr>
</table>
<?php endif ?>


MODIFYING LINKLOK PAYPAL TO SEND DOWNLOAD EMAILS TO MULTIPLE RECIPIENTS - Dec 29th, 2018

This recipe works with the recipe "SHOW A FORM ONLY IF CERTAIN CRITERIA ARE MET" and requires the Website Membership
plugin.

Linklok is an inexpensive program that I've used for quite a while to handle PayPal transaction verification and supply
secure links for downloads (or web pages).

If you should decide to purchase linklok, or any other Vibralogic programs, I'd appreciate your using my affiliate link:

http://www.shareasale.com/r.cfm?B=12671&U=520135&M=3826

Thanks to a suggestion by Adrian Jones at Vibralogic.com, the company that created LinkLok PayPal, I used this code in
their linklokipn.php file: 

Search for Copy of Linklok Paypal and you should find this bit of code:

 

 if ($CopyEmail != "")
 {
  $subject = "Copy of Linklok Paypal email sent to $payer_email for order $txn_id";
  SendEmailOut($CopyEmail, $SellerEmail, $SellerCompany, $subject, $mailBody, $HTMLEmail);
 }
  


Change that code to:

 

 if ($CopyEmail != "")
 {
//   $subject = "Copy of Linklok Paypal email sent to $payer_email for order $txn_id";
  global $custom;
  if ($custom!="")
   $CopyEmail=urldecode($custom);
  SendEmailOut($CopyEmail, $SellerEmail, $SellerCompany, $subject, $mailBody, $HTMLEmail);


To use a Linklok custom download template which shows the email addresses that were sent, add the following code in
linklokipn.php to create a “custom” variable:

Search For:


$buf = str_replace("!!!payment_date!!!", $payment_date, $buf);


and after that line, add:


$buf = str_replace("!!!custom!!!", $custom, $buf); // added to allow $custom email addresses to be shown in download
email template


Then insert something like:


As you requested, download links have been emailed to:   !!!custom!!!


in the Linklok custom download template where you want to show the email addresses.

CREATING A REPORT TO SHOW SENT EMAILS:

I wanted to be able to render a report for each customer (using the website membership plugin and based on which
customer was logged in) that showed all the email addresses that they sent download links to.They needed to be displayed
as both an email list and broken down by date.

The first thing that I did was to create 2 new text box fields in the accounts database.
“Links Sent Emails Only” and “Links Sent Dates And Email”.

Then I inserted this code just before the PayPal button to update the account record of the person who was logged in to
make the purchase with the emails entered for that transaction:


$custom2 = $_REQUEST['custom1'];
 $sent_dates_and_emails =  date("m-j-y g:i:s a").': '.$custom2 .' * '  ;
       $sent_emails = $custom2 .', '  ;        
      $query = "UPDATE `{$TABLE_PREFIX}accounts` SET
                     links_sent_emails_only = CONCAT(links_sent_emails_only, '$sent_emails'),
                      links_sent_dates_and_emails = CONCAT(links_sent_dates_and_emails, '$sent_dates_and_emails'),
                      updatedByUserNum = '".mysql_escape( $CURRENT_USER['num'] )."',
                      updatedDate      = NOW()
                WHERE num = '".mysql_escape( $CURRENT_USER['num'] )."'";
      mysql_query($query) or die("MySQL Error:\n". htmlspecialchars(mysql_error()) . "\n");


Then, in the report viewer, I used this code to render the 2 email lists:

At the top the login redirect


 if (!$CURRENT_USER) { websiteLogin_redirectToLogin(); }


And in the body:


 <h3>EMAIL LIST (Complete)</h3>
                    <?php echo ($CURRENT_USER['links_sent_emails_only']) ?>
                    <hr />
                    
                    
                    <h3>EMAIL LIST (Broken Down By Date And Time)</h3>
                    <?php $dates_and_emails =  mysql_escape($CURRENT_USER['links_sent_dates_and_emails']) ; ?>
                    <?PHP $dates_and_emails preg_replace("[\*]i"""$dates_and_emails ); ?>
                    <?php echo $dates_and_emails ?>
                    <hr />
                    <?php endif?>

CHAPTER 5 - ADMINISTRATION



TRACKING USER LOGINS AND ADDED/MODIFIED/DELETED RECORDS - Sep 6th, 2022

Since Version 3.52, CMSB has included an "Audit Log" that tracks user logins and added/modified/deleted records. In
current versions, the Audit Log can be found under Admin Menu > Security Settings. (You'll have to load the Security
Settings page in order to see the Audit Log link in the menu). Or you can access it directly at
https://yoursite.com/cmsb/admin.php?menu=_log_audit


LIMITING THE NUMBER OF TEXT BOX CHARACTERS SHOWN IN A RECORD LIST COLUMN TO A SHORT SINGLE LINE - Nov 16th, 2022

By default (at least through V 3.57) if you included a text box field in the record list display of a multi-record
editor, the entire content of that field was displayed, making it difficult to display multiple records.

Thanks to Jeff Shields, a very knowledgeable coder and CMSB user, there’s now some easy ways to limit the number of
characters displayed in a record list column.

He offered 2 basic approaches. One using only CSS in a custom.css file that won’t get overwritten when you upgrade the
core CMSB files, and another that changes one of the core files and offers more granularity, but will be overwritten on
a CMSB upgrade.

I used the custom.css approach, and found hat I had to make some modifications to get the code to work.

First Jeff’s code. (You’ll have to change some of the specific values in order to make the code work for your
application.) Followed by my modified implementation.

Global Change (affects all tables) insert the following code block in a file called custom.css, in your root cmsb
folder.


table.data td {
    white-space: nowrap;
    overflow: hidden;
    max-width: 180px;
    max-height: 30px;
    text-overflow: ellipsis;
}

Limiting the effect to a single table

table.data[data-table="blog"] td {
    white-space: nowrap;
    overflow: hidden;
    max-width: 200px;
    max-height: 30px;
    text-overflow: ellipsis;
}

And if you wanted to change only a specific column (in this example, the column (myColumn) in the specific table (blog),
you would first change the lib/menu/default/list_functions.php core file on line 629 (Referencing V3.57), From this:
(UNTESTED)

$tdAttributes = "style='text-align:left'";

To this:

$tdAttributes = "style='text-align:left' data-column='$fieldname'";

And then create a custom.css file with the following code;

[data-table="blog"] [data-column="myColumn"] {
    white-space: nowrap;
    overflow: hidden;
    max-width: 200px;
    max-height: 30px;
    text-overflow: ellipsis;
}


I found it necessary to modify the code slightly to achieve a truncated single line display for a text box field column.

It seems that although all the CSS above did truncate any text that didn’t fit in the predefined max-width, each line
break in the text triggered the display of a new line with a new max-width of its own. This made my column display many
short lines, each with an ellipsis.

I really wanted only a short, single line of text to indicate that the field had actually been filled in, so I added the
following to my custom.css file.


[data-table="myColumn"] br {
    display: none;
}
 
I also found that the max-height had no effect on the display so I removed it from my CSS. (You may get different
results)

I had a a rather lengthy section description field in my table, so I had to change all the line breaks to <p> so that
the text would appear correctly at the top of the record list. 


PHP 8.1 ERRORS (VARIOUS) - Aug 30th, 2022

Thanks to CMSB user Deborah and Daniel Louwe Lead Technician at Interactive Tools for this one...

Deborah was getting the following error when using PHP8.1 with CMSB Version 3.56

Deprecated: preg_match(): Passing null to parameter #2 ($subject) of type string is deprecated in
/home/myweb/public_html/cms/lib/menus/database/editTable_functions.php on line 250 Warning: Cannot modify header
information - headers already sent by (output started at
/home/myweb/public_html/cms/lib/menus/database/editTable_functions.php:250) in
/home/myweb/public_html/cms/lib/menus/header.php on line 4

and line 250 in editTablefunctions.php looked like this: 

$startsWithHttpOrSlash = preg_match("|^(\w+:/)?/|", @$_REQUEST[$name]);

Daniel offered the following: 

To get rid of PHP 8.1 deprecation warnings (specifically ones that mention "passing null"), note which function and
parameter is throwing the warning (in this example, preg_match() and parameter #2), and add ??'' to the end of the
variable being passed to that parameter. It's also possible to remove the @ from the variable if there is one. 

So on line 250, this:

@$_REQUEST[$name]

becomes this:

$_REQUEST[$name]??''

There's another similar error noted in this post: https://www.interactivetools.com/forum/forum-posts.php?postNum=2245997

Other PHP 8 errors and some fixes are talked about in
https://www.interactivetools.com/forum/forum-posts.php?postNum=2246123
and https://www.interactivetools.com/forum/forum-posts.php?postNum=2246122


WANT THE WINDOW THAT SHOWS IN THE CODE GENERATOR TO BE WIDER - Aug 4th, 2010

Dave Edis, from Interactive Tools  says:

You can increase the width by editing this file: /lib/menus/admin/showcode.php

Search for "width: 100%" in the following and replace it with "width: 1200px" like this:



<textarea name="listViewerCode" wrap="off" spellcheck="false" rows="10" cols="50" style="width: 1200px; height: 400px;">



As always, make a backup or the original file in case your change doesn’t work


ADDING ADDITIONAL SPECIFIC REFERRERS TO ELIMINATE EXTERNAL SOURCE SECURITY WARNINGS - Jul 9th, 2020

 I wanted to add a 'modify this record' link on a search results page that's only available to admins so that they can
update information with less clicks.

Here's the link I inserted:

<?php if (@$CURRENT_USER['isAdmin']): ?><a href="cmsAdmin/admin.php?menu=books&action=edit&num=<?php echo $record['num']
?>" ><span class="text_font" style="text-decoration:underline">Modify This Entry</span></a><?php endif ?>


The problem was that the link kept throwing 'Security Warning: A link from an external source has been detected and
automatically disabled' errors.

With a bit of help from Greg Thomas, a senior programmer at Interactive Tools, here's the solution we came up with.

In lib/common.php search for, 'Security Warning: A link from an external source ' (line 2613 in CMSB Version 3.50) and
replace this code:

$programBaseUrl = _security_getProgramBaseRefererUrl();
$isInternalReferer = startsWith($programBaseUrl, $_SERVER['HTTP_REFERER']);
if (!$isInternalReferer) {
$format = "Security Warning: A link from an external source has been detected and automatically disabled.\n"; 

With this:

 $programBaseUrl = _security_getProgramBaseRefererUrl();
  $yourVar1= 'https://your_site.com/search.php'; // the authorized referring pages
  $yourVar2= 'https://your_site.com/m/search.php'; // the authorized referring pages
  $isInternalReferer = ( startsWith($programBaseUrl, $_SERVER['HTTP_REFERER']) || startsWith($yourVar1,
$_SERVER['HTTP_REFERER']) || startsWith($yourVar2, $_SERVER['HTTP_REFERER']) );
  if (!$isInternalReferer) {
    $format = "Security Warning: A link from an external source has been detected and automatically disabled.\n";

IMPORTANT!: When I attempted this change using DreamWeaver, it broke the CMSB admin page formatting. (even when I undid
the changes, the formatting issue remained) 

CMSB user Steve99 suggested that DreamWeaver's editor has corrupted pages for him in the past, and suggested trying the
edit with Notepad++ or Sublime text editors on a fresh, original copy of the file.
 


FIX FOR DISPLAYING VERY NARROW IMAGES IN EDITOR UPLOAD FIELD (THINK HORIZONTAL GRADIENTS) - Apr 25th, 2013

I was having a devil of a time displaying a  tall skinny .jpg (10px x 500px) that I'm using as a page background
gradient. 

Instead of a small thumbnail displayed in my editor upload field I was getting a really tall thumbnail and had to scroll
the thumbnail to see the bottom of it (or the “remove” link)?.

Fortunately Jason Sauchuk from Interactive Tools had some insights and a fix.

He said: 

"The reason for this is that the image CMS Builder displays inside the record editor comes from a function called
showUploadPreview().

In this function, a width for the preview image is set. This has been hard coded to use a width of 50 in the function
call. So if your image has dimensions of 10 X 500, it has an aspect ratio of 1:50. So if the preview image is given a
width of 50, it has a height of 2500!

If you want to override this to use the thumbnail width, here is some code to do this. Please note that I tried this
change through version 2.50, and if you upgrade your installation, this change will  be overwritten.

Open up cmsAdmin/lib/menus/default/uploadList.php. Around line 101 you should see this:



<?php showUploadPreview($row50); ?>


You can make this change to it:


      <?php 
         
    $width 50
        if (
$row['isImage'] && $row['hasThumbnail']) { 
          
$width $row['thumbWidth']; 
        } 
       
?> 
       <?php showUploadPreview($row$width); ?>


this will use the images first thumbnail width. If a thumbnail doesn't exist, it will default back to 50."



That takes care of the editor upload field,. To fix the Record List view, Damon Edis from Interactive Tools offered:

The file you want to modify is list_functions.php. At: /cmsAdmin/lib/menus/default/list_functions.php

Again, make a backup of that file first, just in case.

At (or around) line 575 there is the line:


showUploadPreview($upload, 50);


You can change the 50 to be another number, or to have it show the thumbnails actual size replace the line with this:


showUploadPreview($upload, $upload['thumbWidth']); 


Note: Any change to CMS Builder code will be overwritten in an upgrade so it is a good idea to create a custom code
change text log so that you can make these changes again in the future if needed


LOG OUT NOT LOGGING OUT IN VER 2.51+ ? A POSSIBLE FIX - Apr 25th, 2013

I was not using the Website Membership Plugin and I had a viewer on the site that contained information that was for
anyone with a user account and some additional information that was for admins only. 

Trouble was, that after I logged in to the page,  I couldn’t log out again, even when I followed the log out link. To
log out I had to manually destroy the session login cookies stored by my browser.

Greg Thomas came up with this solution, which may work for you. He said,

“For some reason the server was creating two different sessions depending on if you are logged in at
http://your_site.com/ or http://www.your_site.com. As the log off link was to www.your_site.com this was causing an
issue if the user had logged in at the non www version. I've got around the problem by adding a rule to your htaccess
file that redirects all site visitors to your_site.com. I've also added the session_unset to the if statement that
checks if the user is logged in, as this should completely destroy the session.”

The .htaccess rule is:

# Redirect www urls to non-www
RewriteEngine on
RewriteCond %{HTTP_HOST} ^www\.your_site\.com [NC]
RewriteRule (.*) http://your_site.com/$1 [R=301,L]


The viewer code including the session_unset code is:

<?php
 
    if (!defined('START_SESSION')) { define('START_SESSION'true); }
  
// load viewer library
  $libraryPath 'cmsAdmin/lib/viewer_functions.php';
  
$dirsToCheck = array('/path_to_your_server/','','../','../../','../../../');
  foreach (
$dirsToCheck as $dir) { if (@include_once("$dir$libraryPath")) { break; }}
  if (!
function_exists('getRecords')) { die("Couldn't load viewer library, check filepath in sourcecode."); }
  
//Get the current CMS users details
  $CMS_USER getCurrentUserFromCMS(); 
  
  
//redirect the browser if no user is currently logged into the back end
  if (!@$CMS_USER['num']){ 
    
session_unset();
    
redirectBrowserToUrl("http://your_site.com/cmsAdmin/admin.php?redirectUrl=" $_SERVER['REQUEST_URI']);
    exit;
  }
 
?> 


And in the body of my viewer to separate access for admins and regular users:

<?php if( $CMS_USER['num']): ?>
 <a class="special" href="http://your_site.com/cmsAdmin/admin.php?action=logoff">Click Here to Log Out</a><br /> <br />
Code for any user...
      <?php endif ?>
<br />
 <?php if( $CMS_USER['isAdmin']): ?>
Code for admins only...
<?php endif ?>


IDIOSYNCRACIES WITH CMSB - Aug 4th, 2010

Are you finding that every once in a while there are strange goings on when you attempt to use a function in CMSB that
worked just a short while ago?
 
Try renaming the current likely culprit folder in your installation as .bak or .safety and upload a fresh copy  from the
version of CMSB that you’re using. Of course you all diligently upgrade all of your installations of CMSB to the
latest version and keep backups of your older version files until you do, right?

Well, starting now, make sure that you keep “virgin” backups of the various versions that you’ve got running in
the real world and don’t mix them up. If you’re missing something, a quick post on the 

      http://www.interactivetools.com/forum/Products_C2/CMS_Builder_F35/ 

can probably get you what you need.


CREATING A VIEWER TO LIST THE USERS AUTHORIZED TO ACCESS EACH SECTION, AND THEIR ACCESS LEVELS - Oct 12th, 2015

Here’s a viewer that Dave Edis of Interactive Tools has provided for just that purpose (Modified by Damon Edis). The
code has been updated to work with 2.6+ version of CMSB. 

Only admins can use this form, but there’s an added perk here, because you’ll be logged in to CMSB as an
administrator, you can click on a user and you’ll be taken directly to their profile where you can modify their
permissions. Note: you can’t add users through this page, you’ll have to log in the old way to do that.

Here’s the code for the viewer page. UPLOAD IT TO YOUR cmsAdmin DIRECTORY

Note that the code assumes that you've got a first_name and a last name field in your accounts database. If you're still
using the fullname field, change these to fullname instead.

 
<?PHP
# Require Login
define('START_SESSION'true);
require_once 
"lib/viewer_functions.php";
require_once 
"lib/admin_functions.php";
$CURRENT_USER getCurrentUserFromCMS();

if (!@
$CURRENT_USER['isAdmin']) { die("This page is only available for admin users!"); }


# load access levels
list($accessListRecords) = getRecords(array(
'tableName' => '_accesslist',
'loadCreatedBy' => false,
'loadListDetails' => false,
'orderBy' => 'tableName',
'where' => 'accessLevel >= 6',
));

# load users
list($usersRecords) = getRecords(array(
'tableName' => 'accounts',
'loadCreatedBy' => false,
'loadListDetails' => false,
'orderBy' => 'last_name',
'allowSearch' => false,
));

# create lookup array of users by num
$usersByNum = array();
foreach (
$usersRecords as $user) {
$usersByNum[$user['num']] = $user;
}


?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8" />
<style type="text/css">
<!-- (YOUR CSS STYLES GO HERE) -->
</style>

<!-- (OR THE LINK TO YOUR EXTERNAL STYLE SHEET GOES HERE) -->
<link href="your.css" rel="stylesheet" type="text/css" />
</head>
<body>

<h1 class="your_css_style">Users By Section</h1>

<div>
Viewer - User can view any record in this section (must also be enabled in section editor)<br />
Author - User can only access records they have created
Author & Viewer - User can view any record and modify records they've created<br />
Editor - User can access any records in this section<br />
</div>

<?PHP foreach ($accessListRecords as $accessRecord): ?>

<?PHP ?>
<?PHP if (@$lastTable != $accessRecord['tableName']): ?>
<h2 class="your_css_style"><?PHP echo $accessRecord['tableName']; ?></h2>
<?PHP endif; ?>
<?PHP $lastTable $accessRecord['tableName']; ?>

<?PHP ?>
<?PHP if ($user = @$usersByNum[$accessRecord['userNum']]): ?>

<a class="your_css_style" href="admin.php?menu=accounts&action=edit&num=<?PHP echo $user['num'?>"><?PHP echo
$user['first_name'?> <?PHP echo $user['last_name'?></a>
<?php if($user['isAdmin']) : ?> - Admin <?php endif; ?>
<?php if(@$accessRecord['accessLevel'] == ) : ?> - Author <?php endif; ?>
<?php if(@$accessRecord['accessLevel'] == ) : ?>  - Author &amp; Viewer<?php endif; ?>
<?php if(@$accessRecord['accessLevel'] == ) : ?> - Editor <?php endif; ?>

<?PHP endif; ?>

<?PHP endforeach ?>

</body>
</html>



TEXT STYLING TRICKS FOR TEXT FIELDS AND TEXT BOXES - Aug 4th, 2010

The concept of WYSIWYG fields can be pretty scary for designers who want to give their clients a bit of creative
flexibility but are afraid that their designs will be trashed on a regular basis.
To balance these worlds, I’ve used a combination of Text Fields and Text Boxes and given some of my clients the
following information in their users manual. 
I realize that you all know this already but I thought that it might be nice to have it written out so that you can cut
and paste the information. Add your own favorites to the list.

“There is one caveat: They need to look at the result and make sure that all the results are what they expected. 

With that, here’s what I tell them:

STARTING A NEW LINE OR  PARAGRAPH 
You may have already discovered that if you press the enter key when typing in a multi line “text box”, you’ll
start a new line, and that if you want a new paragraph that you need to press enter twice. 

You may also have discovered that this technique doesn’t  work in “text fields” with only one visible line of
text. If you want to start a new line in a single line “text field”, you’ll need to insert one of those special
characters,  the line break.  If for some reason you want a new paragraph, just insert a <p> instead.
This works for all text fields including image titles and captions.

SPECIAL CHARACTERS
There are many special characters that can be inserted into your text, including those found in other languages. The
codes for all special characters must have a leading ampersand (the & sign) and a trailing semi-colon. A few of the more
common are:

Character - Code
© - &copy;
® - &reg;

Insert the code where you want the special character to be inserted, just don’t forget to add the leading “&” and
the trailing “;”.

Here’s a link to a much more comprehensive list.

http://www.htmlcodetutorial.com/characterentities_famsupp_69.html

STYLING YOUR TEXT
First a short explanation of how web browsers interpret special codes or “markup” that you insert in your text. 

Most markup “tags” have two parts, an opening tag, that tells the browser where a specific effect is supposed to
start, and a closing tag that tells the browser where to end the specific effect. 

Here’s an example:
If you use the <strong> opening tag and then insert the </strong> closing tag, your web page would display: “If you
use the opening tag and then insert the closing tag”. Bear in mind that tags do not create extra spaces between
letters.

Here are few of the more useful tags:

Effect - Opening tag\Closing tag
Bold Text  - <strong></strong>
Underline text  - <u></u>
Italic text - <em></em> 
Superscript (surround  © and ® with this one) - <sup></sup>
Bulleted list (must be placed surrounding each item you want in the list. A “return” is automatically inserted after
each item.) - <li></li>

One that’s more complex but can be very useful is the one that inserts a hyperlink or “link” in the text. Here’s
the format: 

<a href=”http://www.thewebsite.com/thepage.php”>the link text </a> 
(Don’t forget the quotes around the target web page, and to test the finished link on the actual web page)

There’s also some useful single tag markup that can be used to style text. Examples are:

The horizontal line <hr>, which can be modified to specify it’s length and color. So, <hr width="300"
color="#99945e"/> would render a horizontal line that is 300 pixels wide and muddy green in color.

The line break  that  we mentioned earlier which starts a new line.
The paragraph <p> which starts a new paragraph.

Using these tags can drastically change the look of the web page, so make sure to check how they look and adjust
accordingly. 


CHANGE THE DEFAULT 25 RECORDS PER PAGE ON A RECORD LIST - Mar 7th, 2013

NOTE: You can’t drag a record from one section editor record list page to another because the list is too long to fit
on one page...

So to change the default value of "Per Page" pull down to a higher number (say 1000) and still leave the pull down menu
in tact:

FOR CURRENT VERSIONS (1.17+)

If you go to the Section Editor section in the left hand menu, then click modify on a section you want to edit. At the
top of the modify section page should be 5 tabs, if you select the advanced tab you should see a per page drop down that
allows you to select how many records should be displayed per page. Unfortunately this is only a per editor solution.

You can also search through the site's schema files and replace all occurrences of  '_perPageDefault' => '25', with 
'_perPageDefault' => '1000'. 

Or, if you're using Version 2.51 or later, you can implement a global change using the plugin in the recipe called THE
CHANGE RECORDS PER PAGE DEFAULT PLUGIN, which was offered by Dave Edis from Interactive Tools 

AND FOR PRIOR VERSIONS

There is a pull down menu on the view records page allowing you to display up to 1000 records per page, but the default
is still 25 records.

Chris from Interactive Tools offered a way to change that default number. 

He said:

This value is hard coded in lib/menus/default/list_functions.php. You can change it there, but you'll need to remember
to make the same change again if you ever upgrade CMS Builder.

It should be on line 70:



    'perPage'                 => $isRelatedRecords ? $perPage : getFirstDefinedValue( @$_REQUEST['perPage'], 25 ),


You should be able to change that to any of these values:

5, 10, 25, 50, 100, 250, 1000 

You can change the number in the perPage line to whatever value you want provided it's in the drop down list already.
 

If you want to change the values in the drop down list go to cmsAdmin/lib/menus/default/list.php and find this line of
code:



<?php echo getSelectOptions($metaData['perPage'], array(51025501002501000)); ?>
 


You can add values to this list to add them to the drop down box.

Once you're done this, log out and log back into CMS Builder, you should see you changes.

NOTE: if you upgrade CMS Builder, these changes will be overwritten.

 

FOR OLDER VERSIONS
Here are the instruction for older versions.

Edit (backup the old file first) this file:
/lib/menus/default/list.php

You can add a new value to the per page list. Just search for "100" and you’ll find:



<option <?PHP selectedIf($listDetails['perPage'], '100')?>>100</option>



 Then add a new option to the list. So, if you want 500 records per page to show, then add:



<option <?PHP selectedIf($listDetails['perPage'], '100')?>>100</option>
<option <?PHP selectedIf($listDetails['perPage'], '500')?>>500</option>



or



<option <?PHP selectedIf($listDetails['perPage'], '5000')?> value="5000">All</option> 



This will allow you to get the required number of records on a page so that you can drag them up and down at will.


PUBLISH DATE AND REMOVE DATE IS NOT DEFAULTING TO TODAY'S DATE - Aug 4th, 2010

When you create a new record they should default to the current date. Check your timezone offset under Admin > Regional
Settings.

***This will ONLY affect new records, it will not change existing records.***


WANT TO SHOW A RECORD COUNT ON YOUR WEB PAGE - Dec 10th, 2016

You can use this to see the total number of records displayed:



<?php echo $your_table_nameMetaData['totalRecords']; ?> 



There are a few other variables that can be used besides “totalRecords”. You can list them all with this code:



<xmp><?php print_r($your_table_nameMetaData); ?></xmp> 



Here's another approach to counting the number of records in a particular category. (change the "if" criteria to suit
your needs, and if you're using more than one counter on your page, change $count to some unique name, like $count2, or
$count3),

First set a counter to zero. 
Then in your "foreach" loop, if a record meets a particular set of criteria (like field value not = "banana") ,
increment the counter by 1 for each record that meets that criteria. 
Finally, display the record count.



<?php $count 0?><?php foreach ($my tableRecords as $record): ?><?php if ($record['your field'] != 'banana'): ?><?php
$count++; ?><?php endif ?>


If you just want to list records with contents in a field, use this instead


<?php $count 0?><?php foreach ($my tableRecords as $record): ?<?php if ($record['your field'] ): ?><?php $count++;
?><?php endif ?>


And where I want to display the count:


There are <?php echo $count ?> records


Another approach suggested by Jason Sauchuk from Interactive Tools, is:

use the function mysql_select_count_from(). This allows you to set a table name and a where clause. The function will
then return the number of records that meet that where clause.

For example, let's assume you're storing all your items for sale in a table called "items". This table has a field
called "type" whose value is the "num" field that you're outputting on your list page. You can then use the function to
display how many items you have for each type like this:



<?php foreach ($product_typesRecords as $record): ?>  
   <a href="results.php?type=<?php echo $record['num'?>"><?php echo $record['title'?></a> [<?php  echo
mysql_select_count_from('items'"type ='".$record['num']."'");?> Items for sale]  
<?php endforeach ?>




LEAVE INSTRUCTIONS FOR THE PERSON MODIFYING A PARTICULAR PAGE IN THE HEADER OF THE WYSIWYG EDITOR - Aug 4th, 2010

There’s no direct way to do this, but you could create a separate text box called "notes" or “instructions” that
isn't displayed on the website but just used to store and display notes for authors and editors. 

Make sure to set “Admin Only - Field can only be modified by admin or section manager” in the advanced options so
that the note can’t be altered by mistake.


SAVE A CUSTOM MENU/SECTION AS A TEMPLATE AND CREATE NEW SECTIONS FROM IT? - Aug 4th, 2010

All you need to do is copy a menu file ( ending in .ini.php) from /data/schema/ to /data/schemaPresets/ and it will show
up in the “add section” pull down. 

To change the name that appears in the pull down list  - change the “menuName” value in the first section of the
nnn.ini.php file. 

Change any other “label” values to generic values if necessary.


REMOVE THE “VIEW WEBSITE” LINK FROM THE "LOGIN" AND "WELCOME" SCREENS - Aug 4th, 2010

It's not supported, but if you are comfortable editing PHP code you can do it like this:

- Open /lib/menus/header.php
- Make a backup of that file
- Search for "View Website"
- Add the lines indicated above and below that line:



<!-- Insert This Code -->
<?PHP if ($CURRENT_USER && getRequestedMenu() != 'home'): ?>
<!-- End of Insert-->
<td ... >View Website &gt;&gt;</a></td>
<!-- Insert This Code -->
<?PHP endif; ?>
<!-- End of Insert-->


You’ll have to make sure to add these mods to any version updates that you install.


REMOVE "TIP: HOLD <CTRL> TO SELECT MULTIPLE FILES" FROM UPLOAD FIELD - Dec 31st, 2012

This isn't an official hack, but if you search for:

echo htmlspecialchars( t("Tip: hold $key to select multiple files") );

in cmsAdmin>lib>menus>default>edit_functions.php

(it's around line 687 in version 2.17)

Then comment out that code (insert // before the echo) it should hide that text form displaying under the Add or Upload
File(s).

You can also change the text between the quotes.

You can also search for  <a href="<?php echo $uploadLink ?>" class="thickbox"><b><?php echo t('Add or Upload File(s)')
?></b></a> and remove the (s) or change the link text to better meet your needs.

**** IMPORTANT: MAKE A BACKUP COPY OF edit_functions.php BEFORE YOU TRY THIS****

NOTE 1: This will change the text under all upload fields, site wide.

NOTE 2: You'll have to redo this change if you upgrade to another version of CMS Builder.


SET THE HELP OR WEBSITE URL TO POINT TO YOUR SPECIFIC WEB SITE OR HELP DOCUMENT - Aug 4th, 2010

Go to the "Directories and URL" section of the general setup options. 

The "view website URL" entry says /#setThisInAdminMenu.

As of version 1.19 the view website just links to a single url. There's no specific page preview function yet.

Replace the contents of these fields with whatever you want in that field such as:

 http://www.yousite.com/myhelpdoc.php or http://www.yoursite.com

You can safely experiment with those fields. They won't change anything but the links in the corner of the editor
screen.


MOVE A CMSB SITE FROM ONE SERVER TO ANOTHER WITHOUT FEAR - Mar 23rd, 2019

 At some time, many of us will need to move a CMSB web site from one server to another. With recent versions of CMSB
it's become much easier to move an installation:

There are a lot of details here, but the process is pretty straight forward. Just remember backups are the key to
success.

- Back up your CMSB  database Admin > General > Database Backup > all database tables.

- Download your entire CMSB cmsAdmin folder.

- Add the current version number to the folder name so it won’t be overwritten and you can re-upload it if the upgrade
fails.

- Upgrade your CMSB installation to the latest version. NOTE: if you're using the membership plugin, and upgrading from
a version prior to V2.08, remember that the encrypted passwords in version 2.08 and later mean you'll have to update a
number of your viewers. (See the recipes for the Website Membership plugin for more on this) 

- Back up your new upgraded database Admin > General > Database Backup > all database tables.

- Make sure that you can easily identify the latest database backup by the file name, since time stamps will be
overwritten when you download the backups (you can safely change the filename as long as you leave the original
extension in tact).

- Make sure that all your plugins are up to date for the new PHP and CMSB versions.

- Make sure that everything works.

- Download your entire upgraded site including the CMSB cmsAdmin folder again.

- You can (and should) have more than one installation of CMSB during transfers, staging, etc., so I wouldn't delete any
information from your old server until you're sure that things are working on the new one.

- Make sure that there are no references to an older version of PHP in any php.ini files that you’ve copied.

- If possible, create the database on the new server with the same parameters, users  and passwords as the old one.

- Create an identical file structure on your new server if possible. Locations get tricky because different ISPs servers
have different folder structures and may have a separate server for databases, but you should be able to work it
through. Just keep track of any differences, because "The Devil will be in the Details".

NOTE: I usually create a separate subdirectory in my public_html folder with the name of each domain hosted on that
account (minus the tld (.com, .org, etc.), and then use an .htaccess file to redirect visitors to the appropriate
subfolder. (Suggested code below). 

- Assign your domain to the new server. This will probably require un-assigning it from the old server. 

- NOTE: Reassignment can take a while due to hosting schedules and propagation issues, so choose a time that you expect
less site traffic. 

- WARNING: Make sure you have a record of all the existing cron jobs, email accounts and email forwards since they will
be deleted on an unassign.

- NOTE: If you decide to place your site files in a sub folder, before you upload your entire site, 1) place similar
test index.html files that says something like: "yoursite.com is currently down for maintenance and should be back up
shortly" in the public_html folder and the new subfolder, 2) Add some indication to each of the files so you can tell
which location you're accessing. and 3) Make sure that you can access the one in the subfolder with the .htaccess
method. 

- I had some difficulty using the subfolder and .htaccess method above for a single domain hosted on a Bluehost server,
and ended up having to put all of the web files directly into the public_html folder, but hopefully your experience will
differ. (Most tech support is not very forthcoming when sorting out .htaccess issues.) 

- Upload the entire domain directory to your new server’s public_html folder or your subfolder.

- Remove the /cmsAdmin/data/isInstalled.php file from your new server

- Run admin.php and fill out the install screen 

- NOTE: If you get an error message about upload folder paths when you try to access the admin.php file, open the
data/settings.dat file from the new site, enter the correct value, then re-upload

- Select "Restore Backup" instead of "Create Admin User" to restore the site data from an existing database (the latest
backup).

- Using the code generator, check the path to cmsAdmin/lib/viewer_functions.php in your test viewer. If it's different
from that on your old viewer pages, you'll have to change them all and re-upload your revised files. 

- Check any path specific information in your viewers, javascript or other external files and correct accordingly.

- Set up all new cron Jobs, email forwards and email accounts on the new server account.

- Look over the site carefully to make sure that to make sure that all path names are still valid. There are some
entries under the Admin > general tab that may need manual adjustment. 

- NOTE: Don’t forget to clear your browser cache and reload any pages before deciding things are broken.

- Check all pages and check all links and includes to make sure that they still work.

- Check forms and other ad-ons and make sure they still work as planned.

If you have any issues that you can't solve, post the specifics on the CMSB forum and you’ll probably get an answer
very shortly.

Hope these suggestions make the task of changing servers easier.


# php – Possible .htaccess code. Note that there’s a code block for each tld.

# php -- BEGIN cPanel-generated handler, do not edit (depending on host, this block may vary, or not exist at all)
# Set the “ea-php72” package as the default “PHP” programming language.

<IfModule mime_module>
  AddType application/x-httpd-ea-php72 .php .php7 .phtml
</IfModule>

# php -- END cPanel-generated handler, do not edit

RewriteEngine on
RewriteCond %{HTTP_HOST} ^(www.)?your_domain.com$
RewriteCond %{REQUEST_URI} !^/your_domain/
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ /your_sub_folder/$1
RewriteCond %{HTTP_HOST} ^(www.)?your_domain.com$
RewriteRule ^(/)?$ your_sub_folder/index.php [L] 

RewriteEngine on
RewriteCond %{HTTP_HOST} ^(www.)?your_domain.org$
RewriteCond %{REQUEST_URI} !^/your_sub_folder/
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ /your_sub_folder/$1
RewriteCond %{HTTP_HOST} ^(www.)?your_domain.org$
RewriteRule ^(/)?$ your_sub_folder/index.php [L] 

RewriteEngine on
RewriteCond %{HTTP_HOST} ^(www.)?your_domain.net$
RewriteCond %{REQUEST_URI} !^/your_sub_folder/
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ /your_sub_folder/$1
RewriteCond %{HTTP_HOST} ^(www.)?your_domain.net$
RewriteRule ^(/)?$ your_sub_folder/index.php [L] 

RewriteEngine on
RewriteCond %{HTTP_HOST} ^(www.)?your_domain.info$
RewriteCond %{REQUEST_URI} !^/your_sub_folder/
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ /your_sub_folder/$1
RewriteCond %{HTTP_HOST} ^(www.)?your_domain.info$
RewriteRule ^(/)?$ your_domain/index.php [L] 


IMAGES WON’T SHOW AFTER MIGRATING TO A NEW SERVER - THE FIX IS IN - Aug 4th, 2010

If you’ve tried to move your web site from one server to another and even after you’ve carefully updated all the
paths checked your settings.dat file you’re still struggled with existing images not appearing as they should, well
struggle no more.

Although as Dave Edis from Interactive Tools acknowledges (as of version 2.03) that the issue centers on the fact that
the full image paths are stored in the cms_uploads table instead of relative ones, there’s still a pretty easy fix.

Instead of manually updating values in the filePath, urlPath, thumbFilePath, thumbUrlPath columns in the cms_uploads
table, Dave suggests that “...the quickest workaround for now is as follows:

- Click "Backup" under: Admin > General
- Download the new .sql backup file from /data/backups
- Create a backup copy of this file.
- Open the original in your favorite text or file editor
- Do a search and replace for "oldpath" to "newpath"
- Upload it and click restore

Basically: Backup -> Edit .sql file -> Restore

That's also a handy way to do other search and replaces if needed.” 


BACKING UP AND RESTORING DATABASES USING PHPMYADMIN (LEGACY) - Aug 4th, 2010

***NOTE: The facility for automatic database backup was incorporated in version 1.34 which was released on September 8,
2009. 

See the section GIVE YOUR CLIENTS A SECURE FEELING WITH AUTOMATIC BACKUPS in the Best Practices section for more
specific information on this topic.

This example was written for a database called “web_cms ” which is stored on a server at IXWebhosting.com, so your
specifics will differ, but the basic approach is the same. It is here for informational purposes only.

IXWebhosting.com offers a utility program called “phpMyAdmin” to create database backups. It is important to follow
all of the instructions below since phpMyAdmin can also be used to make changes that will make the data unusable on the
web site.

To back up the databases:

1) Log on to the IXWebhosting control panel at https://manage.ixwebhosting.com with the username and e-mail address
given to you by the administrator. 
2) Click the “Manage” icon under “Hosting Products”.
3) Scroll down to the “Databases” section and click the “phpMyAdmin” icon
4) Login to phpMyAdmin by clicking on the “launch” icon to the right of the user web_abcdadm.

You will be backing up the 2 databases that store the web site’s information. They must be backed up one at a time.
One database is named “web_cms ” and the other is named ”information_schema“.

5) On the pull down menu on the left, select the web_cms
6) Click the 'export' tab located near the top of the screen.
7) In the left “export” section, click “select all”
8) Check the format type “SQL” (if it is not already checked)
9) On the right, in the “structure” section check the “Add DROP TABLE / DROP VIEW” box
10) Leave all other options at their default settings
11)  At the bottom, check 'Save as File'. You do not have to change the file name template information.
12) Click the “go” button

Depending on your browser's settings, the backup file may automatically download or your browser may prompt you to save
the file. If prompted, click “save” and “OK”.

After the backup has been saved to your computer, select the ”information_schema“ database from the pull down menu
on the left, and repeat steps 6 through 12

That’s it, you can close the browser window for phpMyAdmin. Your database has been backed up.

Here’s a link to a video tutorial that will walk you through the process. Some of the screens may be slightly
different due to software upgrades, and the details of navigating to your phpMyAdmin may differ, but the tutorial is
very detailed.

       http://www.ixwebhosting.com/index.php/v2/pages.Tutorial_02


IMPORTING DATA TO YOUR ON LINE DATABASE - Aug 4th, 2010

A large amount of data (an organization membership database) needed to be imported into a CMSB database from an Excel
spreadsheet . 

Navicat  http://mysql.navicat.com/  did the job flawlessly. The program will accept data from most other formats as
well. 

At this writing they charge US $179 for either the Windows, Mac or Linux version (all are available with a 30 day fully
functional free trial). We don’t get anything from Navicat if you buy the program, except the good feeling that
you’ve been able to accomplish a task more easily because of a recipe in the CMSB Cookbook.


OVERRIDING THE DEFAULT COLUMN TYPE IN YOUR DATABASE TO DECIMALS? - Aug 4th, 2010

Zaba had that request and Dave said:

If you want to actually override the mysql column type you can specify that in CMS Builder's schema file directly in
/data/schema/yourSection.ini.php like this:



customColumnType = "int(10) unsigned NOT NULL"



You can have a look at /data/schema/uploads.ini.php for an example where you’ll find:



[order]
order = 2
customColumnType = "int(10) unsigned NOT NULL"



Saginetics added:
You can also try:



<?PHP echo number_format($record['number'] ,2?>



The ",2" tells it to give two decimal places (you can change this to 8)

The "number_format" tells it to treat it like a number to include comma separators.

If you plan on sorting by numbers, then use this:



'orderBy' => 'price+0 DESC',



USING CMSB WITH .HTML EXTENSIONS OR WITHOUT .PHP EXTENSIONS - Aug 4th, 2010

This one’s in the on-line CMSB documentation at 

       http://www.interactivetools.com/docs/cmsbuilder/file_extensions.html 

but you may have missed it.

This page outlines techniques that you can use to render CMS Builder's PHP code on your pages without having to use the
".php" file extension, or to remove the file extension from your pages entirely. The methods described here require that
your site is hosted on an Apache server (most Linux hosting plans use Apache).

RENDERING PHP CODE ON ".HTML" FILES
 This method outlines how you can use PHP code on files created with the ".html" extension. 

Create a plain text file called ".htaccess", and add the following line to it:



Addhandler application/x-httpd-php .html .php



Now upload this file to the root web directory on your server. You should now be able to use CMS Builder's code on any
page with the ".html" or ".php" extensions. You can even add your own custom extensions to the code above if you'd like,
such as ".shtml" or ".htm". This method is ideal for situations where you need to use CMS Builder on a site that
currently uses static HTML pages, but would like to preserve the site's existing URL's.

USING MOD_REWRITE TO CHANGE THE FILE EXTENSION
This method will redirect any link that uses the ".html" extension to the corresponding file with the ".php" extension.
The URL in the visitor's address bar will not display ".php", so this process is entirely transparent. To do this,
create a plain text file called ".htaccess" and add the following code to it:



RewriteEngine on
RewriteBase /
RewriteRule ^(.*)\.html$ $1.php [nc]



Once you've uploaded this file to the root directory of your web server, if someone visits a page called "home.html",
the server will show the page using the code from the "home.php" file on your server.

USING MOD_REWRITE TO REMOVE THE FILE EXTENSION
This method will remove the ".php" file extension from all of your pages, so that you can use an URL like
www.yoursite.com/news instead of yoursite.com/news.php. Create a plain text file called ".htaccess", and add the
following code to it:



RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME}.php -f
RewriteRule ^(.+)$ $1.php [L,QSA]



Once this file has been uploaded to the root directory on your web server, the ".php" extension will no longer be
required in your URL's to access pages.


SEE ALL THE FIELDS AND VALUES AVAILABLE TO BE USED - Aug 4th, 2010

As of V 1.35 there’s a new showme() function - You can use this during development while creating viewers. Try 



<?php showme$records ?> 



with any variable name to see all the fields and values available to be used.


RATHER SHOW YOUR ADMIN GROUP AT THE TOP OF THE MENU? - Aug 6th, 2010

If you’ve got a long list of sections, it can mean a lot of scrolling every time you need to access the Admin group.
Here’s how to get the Admin group to show at the top of the section list.

If you look in cmsAdmin/lib/menus/header_functions.php there's a line like this:



$showAdminAtTop = false;



According to Dave Edis from Interactive Tools, if you change that to true the Admin group will show at the top.

Note that one side effect of that is any menu options that were previously at the top that weren't inside a menu group
will then appear as if they are in the admin group.


HIDING FIELDS FROM SECTION EDITORS BUT NOT FROM ADMINS - Nov 6th, 2011

As of version 2.08, this feature was added:
Editors: Fields can now be set to "Editor only" or "Admin only"

Go to Admin > Section Editors > Your Section

Then select the field you want to change access level on and click modify. 

Next, scroll to the bottom and set the Access Level.

LEGACY INFORMATION (Pre CMSB Version 2.08)

Normally when you restrict access to a field through “Advanced Options” to “Admin Only - Field can only be
modified by admin or editor user” , you get exactly that. Both Admins, and users with editor access can see and modify
the information in those fields.  

Here are some code changes Dave Edis, from Interactive Tools offered to keep those fields restricted to admins only and
not available to “editor” users.

Be sure to make backups and test any changes. 

- Open /cmsAdmin/lib/menus/default/edit_functions.php
- Search for:



!$CURRENT_USER['isAdmin'] && !$GLOBALS['hasEditorAccess']



and remove the && !$GLOBALS['hasEditorAccess'] code:

if (@$fieldHash['adminOnly'] && !$CURRENT_USER['isAdmin'] && !$GLOBALS['hasEditorAccess']) { continue; } // skip admin
only fields



- Open /cmsAdmin/lib/menus/default/save.php
- Search for:



!$CURRENT_USER['isAdmin'] && !$GLOBALS['hasEditorAccess']



and remove the && !$GLOBALS['hasEditorAccess'] code (from two lines):



if (@$fieldSchema['adminOnly'] && !$CURRENT_USER['isAdmin'] && !$GLOBALS['hasEditorAccess']) { continue; } // skip admin
only fields

if (@$fieldSchema['adminOnly'] && !$CURRENT_USER['isAdmin'] && !$GLOBALS['hasEditorAccess'] ) { continue; } // skip
admin only fields 



This helped me when I had a field in a “membership” section called “Founding Members”  that I didn’t want to
have changed or seen by anyone other than Admins.


THE PUBLIC DEMO MODE - Dec 29th, 2018

Starting with version 1.29 Interactive Tools has implemented a "Demo" mode for CMSB. This means that you can put
together a demo for general marketing purposes, for a specific client to try out your interface design, to use in focus
group testing, or for anything else that you can think of, and no matter what they do, the users can not break anything.


When someone logs in to the demo mode, a temporary copy of your existing database is automatically created, and the user
makes their changes on that copy, not to the original. After an hour, the copy is erased, and although the user can log
in to the demo again, they will be working on a fresh copy of your original database. 

Setting up a demo site is easy.

Just two caveats...

1) You can't use one install (or one license) for both a demo and a standard installation simultaneously. The good news
is that you can buy a "Demo Only" license for 75% off (~ $50 US). Just use promo code CMSDEMOCOPY and you're good to go.

2) I found that depending on which version of CMSB you're using, some or all of your plugins are disabled and won't work
when you enter the demo mode and some or all of them may need to be manually re-enabled if you you return to the normal
mode. If you make the plugins system plugins, they will not be disabled.

According to Jason Sauchuk at Interactive Tools: 

The reasons plugins are disabled in demo mode is to restrict functionality.  Demo mode is meant to provide a very basic
implementation of the software.  By making plugins system plugins, you're making that functionality available to people
in demo mode.  This isn't really a security flaw (they're not able to do anything with the plugin that they wouldn't be
able to do normally), but could give you some unwanted results.  For example, if you leave auto backup enable during
demo mode, you'll end up backing up the temporary demo tables, which wouldn't be what you want.

That said...

To make a plugin into a system plugin, you only have to add one line of code to the plugin's header: "Required System
Plugin: Yes"  .

Here's and example of a system plugin header with the code added:



<?php 

?>


SETTING UP A DEMO SITE
In a new folder on your server, create a separate installation of CMS Builder. 

You should also set up a separate MySQL database for the demo.

Then set up your demo site as you would any other site powered by CMS Builder.

The demo will not allow a user to login unless there's a valid user account for the username and password they enter.
So, set up a new user account for the demo (I use the word demo for both the username and password) and assign which
folders you want to be accessed for the demo user. (This way you can create a help section that can't be accessed)

CHANGING THE DEMO MESSAGES
If you want to change the default messages that appear on the demo pages, you'll find them in the
cmsAdmin>lib>common.php file and in the displayDemoNotice.php plugin that's included with CMS Builder (Remember, that
you'll want to make this plugin into a "System" plugin so it will be activated in the demo mode.)

Here's an example of the ones I've used. 

In the common.php file:


// otherwise, create new demo
  else {
    echo t("Your personal copy of the files required for this demonstration is being created...") . "<br /> \n";
    _removeOldDemos();
    $demoNum = _createNewDemo();
    $_SESSION['demoCreatedTimeAsFloat'] = $demoNum;
    $refreshUrl = @$_REQUEST['resetDemo'] ? '?' : thisPageUrl();
    printf(t("You'll be redirected to the login page as soon as the files have been created."), $refreshUrl);
    print "<br /> \n<meta http-equiv='refresh' content='1;$refreshUrl' />";

    //
    showBuildInfo();

    exit;
  }
}


and In the Plugin:


 //
  $html .= "<br /> <br /> <div style='font-size: 12px; color: #C00'><b>NOTE: For security reasons, some features are
disabled in the demonstration mode
.<br /> 
When you change any information, you are only affecting a personal copy of the demonstration database
.<br />
**No one but you can see the changes you've made.**
<br />The demonstration will reset and revert to the original data in $minutesRemaining minutes.</b></div>";
  return $html;
}


ADDING A "RESET" SWITCH
If you want to add a reset "panic" button to your demo, you can add it as a new section editor with the type of
advanced>text link. This will appear in your Section editor menu list and when clicked it will reset the demo to it's
original status.

Note: I had to shorten the "Table Name" considerably or I got "couldn't create demo" errors, but I left the "Section
Name" as noted below.

I called my "Panic" button: CLICK TO RESET THE DEMO TO ITS ORIGINAL CONDITION, with the link as:


 http://www.yoursite/your_demo_folder/cmsAdmin/admin.php?resetDemo=1
 

CREATING INSTRUCTIONS THAT CAN'T BE MODIFIED
Since a text field could be edited by the person using the demo, I used an HTML separator field.

Here's the code that I used for a multi-record editor as an example:

<tr>
 <td colspan='2'>
<hr>
<br />
<b><font color="red">INSTRUCTIONS:</font>
<br /> <br /> 
Change or add information below, then click "Save".
<br /> <br />
NOTE: To change the dates, use the pull down menus for Month, Day and Year.
<br /> <br />
Your changes will appear on the live "Exhibitions" demonstration web page in your browser.
<br />
(If the changes don’t appear, just “refresh” the page in your browser by pressing the F5 key)
<br /> <br />
Or, you can click the "Preview" button, then click on the exhibition you've modified to see your changes.</b>
<br /> <br />
<hr>
<br />
<br />
 </td>
</tr>
 

TAKE THE DEMO SITE LIVE
After your demo site is set up and functioning the way you'd like it to  to make the demo site "live, just change the
following value in /data/settings.dat.php: from "0" to "1".



demoMode = 1



In the demo mode, the data on your demonstration pages will be visible to the demo user. They'll be able to change that
data (excluding uploads) and their changes will appear on your demo site. However, they are working on a temporary copy
of the database, so your original content will be available for the next demo user.

DIFFERENCES BETWEEN THE NORMAL AND THE DEMO MODE
Here's how the demo mode differs from a normal installation:
- When a new demo user visits the viewers and admin program, a copy of the database tables get created just for them
- This copy lasts 1 hour and then it's removed
- All the admin features are locked down (not editable)
- Uploads are disabled
- All other parts of the software can be incorporated into the demo site

If you want to make changes to any section editors, you'll have to change the demoMode value back to "0" to unlock the
admin features.

There's a pretty cool demo of the demo mode on-line at:

http://demo2.interactivetools.com/cmsbuilder/

And I've implemented a demo on my site, at:

http://www.JKWebDesigns.com/demo/


ADDING LAST_NAME AND FIRST_NAME FIELDS TO THE USER ACCOUNTS TABLE - May 8th, 2011

If you’ve been frustrated by user accounts that are sorted by the user’s first name in the fullname field, or if you
wanted to add information fields to the user account table but didn’t know if you would break anything,  take heart. 

According to Chris Waddell at interactive Tools, “You can certainly replace the fullname field with first_name and
last_name fields in the accounts table. You can then output both fields in your viewer:



<?php echo $record['createdBy.first_name'] . " " $record['createdBy.last_name'?>



You can also add these and any other fields to your ListPage Fields, sort by these added fields, and use them in viewers
the same way you’d use any other fields.

The Author of a record is currently pulled from the username field so this function should not be affected.

Don't forget to copy any name information from the fullname to the new first_name and last_name fields before you delete
the fullname field.


TIME ZONE DISPLAY IN REGIONAL SETTINGS - Dec 29th, 2018

I was curious as to why “Regional Settings” on some CMSB installs show as: “Timezone Name” with a pull down for
a long list of locations and times and on others as: “Server Time” and “Local Time Adjustment”.

Dave Edis from Interactive Tools had the answer (as always).

He said:

It's actually based on the version of PHP you have. If you have PHP 5.1+ it will show the timezone pulldown because that
version supports timezones with: 

http://php.net/date-default-timezone-set

Earlier PHP versions don't support timezones so we hand-coded a workaround that lets you put in the offset amount in
hours and minutes.

Interestingly, the code in general.php actually decides what to show by checking if that built-in PHP function exists:


<?php if (function_exists('date_default_timezone_set')): ?>


UPDATING A RECORD ONLY WHERE AN INPUT FIELD MATCHES A RECORD FIELD VALUE - Dec 17th, 2011

If you need to update a record in a table I’ll call customer_uploads only if the value of a variable (I’ll call that
$order_code for this example)  matches the value in the existing record’s order_code field, here’s how:

First create a field in your form where the value can be entered. (There are other variables and fields but I left them
out for clarity.) 


<tr>
                 <td valign="top" class="title" height="30">Activation Code:</td>
                 <td><input type="text" name="order_code">
                 </td>
                 </tr>


Then in your update code add a where clause:



mysql_query("UPDATE `{$TABLE_PREFIX}customer_uploads` SET  
             
          first_name         = '".mysql_escape( $_REQUEST['first_name'] )."',
          last_name          = '".mysql_escape( $_REQUEST['last_name'] )."',
          street_address     = '".mysql_escape( $_REQUEST['street_address'] )."'
 
 // Add WHERE clause to filter results 
            WHERE order_code = '".mysql_escape($order_code)."'")  
 
or die("MySQL Error updating Record:\n". htmlspecialchars(mysql_error()) . "\n")
  $userNum = mysql_insert_id(); 


And don’t forget that there’s no comma after the last field to be updated.


 


USING THE “MY ACCOUNT” LINK AFTER LOGIN. - Feb 20th, 2012

If you only want certain user account fields available to users, don’t give the user access to the accounts section at
all.

Instead let them use the "My Account" link in the upper left hand corner, above the section editor menu after login. 

According to Jason Sauchuk from Interactive Tools, “You can control which fields in the accounts table are accessible
from there by checking the "My Account - Show this field in "My Account" section" check box in the User Account field's
editor (found under advanced settings). Another benefit of doing it this way is that the user is automatically asked to
confirm their password.”


EXTRACTING DATA FROM A TEXT FIELD AND INSERTING IT INTO ANOTHER FIELD - Dec 29th, 2018

My client had a text box field called internal_notes in the accounts section which held information about that user.

I needed to check for certain existing data in the text field (in this case the word ‘free’) and insert a value of
‘2' into another field (membership_level) in that user’s record. 

Daryl Maximo, a programmer at Interactive Tools came to the rescue with the following function, which will run as soon
as the web page containing the code is loaded.

CAUTION:
Back up Your database(s) in Admin>general Settings before attempting any changes so that you can restore the unaltered
data if there’s a problem.


<?php
  // load viewer library
  $libraryPath 'cmsAdmin/lib/viewer_functions.php';
  
$dirsToCheck = array('path_to_your_root_directory/','','../','../../','../../../');
  foreach (
$dirsToCheck as $dir) { if (@include_once("$dir$libraryPath")) { break; }}
  if (!
function_exists('getRecords')) { die("Couldn't load viewer library, check filepath in sourcecode."); }

  
// load records from 'accounts'
  list($accountsRecords$accountsMetaData) = getRecords(array(
    
'tableName'   => 'accounts',
    
'loadUploads' => true,
    
'allowSearch' => false,
  ));

?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
 <head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
</head>

<body>

<?php
function extract_membership_level_from($string){
    
$myword 'free' ;
    
preg_match_all("/\b" $myword "\b/i"$string$matches);
    return 
$matches[0];
}
    
?>
      
  <?php
  foreach ($accountsRecords as $record) {
    
$allLevel  extract_membership_level_from$record['internal_notes'] );
if (
$record['membership_level'] || !$allLevel ) { continue; }// skip records that already have a membership level set
(or where there’s no match for $myword in internal_notes )
    
$firstLevel '2';
    print 
"User {$record['username']} will get this level: $firstLevel\n";
   
mysql_update('accounts'$record['num'], null, array('membership_level' => $firstLevel ));
 }
?> 

</body>
</html>


TESTING
The print line of code will show you what the result of your efforts will be.
Comment out the mysql_update code by adding double forward slashes // before the line until you’re sure that the code
will render the appropriate results and then remove the double slashes and reload the page. 
 
MODIFICATION
This function can be easily modified to extract any information string from any text field in any table and insert that
value or any other value into another field by changing one value in the function itself:
$myword = 'the_string_you’re_searching_for '

And a few values in the foreach loop:
The table name that you’re looping through (in the foreach).
The field you’re searching for (in the $allLevel variable).
What to replace the string with if found (in the $first_level variable).
And the table and field where you want to insert that value (in the mysql_update line).

CAVEAT:
Don’t forget to adjust the Load Viewer Library path and Load Records values at the top of the page to suite your
needs.


HIDING PHP ERROR LOGS FROM THE ADMIN SECTION (V2.64+) - Dec 1st, 2015

While best practices dictate that a (perfect) site should throw no errors, reality dictates that almost every site will
throw errors, and that some of those will not affect the site's operation.

With the inclusion of Global error logging in version 2.64, PHP errors, no matter how small, show bright red in the
Admin section and may become an issue for some developer / client relationships.

Dave Edis, the senior programmer at Interactive Tools, offers this workaround (which, for now, will have to be manually
added to any CMSB upgrades).

Note that it does not stop the logging of errors, it just hides them from the admin section until you're ready to deal
with them.

he said

If you want to remove the menu link with some custom code you can do that as follows:

- Open /lib/menus/header_functions.php
- Scroll to the bottom
- Add the code:

 

  array_pop($adminMenus); // remove "Error Log" from menu


after


  'recordCount' => $errorCount,
  );


Like This:


  //
  $errorCount = mysql_count('_error_log');
  $adminMenus[] = array(
    'menuType'    => 'custom',
    'menuName'    => t('Error Log') . " ($errorCount)", //
    'menuOrder'   => ++$menuOrder,
    'link'        => '?menu=_error_log',
    'isSelected'  => ($menu == '_error_log'),

    'tableName'   => '_error_log',
    'recordCount' => $errorCount,
  );

  array_pop($adminMenus); // remove "Error Log" from menu
    
  //
  return $adminMenus;


The PHP array_pop() removes the last item off an array, so that line of code just removes the "Error Log" menu that was
just added.  Note that you'll still be able to directly access the log with admin.php?menu=_error_log  All this code
does is remove the menu link.

Don't' forget to safely copy your code before updating CMSB or it will be overwritten
_________________________________________

Taking this one step further,  I added a check box field called ("No Error Log") in a single record section (called
"Organization Information"), that will show or remove the error log from the Admin menu

Building on the code above, my final now looks like this:


 //
  $errorCount = mysql_count('_error_log');
  $adminMenus[] = array(
    'menuType'    => 'custom',
    'menuName'    => t('Error Log') . " ($errorCount)", //
    'menuOrder'   => ++$menuOrder,
    'link'        => '?menu=_error_log',
    'isSelected'  => ($menu == '_error_log'),

    'tableName'   => '_error_log',
    'recordCount' => $errorCount,
  );
 ?>
<?php 
   $libraryPath 'cmsAdmin/lib/viewer_functions.php';
 
$dirsToCheck = array('/path_to_your_server/','','../','../../','../../../');
  foreach (
$dirsToCheck as $dir) { if (@include_once("$dir$libraryPath")) { break; }}
  if (!
function_exists('getRecords')) { die("Couldn't load viewer library, check filepath in sourcecode."); }
  
 list(
$organization_informationRecords$organization_informationMetaData) = getRecords(array(
    
'tableName'   => 'organization_information',
    
'where'       => ''// load first record
    'limit'       => '1',
  ));
  
$organization_informationRecord = @$organization_informationRecords[0]; // get first record
 ?>

<?php $no_error_log 1 ?>
 <?php $no_error_log $organization_informationRecord['no_error_log'?>
 <?php if ($no_error_log == ):?>
 
  <?php  array_pop($adminMenus); // remove "Error Log" from menu   
  ?>
  <?php endif?>
  <?php

  return $adminMenus;
}

?>




PHP DEVELOPER LOG (ERROR LOG) DISPLAYING A BLANK PAGE OR MEMORY ERROR FIX - Sep 29th, 2020

If you come up with a blank page when trying to view the CMSB PHP error log (now called the developer log) , it’s
probably because you have a lot of error records in the database and you’ve run out of allocated memory.

According to Dave Edis from Interactive Tools, you should be able to view the log by adding: 


&perPage=5


to the end of the URL like this:


yoursite.com/cmsAdmin/admin.php?menu=_error_log&perPage=5


You can also try increasing the &perPage=5 to &perPage=50


ADMINS ONLY TO EDIT CERTAIN FIELDS - Mar 25th, 2015

User Kenny wanted to allow Admins to edit certain fields (in his case the page titles, and the meta data description and
tag fields for his site) but not to allow user editor to do more than view those fields while editing others.

I didn’t think it could be done, but Damon Edis from Interactive Tools came to the rescue with this elegant concept.

He said:

“It sounds like you are wanting the admin user to be able to add/edit page title and metas, both keyword and
description fields in CMS Builder and have editor users only be able to see that content but not edit it.

This can be done with some creative coding. Here’s the steps:

1. Create your three fields: Page Title, Meta Keywords and Meta Descriptions
Edit these fields to make them Admin Only Access Level. When modifying the field, in the popup window, scroll to the
bottom and for Advanced Options, click show all to see this option.

2. Create a new field for your section. The Field Type will be --seperator--. Doesn't need a Field Label or Field Name.
For the Separator Type choose the HTML radio button. 

By default you will see this HMTL:

<tr>
<td colspan='2'>
</td>
</tr>

Change that to this:

<?php if (userSectionAccess($GLOBALS['tableName']) >= 9): ?>
<tr>
<td> Page Title: </td>
<td><?php echo htmlencode(@$GLOBALS['RECORD']['page_title']); ?></td>
</tr>
<tr>
<td> Meta Tag: </td>
<td><?php echo htmlencode(@$GLOBALS['RECORD']['meta_tags']); ?></td>
</tr>
<tr>
<td> Meta Description: </td>
<td><?php echo htmlencode(@$GLOBALS['RECORD']['meta_description']); ?></td>
</tr>
<?php endif ?>


This first line of code is used to only displays the output Page Title, Meta Tags and Meta Description for Editor users
only. Admin users will see this data in editable text fields. 

After you setup this up, log in as an Editor to test.”

RSS FEEDS



CREATING AN RSS FEED FOR A MULTI-RECORD SECTION - Feb 17th, 2015

If you’ve had mixed results trying to create an RSS feed for a multi-record section you’re not alone. There seem to
be many “details” for the “Devil” to hide in.

Here’s an approach that worked for me which you can build on for your own uses.

For a table called “current_events”, I wanted to show the most current records in my events list and limit the RSS
display to only the 2 most recently created records. I also wanted a masthead logo and a link to the main event’s page
to appear at the top of the feed.

The code that I finally came up with for the XML file I named happening-now-rss.xml.php is:



<?php header('Content-type: application/xml; charset=utf-8'); ?><?php echo '<?xml version="1.0" encoding="UTF-8"?>'?> 
 
<?php 
   
  require_once "/your_server_path/cmsAdmin/lib/viewer_functions.php"
 
  list(
$current_eventsRecords$current_eventsMetaData) = getRecords(array( 
    
'Table name'   => 'current_events'
    
'orderBy'     => 'createdDate DESC',
    
'limit'   => '2',  
  )); 
 
?> 
 
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">  
  <channel>  
   <atom:link href="http://www.your_domain.org/rss/happening-now-rss.xml.php" rel="self" type="application/rss+xml" /> 
 <title>AHAPPENING NOW RSS FEED</title>  
      <link>http://www.your_domain.org/events.php</link>  
      <description>Arts Events Worth Knowing About</description> 
      

      <language>en-us</language> 
 
  <item> 
<title>www.your_domain.org</title>  
 <description> 
<![CDATA[ 
 <div align="left">
<a href="http://www.artistsofpalmbeachcounty.org"><img src="http://www.your_domail.org/images/LOGO.png" /></a><br
/>These are the latest two events that were added to our <b>Happening Now</b> events list. For a complete list of events
<a href="http://www.your_domain.org/events.php" target="_blank">click here</a>.<br /><b><i>The date is when the event
was added.</i></b></div>
]]> 
</description>
  <guid isPermaLink="true">http://www.your_domain.org</guid> 
 <pubDate><?php echo date('r'); ?></pubDate> 
  </item>  
 
 <?php foreach ($current_eventsRecords as $record): ?>  
        <item>  
          <?php $title htmlspecialchars($record['title']); ?>
<title><![CDATA[<?php echo strtoupper($title); ?><?php if ($record['end_date']): ?> - Now through <?php echo
$record['end_date'?><?php endif; ?> ]]></title> 
          <link>http://www.artistsofpalmbeachcounty.org<?php echo $record['_link'?></link>  
          <description><?php echo htmlspecialchars($record['rss_description']); ?></description>  
          <pubDate><?php echo date("D, d M Y H:i:s O"strtotime($record['createdDate'])) ?></pubDate> 

           <guid isPermaLink="true">http://www.your_domain.org<?php echo $record['_link'?></guid>  
        </item>  
<?php endforeach ?>  
 
  </channel>  
</rss>



Here are some of the things that I learned along the way.

This code in the example above makes Atom feeds happier.:



<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">  
  <channel>  
   <atom:link href="http://www.your_domain.org/rss/happening-now-rss.xml.php" rel="self" type="application/rss+xml" />  



You can include an image in your channel (not in an item) but it may or may not appear at the upper right corner of your
feed depending on the feed reader that you’re using.

 If you’re embedding an image in the <channel> area it’s preferable to have your image title match the channel
title.

If you want to include a logo image, you can try to include it in a separate <item> instead if in the channel area (it
will not always appear at the top of your feed unless you add <pubDate><?php echo date('r'); ?></pubDate> to that item).

The date format "D, d M Y H:i:s O insures that your date will render in the appropriate format for RSS feeds.


VALIDATING AN RSS FEED - Feb 17th, 2015

You should validate your RSS feed before you publish it. You can do that here:

       http://validator.w3.org/feed/

You can find out more about the tags that are available for use in an RSS feed at:

       http://www.rss-specifications.com/


EMBEDDING AN IMAGE IN AN RSS FEED - Aug 6th, 2010

RSS feeds can be finicky, but this should work to display an image as part of an item in the foreach loop.



<description> 
<![CDATA[ 
 
<p><u>Here is an image thumbnail</u> </p> 
 
<?php foreach ($record['image'] as $upload): ?> 
<img src="http://www.my_web_site.com<?php echo $upload['thumbUrlPath'?>" width="<?php echo $upload['thumbWidth'?>"
height="<?php echo $upload['thumbHeight'?>" alt="" />
<?php endforeach ?></p> 
 
<p align="center"><strong><font color="#ba1419">My First Image</font></strong></p> 
]]> 
</description>



In general, any time you want to treat code as "code" in an RSS feed, you need to surround the code with CDATA tags.

Note: You'll have to add the URL to change the relative image thumbURLpath location to an absolute URL. 

Any code (formatting, etc) within the CDATA tags will be treated as code and should render correctly.


RSS FEED CAVEATS - Aug 6th, 2010

Here are a few things that I’ve discovered that may save you some time.

You can only add CDATA to fields in an <item> area

Some readers and browsers will not render an <item> correctly without a <guid> or a <title>

Find more? Email them to me at cookbook@thecmsbcookbook.com and I'll include them.

PLUGINS



SELECT MULTIPLE RECORDS AND DRAG PLUGIN - Aug 8th, 2023

Steve99 has generously created and shared a plugin that allows you to select multiple records in a manually sorted
(dragSortOrder) multi record section and drag them to anew position in the record list.

Note: This plugin has not been tested with Category type and has built-in checks to not enable for these sections.

You can download a copy of this plugin from here: http://thecmsbcookbook.com/downloads/moveRecordsHere_1-00.zip

and here's the original post on the forum: https://www.interactivetools.com/forum/forum-posts.php?82694 


HIDING THE ERASE BUTTON ON SINGLE RECORD SECTIONS (VERSION 3.54+) - Jan 7th, 2021

User MercerDesign wanted to know if there was way to remove the erase button from the single record section editor, the
button that sits next to the Save, Preview and Cancel buttons, so that client's who had access to the CMS back end could
not erase the section by mistake.

Interactive Programmer Hans Marcon suggested adding the following code to lib/menus/header.php replacing "section_name"
(keeping the quotes) with the name of the section where you want the "Erase" button to be hidden, and
$your_admin_sectionRecord['hide_the_erase_button'] with the name of the 'Hide The Erase Button' checkbox in one of your
admin single record sections, so that you can turn the erase button on and off.


<?php if($your_admin_sectionRecord['hide_the_erase_button'] == && isset($_GET['menu']) && $_GET['menu'] ===
"section_name"): ?>

    <style type="text/css">
      button.btn.btn-primary[name=Erase] {
          display: none;
      }
    </style>

<?php endif; ?>

User Djulia also offered a plugin as a global solution that you can download from
http://thecmsbcookbook.com/downloads/hide-erase-buttons.zip. Just unzip the file, upload it to your plugin's folder and
activate the new plugin.



SETTING UP MULTI PIN MAPS USING THE GEOCODER PLUGIN - Nov 15th, 2018

I needed to display a multi pin map from an accounts section of directory subscribers using the Geocoder plugin and was
a bit lost until Daniel Loewe from Interactive Tools came to my rescue.

I had a few search criteria to implement:
1) Allow for limiting searches to within a radius around a zip code
2) Exclude any account records from the search results that had either one of 2 check boxes checked (isAdmin and
notAdmin). 
3) Exclude any account record from the search results that was not updated within the last 6 months
4) Display custom information when a pin is clicked/tapped

I also wanted to include error messages for invalid zip code entries and for when there were no search results matching
the search criteria, with telltale indicators of the submitted search.

Here's what we came up with.

At the top of the search page after the load records calls:


$kmOrMiles           = 'miles'; // can be 'miles' or 'km'
  $geoOptions = [];
if (!empty( $_REQUEST['fromAddress'] )) {
 // get geocoding data
  list($myLat, $myLng) = geocodeAddress( @$_REQUEST['fromAddress'] );
  
  $geoOptions          = geocoder_getOptions($myLat, $myLng, @$_REQUEST['maxDistance'], $kmOrMiles);
}
  // get records
 $sixMonthsAgo = strtotime( '6 months ago' );
  list($myRecords, $myMetaData) = getRecords(array(
    'tableName'     => $GLOBALS['GEOCODER_SAMPLE_TABLENAME'],
     'where'    => " (isAdmin = '0' OR isAdmin = '') AND (notAdmin = '0' OR notAdmin = '') AND updatedDate > '" .
date('Y-m-t H:i:s', $sixMonthsAgo) . "'",
    
  ) + $geoOptions);  // geoOptions WILL NOT override the above options
 
 $errorsAndAlerts = '';
   if (@$_REQUEST['save']) 
  { 
  @$count = '';
   $action = '' ;
     @$count = mb_strlen($_REQUEST['fromAddress']  );
    // echo $count ;

    if     (!@$_REQUEST['fromAddress']) { $errorsAndAlerts .= ""; }
      elseif (@$count < 5 )                                  { $errorsAndAlerts .= "Your zip/postal code must be at least 5
digits long.\n"; }
     elseif (!$myLat || !$myLng)         { $errorsAndAlerts .= "Please enter a valid zip/postal code\n"; }
    

  }
?>


Then in  the head of the page:

<script type="text/javascript" src="https://maps.googleapis.com/maps/api/js?key=<?php echo
htmlEncode($GLOBALS['GEOCODER_GOOGLE_API_KEY']); ?>"></script>
<script type="text/javascript">
function initialize() {
  var mapCanvasId = 'map_canvas';
  var mapOptions  = { mapTypeId: google.maps.MapTypeId.ROADMAP };
  var map         = new google.maps.Map(document.getElementById(mapCanvasId), mapOptions);
  var bounds      = new google.maps.LatLngBounds();
  var infowindow  = new google.maps.InfoWindow();
<?php
foreach ($myRecords as $record) {
  if (!
$record['latitude'] || !$record['longitude']) { continue; }
  
$jsFunctionArgs "{$record['latitude']}{$record['longitude']}{$record['num']}, '" .jsEncode($record['_link']).
"'";
  print 
"  _geocoder_addMarker($jsFunctionArgs);\n";
}
?>

  //
  function _geocoder_addMarker(latitude, longitude, recordNum, detailLink) {
    var latLng       = new google.maps.LatLng(latitude, longitude);
    var infowindowEl = document.getElementById('marker_infowindow_' + recordNum);
    var marker       = new google.maps.Marker({ map: map, position: latLng });
    google.maps.event.addListener(marker, 'click', function() {
      if (infowindowEl) {
        infowindow.setContent(infowindowEl.innerHTML);
        infowindow.open(map, marker);
      }
      else {
        window.location = detailLink;
      }
    });
    bounds.extend(latLng);
  }

  //
  map.fitBounds(bounds);
}

</script>


The search form in the body of the page:


<form method="post"  action= "?">
        <input type="hidden" name="save" value="1" />
        <input type="hidden" name="km_miles" value="<?php echo $kmOrMiles ?>" />
        <table>
          <tr>
            <td valign="top"  align="right" class="text_font"><b>Enter A Zip/Postal Code
              
              </b></td>
            <td align="left" valign="top" colspan="2"><input type="text" name="fromAddress" value="<?php echo
htmlspecialchars(@$_REQUEST['fromAddress']); ?>" size="38" class="text_font" style="background-color:#<?php echo
$dbt_colorsRecord['main_box_background_color'?>;" placeholder="Please Enter A Zip/Postal Code" id="zip">
              
           </td>
          </tr>
          <tr>
            <td  align="right" class="text_font"><b>And A Distance To Narrow Your Search
              </b></td>
            <td align="left" valign="top" colspan="2"><select name="maxDistance" width="300" class="text_font"
style="width: 300px; max-height: 50px; background-color:#<?php echo $dbt_colorsRecord['main_box_background_color'?>;">
                <option value="">At Any Distance</option>
                <option value="100" <?php selectedIf(100, @$_REQUEST['maxDistance']) ?> >within 100
                <?php if ( $kmOrMiles == 'miles'):?>
                miles
                <?php elseif ( $kmOrMiles == 'km'):?>
                km
                <?php endif ?>
                </option>
                <option value="50"  <?php selectedIf50, @$_REQUEST['maxDistance']) ?> >within 50
                <?php if ( $kmOrMiles == 'miles'):?>
                miles
                <?php elseif ( $kmOrMiles == 'km'):?>
                km
                <?php endif ?>
                </option>
                <option value="25"  <?php selectedIf25, @$_REQUEST['maxDistance']) ?> >within 25
                <?php if ( $kmOrMiles == 'miles'):?>
                miles
                <?php elseif ( $kmOrMiles == 'km'):?>
                km
                <?php endif ?>
                </option>
                <option value="10"  <?php selectedIf10, @$_REQUEST['maxDistance']) ?> >within 10
                <?php if ( $kmOrMiles == 'miles'):?>
                miles
                <?php elseif ( $kmOrMiles == 'km'):?>
                km
                <?php endif ?>
                </option>
                <option value="5"   <?php selectedIf5,  @$_REQUEST['maxDistance']) ?> >within 5
                <?php if ( $kmOrMiles == 'miles'):?>
                miles
                <?php elseif ( $kmOrMiles == 'km'):?>
                km
                <?php endif ?>
                </option>
              </select></td>
          </tr>
          <tr>
            <td align="right" class="text_font"><b>&nbsp;</b></td>
            <td align="left" valign="bottom" colspan="2"><input type="submit" value="Submit Search Filters" ></td>
          </tr>
        </table>
      </form>

And where I wanted the form to be displayed:


 <?php $hasAddresses array_filter(array_pluck($myRecords'latitude')); ?>
      <?php if ($hasAddresses): ?><span class="heading-text" style="color:#F03">CLICK/TAP A PIN FOR MORE
INFORMATION</span>
      <div id="map_canvas" style="width: 800px; height: 800px;; float: left; margin: 0px 15px;"></div>
      <?php else :?>
      <span class="heading-text" style="color:#F03">Sorry, there are no provider listings
      <?php if ($myLat):?>
      within
      <?php if (@$_REQUEST['maxDistance']):?>
      <?php echo htmlspecialchars(@$_REQUEST['maxDistance']); ?>
      <?php else:?>
      unlimited
      <?php endif ?>
      <?php if ( @$_REQUEST['km_miles'] == 'miles'):?>
      miles
      <?php elseif (  @$_REQUEST['km_miles'] == 'km'):?>
      km
      <?php endif ?>
      <?php endif?>
      of <?php echo htmlspecialchars(@$_REQUEST['fromAddress']); ?>.</span>
      <?php endif ?>
      <div id="marker_details" style="display: none;">
        <?php foreach ($myRecords as $record): ?>
        
         <?php 
$updateUnixTime     strtotime$record['updatedDate'] ); // seconds since 1970 
$sixMonths time() - (180*60*60*24) ; //(changed from + to -)
$sixMonthsGone $updateUnixTime $sixMonths;

?>
<?php if((!$record['isAdmin'] ==|| !$record['notAdmin'] == '1') || !$record['hidden'] == '1' || ( $sixMonthsGone) ):?>
        <?php // marker_infowindow_### is the content displayed in the info-window on click ?>
        <div id="marker_infowindow_<?php echo $record['num']; ?>">
          <h3><?php echo htmlencode( @$record['practice_name']); ?><?php echo htmlencode(
@
$record['practice_street_address']); ?>
            <?php echo htmlencode( @$record['practice_city']); ?>, <?php echo htmlencode( @$record['practice_state']);
?> <?php echo htmlencode( @$record['practice_zip']); ?></h3>
          <a href="<?php echo $record['_link']; ?>"><span class="text_font">Learn About This Provider</span></a> </div>
        <?php endif ?><?php endforeach ?>
      </div>




ADDING LINKS TO THE CMSB INTERFACE USING THE MODIFYHEADERLINKS PLUGIN *WAS CALLED EXAMPLEHEADERLINKS* - Mar 27th, 2019

Since the header_links hook has been removed from CMSB starting with the release of version 3.0, Dave Edis from
Interactive Tools has shared a new version of the examplHeaderLinks plugin called modifyHeaderLinks.php that allows the
addition of custom links in the upper left portion of the main CMSB management interface page.

Here's Dave's plugin code:

<?php


// NOTE: RENAME this plugin so when you download updates you don't OVERWRITE your changes!!!

addFilter('menulinks_myAccount''modifyHeaderLinks');

//
function modifyHeaderLinks($menuArray) {

  
// Example of custom menu:
  

  // remove license link
  foreach ($menuArray as $index => $menuAttr) {
    if (
preg_match("/\b(menu=license)\b/", @$menuAttr['link'])) { unset($menuArray[$index]); }
  }

  
// add links to the beginning
  $newMenu = [
    
'menuName'          => t('New Menu'),
    
'menuType'          => 'custom',
    
'link'              => "?menu=new_menu",
    
'visibility'        => 'requireLogin',                     // will be displayed in the user is logged in
    'isSelected'        => (@$_REQUEST['menu'] == 'new_menu'), // set to true to show this menu as selected
    'br_after'          => true,                               // optional: add line break after menu item instead of
separator ("|")
  ];
  
array_unshift($menuArray$newMenu);

  
// add links to the end
  $newMenu = [
    
'menuName'          => 'Google'
    
'menuType'          => 'custom',
    
'link'              => "https://www.google.com/",
    
'isSelected'        => false,                     // set to true to show this menu as selected
    'linkTarget'        => '_blank',                  // optional: set this to open link in a new tab
    'visibility'        => 'showAlways',              // will be displayed always
  ];
  
array_push($menuArray$newMenu);

  return 
$menuArray;
}

// eof


I was a bit confused as to how to implement the plugin until Daniel Louwea programmer at Interactive Tools came to my
rescue.

Daniel said:

"For adding these external links before the 'My Account' link , you would use something like this:

$newMenu = [
  'menuName' => 'The Free Pixlr On-Line Image Editors &gt;&gt;',
  'menuType' => 'custom',
  'link' => "http://pixlr.com/express/",
  'isSelected' => false,
  
'linkTarget' => '_blank',
  
'visibility' => 'showAlways',
  
'br_after' => true,
];
array_unshift($menuArray$newMenu);

$newMenu = [
  
'menuName' => 'How to Use Pixlr Express &gt;&gt;',
  
'menuType' => 'custom',
  
'link' => "http://www.elleschorrphotography.com/using-pixlr.php",
  
'isSelected' => false,
  
'linkTarget' => '_blank',
  
'visibility' => 'showAlways',
  
'br_after' => true,
];
array_unshift($menuArray$newMenu);


And to add external links after the 'View Website' link

 $newMenu = [
    
'menuName'          => 'COMPLETE IMAGEUPLOADING INSTRUCTIONS >> '
    
'menuType'          => 'custom',
    
'link'              => "http://www.yoursite.com/faqdetails.php?21",
    
'isSelected'        => false,                     // set to true to show this menu as selected
    'linkTarget'        => '_blank',                  // optional: set this to open link in a new tab
    'visibility'        => 'showAlways',              // will be displayed always
     'br_after'          => true
  ];
  
array_push($menuArray$newMenu);
  
   
$newMenu = [
    
'menuName'          => 'UPLOAD YOUR NEW IMAGES ORREVISE EXISTING SUBMISSIONS >> '
    
'menuType'          => 'custom',
    
'link'              => "http://www.yoursite.com/cmsAdmin/admin.php?menu=exhibition_submissions",
    
'isSelected'        => false,                     // set to true to show this menu as selected
    'linkTarget'        => '_blank',                  // optional: set this to open link in a new tab
    'visibility'        => 'showAlways',              // will be displayed always
     'br_after'          => true
  ];
  
array_push($menuArray$newMenu);

As you can seeyou can keep using the $newMenu variable name simply call either array_unshift($menuArray$newMenu)
(
add before the 'My Account' link) or array_push($menuArray$newMenu) (add after the 'View"Website' linkafter each
option array definition to add it to the menu.

In answer to "how would I set the following line to TRUE?
 'isSelected' => (@$_REQUEST['menu'] == 'new_menu'), // set to true to show this menu as selected 

Daniel offered the following:

This is only necessary for links that point to pages within CMSB and is mostly done automaticallyYou would just need
to make sure that the value in that line matches the "menu=" value in the link option. For example:  

'link' => "?menu=example",
'isSelected' => (@$_REQUEST['menu'] == 'example'),
     


AUTOMATICALLY POPULATING FIELDS IN A TARGET TABLE USING THE CREATEDBY FUNCTIONALITY IN CMSB - Dec 9th, 2018

The createdBy functionality built into CMSB allows you to echo field values from the account record of the person who
created a record in another table.

I was trying to use that createdBy functionality to sort records by last name in a multi-record table that does not
contain a last_name field field.

When I tried to sort the records in the table using  'orderBy' => 'createdBy.last_name ASC', in the load records call,
that didn’t work.

Thankfully, Dave Edis came to the rescue once again.

He pointed out that although you can’t use the createdBy functionality for sorting directly, you can use a plugin to
populate real fields in your target table from those createBy values and then use the actual field data to sort your
records.

Based on what he suggested, here's the plugin that we came up with:


<?php
 

addAction('record_postsave', function($tableName) {
  
   
$sourceField   "first_name"// from accounts table
   $fieldToUpdate "createdBy_first_name";
   
$sourceField2   "last_name"// from accounts table
   $fieldToUpdate2 "createdBy_last_name";
 
// Error Checking
 if (!isset($GLOBALS['schema'][$fieldToUpdate])) { return; } // skip if field doesn't exist in current table
 if (!isset($GLOBALS['schema'][$fieldToUpdate2])) { return; } // skip if field doesn't exist in current table
 
//Update Values
 $tablePrefix   $GLOBALS['TABLE_PREFIX'];
  
  
$query "UPDATE {$tablePrefix}$tableName t
         LEFT JOIN {$tablePrefix}accounts a ON t.createdByUserNum = a.num
               SET t.`$fieldToUpdate` = a.`$sourceField`,
              `$fieldToUpdate2` = a.`$sourceField2`";
               
  
mysql_do($query);
});



USING THE EMAILONAPPROVED PLUGIN WITH ANY MULTI-RECORD SECTION - Oct 26th, 2017

Because I didn't want to allow non-members to upload images (or other files) directly to the database, I wanted to send
an email to a person that posted a listing request once it was approved and listed.
That email would ask them to reply to the email with an image attached. 

I was at a loss until I got some help from Jeff Shields a long time CMSB user. 

Here's how it works:
A person submits an event for listing.
The submission creates an email to the admin with all the information about the event, and a preformatted link that will
create the listing record. (see the recipe: USING AN EMAIL TEMPLATE TO CREATE A NEW ACCOUNT RECORD )
The admin reviews the information, and if it’s appropriate, clicks the link to create a record.
When they save the record (with an 'approved' check box automatically checked) the modified plugin automatically sends
an email to the person who listed the event that they should email an event image to the admin.

Here's the original plugin code:

<?php


addAction('record_postsave',  'emailOnApproved_sendPassword'null4);

//
function emailOnApproved_sendPassword($tableName$isNewRecord$oldRecord$recordNum) {
  global 
$CURRENT_USER$SETTINGS;
  
$fieldname 'approved';

  
// error checking
    if ($tableName != 'accounts') { return; } 
  if (!
array_key_exists($fieldname$CURRENT_USER)) {
    die(
__FUNCTION__ .": You must create an accounts fields called '$fieldname'!");
  }

  
// send email
  $wasChecked   intval(!$oldRecord[$fieldname] && $_REQUEST[$fieldname]);
  
$wasUnchecked intval($oldRecord[$fieldname]  && !$_REQUEST[$fieldname]);

$message=<<< __TEXT__
    Welcome!  
  
Your subscription has been  processed successfully and you now have access to the Members Only area of our web site.  
Your user name is: {$_REQUEST['username']}  
and your temporary password is: {$_REQUEST['password']} 
Once you have successfully logged in, you can change your password and update your profile information.  
  
  
<a href="http://www.your_website_URL.com{$GLOBALS['WEBSITE_LOGIN_LOGIN_FORM_URL']}">Click here to login</a>  
  
Best,   
    
The Subscription Committee  
__TEXT__;


$headers  'MIME-Version: 1.0' "\r\n";  
$headers .= 'Content-type: text/html; charset=iso-8859-1' "\r\n";  
$headers .="FROM:"$SETTINGS['adminEmail'];  
  
   if (
$wasChecked) { 
    
$errors mail($_REQUEST['email'],"Your membership has been successfully processed!",$message,$headers);  
    if (
$errors!=1) { die("Mail Error: $php_errormsg"); } 
  } 
 

 
?>

and here's the code after Jeff's suggestions:
It assumes a multi-record table called: 'your_table_name', a check box field called 'approved', a first name field
called 'first_name', and an email field called 'email'.
note that all references to $CURRENT_USER have been removed since they are related to the website membership plugin.

<?php


addAction('record_postsave',  'emailOnApproved_sendPassword'null4);


//
function emailOnApproved_sendPassword($tableName$isNewRecord$oldRecord$recordNum) {
  
$fieldname 'approved';

  
// error checking
    if ($tableName != 'your_table_name') { return; } 
 
  
// send email
  $wasChecked   intval(!$oldRecord[$fieldname] && $_REQUEST[$fieldname]);
  
$wasUnchecked intval($oldRecord[$fieldname]  && !$_REQUEST[$fieldname]);

$message=<<< __TEXT__
    Hello {$_REQUEST['first_name']},
  
Your event has been posted, but a compelling picture would make your listing really stand out. 

You can send us an image by replying to this email with your image attached. 
 
Please note, images should be in .jpg format and no larger than 1mb in size.  
  
Best, 
    
The Listing Team  
__TEXT__;

$the_from 'your_from_email@your_site.com';
$the_to $_REQUEST['email'];
$the_subject 'Your Subject Line';
$headers  'MIME-Version: 1.0' "\r\n";  
$headers .= 'Content-type: text/html; charset=uft-8' "\r\n";  
$headers .="From:"$the_from
  
   if (
$wasChecked) { 
    
$errors mail($the_to,$the_subject,$message,$headers);  
    if (
$errors!=1) { die("Mail Error: $php_errormsg"); } 
  } 
 

 
?>





CAVEATS FOR PLUGIN MODIFICATION - Aug 29th, 2011

Although it's possible and sometimes very handy to modify your plugins to pull data from other editors, It's important
that there are no spaces, blank lines, or other code before the code:

<?php
 

If you want to add any load records callsadd:

?>
<?php

after the code above, and insert the calls in a new set of php tags.

So your code might look like:

<?php


?>
<?php
  
 require_once "/path_to_your/cmsAdmin/lib/viewer_functions.php";
  
  
// load records
  list(your_tableRecords$your_tableMetaData) = getRecords(array(
    
'tableName'   => 'your_table',
    
'where'       => whereRecordNumberInUrl(1),
    
'limit'       => '1',
  ));
  
$your_tableRecord = @$your_tableRecords[0]; // get first record

?>
<?php 

the rest of your plugin code with modifications...


You can learn more about how to modify a plugin in the recipe about the SHOW HIDE FIELDS FOR USERS PLUGIN


RSS FEED FOR PLUGIN AND ADD ON UPDATES - Dec 29th, 2018

Use this RSS feed to be kept up to date on new information about both Official and User submitted plugins and add ons.

http://www.interactivetools.com/add-ons/rss.php


THE AUTO BACKUP PLUG IN - Aug 6th, 2010

GIVING YOUR CLIENTS A SECURE FEELING WITH AUTOMATIC BACKUPS
With the Auto Backup plug-in from the Interactive Tools App Store 

       http://www.interactivetools.com/add-ons/

there’s now a simple way for you or your clients to schedule automatic daily, weekly, and monthly database backups for
CMS Builder. 

New in Version 1.01
- Automatic hourly backups are now supported (up to 24 per day)
- Email Backups: Enter your email address to receive automatic backups via email
- Email Backups: Email backup files will automatically be compressed if supported by your server

Backup timers will automatically be set/reset each time the Admin or section editors are accessed or someone accesses
your pages on the web. By default, backups will be kept, for 3 time periods. Daily backup for 3 days, Weekly backup for
3 weeks,. and monthly backups for three months. The hourly default is 24 hours 

You can also specify an e-mail address to receive backups and the frequency that those e-mails will be sent.

You can modify the code to change these default values. Backups are automatically saved on your server where you can
download them to your local computer for safe keeping.

Auto Backup is extremely easy to set up. All you need to do is upload the plug-in file to your server and activate the
plug-in through your CMS Builder.

CAUTION: Auto Backup only backs up your MySQL data and creates snapshots of the database that can only be restored using
THE SAME VERSION of CMS Builder with which they were created. 

FTP BACKUP - The programming gurus at Interactive Tools are wrestling with the complexities of adding automatic FTP
backups to the plug-in so you can have a copy of these files on a local computer for safe keeping, but the hurdles are
high and it may be a while before a flexible enough system can be developed. Until then you might consider offering a
backup service to your clients. Both WS-FTP Pro and CuteFTP offer automatic backup scheduling and you could use it
create a new profit center for your business. Just don’t forget to add a legal disclaimer to the contract with your
clients if you haven’t already done that. The one that Interactive Tools uses reads: 

LIMITED WARRANTY
Although Vendor has tested this Software and reviewed the documentation, Vendor makes no warranty or representation,
either expressed or implied, with respect to this Software, its quality, performance, merchantability, or fitness for a
particular purpose. As a result, Software is licensed AS-IS, and you are assuming the entire risk as to its quality and
performance.

Vendor shall not be liable for any claim or right to recover damages, including, but not limited to, loss of profit,
data, or use of the software or special, incidental, or consequential damages, or other similar claims, even if Vendor
has been specifically advised of the possibility of such damages.

I asked Dave Edis at Interactive Tools for clarification on a few issues:

Me: If I haven’t accessed my CMS interface in months and the auto delete is set for
3 weeks, will I find any backup file to restore?

Dave: Yes, assuming you had one page view per day you'd have snapshots for the last 3 days, last 3 weeks, and last 3
months.

Me: If I manually create a backup in admin > general settings, will that be automatically deleted as well?

Dave: No, the auto-backup files use a different naming scheme and won't erase any of your manually generated backups.

Me: If I check my database and find that it’s missing will the restore function attempt to recreate it for me using
the info for IP address, password and user name in General Settings

Dave: Yes, assuming everything is corrupted you may need to do a new install.  But autobackup creates the same type of
backup file as clicking backup would.

Observation: Seems like it’s a good idea to create a manual backup when you set up your plug-in. That way you’ll
have a baseline backup that you can restore if necessary.


MAKING THE AUTOBACKUP PLUGIN RUN AUTOMATICALLY - Dec 29th, 2018

It seems that the auto backup plugin doesn’t run in the background on a server. Which means that no backups will be
run unless, according to Chris Waddell at Interactive Tools, “...something hits admin.php periodically.”

You can read the entire thread at:

http://www.interactivetools.com/iforum/Products_C2/CMS_Builder%3A_Plugins_%26_Add-ons_F40/P84078

Chris offered a few solutions. 

1) Set up one of the many Web Cron services to hit your admin.php page, allowing Auto Backup to do its thing. You won't
need to provide a username/password or anything. Also, Web Crons tend to be very easy to configure!  

User kblm suggested that to keep your backups from getting a prefix of “no-hostname” when running a cron, you can
enter a file prefix on the line:


$GLOBALS['AUTOBACKUP_DEFAULT_PREFIX'] = ''; // Backup file prefix, leave blank to use current domain name 


2) Changing the line in the plugin below will cause a normal site visit to trigger automatic backups: 


addAction('init_complete', 'autoBackup_updateBackups',  null, 0);


to


autoBackup_updateBackups();


Chris noted that this might cause some extra strain on your server if you get a lot of visitors. 

User aev suggested:

“If we have 1000 pageviews/hour wouldn't that require only a simple 'if' check or something similar for 999 of those?
And then pageview #1000 triggers the backup?“


THE WEBSITE SAVED SEARCHES PLUGIN - Dec 9th, 2010

This handy plugin works in conjunction with the Website Membership plugin and lets your visitors name and save links to
their favorite pages to a list, and to manage that list. The plugin is designed to work only if the visitor has a
membership account and is logged in, and will only show that visitors list entries. 

It’s pretty easy to implement by following the instructions in the included readme file, but as always, there are some
details worth noting.

The first thing that I found was that the path listed in the code to be inserted in the head of your viewer for the
“websiteSavedSearches.js” assumed that it was to be found in a “plugins/websiteSavedSearches” sub-directory. If
this is not your situation, you’ll need to adjust the path.

The next thing was that although the jQuery script called was version 1.3.2, the jQuery script included with CMSB V2.06
is version 1.4.1. For now you’ll have to manually adjust the code so that the versions match.

Note that this <head> code block must be included on every page that uses the “Saved Searches” plugin. 

Here’s how I implemented the plugin for the CMSB Cookbook recipe detail page:

Since the “/lib/viewer_functions.php” file was already loaded on that page, all I had to do was insert the
previously modified  <script> code block just after the <head> tag, and above any other <script> tags:



 <!-- saved searches -->
  <script type="text/javascript" src="/cmsAdmin/plugins/websiteSavedSearches.js"></script>
  <script type="text/javascript" src="/cmsAdmin/3rdParty/jquery/jquery1.4.1.js"></script>
  <script type="text/javascript">jQuery.noConflict();</script>
  <?php $GLOBALS['WSS_SCRIPTS_LOADED'] = true?>
  <!-- /saved searches --> 


Then in the body where I wanted to display the “Save”, “Remove” and “View My Saved Searches” page links, I
used this code:

NOTES: 
1) My “View My Saved Searches” page is called saved_searches.php
2) I added “AndReload” to both of the “onclick” commands as suggested in the readme file, so that the page would
automatically reload to show the “Remove This Recipe From My Saved Searches” link after a page was saved.
3) You can remove the class="<?php wss_cssClassFor('add'); ?>" code from the div tags, but don’t remove the
style="<?php wss_displayStyleFor('add'); ?>" code, or both links will show on your page.



<div class="<?php wss_cssClassFor('add'); ?>"
       style="<?php wss_displayStyleFor('add'); ?>">
    <a class="special" href="#" onclick="<?php wss_onClickFor('addAndReload'); ?>">Save This Recipe To My "Saved
Searches" List</a>
  </div>

  <div class="<?php wss_cssClassFor('remove'); ?>"
       style="<?php wss_displayStyleFor('remove'); ?>">
    <a class="special" href="#" onclick="<?php wss_onClickFor('removeAndReload'); ?>">Remove This Recipe From My "Saved
Searches" List</a>
  </div>
  <div ><a class="special"href="saved_searches.php" target="_blank"><br />
    View My "Saved Searches" List</a></div>


On the saved_searches.php page, since the “/lib/viewer_functions.php” file was loaded, I inserted the following code
into the code at the top pf the viewer page:



 // load saved searches
    list($savedSearches, $savedSearchesMetaData) = getRecords(array(
      'tableName'     => '_website_saved_searches',
      'where'         => "createdByUserNum = '" .mysql_escape( @$CURRENT_USER['num'] ). "'",
      'loadCreatedBy' => false,
      'allowSearch'   => false,
    )); 


In the <head> section, I added the <script> calls as before:



 <!-- saved searches -->
  <script type="text/javascript" src="/cmsAdmin/plugins/websiteSavedSearches.js"></script>
  <script type="text/javascript" src="/cmsAdmin/3rdParty/jquery/jquery1.4.1.js"></script>
  <script type="text/javascript">jQuery.noConflict();</script>
  <?php $GLOBALS['WSS_SCRIPTS_LOADED'] = true?>
  <!-- /saved searches --> 


Then in the body of the page, I inserted the following to show the “Saved Searches” list:



 <?php if ($savedSearches): ?>                  
    <table width="737" border="0" cellspacing="0" cellpadding="0"><tr>
      <td width="79%" class="Medium-Text-Bold">Click On An Entry To View</td>
      <td width="21%"  class="Medium-Text-Bold">Delete From List?</td></tr>
 <?php foreach ($savedSearches as $record): ?> 
 <tr>
   <td colspan="2"><hr /></td>
   </tr>
 <tr>
    <td > <a class="saved" href="<?php echo $record['url']; ?>"><?php echo htmlspecialchars($record['name']);
?></a></td>
    <td ><a class="Small-Text-Bold" href="#" onclick="<?php wss_onClickFor('removeAndReload'$record['url']);
?>">Delete Search</a></td>
  </tr> <?php endforeach ?>
</table>
 
  <?php else: ?>
     <div align="center"><span  class="Medium-Text-Bold">You haven't saved any searches so far.<br />But when you do,
they'll be listed here.</span></div>
  <?php endif; ?>


I’ve posted a note on the forum about the jQuery version issue, and will update this when I find out more.




RESET THE VALUE OF A FIELD IN ALL RECORDS OR IN FIELDS IN MULTIPLE TABLES (CHECK BOXES ONLY) - Nov 11th, 2011

And here’s another useful plugin from Carl Crowder at Interactive Tools called fieldResetter.  This one does exactly
that. It resets the all the values of one or more fields in one or more tables globally. I’m using it to reset a field
in a membership database called paid_for_current_year, which needs to be reset at the end of each year.

After implementing the plugin, you'll find the option to execute the global change in the "Advanced Commands" pull down
menu at the bottom of the record list for that section editor.

You can download this plugin from:

       http://www.thecmsbcookbook.com/downloads/fieldResetter.zip

Implementation is really simple.

If you want to globally reset the value of a check box field called “paid_for_current_year” to “0" in a table
called “members”, you’d adjust the array on line 15 to:



$GLOBALS['FIELD_RESET_FIELDS'] = array( 
  array('Table name' => 'members', 'fieldName' => 'paid_for_current_year', 'resetValue' => 0 ) );



 If you have 2 fields to be reset, you’d adjust the array to:



$GLOBALS['FIELD_RESET_FIELDS'] = array( 
  array('Table name' => 'members', 'fieldName' => 'paid_for_current_year', 'resetValue' => 0 ),
  array('Table name' => 'members', 'fieldName' => 'other_field', 'resetValue' => 0 ) );
);



For 2 tables:



$GLOBALS['FIELD_RESET_FIELDS'] = array(
  array('Table name' => 'members', 'fieldName' => 'paid_for_current_year', 'resetValue' => 0 ),
  array('Table name' => 'other_table', 'fieldName' => 'other_field', 'resetValue' => 0 ) );




Let’s say you wanted to give permission to accomplish this to a “Super Admin only, you could add a check box field
to your Accounts section called 'super_admin' and change the check on line 27 to



if ( !$CURRENT_USER['super_admin'] ) return;



If you make the “super_admin” field a system field, it will be harder to inadvertently change it’s value.


RESET THE VALUE OF A FIELD IN A MULTI RECORD TABLE (DATE FIELDS ONLY) - Aug 29th, 2014

My client needed to update the value of a date field in all of the records in a multi-record table, where the hidden
field value was “0" (not hidden) with a specific date.

Here’s the approach that we used. The date must be in the format YYYY-MM-DD 



<?php header('Content-type: text/html; charset=utf-8'); ?>
<?php
  
    // load viewer library
  $libraryPath 'cmsAdmin/lib/viewer_functions.php';
   
$dirsToCheck = array('/path_to_your_root_directory/','','../','../../','../../../');
  foreach (
$dirsToCheck as $dir) { if (@include_once("$dir$libraryPath")) { break; }}
  if (!
function_exists('getRecords')) { die("Couldn't load viewer library, check filepath in sourcecode."); }

  
// load records from 'your_table'
  list($your_tableRecords$your_tableMetaData) = getRecords(array(
    
'tableName'   => 'your_table',
    
'loadUploads' => true,

  ));

?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
 <head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>access-update2</title>
</head>

<body>
<?php foreach ($your_tableRecords as $record): ?>
<?php
    mysql_query("UPDATE `{$TABLE_PREFIX}your_table`    
    SET  expiresDate = '2015-04-01' 
    WHERE hidden = '" "'"
      )  
       or die(
"MySQL Error:\n"htmlspecialchars(mysql_error()) . "\n");  
        
$userNum mysql_insert_id();  
   
?>
<?php endforeach ?>   
  </body></html>



REMOVE EXTRA WHITE SPACE AND BLANK DATE PLUGIN - Mar 7th, 2023

When using “if” statements to suppress titles and other information in a viewer if a field is empty, it’s easy for
client’s to leave blank spaces in an otherwise empty field and defeat the “if” test. 

Now, thanks to a plugin (CMSB version 2.04 or above required)  written by Dave Edis of Interactive Tools, it’s even
easier to guard against this by checking to see if the content is text and not just a space and automatically deleting
the space before saving the record so that they can’t interfere with any “if” tests. It works in both text and
WYSIWYG fields

The plugins won’t delete errant spaces in otherwise blank fields that already exist in your saved records, but you can
still check for them in each if statement used on your page.

UPDATED VERSION
The latest version of Dave Edis’ ExtraWhitespace plugin (CMSB version 2.05 or above required) adds the ability to
check for blank date fields to checking for white space in otherwise empty text fields, and eliminate the possibility of
returning a Jan 1, 1970 or similar date, if a blank date field is encountered. It is compatible with CMSB version 2.53
and above

You can download this version of the plugin from:

       http://www.thecmsbcookbook.com/downloads/removeExtraWhitespace3.zip 


THE CREATEPDF PLUGIN - Nov 16th, 2019

The create PDF can be extremely useful to generate PDF documents from web pages or URLs, and display them as in line
content or as attachments.

Here’s the step by step recipe on how to install this plugin.

2) Upload the complete createPDF folder including the 2 sub-folders included in the plugin (“wkhtmltopdf” and
“examples”) to the cmsAdmin/plugins folder on your server.

3) Activate the plugin and click on "Test Server Requirements". If you encounter errors, there are some required server
libraries listed in the readme file that came with the plugin, and you may have to work with your server admin to get
them up and running. If you still have issues, post the specifics on the plugins forum and IT will help to resolve them.

NOTE: I had to adjust the server path to my viewer_functions.php by removing one of the ../ from the path. I also
encountered a “No input file specified” error in the PDF output test, but that was resolved by Dave’s first
suggestion below.

4) After you’ve eliminated all the errors, test some of the examples in the examples folder

5) You can run the example code from viewers in another directory, but you’ll have to adjust the path to your
viewer_functions.php file

When I ran the example code, the URL as inline opened as a web page in  FF(3),  IE(8), Safari (4), or Chrome (7) and
look really good. If I ran the URL as attachment code, it downloaded a PDF that also looked good.

No matter what I tried, I could not display an html or output PDF, either as inline or as attachment. The results all
showed "no input file specified" as the only text in the resulting PDFs.

Dave Edis from Interactive Tools came to the rescue. He said, 

“The plugin creates temp .html files and your server was redirecting *.html to *.php so the plugin couldn't find it's
temp .html files.  Add this line to your .htaccess so requests will only be rewritten if the .html file doesn't
exist:”


RewriteEngine on
RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)\.html$ $1.php [nc]


Another issue that I came across (V 1.09 - 2019) was that if any of the links on my page were broken, I'd get a 'content
not found' error when I sent the convert to pdf request.

The next issue involved creating a PDF from a URL that was protected by a membership plugin password, all I could get
was a PDF of the login screen, even if I was logged in.

Again, Dave came to the rescue, he reminded that in the advanced topic section of the readme file about creating a PDF
of the page that’s currently being viewed in a browser. It suggests:

To create a link that shows the current page as a PDF do the following:

LIST VIEWER
First, create a link on your list viewer to the current page, but with ?pdf=1 as the url.  Example: 


<a class="your_css_class" href="?pdf=1">CLICK TO CONVERT THIS PAGE TO A PDF</a>


Another anomaly (this one on Bluehost ising V 1.09 in 2019). I found that if I was trying to convert an index.php page
to aPDF, I'd have to add index.php to the link above, or I'd get content not found errors:  


<a class="your_css_class" href="index.php?pdf=1">CLICK TO CONVERT THIS PAGE TO A PDF</a>


DETAIL PAGE
This gets a bit more complicated. You'll need to call the specific page and record number, like this:


<a class="your_css_class" href="http://www.your_site_URL/your_detail_page.php?pdf=1&<?php echo $your_tableRecord['num']
?>">CLICK TO CONVERT THIS PAGE TO A PDF</a>



Next, for either, add this code to the top of the viewer after the get records code block:


  <?php if (@$_REQUEST['pdf']) { createPDF_fromOutput('inline''example.pdf'); } ?>


Another anomaly (this one on Bluehost in 2019). I found that if I was trying to convert an index.php file, I'd normally
just use the link:  

ONE (OR MORE) OF MY SERVERS WON'T SUPPORT THE PLUGIN
If you have site(s) (client(s)) on a server or servers that don't meet the requirements of this plugin, but you do have
even one server that does support them, you could use that server as a “proxy server” (host) to do all of the heavy
lifting required. (V1.01 and above) This approach will work with one "host" and any number of "clients". 

Here’s how to set up the "host", "client" relationship:

1) Upload the createPDF plugin as above, and enable the plugin on both the site that you’ll be using as a server
(host) and the site(s) that needs to be “served” (client)

2) In the createPDF.php plugin on the “host” set the values on these two lines:


$GLOBALS['CREATEPDF_PROXY']          = true;  
$GLOBALS['CREATEPDF_PROXY_PASSWORD'] = 'enter_your_own_password_here';


NOTE: The password can be anything you like, just make sure that it is identical on the “host” and on any
“client” sites.

3) In the createPDF.php plugin on the “client(s)” set the values on these two lines:


$GLOBALS['CREATEPDF_PROXY']          =
‘http://url_to_your_host_server/cmsAdmin/plugins/createPDF/createPDF_proxy.php’ ;   
$GLOBALS['CREATEPDF_PROXY_PASSWORD'] = 'enter_your_own_password_here';


4) When you click on "Test Server Requirements", in the cmsAdmin/plugins menu on the "client" site, you should see an
indication that you are "Using Proxy" and, if you haven't made any errors when typing in the required information in
steps 2 and 3, you should see "Passed" next to the "Test downloading content from remote URL" and Test downloading
content from proxy URL" tests.

After that, any time you implement a create pdf request on the "client" it should execute as planned.

CHANGE THE COLOR OF PAGE BACKGROUND TO WHITE FOR THE PDF

Dave Edis offered this idea..

He said, since the page is reloaded after the request to create a PDF is executed, you could replace the opening body
tag with something like:


<?php if (@$_REQUEST['pdf']): ?>
    <body style="background-color:#FFFFFF">
<?php else: ?>
    <body style="background-color:#569F00">
<?php endif ?>


HIDING THE "CLICK TO CONVERT THIS PAGE TO A PDF" LINK ON THE PDF

You could also replace the createPDF execution link with something similar, like this for a list page:


<?php if (@$_REQUEST['pdf']): ?>&nbsp;<?php else: ?><a class="your_css_class" href="?pdf=1">CLICK TO CONVERT THIS PAGE
TO A PDF</a><?php endif ?>


Or, for a detail page:


<?php if (@$_REQUEST['pdf']): ?>&nbsp;<?php else: ?><a class="your_css_class"
href="http://www.your_site_URL/your_detail_page.php?pdf=1&<?php echo $your_tableRecord['num'?>">CLICK TO CONVERT THIS
PAGE TO A PDF</a>
<?php endif ?>


You can also add some parameters to the end of the convert code to make the PDF open in specific ways.

To make the reader open at 100% magnification, with scroll bars, a toolbar and a navigation pane, I used:


<a class="small" href="http://50.6.159.105/about.php?pdf=1&<?php echo $resumeRecord['num']
?>#toolbar=1&navpanes=1&scrollbar=1&zoom=100">CLICK TOCONVERT THISPAGE TO A PDF</a> 


You can finds other useful parameters in the PDF at:

http://www.thecmsbcookbook.com/downloads/pdf_open_parameters_v9.pdf

CAVEATS:
Be careful when using in line styles to change font sizes. They can wreak havoc with rendered PDF.

I thought that I was being very cool and used a special class to change the font size in some links when displayed in
the PDF and spent the better part of a day finding out that this was the cause of my PDF font sizes coming out much
smaller than I wanted them to.

THINGS LEARNED FROM IMPLEMENTING THE createPDF PLUGIN ON THE COOKBOOK SITE

Aside from the information above, I found out a few things that were specific to my implementation. I was using
highlight_string and was having issues with links in the PDF that were offset from the text that they were supposed to
be connected to.

I was originally using 


<?php
$recipe wordwrap($record['recipe'],140) ;
$recipe highlight_string($recipe) ;

?>


to render the recipes on my list pages.

and for my detail pages.


<?php
$recipe wordwrap($table_of_contentsRecord['recipe'],140) ;
$recipe highlight_string($recipe) ;

?>


Dave Edis took a stab at solving the issue. He said:

It looks like highlight_string() replaces all the spaces with &nbsp;. My guess is something about the long lines was
causing problems with the PDF conversion. 

This code will switch them back to spaces:


<?php 
$recipe $record['recipe']; 
$recipe highlight_string($recipetrue); 
$recipe str_replace('&nbsp;'' '$recipe); 
$recipe wordwrap($recipe140''true);
print 
$recipe
?>


I needed to modify things like this to get the detail pages to render correctly:


<?php 
$recipe $record['recipe']; 
$recipe highlight_string($recipetrue); 
$recipe str_replace('&nbsp;'' '$recipe); 
// $recipe = wordwrap($recipe, 140, '', true); 
print $recipe
?>


User MickC offered this code to rename the PDF that's created to something more meaningful then example.pdf.

In it's current form it creates the PDF from "your_detail_page"  as an attachment and the creates file name for the PDF
from the value of "field_1" and "field_2" of the table "your_table". To create the document as an inline PDF, just
change 'attachment' to 'inline' in the last line of the code.


<?php require_once "lib/viewer_functions.php"?> 
<?php 
  $filename$your_tableRecord['field_1']."-".$your_tableRecord['field_2']; 
  
$num$your_tableRecord['num']; 
  
$url  "your_detail_page.php?".$num
  
$data createPDF_fromUrl($url); 
  
createPDF_display('attachment'$data$filename.'.pdf'); 
?>


You can download a PDF test page that implements some if statements to change various parameters on the resulting PDF
from:

http://www.thecmsbcookbook.com/downloads/pdftest.zip

My next issue (as yet unresolved) was using named anchor tags within the pages and having them operate correctly in the
rendered PDF.

I'll post a solution as soon as I have one (or one of my subscribers sends one along).


CREATING PDF'S FROM VERY LARGE PAGES - Jan 6th, 2015

As the cookbook grew to  well over 400 recipes (records) in late 2014, conversion to PDF became a problem. Server memory
allocation issues were keeping the PDFs from being created.

Dave Edis from Interactive Tools came up with a simple, elegant solution for creating multiple PDFs, each containing a
set number of recipes (records) , and displaying a paging menu at the top of the viewer.

At the top of the viewer, the load records call contains a conditional statement that determines when perPage
limitations are implemented. In this case when there is a request for a PDF to be created. (The 'loadCreatedBy' =>
false, reduces the server load slightly). There’s also a createPDF_fromOutput instruction.


if (@$_REQUEST['pdf']) {
  list($table_of_contentsRecords, $table_of_contentsMetaData) = getRecords(array(
    'tableName'     => 'table_of_contents',
    'perPage'       => 100,
    'loadCreatedBy' => false,
  ));
 }
 else {
  list($table_of_contentsRecords, $table_of_contentsMetaData) = getRecords(array(
    'tableName'     => 'table_of_contents',
    'loadCreatedBy' => false,
  ));
 }   

  if (@$_REQUEST['pdf']) { createPDF_fromOutput('inline', 'example.pdf'); }


Then in the body, this code changes the background color of the page if a PDF is requested:


<?php if (@$_REQUEST['pdf']): ?>
    <body style="background-color:#FFF">
<?php else: ?>
    <body style="background-color:#9FF">
<?php endif ?>


And the code that creates a dynamic paging menu:


 <?php
  $recordCount    mysql_count('table_of_contents');
  
$recordsPerPage 100;
  
$pageCount      1;
  
  print 
"We now have $recordCount recipes!\n";
  
  while ((
$pageCount $recordsPerPage) < ($recordCount $recordsPerPage)) {
    
$recipesLow  sprintf("%03s", ($pageCount-1) * $recordsPerPage );
    
$recipesHigh min($recordCount, ($pageCount $recordsPerPage) );
  
    print 
"Section $pageCount (Recipes $recipesLow - $recipesHigh)";
    print 
" - <a href='cookbook.php?page=$pageCount'>View HTML</a>";
    print 
" - <a href='cookbook.php?page=$pageCount&amp;pdf=1'>Download PDF</a>";
    
    
$pageCount $pageCount 1// show next page
  }
?>


CREATING PDF'S FROM SEARCH RESULTS - Nov 18th, 2019

I had a rather complex search form and needed to create a PDF of the search results so they could be easily downloaded
and printed.

Daniel  Louwe from Interactive Tools came to the rescue (again) with the following suggestion:

"Because you want to create a PDF of the search results, you need to simulate the form being submitted at the same time
as the PDF generation. Clicking the link with "?pdf=1" clears all of the search values, as well as the "save" value that
tells the page to show the search results.

One fairly minimal solution I can think of is instead of using a "convert this page to PDF" link, you could add a
checkbox to the form with the name "pdf" and value "1" labelled something like "View search results as a PDF" so that
the user can choose to receive a PDF of the results instead of a web page when they submit the form, rather than
clicking a link afterwards."

For example, the final code in my form was

<tr>
              <td align="right" class="text_font">&nbsp;</td>
              <td align="left" valign="bottom" colspan="2"> <input type = "checkbox" id="pdf" name="pdf" value = "1"
<?php checkedIf(1,
@
$_REQUEST['pdf']);?> /> <span class="text_font"><b>Check to create a downloadable PDF of the search results instead of
a web page</b></span></td>
                  
            </tr>

Daniel went on to suggest:
"Another simple - but more tedious - solution is  to add "save=1" to the URL in addition to the relevant form values. A
simple working example is http://your_site.com/your_search_page.php?pdf=1&save=1&your_search_field_keyword[]=1. The two
main tasks you'd have are 1) building the URL string based on the submitted form values, and 2) updating the code to
account for when both $_REQUEST['pdf'] and $_REQUEST['save'] are on; e.g. removing the original "convert this page to
PDF" link."

Note: The brackets [] after _keyword are because in my form this field in the table that I'm searching got its values
from a pulldown list that accessed second table, and so this was that field's name in the form.

 


THE SAVE AND COPY BUTTON PLUGIN - Dec 29th, 2018

This free plugin form Dave Edis at Interactive Tools adds a new "Save & Copy" button to edit pages for all sections. The
"Save & Copy" button copies both a record and its uploads. 

This new button can be clicked to instantly create a copy of the current record. 

This new record will not be saved until you click the save button (or click "Save & Copy" again).

Note: There is a known issue where if you click "Save & Copy" on a record, and then try to upload an image right away,
the image doesn't appear and it actually adds it to the original record. To get around this just click "Save" and then
"Modify" the new record again. IT says it hopes to address this limitation in a future release.

You can download the free plugin from:

http://www.interactivetools.com/add-ons/?user_submitted=1


THE MODIFY HOME PAGE PLUGIN - Dec 29th, 2018

This is a great little plugin created by Chris Waddell at Interactive Tools. It allows you to easily add a heading and a
"quick pick" list to the page that a user sees (in addition to the usual navigation menu) when they first log on to CMS
Builder.

To customize the plugin and make it ready fro implementation, you'll want to change some things and remove a few lines
in the code.

First, find the list:


<ul>
      <li><a href="?menu=news&action=add">Add a News Item</a></li>
      <li><a href="?menu=jobs&action=add">Add a Job Listing</a></li>
      <li><a href="?menu=events&action=add">Add an Event</a></li>
    </ul>


This needs to be modified to reflect the tasks and editors that are appropriate for your site.

So you might change:


<li><a href="?menu=news&action=add">Add a News Item</a></li>


to:


<li><a href="?menu=projects&action=add">Add a project to your "project" list</a></li>


You can add as many items as you like.

The items in the list could also be links to external sites. (don't forget the http:// ) 


<li><a href="http://www.yoursite.com">Visit my web site</a></li>


You'll also want to change the "To modify this title, find and change this text in the Modify Homepage plugin!", that's
used as a title for the page to something more appropriate, or remove the text altogether.

You'll also want to remove the: 


<p><strong>To modify this content, find and change this text in the Modify Homepage plugin.</strong></p>


If you get adventurous, you can change the items (or the entire list) to show only for specific users classes. 

Add some check boxes to your user accounts section and call them similarly to the way the plugin checks for admin
status:


<?php if ($CURRENT_USER['isAdmin']): ?>
<p><b>Administrators:</b> Use the <a href="?menu=database">Section Editors</a> to add sections and generate PHP
viewers.</p>
    <?php endif ?>


For example, you might create a check box field called "project" and use this code to display some code only if the
check box is checked :


<?php if ($CURRENT_USER['project'] == 1): ?>
Some code...
<?php endif ?>


Tim Forrest from Toledoh Enterprises suggested adding a frame to display the free Pixlr image editor as part of the
page.


<iframe id="pixlr" type="text/html" width="100%" height="600" src="http://pixlr.com/express/" frameborder="0"></iframe> 


I love the idea, but preferred to make it a link at the bottom of my list, by adding:

 
<br />
      <li><a href="http://pixlr.com/express/" target="_blank">Quickly crop or edit an image using the free Pixlr Express
image editor</a></li>


To further customize this plugin, you might want to pull data directly from a CMSB Section editor.

I set up a single record editor called "Quick Start Guide" with fields for "guide" information, and "resources". I
wanted to show that information on the CMSB Interface's "Home Page" as well as in a separate viewer that could be
accessed at all times.

Here's How I did that:

After the code


function modifyHomepage_content() {
  global $CURRENT_USER;
  ?>
__


I added a standard // load viewer library call


  $libraryPath = 'cmsAdmin/lib/viewer_functions.php';
  $dirsToCheck = array('/path_to_your_server/','','../','../../','../../../','../../../../');
  foreach ($dirsToCheck as $dir) { if (@include_once("$dir$libraryPath")) { break; }}
  if (!function_exists('getRecords')) { die("Couldn't load viewer library, check filepath in sourcecode."); }
__


Then I added the get records call


<?php  
  // load records
  list($quick_start_guideRecords$quick_start_guideMetaData) = getRecords(array(
    
'tableName'   => 'quick_start_guide',
   
    
'limit'       => '1',
  ));
  
$quick_start_guideRecord = @$quick_start_guideRecords[0]; // get first record

?>
__


Then I added the code required to display my data.


<span class=" heading-text-bold">Welcome <?php echo mysql_escape($CURRENT_USER['first_name']); ?> <?php echo
mysql_escape($CURRENT_USER['last_name']); ?>,</span>
 <?php  

  // load records
  list($quick_start_guideRecords$quick_start_guideMetaData) = getRecords(array(
    
'tableName'   => 'quick_start_guide',
   
    
'limit'       => '1',
  ));
  
$quick_start_guideRecord = @$quick_start_guideRecords[0]; // get first record

?>
 <span class="medium"><?php echo $quick_start_guideRecord['guide'?><?php echo $quick_start_guideRecord['resources']
?></span>

<?php endif ?>
<span class=" medium">       


      <a href="http://your_web_URL/listing.php" target="_blank"><b>Click To View All Of Your Records</b></a>
        
       
  <b>This &quot;Quick Start Guide&quot; will always be available from the link on the menu on the left side of your
screen.</b></span>


I enabled a plugin called exampleHeaderLinks to add the link to my Quick Start Guide to the menu on the left side of the
CMSB Interface.

You can download a copy of that plugin from:

http://www.thecmsbcookbook.com/downloads/exampleHeaderLinks.zip


THE GREY HIDDEN RECORDS PLUGIN - Dec 29th, 2018

This free plugin from Chris Waddell at Interactive Tools changes the color of records in the CMS Admin menus to Grey if
their "hidden" field is checked. It has no effect on sections without a hidden field. 

You can download the free plugin from:

http://www.interactivetools.com/add-ons/?user_submitted=1


THE SHOW HIDE FIELDS FOR USERS PLUGIN - Oct 15th, 2011

This very handy plugin was written by Chris from Interactive Tools allows customization of which fields appear in an
editor depending on the current user's 'skill_level' field.

The first thing that you'll need to implement this plugin is a new single value pull down list field in the User
Accounts (accounts) editor called skill_level. You can name it anything that suites your application because you'll
reference that field in the plugin.

Decide on the skill levels you'll need for your application and enter them as the values to be chosen from the pull down
list for each user account.

For this example there are 2 skill levels, Beginner and Expert.

If you want to manage the fields in a section called "my_section" you'll want to have a list of the field names in that
section handy.

Let's assume you've got 4 fields named beginner_field_1, beginner_field_2. expert_field_1, and expert_field_2

Next open the plugin and insert the name of your skill level field in the code:


$GLOBALS['BETA_USER_SKILL_FIELDS_SKILL_FIELD_NAME'] = 'skill_level';



In in the code below, (remember, my_section refers to the section where you want to show or hide fields), both expert
fields will be hidden from any user whose skill_level is "Beginner". You can add as many skill levels as you like, but
the last one has no trailing comma.



$GLOBALS['BETA_USER_SKILL_FIELDS_CONFIG']['my_section'] = array(
  
  // user's skill values and lists of fields to hide
  'Beginner' => array('expert_field_1', 'expert_field_2'),
  'Expert'   => array()
  
);


Repeat this code block as many times as necessary and replace "my_section" with the name of the editor that you want to
affect.



PULLING ARRAY VALUES FROM ANOTHER EDITOR
Here's where the fun begins. Set up this plugin so that your clients can change the hidden fields for the various skill
levels without needing to dive into the back end of CMSB and mucking up the works.

Here's how:

THE NEW EDITOR
Create a single record editor with a multi-value check box list field for each skill level you'll need to control.
beginner_hide and expert_hide are used for this example.

Insert the field names in the table you want to show or hide as the option values for the lists.
Note: Don't forget that you can use field names followed by the "pipe" character "|" followed by a user friendly name to
make your list options more readable. Like this:


my_first_field_name_with_lots_of_complex_characters|The Simple Name


Check some boxes and save the record

MODIFY THE PLUGIN
CAVEAT: Remember nothing can precede the"Plugin Name" through "Requires at least" code block.

So, insert a:



?>
<?php


after that code block:



<?php

?>
<?php


Insert your load records calls in between the ?> and the <?php you just addedlike this:



<?php


?>
<?php
  
 require_once "/path_to_your/cmsAdmin/lib/viewer_functions.php";
  
  
// load records
  list($your_list_tableRecords$your_list_tableMetaData) = getRecords(array(
    
'tableName'   => 'your_list_table',
    
'allowSearch' => false,
    
'limit'       => '1',
  ));
  
$your_list_tableRecord = @$your_list_tableRecords[0]; // get first record

?>


Instead of the code:


 'Beginner' => array('expert_field_1', 'expert_field_2'),
  'Expert'   => array()


Insert the code:


 'Beginner'   => explode("\t", trim($your_list_tableRecord['beginner_hide'], "\t")),
 'Expert'   => explode("\t", trim($your_list_tableRecord['expert_hide'], "\t"))


This will populate your arrays with the checked values in each of the check box fields.

You can add as many arrays as you need to, but remember the last one has no trailing comma
 
Your modified code should look something like this:

<?php



?>
<?php
  
 require_once "/path_to_your/cmsAdmin/lib/viewer_functions.php";
  
  
// load records
  list($your_list_tableRecords$your_list_tableMetaData) = getRecords(array(
    
'tableName'   => 'your_list_table',
     
'allowSearch' => false,
    
'limit'       => '1',
  ));
  
$your_list_tableRecord = @$your_list_tableRecords[0]; // get first record

?>
<?php 

// CAVEATS:
//   - it is not currently possible to hide Separator fields.

// UPDATE THESE VALUES

$GLOBALS['BETA_USER_SKILL_FIELDS_SKILL_FIELD_NAME'] = 'skill_level';
 
// For each section, copy this block
$GLOBALS['BETA_USER_SKILL_FIELDS_CONFIG']['my_section'] = array(
  
  
// user's skill values and lists of fields to hide
  
 'Beginner'   => explode("\t"trim($your_list_tableRecord['beginner_hide'], "\t")),
 
'Expert'   => explode("\t"trim($your_list_tableRecord['expert_hide'], "\t"))
  
);

// DON'T UPDATE ANYTHING BELOW THIS LINE



**IMPORTANT**: Changing the values in the arrays MAY NOT affect any existing recordsso you'll need to test by creating
NEW records to see which fields are actually hidden or shown.


To see what values are actually being inserted into your arrays, you can add:



showme($GLOBALS['BETA_USER_SKILL_FIELDS_CONFIG']['master_subscription_pages']);


Just above the // DON'T UPDATE ANYTHING BELOW THIS LINE

And when you log into your CMSB admin Interface you'll see the array values superimposed over your interface. 
View the source of that page and you'll see the values much more clearly.

When you're done, remove or comment out the showme line with double slashes (//), or your users will see the
superimposed values as well.




HIDING SEPARATORS
Instead of using standard separators, use HTML separators with code like these examples (the ID name of the separator is
copied from your field list)

Red Headline Text


<tr>
 <td colspan='2'>
 <div class="content-box content-box-divider" name="__separator017__"> 
     <div class="content-box-header"><h3><font color="red">YOUR MENU INFORMATION</font></h3></div> 
   </div>
 </td>
</tr>


or explanatory text inside a box



<tr>
 <td colspan='2'> <div class="content-box content-box-divider" name="__separator016__">Your explanatory text goes
here.<br /> <br /></div>
 </td>
</tr>


Or explanatory text without a box


<tr>

 <td colspan='2'><div  name="__separator015__"><font color="red">IMPORTANT:</font> Your explanatory text goes here. <br
/> <br /></div>
 </td>
</tr>


Then include each separator names in your field name list




__separator015__|Explanatory text
__separator017__|The Menu


THE CHANGE RECORDS PER PAGE DEFAULT PLUGIN - Mar 7th, 2013

One of the new features of CMS Builder (since version 2.17)  is the ability select how many records are displayed on the
list page of a section. If you go to the Section Editor section in the left hand menu, then click modify on a section
you want to edit. At the top of the modify section page should be 5 tabs, if you select the advanced tab you should see
a per page drop down that allows you to select how many records should be displayed per page.

If you want to institute a "pseudo" global approach to this issue, see the recipe called CHANGE THE DEFAULT 25 RECORDS
PER PAGE ON A RECORD LIST. 

And, if you're using Version 2.51 or later, and are looking for a more global change, you can use the plugin below
offered by Dave Edis from Interactive Tools to change all of the default per page values to any value you need.

He suggests backing up all of your /data/schema/  files first and then try this:

Copy the code below to as blank document, name it perpagechanger.php, upload it to your plugins folder and activate the
plugin under Admin>plugins.



<?php


### UPDATE THESE VALUES
$GLOBALS['PERPAGECHANGER_NEWVALUE'] = 1000//

// DON'T UPDATE ANYTHING BELOW THIS LINE

// plugin menu - add link
pluginAction_addHandlerAndLink(t('Update Sections'), 'perPageChanger''admins');

//
function perPageChanger() {
  foreach (
getSchemaTables() as $tableName) {
    
$schema loadSchema($tableName);
    
$schema['_perPageDefault'] = $GLOBALS['PERPAGECHANGER_NEWVALUE'];
    
saveSchema($tableName$schema);
    
alert("Updated '$tableName' to have a per page default value of {$GLOBALS['PERPAGECHANGER_NEWVALUE']}\n");
  }
}

?>


USING THE WEBSITE MEMBERSHIP PLUGIN

An effective membership system has a number of components. Some are listed here, if you think of others, please let me
know. At this writing, accounting is a manual process, but I’m sure that some of you will have ideas along those
lines. I’ll share what I’ve done in each of these areas and expand this section as new information is available.

•    Automated Signup
•     Automatic/manual approval, pre-approval emails, email confirmation requirements
•    Account duration
•     Unlimited user "groups". Provide access to multiple directories to different members.
•     "Individual" group option. Give your members access to their own private directory, unique to their username. Only
they can access it.
•     Customizable templates for all HTML and Email responses, including personalization fields. Example: "Hello
[FIRST_NAME]"
•     Mass mail members and member groups with personalization fields. Example: "Hello [FIRST_NAME]"
•     New News Updates section. Important security or release information provided. Pop-up notification of important
updates.
•     Member failed login attempts receive easy to understand responses and tool to retrieve member login information via
email.
•     Automatically temporarily block large number of failed login attempts for a configurable period of time.
Password-hacking protection!
•     Permanently ban email addresses or entire domains which you do not wish to give membership accounts to.
•     Form fields validated for accuracy required status, and formatting.
•     User search by Username, First Name, Last Name, or Email Address
•     Different admin assignment for different groups

ACCOUNTING
•     Automated Recurring Billing with hooks to major pay services 
•     Day-at-a-glance sales/income totals
•    Accounting Functions like account charges, billing cycle, customer edit credit card info, response to successful and
unsuccessful registration attempts, automated email to remind of expiring accounts (non-recurring accounts and invalid
CC info), etc.


THE WEBSITE MEMBERSHIP PLUGIN - Apr 16th, 2013

There are some interesting features built in to this plugin. I’m sure that there will be many more to come.

Here’s the brief summary:

Website Membership works by allowing site visitors to sign-up for a user account. Once signed up they can log in to view
password protected content on your website. At any time, if your visitor has forgotten their password they can request
to have it emailed to them.

The changes in Version 1.08+ require some code changes in viewers to work correctly, I'll be documenting them over the
next few weeks. 

Version 1.10 Released 3/18/13 

SERVER REQUIREMENTS: CMS v2.52

NEW FEATURES
- Security: Added blacklist of top 100k common passwords.  Found in /lib/login_password_blacklist.txt
- Usability: Whitespace is removed from beginning and end of username and password on login (to prevent copy and paste
errors)
- Usability: Passwords that start or end with spaces are no longer allowed.
NOTE: If upgrading, use Code Generator to recreate the Password Reset and Edit Profile pages to take advantage of new
features.

MINOR CHANGES & BUG FIXES
- Misc Code and other minor improvements


Version 1.09 Released 1/29/13

NEW FEATURES
- Login Form: Users can now login with either username or email (on sites supporting usernames)
- Signup Form: Username support can be disabled by setting $useUsernames = false;
- Profile Form: Username support can be disabled by setting $useUsernames = false;
- Profile Form: Added sample commented code for adding "Agree to Terms of Service" checkbox
- Email Templates: can now be edited in CMS under: Admin > Email Templates
- Example Pages: can now be generated under: Admin > Code Generator
- Account Table: Changing account table name will cause new table to be created if it doesn't exist


MINOR CHANGES & BUG FIXES
- Enabled "Required System Plugin: True" in plugin so plugin can't be accidentally disabled
- Fixed bugs related to invalid links being generated in some instances
- Logoff now redirects back to referring page if no other url specified
- Login page now redirects back to referring page if no other url specified
- Removed files: Old email templates folder /emails/ has been removed
- Removed files: Old example files folder /examples/ has been removed
- Misc Code and other minor improvements

Version 1.08 Released 10/29/12 

MINOR CHANGES & BUG FIXES
- Added a plugin hook 'wsm_loginSuccess' to allow custom code to always run after a user successfully logs in
- Added a plugin hook '_websiteLogin_init.pre_actionHandler' to support future social media login plugins
- Added new option 'WEBSITE_LOGIN_REQUIRED_FIELDS' to force users to fill out required profile fields
- Fixed bug where entire $_SESSION array would be erased when a user was not logged in

Version 1.07 Released 1/27/12

MINOR CHANGES & BUG FIXES
- On save of WSM_ACCOUNTS_TABLE, if it's not accounts, encrypt passwords if needed
- Example files now use $GLOBALS['WSM_ACCOUNTS_TABLE'] instead of 'accounts' so they recognize different accounts
tablenames
- Sign-up example file no longer updates the _accesslist table if using a seperate table for website users


New in Version 1.06

NEW FEATURES
- You can now specify a separate accounts tables for website users (see WSM_ACCOUNTS_TABLE in plugin code)
- You can now login to the CMS and website as different users simultaneously (see WSM_SEPARATE_LOGIN in plugin code)

MINOR CHANGES & BUG FIXES
- Fixed bug where plugin code was being automatically run by command-line scripts
- Fixed typo on examples/user-signup.php.  Changed $errors to $mailErrors on line 96

New in Version 1.05

MINOR CHANGES & BUG FIXES
- Fixed display of mail errors in email template
- Fixed issue with reset password feature causing an error

New in Version 1.04
NEW FEATURES
- Updated Plugin and sample_profile.php to support new Encrypted Passwords feature in v2.08
- Email templates are now available in /emails/ 

MINOR CHANGES & BUG FIXES
- You can now add false to websiteLogin_redirectToLogin(false) to prevent the user from being redirected back to the
previous page after login
- Profile: You can now avoid having users logged out after changing their username and/or password by calling the new
websiteLogin_setLoginTo() function

New in Version 1.03
MINOR CHANGES & BUG FIXES
- Login: After login users are now directed to the last "login-required" page instead of WEBSITE_LOGIN_POST_LOGIN_URL
- Docs: Added section to readme.txt: How to display custom login errors (Not approved yet, etc)
- Errors: Message is now displayed for disabled accounts on login: "Your account has been disabled!"

New in Version 1.02
- Docs: Added section to readme.txt: How to add a custom text field
- Docs: Added section to readme.txt: How to add radio fields with values loaded from CMS
- Erase Account: Added new button on profile page to allow users to remove their accounts
- Misc Code and other minor improvements

New in Version 1.01
 Signup: user is now emailed a random password to confirm their email address (they can't login without receiving
password)
- Edit your Profile: added new form to allow user to update their profile details and change their password, and any
other fields you've setup.
- Docs: Added section to readme.txt on how to use email as the username (if you don't want users making up their own
usernames)

Along with other functionality that’s built in to this module, you can display certain information in your viewers on
a restricted basis. 

To determine if the current user is logged in, you can test with:



<?php if (current_user): ?> 
only logged in user will see this... 
<? php endif ?>



You could set up a separate (text or check box) field in the user accounts editor and assign it a value for that
particular class of users. Then test with:    



<?php if ($CURRENT_USER['your_field'] == "your_value"): ?>Current user will be able to see this<?php endif ?>



Only user(s) who have that value entered/selected in their user record will be able to view the protected information on
the web page. 

Chris Waddell from Interactive Tools added these hints:

To determine if the current user is the "author" of a record, you can test with:


    
<?php if ($record['createdByUserNum'] == $CURRENT_USER['num']): ?>Only the author will be able to see this<?php endif ?>



You may want to also allow admins to see your protected information:



<?php if ($record['createdByUserNum'] == $CURRENT_USER['num'] || $CURRENT_USER['isAdmin']): ?>Author and administrators
will be able to see this<?php endif ?>


ARE YOU TRAPPED IN A “YOU ARE ALREADY LOGGED IN” LOOP? - Aug 9th, 2020

The solution to this issue proved to be very illusive but it's now solved, and I hope that this will save someone a
whole heap of time and frustration.

The one small coding change that actually fixed the issue was in the website membership plugin (V1.13) itself.

The  $GLOBALS['WEBSITE_LOGIN_LOGIN_FORM_URL']  had a full URL entered.

When I changed the full URL in $GLOBALS['WEBSITE_LOGIN_LOGIN_FORM_URL']  = 'http://www.my-site.com/member_login.php';  
to a relative URL: $GLOBALS['WEBSITE_LOGIN_LOGIN_FORM_URL']  = '/member_login.php';

The loop issue and the misdirects after login issues that my client had mentioned all disappeared.

And just to be sure, every time I changed the relative URL back to a full URL, the issue returned.

Another possibility is that you don't have a destination set in $GLOBALS['WEBSITE_LOGIN_POST_LOGIN_URL'] =

Try setting a relative URL her and see if that helps.

I'm still curious as to why that made a difference, but there you have it...


ADDING PROFILE IMAGES USING THE USER UPDATE FORM (REVISED FOR MYSQLI) - Aug 3rd, 2019

I needed to add a profile image to existing account records using the user update form that’s part of the Website
Membership Plugin. I also wanted to enable replacing that profile image when desired.

I was stumped until I came across this hidden gem provided by Chris way back in 2010.

Note that  with a great deal of help from Daniel Louwe, 'ignoreHidden' => true was added to the get records call so that
hidden records would not throw an error and I was able to revise the code to accommodate multiple profile images in the
same record (My account records were for group therapy practices with more than one therapist and I wanted to keep all
therapists on the same record and have a profile image for each)

*** The key is to increment all variables for each new profile image. So $current_user_with_uploads becomes
$current_user_with_uploads1, then $current_user_with_uploads2. $uploadFieldName becomes $uploadFieldName1, then
$uploadFieldName2 etc.

1) Add an upload field to the accounts table called profile_image and limit the maximum uploads to 2 (this allows an
existing profile image to be replaced with a new one). If more than one profile image is required, add additional upload
fields named profile_image_1, profile_image_2, etc.

2) Add the following to your existing form table (style as appropriate):


 <tr>
<td class="your_class" colspan="2"><b>FIRST PROFILE IMAGE</b></td>
</tr>
<tr>
<td colspan="2" class="your_class" valign="top">1 JPG Only - 2 MB max. - Uploading a new image will replace any existing
image.  Images will be uploaded when the &quot;revalidate or update&quot; button at the bottom of this page is
clicked/tapped.</td>
<td><?php  list($accountRecords,) = getRecords(array('tableName' => 'accounts''where' => mysql_escapef('num = ?',
$CURRENT_USER['num']), 'allowSearch' => false'ignoreHidden' => true));
 
$current_user_with_uploads = @$accountRecords[0];
?>
<?php if (sizeof(@$current_user_with_uploads['profile_image'])):
$upload2 $current_user_with_uploads['profile_image'][0?>
<a href="<?php  echo $upload2['urlPath'?>"> <img src="<?php  echo $upload2['thumbUrlPath'?>" width="<?php echo
$upload2['thumbWidth'?>" height="<?php echo $upload2['thumbHeight'?>" alt="" />
</a>
<?php  else: ?>
<span class="your_class">No (new) image uploaded</span>
<?php  endif ?>
<span class="your_class">Image To Upload:</span>
<input type="file" name="profile_image"></td>
</tr>

<tr>
<td class="your_class" colspan="2"><b>SECOND PROFILE IMAGE</b></td>
</tr>
<tr>
<td colspan="2" class="your_class" valign="top">1 JPG Only - 2 MB max. - Uploading a new image will replace any existing
image. Images will be uploaded when the &quot;revalidate or update&quot; button at the bottom is clicked/tapped.</td>
<td><?php   list($accountRecords,) = getRecords(array('tableName' => 'accounts''where' => mysql_escapef('num = ?',
$CURRENT_USER['num']), 'allowSearch' => false'ignoreHidden' => true));
 
$current_user_with_uploads1 = @$accountRecords[0];
// if (!$current_user_with_uploads ) { dieWith404("Record not found!") ; } // code suggested by Daniel 10/2/18 
?>
<?php if (sizeof(@$current_user_with_uploads1['profile_image_1'])):
$upload1 $current_user_with_uploads1['profile_image_1'][0?>
<a href="<?php echo $upload1['urlPath'?>"> <img src="<?php echo $upload1['thumbUrlPath'?>" width="<?php echo
$upload1['thumbWidth'?>" height="<?php echo $upload1['thumbHeight'?>" alt="" />
 </a>
 <?php else: ?>
 <span class="your_class">No (new) image uploaded</span>
<?php endif ?>
 <span class="your_class">Image To Upload:</span>
 <input type="file" name="profile_image_1"></td>
</tr>
 

3) Add 
ENCTYPE="multipart/form-data" 
 to any other code in your opening <FORM> tag.

4) Add the following just before // update user 

// +++++++++++++++++++++++++++++ Begin upload Image 1 +++++++++++++++++++++++++++++++++
$uploadFieldName1 = 'profile_image_practitioner_1';
$uploadInfo1 = @$_FILES[$uploadFieldName1];
if (!empty($uploadInfo1['name']) && !$errorsAndAlerts) {
// attempt to save the upload
$errors1 = saveUpload('accounts', $uploadFieldName1, $CURRENT_USER['num'], null, $uploadInfo1, $newUploadNums1);
// check for errors
if ($errors1) {
$errorsAndAlerts .= "There was a problem with your upload: $errors1\n";
}
else {
// if the upload was successful, delete any other uploads associated with that record and field (so users only ever have
one Head Shot)
global $TABLE_PREFIX;
// create query
$where = mysql_escapef(" WHERE tableName = 'accounts' AND recordNum = ? AND num != ? AND fieldName = ?",
$CURRENT_USER['num'], $newUploadNums1[0], $uploadFieldName1);
// remove upload files
$query1 = "SELECT (asterisk) FROM `{$TABLE_PREFIX}uploads` $where";
$result1 = mysqli()->query($query1) or die("MySQL Error: ". htmlspecialchars(mysqli()->error) . "\n");
while ($row1 = $result1->fetch_assoc()) {
$files1 = array($row1['filePath'], $row1['thumbFilePath'], @$row1['thumbFilePath2'], @$row1['thumbFilePath3'],
@$row1['thumbFilePath4']);
foreach ($files1 as $filepath1) {
if (!$filepath1 || !file_exists($filepath1) || @unlink($filepath1)) { continue; }
}
}
if (is_resource($result1)) { mysql_free_result($result1); }
// remove upload records
mysqli()->query("DELETE FROM `{$TABLE_PREFIX}uploads` $where") or die("MySQL Error: ". htmlspecialchars(mysql_error()) .
"\n");
}

//++++++++++++++++++++++++++++  End upload image 1 ++++++++++++++++++++++++++++

// ++++++++++++++++++++++++++++ Begin upload image 2 +++++++++++++++++++++++++++
$uploadFieldName2 = 'profile_image_practitioner_2';
$uploadInfo2 = @$_FILES[$uploadFieldName2];
if (!empty($uploadInfo2['name']) && !$errorsAndAlerts) {
// attempt to save the upload
$errors2 = saveUpload('accounts', $uploadFieldName2, $CURRENT_USER['num'], null, $uploadInfo2, $newUploadNums2);
// check for errors
if ($errors2) {
$errorsAndAlerts .= "There was a problem with your upload: $errors2\n";
}
else {
// if the upload was successful, delete any other uploads associated with that record and field (so users only ever have
one Head Shot)
global $TABLE_PREFIX;
// create query
$where = mysql_escapef(" WHERE tableName = 'accounts' AND recordNum = ? AND num != ? AND fieldName = ?",
$CURRENT_USER['num'], $newUploadNums2[0], $uploadFieldName2);
// remove upload files
$query2 = "SELECT  (asterisk) FROM `{$TABLE_PREFIX}uploads` $where";
$result2 = mysqli()->query($query2) or die("MySQL Error: ". htmlspecialchars(mysqli()->error) . "\n");
while ($row2 = $result2->fetch_assoc()) {
$files2 = array($row2['filePath'], $row2['thumbFilePath'], @$row2['thumbFilePath2'], @$row2['thumbFilePath3'],
@$row2['thumbFilePath4']);
foreach ($files2 as $filepath2) {
if (!$filepath2 || !file_exists($filepath2) || @unlink($filepath2)) { continue; }
}
}
if (is_resource($result2)) { mysql_free_result($result2); }
// remove upload records
mysqli()->query("DELETE FROM `{$TABLE_PREFIX}uploads` $where") or die("MySQL Error: ". htmlspecialchars(mysql_error()) .
"\n");
}

// +++++++++++++++++++++++++++ End upload image 2 ++++++++++++++++++++++++++++++++++ 


Chris added that more than half of that code is dedicated to removing any other uploads after an upload is successful so
that users never have more than one profile image. This is important since I haven't provided any way for users to
remove a profile image. They can, of course, replace their current image with another. (This is where the maximum upload
limit of 2 comes in. If it was set to allow only 1, then the upload of the replacement image, seen as a second image
before the original was removed, would fail.)


UPGRADING THE WEBSITE MEMBERSHIP PLUGIN FROM V1.03 - Dec 29th, 2018

IMPORTANT: There have been some major changes since earlier versions of the plugin, so you won’t be able to just
overwrite the old plugin this time. 

According to Dave Edis, from Interactive Tools, “There's no easy upgrade so you need to use this (latest version) on
new sites or recreate the pages.”

YOU MUST MODIFY SOME OF YOUR WEB PAGES TO USE THIS NEW VERSION

According to Robin Brayer from Interactive tools, the pages that need to be updated to upgrade the Website Membership
plugin from 1.03 to the latest version are:

login
password-reset
user-profile 
user-signup

He said, “The simplest thing to do is to replace the whole block of php at the top of each membership page (login,
password-reset, user-profile and user-signup pages) with the new block from the example files. If there are any custom
changes, you'll need to re-integrate them.”

NOTE: This recipe I've tried to present a pretty detailed, step by step approach, so even inexperienced programmers
shouldn’t have too much trouble upgrading.

So...

Here we go...

CAVEAT: If you’re not running at least version 2.08 of CMS Builder, you’ll have to upgrade to that version. V 1.04
of the membership plugin won’t work without it. Later versions may require the then current version of CMSB to
operate. (membership V1.06 and V1.07 require CMSB 2.13)

UPGRADING CMS BUILDER
You can download the current CMSB version from:

http://www.interactivetools.com/upgrade/

To begin the upgrade to the latest version of CMSB, backup your entire MySQL database through the general settings page
of your current CMSB Admin interface.

It's a good idea to download your entire cmsAdmin directory to your local computer just in case the upgrade doesn’t
work as expected and you have to restore the old CMSB version. I'd suggest that you download a copy of your existing
membership related pages as well, since you're going to be making some pretty drastic changes to them.

Once you’ve backed up all the files, upload all of the new folders and files from the latest version of CMSBuilder to
your on-line cmsAdmin folder and overwrite all of the old CMSB operating files with these new files.

Beginning with Version 2.08 the concept of user password encryption was incorporated to CMSB. 

IMPORTANT: Make sure that you don’t choose to encrypt your passwords at this point because your current version of the
website membership plugin won’t work with encrypted passwords. 

ALSO: If you’ve made any custom changes to your CMSB operating files for added functionality, you’ll have to make
those changes again to restore that functionality.

UPGRADING THE PLUGIN

Once the new version is installed and working correctly, it’s time to begin the upgrade to V1.07 of the website
membership plugin

First download the latest version of the Website Membership plugin from:

http://www.interactivetools.com/add-ons/

and extract the files to a new folder so you don’t overwrite your older plugin files.

*** You'll have to update a number of file paths at the top of the new plugin so that your log in and update files can
be found. Sp do that before you upload the new version to your plugins folder.

 Next, make a copy of all of the pages that you’re planning to update. (the log in page, the user profile page, the
password reminder page, etc.)

If you’re like me, you’ve customized the code on those pages to match your site’s requirements, so you won’t be
able to just cut and paste a lot of the code.

Instead, you’ll probably find yourself comparing the active code line by line and updating as required.

THE LOG IN PAGE
Start with the easiest one, which is probably your login page.

Open the copy you made of your current user login page and also the current plugin’s sample login page
(user-login.php) from the “examples” directory.

Examine the active code of both, line by line and if you find something that you need to update, change that code and
then move to the next.

When you’re done, move on to another pair of pages, like the user signup form.  

The first place in the signup form that I found that needed changing was to change the // load viewer library code from
absolute to relative paths.

The next was to change the password related values in the // Add User section to accommodate the use of encrypted
passwords.

After that there are changes the send email section.

In the old form, that code started with:


_websiteLogin_sendSignupMessage();

Since the plugin now incorporates the ability to automatically send emails using emails/user-new-signup.php The code in
the new form starts with:  


// send message
      $emailTemplate = "emails/user-new-signup.php";


First, copy any custom error message code you created, then replace the code from:


 _websiteLogin_sendSignupMessage();


up to:  


$_REQUEST        = array(); // clear form values


with the new block of code.



THE PASSWORD REMINDER PAGE
If you've got a password reminder page, the active code it will have to be replaced. 

Begin with the code in the error checking section at the top of the page, and just below your "Already logged in /
logoff" code after the brace,  add:


### send reset email
  if (@$_REQUEST['action'] == 'sendPasswordReminder') {
    global $SETTINGS, $TABLE_PREFIX;

    // display errors
    if (!@$_REQUEST['usernameOrEmail']) { $errorsAndAlerts .= "No username or email specified!<br />\n"; }

    // send emails
    if (@$_REQUEST['usernameOrEmail']) {
      $where = mysql_escapef("? IN (`username`,`email`)", $_REQUEST['usernameOrEmail']);
      $user  = mysql_get('accounts', null, $where);

      // send message
      if ($user) {

        // get reset password url
        $resetCode  = _generatePasswordResetCode( $user['num'] );
        $resetQuery = "?userNum={$user['num']}&resetCode=$resetCode";

        //
        $emailTemplate = "emails/user-password-reset.php";
        $emailHeaders  = emailTemplate_load(array(
                          'template'     => websiteLogin_pluginDir() . "/$emailTemplate",
                          'subject'      => '', // set in template
                          'from'         => '', // set in template
                          'to'           => $user['email'],
                          'placeholders' => array(
                            'username' => $user['username'],
                            'loginUrl' => "http://" . $_SERVER['HTTP_HOST'] . $GLOBALS['WEBSITE_LOGIN_LOGIN_FORM_URL'],
                            'resetUrl' => "http://" . $_SERVER['HTTP_HOST'] . $GLOBALS['WEBSITE_LOGIN_RESET_URL'] .
$resetQuery,
                          ),
                        ));
        $mailErrors   = sendMessage($emailHeaders);
        if ($mailErrors) { die("Mail Error: $mailErrors"); }

        //
        $errorsAndAlerts .= "Thanks, we've emailed you instructions on resetting your password.<br /><br />

        If you don't receive an email within a few minutes check your
        spam filter for messages from {$emailHeaders['from']}<br />\n";

        // clear form
        $_REQUEST['usernameOrEmail'] = '';
      }

      //
      if (!$user) { $errorsAndAlerts .= "No matching username or email was found!<br />\n"; }
    }
  }


THE PASSWORD REMINDER EMAIL
To implement the password reminder (now a password reset request) you'll need to have 2 new files on your site in
addition to the revised "password reminder".

1) A sample password reminder email template that comes with v1.07 of the plugin can be found in the "emails" folder.
Change the message text if required, and upload the file to your plugins folder. This send an email to the member
requesting their password.

2) A user-password-reset.php that can be found in the examples folder of the plugin. This file updates the user's
password  Re-skin this file and upload it to the same directory that stores the other user forms, usually the root
directory of your site.

Since the idea of resetting passwords may be new to your members, you may find it handy to change the original sample
email message from:


 Hi <?php echo htmlspecialchars($PLACEHOLDERS['username']) ?>,<br /><br />

You requested a password reset for <?php echo htmlspecialchars($_SERVER['HTTP_HOST']) ?>.<br /><br />

To reset your password click this link:<br />
<a href="<?php echo $PLACEHOLDERS['resetUrl'?>"><?php echo $PLACEHOLDERS['resetUrl'?></a><br /><br />


 to:


Hi <?php echo htmlspecialchars($PLACEHOLDERS['username']) ?>,<br /><br />

For security reasons, passwords can no longer emailed. If you have forgotten your password, you will have to reset it to
a new value for access to <?php echo htmlspecialchars($_SERVER['HTTP_HOST']) ?>.<br /><br />

To reset your password click this link:<br />
<a href="<?php echo $PLACEHOLDERS['resetUrl'?>"><?php echo $PLACEHOLDERS['resetUrl'?></a><br /><br />


THE PROFILE CHANGE REQUEST PAGE
If you're not too confused yet, the next change is to your user profile change request page:

The first change is in the "// new password checking" section at the top of your viewer. Copy the entire block and paste
it just before the corresponding block in your existing viewer. Change any messages that you've modified, and then
delete your old code block. 

Then after //update password insert the entire new "// update password if needed", code block:


 // update password if needed
      $password         = @$_REQUEST['newPassword2'] ? @$_REQUEST['newPassword2'] : $CURRENT_USER['password'];
      if (@$SETTINGS['advanced']['encryptPasswords']) { $passwordHash = getPasswordDigest($password); }
      else                                            { $passwordHash = $password; }


Next, in your  $query = "UPDATE `{$TABLE_PREFIX}accounts` SET section, replace: 


password         = '".mysql_escape( $CURRENT_USER['password'] )."',


with:


password         = '".mysql_escape( $passwordHash )."',


That should do it.

If you are using CAPTCHA creator, see the recipe entitled, "IMPLEMENTING CAPTCHA ON THE WEBSITE MEMBERSHIP PLUGIN 1.04+
SIGNUP FORM".

If you're using the updated SpamBot plugin 2.0 and you are getting strange results returned in your email form fields,
consider adding <?php $GLOBALS['SEP_DISABLED'] = 1?> to the top of those pages, before any includes or requires. This
will disable the plugin on those pages annd your results should look normal again.

If you find anything that I've forgotten, or that needs further explanation, please send me an email and I'll add your
discovery to this recipe.


UPDATING OR INSTALLING V1.07 - Aug 15th, 2012

If you updated to or installed ONLY Version 1.07 of the website membership plugin with code you downloaded before Jan 30
2012, and find that when a current user uses the profile.php file to change their password, it doesn't actually change
the password, here’s the probable culprit:

There was a bug discovered in the profile page example code that was squashed (and the example code revised) on Jan 30,
2012. 

To fix it in older builds, search for:



$query = "UPDATE `{$TABLE_PREFIX}accounts` SET


and change it to:

    

$query = "UPDATE `{$TABLE_PREFIX}" . @$GLOBALS['WSM_ACCOUNTS_TABLE'] . "` SET


PERSONALIZING PAGES USING THE WEB SITE MEMBERSHIP PLUGIN - Aug 18th, 2011

Adding a personal touch like "Welcome Aubry Jones" to a page that uses the membership plugin is pretty easy, and it can
be done with as little as one line of code.

Here's how...



<?php if (@$CURRENT_USER): ?> Welcome <?php echo mysql_escape($CURRENT_USER['first_name']); ?> <?php echo
mysql_escape($CURRENT_USER['last_name']); ?> <?php endif ?>



By substituting any field that exists in your membership account table, and by setting up "elseif" and "else" criteria,
you can display many other types of information to users.


IMPLEMENTING THE EMAILONAPPROVED PLUGIN AND A MANDATORY PASSWORD CHANGE ON FIRST LOGIN - Jul 7th, 2020

Website Membership Plugin Required https://www.interactivetools.com/plugins/website-membership/

When a prospective member filled out the membership application, I wanted their application to be approved manually. I
wanted an e-mail to be sent stating that their application had been received and that as soon as it was processed they
would get a second “welcome” e-mail with their username and a temporary password.

As password encryption became the norm, it became more difficult and then impossible to retrieve a password from the
database and send it to a member.

To get around this obstacle, here’s an approach to send members a generic password ands force them to change their
password the first time they log in.

NOTE: Because of security measures implemented on your server, you might have to change your admin email to a valid
email address on your hosting account, or use an SMTP server email account (set in the Admin > General tab) for emails
to automatically be sent by CMS Builder. 

1) If you don’t have it already, you’ll need to download the emailOnApproved plugin from:

http://www.thecmsbcookbook.com/downloads/emailOnApproved.zip  

2) in emailOnApproved.php search for $message=<<< __TEXT__

remove the {$_REQUEST['password']} and replace it with a generic password that you’ll also enter into your user-signup
form in step 6 

3) You’ll also need to download and install the latest version of the Website Membership plugin.

4) Create 2 new check boxes in the ‘account’ section of your CMS, an ‘Approved’ check box and a ‘First Time
Login’ check box. Change the checked value to NO and the unchecked value to YES in the First Time Login field.

5) In websiteMembership.php search for return $CURRENT_USER;

add this code just before that line:


if (@$_REQUEST['action'] == 'login')   {if (@$CURRENT_USER && (@$CURRENT_USER['first_time_login'] ==
'0'||@$CURRENT_USER['first_time_login'] == "")){ redirectBrowserToURL("cp.php");exit;} // if first time login redirect
to change password page
else; }


7) In the USER_SIGNUP Email template (you’ll find the templates at the bottom of the ADMIN menu group), delete the
username, password and login reference and insert the text that’s appropriate for your site. 

For a membership site, it could be:

“Thanks for signing up.

We’ll review your application and email your login credentials to you as soon as your application is approved.”  

6) In your user-signup form, search for:  $colsToValues['password']         = $passwordHash;

Delete that code and replace it with:  $colsToValues['password']         = ‘YourGenericPassword’; (replacing
YourGenericPassword with the one you used in step 2. Keep the single quotes before and after YourGenericPassword.)

7) In your user-signup form, change this:

 

$errorsAndAlerts  = "Thanks, We've created an account for you and emailed you your password.\n";
$errorsAndAlerts .= "If you don't receive an email from us within a few minutes check your spam filter for messages from
{$fromEmail}\n";

to something like this: 

 $errorsAndAlerts  = "Thanks, we've created an account for you. As soon as you're approved we'll email you your
password.\n";
$errorsAndAlerts .= "If you don't receive an email from us within a reasonable time your spam filter for messages from
{$fromEmail}\n";

Create a web page named cp.php with the following code and upload to your server in the website root directory:

At the top of your page:


<?php $GLOBALS['SEP_DISABLED'] = 1?>
<?php header('Content-type: text/html; charset=utf-8'); ?>
<?php
   // load viewer library
  $libraryPath 'cmsAdmin/lib/viewer_functions.php';
  
$dirsToCheck = array('path_to_your_server/','','../','../../','../../../');
  foreach (
$dirsToCheck as $dir) { if (@include_once("$dir$libraryPath")) { break; }}
  if (!
function_exists('getRecords')) { die("Couldn't load viewer library, check filepath in sourcecode."); }
?>
<?php $GLOBALS['WEBSITE_MEMBERSHIP_PROFILE_PAGE'] = true// prevent redirect loops for users missing fields listed in
$GLOBALS['WEBSITE_LOGIN_REQUIRED_FIELDS'?>
<?php # Developer Notes: To add "Agree to Terms of Service" checkbox (or similar checkbox field), just add it to the
accounts menu in the CMS and un-comment agree_tos lines
  
  //
  $useUsernames   true// Set this to false to disallow usernames, email will be used as username instead

  // error checking
  $errorsAndAlerts "";
  if (@
$_REQUEST['missing_fields']) { $errorsAndAlerts "Please fill out all of the following fields to continue.\n"; }
 
// if (!$CURRENT_USER) { websiteLogin_redirectToLogin(); }


  ### Update User Profile
  if (@$_POST['save']) {
    
// update user
    if (!$errorsAndAlerts) {
     
$colsToValues = array();
      
// ... add more form fields here by copying the above line!
      $colsToValues['first_time_login'] =     '1';
      
$colsToValues['updatedByUserNum'] = $CURRENT_USER['num'];
      
$colsToValues['updatedDate=']     = 'NOW()';
      
mysql_update(accountsTable(), $CURRENT_USER['num'], null$colsToValues);

      
// on success
      websiteLogin_setLoginTo$colsToValues['username'], $CURRENT_USER['password'] );  // update login session username
in case use has changed it.
      
$errorsAndAlerts "Thanks, we've updated your password.\n";
    }
  }


  
### Change Password
  if (@$_POST['changePassword']) {
  
//update fields
    $colsToValues = array();
      
$colsToValues['first_time_login'] =     '1';
      
$colsToValues['updatedByUserNum'] = $CURRENT_USER['num'];
      
$colsToValues['updatedDate=']     = 'NOW()';
      
mysql_update(accountsTable(), $CURRENT_USER['num'], null$colsToValues);
  
// change passwords
    $encryptPasswords = @$SETTINGS['advanced']['encryptPasswords'];

    
// error checking
    $_REQUEST['oldPassword'] = preg_replace("/^\s+|\s+$/s"'', @$_REQUEST['oldPassword']); // v1.10 remove leading and
trailing whitespace
    $oldPasswordHash  $encryptPasswords getPasswordDigest(@$_REQUEST['oldPassword']) : @$_REQUEST['oldPassword'];
    if     (!@
$_REQUEST['oldPassword'])                             { $errorsAndAlerts .= "Please enter your current
password\n"; }
    elseif (
$oldPasswordHash != $CURRENT_USER['password'])          { $errorsAndAlerts .= "Current password isn't
correct!\n"; }
    
$newPasswordErrors getNewPasswordErrors(@$_REQUEST['newPassword1'], @$_REQUEST['newPassword2'],
$CURRENT_USER['username']); // v2.52
    $errorsAndAlerts  .= nl2br(htmlencode($newPasswordErrors));

    
// change password
    if (!$errorsAndAlerts) {
      
$passwordHash $encryptPasswords getPasswordDigest($_REQUEST['newPassword2']) : $_REQUEST['newPassword2'];
      
mysql_updateaccountsTable(), $CURRENT_USER['num'], null, array('password' => $passwordHash)); // update password
      websiteLogin_setLoginTo$CURRENT_USER['username'], $_REQUEST['newPassword2'] );                // update current
login session
      unset($_REQUEST['oldPassword'], $_REQUEST['newPassword1'], $_REQUEST['newPassword2']);          // clear form
password fields
      $errorsAndAlerts "Thanks, we've updated your password!\n";
      
redirectBrowserToURL("members-only.php");
    }
  } 
### END: Change Password


  // prepopulate form with current user values
  //foreach ($CURRENT_USER as $name => $value) {
  //  if (array_key_exists($name, $_REQUEST)) { continue; }
  //  $_REQUEST[$name] = $value;
 // }

?>


And in the body (NOTE: You can style your page to match your site design):


<?php if (@$errorsAndAlerts): ?>
      <div align="left" class="your_class" style="color:#F00">
        <?php echo $errorsAndAlerts?>
      </div>
      <?php endif ?>
      <div style="width:90%" align="left">
        <div class="your_class" align="center">PLEASE CHANGE YOUR PASSWORD</div>
        
        
        <span class="your_class"><b>Welcome <?php echo $CURRENT_USER['first_name'?>, 
        
        Since this is the first time you've logged in, we ask that you
        change your password to protect your privacy.
        
        NOTE: Once you've changed your password, you'll no longer be logged in,
        and you'll have to </b> </span><a class="your_class" href="member_login.php">LOGIN AGAIN</a> <span
class="your_class"><b>with your new credentials.</b>
        
        
        <!-- CHANGE PASSWORD FORM -->
        <div > <b>Change your Login Password - (Don't forget to write down the new one!)</b>
          <form method="post" action="?">
            <input type="hidden" name="changePassword" value="1" />
            <p>
            <table border="0" cellspacing="0" cellpadding="1">
              <tr>
                <td>Enter Your Current Password</td>
                <td><input type="password" name="oldPassword" value="<?php echo htmlencode(@$_REQUEST['oldPassword']);
?>" size="40" /></td>
              </tr>
              <tr>
                <td> Enter Your New Password</td>
                <td><input type="password" name="newPassword1" value="<?php echo htmlencode(@$_REQUEST['newPassword1']);
?>" size="40" /></td>
              </tr>
              <tr>
                <td> Enter Your New Password (again)</td>
                <td><input type="password" name="newPassword2" value="<?php echo htmlencode(@$_REQUEST['newPassword2']);
?>" size="40" /></td>
              </tr>
              <tr>
                <td >&nbsp;</td>
                <td align="center">
                  <input class="button" type="submit" name="submit" value="Change Password &gt;&gt;" /></td>
              </tr>
            </table>
          </form>
        </div>
        
        <!-- /CHANGE PASSWORD -->
        <?php if (@$errorsAndAlerts): ?>
        <div class="your_class" style="color:#F00">
          <?php echo $errorsAndAlerts?>
          
        </div>
        <?php endif ?>


7) If you're implementing the mandatory password change on an existing site, you might want to change all the First Time
Login check boxes to checked so that existing users won't get a reset your password message the next time they log in.
To change all the check boxes with one click, you can use the Field Resetter Plugin that you can download from
http://www.thecmsbcookbook.com/downloads/fieldResetter.zip

8) TEST TO MAKE SURE EVERYTHING WORKS AS PLANNED!!!

__________________________________________________________________________________________________

NOTE: THE STEPS OUTLINED IN THE (LEGACY) RECIPE BELOW WILL ALLOW THIS PLUGIN TO WORK CORRECTLY WITH ENCRYPTED PASSWORDS
(CMSB VERSION 2.08+ AND THE WEBSITE MEMBERSHIP PLUGIN V1.05+ (BUT NOT WITH V1.09 or later (SEE ABOVE). 

***V 1.10 OF THE MEMBERSHIP PLUGIN IS HIGHLY RECOMMENDED***

NOTE: Thanks to Steve from MustBeOnLine.com for discovering a coding error in the zipped plugin (now fixed). He
discovered that there was a double $errors = in the line of code:


 $errors = $errors=mail($_REQUEST['email'],"Your membership has been approve!",$message,$headers);,


It should be:


 $errors = mail($_REQUEST['email'],"Your membership has been approve!",$message,$headers);,


Download and install the emailOnApproved plugin. Do a search on the forum for the latest version, or you can download my
modified (and corrected) version from here:

       http://www.thecmsbcookbook.com/downloads/emailOnApproved.zip 

This modified plugin will allow you to set up a manual approval process and send an e-mail to your new members when they
are approved.

Before encrypted passwords it was easier to email login credentials to a member after their account had been manually
approved. (Their application was approved, their payment had been verified, etc.)

Since the implementation of encrypted passwords, the process became a bit more involved.


Here’s are the steps necessary: 

First, create a visible password text field in the “Accounts” section (I call it visible_password in this recipe)
and an "approved" check box

Then have your signup form automatically fill the visible_password field with the automatically generated password
before it’s encrypted.

You’ll need to comment out (or remove) the send email instructions in the signup form

You’ll also want to change the “show thanks” message that is presented on a successful signup.

Then in the emailOnApproved plugin, you’ll want to change the ‘password’ field to ‘visible_password’

Here are the specifics:

First add a text field called "visible_password" to your “accounts” section. (You can call it anything you want to,
but be consistent)

Next add a check box field called "approved"

Then open your signup form and somewhere in the  mysql_query("INSERT INTO `{$TABLE_PREFIX}accounts` SET code insert the
following code as a separate line: 


visible_password = '$password',


it doesn’t matter where in the series you insert the line, as long as it’s a separate line.

Next search for // send message and comment out the entire section with a   after the section:


  


Just under that section of code you should find the section called  // show thanks

You’ll want to modify that message to something like:


 // show thanks
      $errorsAndAlerts  = "Thanks, We've created an account for you. As soon as you're approved we'll email you your
password.\n";
      $errorsAndAlerts .= "If you don't receive an email from us within a few minutes check your spam filter for
messages from {$SETTINGS['adminEmail']}.\n";
   //   $errorsAndAlerts .= "<a href='{$GLOBALS['WEBSITE_LOGIN_LOGIN_FORM_URL']}'>Click here to login</a>.";


Notice that I’ve removed the reference to {$emailHeaders['from']} in the “spam filter” text and replaced it with
{$SETTINGS['adminEmail']} (you’ll get an error if you don’t because you removed the original variable in the
previous step) , and commented out the login URL line with a double forward slash (you can remove this line if you’d
prefer).

Now save your signup form and open the emailOn Approved plugin.

The instructions on how to implement this plugin are below.

Search for ‘password’ and replace this with ‘visible_password’ (unless you called your field something else)

That’s it. 

LEGACY INFORMATION


BACKGROUND
When I first activated the original emailOnApproved plugin, I was getting Undefined index errors reported on saving a
record until I added this line suggested by Dave Edis to the error checking section of the code:


 if ($Table name != 'accounts') { return; } 


So now the error checking section looks like


 // error checking
    if ($tableName != 'accounts') { return; } 
  if (!array_key_exists($fieldname, $CURRENT_USER)) {
    die(__FUNCTION__ .": You must create an accounts fields called '$fieldname'!");
  }
 


And there are no more Undefined index errors.

I decided that it would make more sense to include the new member’s username, temporary password and a login URL in
this email, so I modified the original:


 // send email
  $wasChecked   = intval(!$oldRecord[$fieldname] && $_REQUEST[$fieldname]);
  $wasUnchecked = intval($oldRecord[$fieldname]  && !$_REQUEST[$fieldname]);

  if ($wasChecked) {
    $errors = sendMessage(array(
      'from'    => $SETTINGS['adminEmail'],
      'to'      => $_REQUEST['email'],
      'subject' => "You have been approved!",
      'text'    => "Congradulations!

You have been approved for our website.
Your password is: {$_REQUEST['password']}

See you soon!
",
    ));
    if ($errors) { die($errors); }
  }

}

?>


To this:

NOTE: Make sure that there are no spaces after the $message=<<< __TEXT__ and that the __TEXT__; is flush against the
left margin or you’ll generate errors.


  // send email
  $wasChecked   = intval(!$oldRecord[$fieldname] && $_REQUEST[$fieldname]);
  $wasUnchecked = intval($oldRecord[$fieldname]  && !$_REQUEST[$fieldname]);

$message=<<< __TEXT__
    Congratulations!  
  
Your subscription has been  processed successfully and you now have access to the "Members Only" area of our web site.  
Your user name is: {$_REQUEST['username']}  
and your temporary password is: {$_REQUEST['password']}. 
Once you have successfully logged in, you can change your password and update your profile information.  
  
  
<a href="http://www.your_web_site_URL.com{$GLOBALS['WEBSITE_LOGIN_LOGIN_FORM_URL']}">Click here to login</a>  
  
Best,   
    
The Subscription Committee  
__TEXT__;

$headers  = 'MIME-Version: 1.0' . "\r\n";  
$headers .= 'Content-type: text/html; charset=iso-8859-1' . "\r\n";  
$headers .="FROM:". $SETTINGS['adminEmail'];  
  
   if ($wasChecked) { 
    $errors = $errors=mail($_REQUEST['email'],"Your membership has been processed!",$message,$headers);  
    if ($errors!=1) { die("Mail Error: $php_errormsg"); } 
  } 

 ?>


ADDING CUSTOMIZABLE TEXT TO THE EMAILONAPPROVED PLUGIN WELCOME MESSAGE - Sep 9th, 2015

You can add custom text from a field ('member_welcome'_in a single record editor ('common_information') or add multiple
variables from multiple fields. 

The trick is to keep the load viewer library (don't forget to change the server path) and load records calls, and the
variables you create within the function itself.

Then place each variable in a set of braces {} inside the $message=<<< __TEXT__ block


<?php

// UPDATE THESE VALUES
// DON'T UPDATE ANYTHING BELOW THIS LINE
 
addAction('record_postsave',  'emailOnApproved_sendPassword'null4);
//
function emailOnApproved_sendPassword($tableName$isNewRecord$oldRecord$recordNum) {
  global 
$CURRENT_USER$SETTINGS;
  
$fieldname 'approved';
  
// error checking
    if ($tableName != 'accounts') { return; }
  if (!
array_key_exists($fieldname$CURRENT_USER)) {
    die(
__FUNCTION__ .": You must create an accounts fields called '$fieldname'!");
  }
  
// send email
  $wasChecked   intval(!$oldRecord[$fieldname] && $_REQUEST[$fieldname]);
  
$wasUnchecked intval($oldRecord[$fieldname]  && !$_REQUEST[$fieldname]);

// load viewer library
  $libraryPath 'cmsAdmin/lib/viewer_functions.php';
  
$dirsToCheck = array('/path_to_server/','','../','../../','../../../');
  foreach (
$dirsToCheck as $dir) { if (@include_once("$dir$libraryPath")) { break; }}
  if (!
function_exists('getRecords')) { die("Couldn't load viewer library, check filepath in sourcecode."); };

// load record from 'common_information'
  list($common_informationRecords$common_informationMetaData) = getRecords(array(
    
'tableName'   => 'common_information',
    
'where'       => ''// load first record
    'loadUploads' => true,
    
'allowSearch' => false,
    
'limit'       => '1',
  ));
  
$common_informationRecord = @$common_informationRecords[0]; // get first record
  $member_welcome  $common_informationRecord['member_welcome'];

$message=<<< __TEXT__
    Welcome {$_REQUEST['first_name']} {$_REQUEST['last_name']},
 
{$member_welcome}



Your membership has been  processed successfully and you now have access to the Members Only area of our web site.  

Your user name is: {$_REQUEST['username']} 
and your temporary login password is: XXXXX 

Once you have successfully logged in, you must change your password and can then update your own profile information. 
 
<a href="{$GLOBALS['WEBSITE_LOGIN_LOGIN_FORM_URL']}">Click here to login</a> 
 
Best,   
    
The Membership Committee  
__TEXT__;


$headers  'MIME-Version: 1.0' "\r\n";  
$headers .= 'Content-type: text/html; charset=iso-8859-1' "\r\n";  
$headers .="FROM:"$SETTINGS['adminEmail'];  
 
   if (
$wasChecked) {
    
$errors mail($_REQUEST['email'],"Your membership has been successfully processed!",$message,$headers);  
    if (
$errors!=1) { die("Mail Error: $php_errormsg"); }
  }
 
}
 
?>


CHANGING THE TEXT OF THE SIGNUP EMAIL - Jun 1st, 2011


Another thing you’ll probably want to do is to change changing the text of the sign up e-mail that’s sent to
prospective members.

At the bottom of the websiteMembership.php plugin file, you’ll find the following block of code.




  // send email
  $to        = $_REQUEST['email'];
  $subject   = "{$_SERVER['HTTP_HOST']} Account Details";
  $message   = "Hi {$_REQUEST['email']},\n"
             . "\n"
             . "Thanks for signing up to {$_SERVER['HTTP_HOST']}.\n"
             . "\n"
             . "Your username is: {$username}\n"
             . "Your password is: {$_REQUEST['password']}\n"
             . "\n"
             . "Please click here to login:\n"
             . "http://{$_SERVER['HTTP_HOST']}{$GLOBALS['WEBSITE_LOGIN_LOGIN_FORM_URL']}\n"
             . "\n"
             . "Thanks!\n";

  $mailResult = @mail($to, $subject, $message, "From: {$SETTINGS['adminEmail']}");
  if (!$mailResult) { die("Mail Error: $php_errormsg"); }

}


?>



 While this is OK for a one step sign up approach, you’ll probably not want to include the user name, password, or
login address in this e-mail.

In the version below, I’ve changed that to a more logical set of instructions. Including a payment link if required.

Note that /n is the code for a new line, there is only one ; and it marks the end of the message $section, the . is the
operator that says “add” this line to the string, 



// send email
  $to        = $_REQUEST['email'];
  $subject   = "{$_SERVER['HTTP_HOST']} Membership Application";
  $message   = "Hi {$_REQUEST['email']},\n"
. "\n"
. "Thanks for applying to be a member of {$_SERVER['HTTP_HOST']}.\n"
. "\n"
             . "As soon as your membership has been approved you'll receive a confirmation email with a link to our
login page.\n"
             . "Once you log in you'll be able to enter your member profile information so that others can contact you.\n" 
            . "\n"
             . "Remember, you can not be approved if you haven't paid your first year's dues.\n"
             . "\n"
             . "You can pay your dues on-line using PayPal here:\n"
             . "http://www.your_website.org/paypal.php\n"
             . "\n"
             . "Thanks and Welcome!\n"
              . "\n"
              . "The Membership Committee\n";

  $mailResult = @mail($to, $subject, $message, "From: {$SETTINGS['adminEmail']}");
  if (!$mailResult) { die("Mail Error: $php_errormsg"); }

}


FORCING UPPER CASE, LOWER CASE, AND INITIAL CAPS TO BE SENT TO THE DATABASE - Jun 1st, 2011

Sometimes it’s important to send only certain case formats to your database. State abbreviations, first and last
names, and middle initials are a few examples.

It’s pretty easy, but you’ll have to implement any changes on both the signup and the profile revision form pages.

In the “// add user” section of the signup form, fairly near the top of the page, you’ll find:



mysql_query("INSERT INTO `{$TABLE_PREFIX}accounts` SET



And in the “// update user” section of the profile page, you’ll find:



 $query = "UPDATE `{$TABLE_PREFIX}accounts` SET



Just before each of those lines, insert any of the following, creating a new line of code for each field that you want
to control:

To force each word to begin with a capital letter:



@$_REQUEST['your_field']=ucwords(@$_REQUEST['your_field']);



To force only the first character in the field to begin with a capital letter:



@$_REQUEST['your_field']=ucfirst(@$_REQUEST['your_field']);



To force the entire field to be converted to capital letters:



@$_REQUEST['your_field']=strtoupper(@$_REQUEST['your_field']);




ADDING/CHANGING FIELDS AFFECTED BY THE SAMPLE SIGN UP FORM - Jun 1st, 2011

You can add, change, and remove any fields that you want to be automatically affected by the membership plugin sign up
form. Here’s how.

First, in the user accounts table create any fields that don’t exist.

Then open sample_signup.php 

Near the top, you’ll find the error checking code section that starts with:



  // error checking
    $errorsAndAlerts = "";



If any of your new fields are going to be “required’ fields, here’s where you’d list them. If you’re removing
any, make sure you remove them here too. Copy existing lines of code and change the field name and message to match your
requirements.

Next scroll down to the “add user” section and copy an existing line of code and change the field name to match your
requirements.  

Next, scroll down to the form in the body of your page and add the required fields by again copying and pasting rows
into your form and changing the information as required. 

If you want some of your fields to be text areas instead of text fields, you can change this field format:



<td class="body-text-bold">City</td>
<td>
<input type="text" name="city" value="<?php echo htmlspecialchars(@$_REQUEST['city']); ?>" size="50" />
</td>
</tr>


To this for a 50 column, 6 row text area:



<tr>
<td class="body-text-bold">Describe Your Qualifications</td>
<td>
<TEXTAREA NAME="describe_discipline" COLS=50 ROWS=6 value="<?php echo
htmlspecialchars(@$_REQUEST['desctribe_discipline']); ?>"></TEXTAREA> 
</td>
</tr>



You can further modify the look of a form using MAXLENGTH="n" to limit your fields to n characters (good for zip codes
and States for example.)


DISPLAYING THE CURRENT VALUE OF A CHECKBOX OR RADIO BUTTON - Jun 1st, 2011

If you want to display the current values in a checkbox or radio button field when a member logs in to revise their
profile, you can take this advice offered by Jason Sauchuk from Interactive Tools:

A checkbox in CMS Builder can only store a value of either 1 or 0.

What you need to do is to display your form element as either selected or unselected based on the value currently stored
in the database. There are two solutions that you could use.

1) You can use a single check box. If a member had previously checked yes, the box on the form will be checked,
otherwise, it will be blank:



<tr>  
  <td >Your Question Goes Here:</td>  
  <td ">Check for "Yes" <br /> <input type="checkbox" name="your_field" value="1"  <?php
if(@$_REQUEST['your_field']){echo "checked='checked'";}?> /> </td>  
</tr>



2) The other option would be to use radio buttons for yes and no. This way they can only select 1:



<tr>  
  <td >Your Question Goes Here:</td>  
  <td ><input type="radio" name="your_field" value="1"  <?php if(@$_REQUEST['your_field']){echo "checked='checked'";}?>
/> Yes please <br /> 
<input type="radio" name="your_field" value="0" <?php if(!@$_REQUEST['your_field']){echo "checked='checked'";}?>> No
Thanks</td>  
</tr>



RETAINING THE VALUE IN A TEXTAREA FORM FIELD AFTER FORM IS RE-SENT - Jul 2nd, 2012

In a member signup form I had a textarea form field that would lose the information that was entered each time the form
was re-sent (as in error checking, etc.) and it took Jason Sauchuk from Interactive Tools to help me see the tree in the
forest.

He said (very patiently, I might add), 

"To put information into a textarea, you output it between the <textarea> tags."

For example: 

<textarea name="your_name" COLS=50 ROWS=6 ><?php echo htmlspecialchars(@$_REQUEST['your_field']); ?></textarea>


RETAINING THE VALUE OF A PULL DOWN LIST AFTER A FORM IS RE- SENT - Dec 27th, 2022

Here's what I've used for a single value list field:

<select name="venue_parking" >
<option value"" <?php if($_REQUEST['venue_parking'] == ""){echo "selected=\"selected\"";} ?> >Select </option>
<option value"Street" <?php if($_REQUEST['venue_parking'] == "Street"){echo "selected=\"selected\"";} ?>
>Street</option>
<option value"On Site Free" <?php if($_REQUEST['venue_parking'] == "On Site Free"){echo "selected=\"selected\"";} ?> >On
Site Free</option>
<option value"On Site Paid" <?php if($_REQUEST['venue_parking'] == "On Site Paid"){echo "selected=\"selected\"";} ?> >On
Site Paid</option>
</select>

And here's what was suggested for a multi value list field (never tested):

<select name="foo[]" multiple="multiple" size="3">
     <option value="" <?php if(isset($_POST['foo'])) { foreach($_REQUEST['foo'] as $tmp) {  if($tmp == "") { echo
"selected=\"selected\""; break; }}}?>>Please Select One Or More Values</option>
     <option value="bar" <?php if(isset($_POST['foo'])) { foreach($_REQUEST['foo'] as $tmp) {  if($tmp == "bar") { echo
"selected=\"selected\""; break; }}}?>>bar</option>
     <option value="hello" <?php if(isset($_POST['foo'])) { foreach($_REQUEST['foo'] as $tmp) {  if($tmp == "hello") {
echo 
"selected=\"selected\""; break; }}}?>>hello</option>
     <option value="world" <?php if(isset($_POST['foo'])) { foreach$_REQUEST['foo'] as $tmp) {  if($tmp == "world") {
echo 
"selected=\"selected\""; break; }}}?>>world</option>
</select>


ADDING/CHANGING FIELDS AFFECTED BY THE SAMPLE PROFILE UPDATE FORM - Jun 1st, 2011

The process here is similar to modifications to the sign up form except here you’d be updating the “error
checking”, “update user” and “form” sections of your page. Again it’s easier to copy lines of code and
change information as required.


SETTING ACCESS RIGHTS FOR NEW USERS BY SECTION - Aug 30th, 2014

If you’ve had trouble setting a new user’s access rights “by Section”, there are a few counter-intuitive steps
that need to be added to the process.

If you look at the sample signup form in the Website Membership Plugin version1.10, you’ll find this block of code.


 // NOTE: You can repeat this block to grant access to multiple sections
        mysql_insert('_accesslist', array(
          'userNum'      => $userNum,
          'tableName'    => '_sample',   // insert tablename you want to grant access to, or 'all' for all sections
          'accessLevel'  => '0',         // access level allowed: 0=none, 6=author, 9=editor
          'maxRecords'   => '',          // max listings allowed (leave blank for unlimited)
          'randomSaveId' => '123456789', // ignore - for internal use
        ));
      }


SETTING ‘BY SECTION’ ACCESS RIGHTS 
You'll first have to set the access permission for that user to By Section, to allow you to set the user's permissions
for individual sections.

Copy the code block above, and in the first copy of the code on the page, set the tableName to 'all', and the
accessLevel to '1' (which means ‘By Section’), like this:


mysql_insert('_accesslist', array(
 'userNum'      => $userNum,
    'tableName'    => 'all',   // insert tablename you want to grant access to, or 'all' for all sections
   'accessLevel'  => '1',         // access level allowed: 0=none, 6=author, 9=editor
 //  'maxRecords'   => '',          // max listings allowed (leave blank for unlimited)
    'randomSaveId' => '123456789', // ignore - for internal use
  ));


Notice that the maxRecords has been commented out. According to Daryl Maximo, a programmer at Interactive Tools, "This
is because unless there’s a number in the maxRecords value, the value will actually be set to '0'" If that happens,
you won't get the result that you're expecting, or an error will be thrown and the access levels won’t be set at all.

SETTING ACCESS RIGHTS FOR INDIVIDUAL SECTIONS
Now that access by section has been set, the next code blocks can set the individual section access rights. My example
below sets the access rights to the section tableName ‘listing’ for this new user to only those records where they
are the Author (6).  Use as many copies of this code block required to set each one of your section access scenarios.

Again, be careful about that maxRecords value. If you want to allow unlimited records, just comment out the line by
putting a double forward slash // in front of it.


         mysql_insert('_accesslist', array(
          'userNum'      => $userNum,
          'tableName'    => 'listing',   // insert tablename you want to grant access to, or 'all' for all sections
          'accessLevel'  => '6',         // access level allowed: 0=none, 6=author, 9=editor
        'maxRecords'   => '1',          // max listings allowed (leave blank for unlimited)
          'randomSaveId' => '123456789', // ignore - for internal use
        ));


So the complete code for the page would be:


<?php header('Content-type: text/html; charset=utf-8'); ?>
<?php
  
  
  // load viewer library
  $libraryPath 'cmsAdmin/lib/viewer_functions.php';
    
$dirsToCheck = array('/path_to_your_serverl/','','../','../../','../../../');
  foreach (
$dirsToCheck as $dir) { if (@include_once("$dir$libraryPath")) { break; }}
  if (!
function_exists('getRecords')) { die("Couldn't load viewer library, check filepath in sourcecode."); }

  
// load records from 'accounts'
  list($accountsRecords$accountsMetaData) = getRecords(array(
    
'tableName'   => 'accounts',
    
'loadUploads' => true,

  ));

?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
 <head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>access-update2</title>
</head>

<body>
<?php foreach ($accountsRecords as $record): ?>
<?php

$accountsRecords mysql_select(accountsTable(), 'isAdmin != 1');
foreach(
$accountsRecords as $record){
  
  
$userNum $record['num'];
  
  
mysql_insert('_accesslist', array(
    
'userNum'      => $userNum,
    
'tableName'    => 'all',   // insert tablename you want to grant access to, or 'all' for all sections
    'accessLevel'  => '1',         // access level allowed: 0=none, 6=author, 9=editor
    //'maxRecords'   => '',          // max listings allowed (leave blank for unlimited)
    'randomSaveId' => '123456789'// ignore - for internal use
  ), true);
  
  
// NOTE: You can repeat this block to grant access to multiple sections
  mysql_insert('_accesslist', array(
    
'userNum'      => $userNum,
    
'tableName'    => 'lm_listing',   // insert tablename you want to grant access to, or 'all' for all sections
    'accessLevel'  => '6',         // access level allowed: 0=none, 6=author, 9=editor
    //'maxRecords'   => '',          // max listings allowed (leave blank for unlimited)
    'randomSaveId' => '123456789'// ignore - for internal use
  ), true);
  
}
?>
<?php endforeach ?>   
  </body></html>


LIMITING DIRECTORY ACCESS AND PROTECTING DIRECTORIES - Jun 1st, 2011

Thanks to Dave Edis from Interactive Tools for this part.

Since all the files are PHP they are executed server side and protected from prying eyes by default.

1) In your user accounts editor, create a multi value list field called allowedFolders
The values I used are:
board_access
committee_access
general_access

Go into the User Accounts list and check some values in your account to authorize access to some of those folders.
 
2) In the root directory of your site create 3 folders:
board_access
committee_access
general_access
 
3) In each folder upload a test file called access.php
 
the code in each of the access.php files is:


 
<?php
  require_once "../cmsAdmin/lib/viewer_functions.php";
  if (!@
$GLOBALS['WEBSITE_MEMBERSHIP_PLUGIN']) { die("You must activate the Website Membership plugin before you can
access this page."); }
  if (!
$CURRENT_USER) { websiteLogin_redirectToLogin(); }

  
//
  $folderName  basename(dirname(__FILE__));
  
$allowAccess preg_match("/\t$folderName\t/"$CURRENT_USER['allowedFolders']);
  if (!
$allowAccess) { die("You don't have access to this folder!"); }
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Access Test</title>
</head>

<body>
<?php echo $CURRENT_USER['allowedFolders'?>
</body>
</html>



The code in the body just prints out the directories that the particular member is authorized to view.

After you’ve got the basic code working, you can paste the code required into an existing page, upload it to one of
those directories,  and make sure it all works as planned.


LIMITING FILE ACCESS AND PROTECTING INDIVIDUAL FILES - Jun 1st, 2011

If you’ve only got a few files to protect, you can use something like this:

Set up a single value list field in the user accounts table with the membership levels required, for example:

General
Committee
Board

Assign access to one of these levels to your user account.

Then use code similar to this to protect whole pages or portions of a page. If the user is not logged in, line 3 in the
code will force them to log in before they can access this page:



<?php   require_once "cmsAdmin/lib/viewer_functions.php"
if (!@
$GLOBALS['WEBSITE_MEMBERSHIP_PLUGIN']) { die("You must activate the Website Membership plugin before you can
access this page."); }
  if (!
$CURRENT_USER) { websiteLogin_redirectToLogin(); }
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Information Access Test</title>
</head>
 
<body>
<?php if ($CURRENT_USER['membership_level'] == "Committee"): ?>You are logged in and are authorized to see this
Committee level information.
<?php elseif ($CURRENT_USER['membership_level'] == "Board"): ?>You Are Logged in and are authorized to see this Board of
Directors level information.
<?php elseif ($CURRENT_USER['membership_level'] == "General"): ?>You Are Logged in and are authorized to see this
General membership level information.
<?PHP else: ?> You must log in before you can see the information that is authorized for your level of membership.
<?PHP endif ?>
 
</body>
</html>



If you’re using a multi value list to define your authorization levels, you can’t use the == test, since your field
can actually contain more than just an exact match. You can however use the strpos function to determine if a particular
membership level is contained in the list. 

Theoretically the strpos function returns the numeric position of the first occurrence of the pattern that you’re
searching for, however, before it can do that it has to decide if the pattern exists in the string.

Here’s the code using strpos



<?php if (strpos($CURRENT_USER['membership_level'], 'committee_access')): ?>
You Are Logged in and are authorized to see committee level level information.
<?PHP endif ?>



SETTING CRITERIA FOR WHICH RECORDS WILL SHOW ON AN "ACCESS PERMISSIONS" REPORT BASED ON THE AMOUNT OF CHECKED LIST FIELD VALUES. - Apr 13th, 2016


There were a number of reports and other pages associated with my membership site that needed access restricted to
particular members. I.E. The membership committee did not have any reason to have access to the Exhibition Submission
reports, etc.

To accomplish this I created a multi value check box list field of access permissions (allowedFolders) in the user
database, and checked the appropriate permissions on a member by member basis. 

All members were granted member_access and some had other permissions as well.

To aid in the assignment and reassignment process, I created a report that shows the all the checked value label in the
allowedFolders list field for all user accounts.

The problem was that the list page showed all users, and I wasn’t sure how to exclude those records with only the one
value checked.

Damon Edis form Interactive Tools came to the rescue by adding the substr_count function in the foreach loop to count
the tab spaces in the allowedFolders string. He explained: “One value equals two tab spaces so I added the ' more than
2' condition.”


 <table class="text_font" width="100%" border="1"  cellspacing="0">
  <tr>
    <td style="padding-left:10px;">Last Name</td>
    <td style="padding-left:10px;">First Name</td>
    <td style="padding-left:10px;" >Assignments</td>
    <td style="padding-left:10px;" >Administrator</td>
   
  </tr>
 

<?php foreach ($accountsRecords as $record ): ?>
        <?php if(substr_count($record['allowedFolders'], "\t") > 2): ?>
     
      <tr>
    <td style="padding-left:10px;"><?php echo $record['last_name'?></td>
    <td style="padding-left:10px;"><?php echo $record['first_name'?></td>
    <td style="padding-left:10px;"><?php echo join(', '$record['allowedFolders:labels']);?></td>
    <td style="padding-left:10px;"><?php if ($record['isAdmin'] == 1): ?>Yes<?php else :?>No<?php endif?></td>
  </tr> 
       <?php endif?>
          <?php endforeach; ?>
       
        
        </table> 





LIMITING ACCESS TO RECORDS BY AUTHOR - Jun 13th, 2011

To limit access to only those records in a multi-record section that were created by the current user, since each record
is flagged with the user number of the user that created it (['createdByUserNum']), you can use this approach:

<?php foreach ($your_sectionRecords as $record): ?>
 <?php if ($record['createdByUserNum']== @$CURRENT_USER['num']): ?> 

<!-- the fields that you want to show go here -->

<?php endif ?>
  <?php endforeach ?>



RESTRICTING LIST VALUES TO AUTHORS ONLY - Apr 13th, 2016

On a site where I had employed the membership plugin, I was trying to set up a list that gets it’s options from the
“title” field of another table (your_source_table), using the “get options from mySQL query” option.

The trick was that I wanted to restrict the options that a member user can see to only those where they are the
option’s author, but as an administrator I would need to see them all.

Thanks to user Zip222, I came up with this solution. 

First I set up a multi-record editor we'll call "your_source_table" with only one field called "title".

Next I created a list field in my 'receiving" editor and for the list values I chose the third option in the pull down
menu, "Get Options From MySQL Query (advanced)

I put the following code in the box to replace the dummy code that is there.

NOTE: The first entry in the SELECT determines the field from which the"Value" is returned (so that could be 'num',
which would return the record number, etc) and the second determines the Label that appears in the pull down list.



SELECT title, title
 
  FROM <?php echo $TABLE_PREFIX ?>your_source_table_name
<?php if (!$CURRENT_USER['isAdmin']): ?>
<?php   $where="WHERE createdByUserNum='".$CURRENT_USER['num']."'"?> 
<?php echo $where ?> 
<?php endif   ?>



PROTECTED CLIENT ACCESS TO SPECIFIC RECORDS IN A MULTI-RECORD SECTION - Mar 11th, 2015

Using CMSB V2.64 and Website Membership V1.10, I needed to set up a protected client area on a site where client access
is granted to certain records in a multi-record section based on a master project assignment list. I needed to be able
to grant any client access to a list page with all the projects they were authorized to see but I also needed to protect
project detail pages from other clients who were logged in, but did not have access to view particular records.

Here’s what was needed:

1) A multi-record “Project Name” section with the name of the project in a text field called ‘title’ (this field
was set as “don’t allow removing of records” on the “advanced” tab) and a warning about creating a new record
for each new project so that project information did not fall into the wrong hands)

2) A multi-record “Project Information” section with a pull down list field called ‘project_name’ that get’s
its option values from the num field in the “Project Name” table and it’s option labels from the title field in
the same table. (This field is used to assign progress report names)


3) A multi-value pull down list field in the “accounts” section that gets it’s data from the ‘Project Name”
table in the same way. (This field is used to assign users permission to view project progress reports)

On the list page, called client.php, at the top of the document, after the load records calls, I included the code: 


<?php if (!$CURRENT_USER) { websiteLogin_redirectToLogin(); } ?>


And in the body:


<?php $rec_count 0?>
<?php foreach ($project_informationRecords as $record): ?>
<?php if (strpos($CURRENT_USER['project_access'], $record['project_name'])): ?>
<div  style="text-align:left;" > 
<?php $usernum1 $CURRENT_USER['num'?>
<a class="special" href="clientdetail.php?usernum=<?php echo $usernum1 ?>&num=<?php echo $record['num']?>"><span
style="text-decoration:underline;" class="navigation_font"><i><?php echo strtoupper($record['project_name:label'])
?></i></span></a>
</div>
<?php endif ?>
<?php endforeach ?>

<?php if (!$project_informationRecords): ?>
<span class="text_font">Sorry, You Don't Have Permission To Access Any Project Progress ReportsIf you feel that you
should have access, please </span><a href="http://www.your_site/contact.php"><span class="text_font">"CONTACT
US</span></a>
    <?php endif ?>


And on the detail page at the top of the document, below the load records calls:


<?php if (!$CURRENT_USER) { websiteLogin_redirectToLogin(); } ?>
<?php $usernum $_REQUEST['usernum']; ?>
<?php $record['num'] = $_REQUEST['num']; ?>
<?php @$errorsAndAlerts ''?>
<?php if ($CURRENT_USER['num'] == $usernum ):?>
<?php $show_data 1 ?>
<?php else: ?>
<?php $show_data 0 ?>
<?php $errorsAndAlerts "Sorry, You're Currently Not Authorized To View Project Progress Reports." ?>
<?php endif ?>


And in the body:


<table align="center" width="50%" border="0" cellpadding="3">
<tr>
<td align="center" class="heading_font">PROJECT PROGRESS REPORTS
<?php if($show_data == 1):?>
<?php echo strtoupper($project_informationRecord['project_name:label']) ?>
FOR <?php echo strtoupper($CURRENT_USER['first_name']) ?> <?php echo strtoupper($CURRENT_USER['last_name']) ?>

<a href='http://www.your_site.com/client.php?action=logoff'><span class="text_font">WHEN YOU'RE FINISHED, PLEASE
CLICK/TAP HERE TO LOG OUT</span></a>
 
</td>
</tr>
<tr>
<td >
YOUR DETAIL PAGE DATA GOES HERE...
</td>
</tr>
</table>
<?php else :?>
<span class="navigation_font">
<?php echo $errorsAndAlerts ?>
For Authorization, Please <a href="contact.php"><span class ="text_font"><font color="#D01E1E">Contact
Us</font></span></a>.
<?php endif ?>



CHECKING TO SEE IF AN EMAIL ADDRESS (ACCOUNT) EXISTS IN THE ACCOUNTS DATABASE - Aug 6th, 2012

In my organization’s scenario, if a member has not paid their dues for a period of 2 years their record is deleted
from the database.

To deal with those members who still have a record in the database, I created (with a lot of help from Jason Sauchuk
from Interactive Tools) a function that checked a prospective member’s e-mail and reported whether their account still
exists.

Here’s what I did.

On the member signup page I created this form with the following explanatory text. (I'll show the form in the context of
the page code below)

To find out if you're still in our member database, enter the e-mail address you used to sign up with in the box below
and click on &quot;submit&quot;. If your records are found, all you’ll have to do to reinstate your account is to pay
your current dues. Once your payment is processed, you’ll receive an e-mail with your login username and password.
Then you’ll again be able to enjoy the benefits of membership and update your member profile information.


             
  <?php if (!@$CURRENT_USER): ?>
        <form action="?" method="post">
    <input type="hidden" name="action" value="pastInformation" />
   <span class="body-text-bold">Enter your e-mail address:</span>
    <input type="text" name="pastInformation" value="<?php echo htmlspecialchars(@$_REQUEST['pastInformation']) ?>"
size="20" />
    <input type="submit" name="submit" value="Submit" />
    </form>
  <?php endif ?>



To display the appropriate error messages, like "please enter an email address" if the submitted form was empty, or
"your information was found" or "your information was not found" on the member page, I added the following to the top of
the page after the record calls:



<?php
// clear errors and set form to show on page load
 $errorsAndAlerts ""
  
$showForm true
  
 if (@
$_REQUEST['submit']) { 
    
$showForm false;  
    if (!@
$_REQUEST['pastInformation']) { 
       
$emptyField "<span class='heading-text-yellow'>Please enter an email address</span>\n"
    
$showForm true
    }
     }
  
// error checking
  $errorsAndAlerts alert();
 
  if (@
$CURRENT_USER) {
    
$errorsAndAlerts "<span class='heading-text-yellow'>YOU ARE ALREADY LOGGED IN!</span><a class='special'
href='{$GLOBALS['WEBSITE_LOGIN_POST_LOGIN_URL']}'>CLICK HERE TO CONTINUE TO THE WEB SITE</a> or <a class='special'
href='?action=logoff'>LOG OUT</a>.";
  }
   
?>


I wanted the form and instructions to show along with the, "please enter an email address" error message and wanted it
to be hidden if the email address was either found or not found in the accounts database. 

I added this code to the body of the page:


<?php if (@$emptyField): ?>
<?php $showform true?>
<div >
<br />
<?php echo $emptyField?><br /></div>
<?php elseif (@$errorsAndAlerts): ?>
<?php $showform false?>
 <div ><br />
<?php echo $errorsAndAlerts?><br />
</div>

<?php else: ?>

...some code and text...

 <?php if ($showForm == 'true'): ?> 

... some text...

<?php if (!@$CURRENT_USER): ?>
<form action="?" method="post">
<input type="hidden" name="action" value="pastInformation" />
<span class="body-text-bold">Enter your e-mail address:</span>
<input type="text" name="pastInformation" value="<?php echo htmlspecialchars(@$_REQUEST['pastInformation']) ?>"
size="20" />
<input type="submit" name="submit" value="Submit" />
</form>
<?php endif ?>

...more instructions...

<?php endif ?>


In the websiteMembership plugin in the "// perform website login actions" section, I added the following to the list of
actions:



 if (@$_REQUEST['action'] == 'pastInformation') { _websiteLogin_pastInformation(); }


Then, immediately before the "// perform website login actions" section of code, I added this block of code to check for
the existence of the email address in the accounts database.



// Existing Account Request
function _websiteLogin_pastInformation() { 
  global $SETTINGS, $TABLE_PREFIX; 
 
  $exists=0; 
 if(@$_REQUEST['pastInformation']){ 
  $where = "email ='".mysql_escape(@$_REQUEST['pastInformation'])."'"; 
  $exists=mysql_select_count_from('accounts',$where); 
 } 
  
  if ($exists) { 
    alert("<span class='heading-text-yellow'>Congratulations!!!<br />Your Account Exists</span><br /><span
class='your_class'>You do not need to create a new account.</span>\n"); 
    } 
    if (!$exists) { 
alert("<span class='your_class'>Sorry!!!<br />Your Account No Longer Exists</span><br /><span class='your_class'>You'll
have to fill out a new membership application after you you've paid your current year's dues.<br />To search for your
information using another email address <a href='becomeamember.php'><span class='your_class'>CLICK HERE</span></a><br
/></span>\n"); 
    
  } 
}


To make contents that show on your viewer dependent on the values returned, you can use a combination of:


 <?php if ($showForm == 'true'): ?> your contents <?php endif ?>


and 


<?php if ($showForm 'false' && (strpos($errorsAndAlerts,'Your-case-sensitive--text-string'))): ?> your contents <?php
endif ?>


or add a ! before the variables (!$showform = etc. to cover situations that do not meeting those criteria



REDIRECTING VISITOR TO A SPECIFIC PAGE AFTER UPDATING THEIR PROFILE, ETC. - Jun 1st, 2011

Here's a tidbit that was offered by Jason Sauchuk from Interactive tools.

He said:

You can redirect to any page  after you finish updating the information in the database with this function



redirectBrowserToURL("YOUR_PAGE.php");


ADDING A FIELD TO SHOW WHEN A MEMBER LAST LOGGED IN - Jun 1st, 2011

If you add a date field called lastLoginDate, to the User Accounts database, the website membership plugin will
automatically store the last time the user logged in for you

(This has been added to version 2.08 as a default field under User Accounts that is automatically updated when you login
to the CMS)


WORKING WITH DATE FIELDS IN THE MEMBER SIGNUP AND PROFILE UPDATE FORMS - Jan 20th, 2011

BACKGROUND
Along with other information, my client needed to list the dates of birth for her students in their records in the
accounts database.

She wanted to include date fields for 3 students. Student 1, 2 and 3.

This meant that the parents would have to be able to enter the dates into date fields in the database record they were
creating, and also be able to update those date fields when required.

Here's the code we used to accomplish these tasks, (with a lot of help from Jason Sauchuk from Interactive Tools).

THE MEMBER APPLICATION FORM
In the original application form, in the PHP code at the top of the page, in the //add user section:


$student_1_dob = date("Y-m-d",mktime(0,0,0,@$_REQUEST['month_1'],@$_REQUEST['day_1'],@$_REQUEST['year_1']));     
$student_2_dob = date("Y-m-d",mktime(0,0,0,@$_REQUEST['month_2'],@$_REQUEST['day_2'],@$_REQUEST['year_2'])); 
$student_3_dob = date("Y-m-d",mktime(0,0,0,@$_REQUEST['month_3'],@$_REQUEST['day_3'],@$_REQUEST['year_3'])); 


and in the mysql_query("INSERT INTO `{$TABLE_PREFIX}accounts` SET section:

    
 student_1_dob          = '".mysql_escape($student_1_dob)."', 
 student_2_dob          = '".mysql_escape($student_2_dob)."', 
 student_3_dob          = '".mysql_escape($student_3_dob)."', 


Then in the form, the following code (in the appropriate place for your particular form):
NOTE: You only have to define the variables $lowestYear and $highestYear once per viewer.

<tr> 
     <td>Date of Birth - Student 1</td> 
     <td><?php  
$lowestYear  1920;  
$highestYear 2006;  
?> 
Month:  
<select name="month_1">  
  <?php foreach(range(1,12) as $month_1): ?>     
    <option value="<?php echo $month_1;?>"><?php echo date("F",strtotime("0000-$month_1"));?></option>  
  <?php endforeach ?>  
</select> Day:  
<select name="day_1">  
  <?php foreach(range(1,31)as $day_1): ?>  
      <option value="<?php echo $day_1;?>"><?php echo $day_1;?></option>    
  <?php endforeach ?>  
</select> Year:  
<select name="year_1">  
   <?php foreach (range($lowestYear,$highestYear) as $year_1):?>  
       <option value="<?php echo $year_1;?>"><?php echo $year_1;?></option>  
   <?php endforeach?>  
</select> 

   </tr>




<tr> 
     <td>Date of Birth - Student 2</td> 
     <td>Month:  
<select name="month_2">  
  <?php foreach(range(1,12) as $month_2): ?>     
    <option value="<?php echo $month_2;?>"><?php echo date("F",strtotime("0000-$month_2"));?></option>  
  <?php endforeach ?>  
</select> Day:  
<select name="day_2">  
  <?php foreach(range(1,31)as $day_2): ?>  
      <option value="<?php echo $day_2;?>"><?php echo $day_2;?></option>    
  <?php endforeach ?>  
</select> Year:  
<select name="year_2">  
   <?php foreach (range($lowestYear,$highestYear) as $year_2):?>  
       <option value="<?php echo $year_2;?>"><?php echo $year_2;?></option>  
   <?php endforeach?>  
</select> 

   </tr>


And,

<tr> 
     <td>Date of Birth - Student 3</td> 
     <td>Month:  
<select name="month_3">  
  <?php foreach(range(1,12) as $month_3): ?>     
    <option value="<?php echo $month_3;?>"><?php echo date("F",strtotime("0000-$month_3"));?></option>  
  <?php endforeach ?>  
</select> Day:  
<select name="day_3">  
  <?php foreach(range(1,31)as $day_3): ?>  
      <option value="<?php echo $day_3;?>"><?php echo $day_3;?></option>    
  <?php endforeach ?>  
</select> Year:  
<select name="year_3">  
   <?php foreach (range($lowestYear,$highestYear) as $year_3):?>  
       <option value="<?php echo $year_3;?>"><?php echo $year_3;?></option>  
   <?php endforeach?>  
</select> 

   </tr>



THE PROFILE UPDATE FORM
For the profile update page, the code at the top of the page,  in the //update user section is:


 $student_1_dob = date("Y-m-d",mktime(0,0,0,@$_REQUEST['month_1'],@$_REQUEST['day_1'],@$_REQUEST['year_1']));     
$student_2_dob = date("Y-m-d",mktime(0,0,0,@$_REQUEST['month_2'],@$_REQUEST['day_2'],@$_REQUEST['year_2'])); 
$student_3_dob = date("Y-m-d",mktime(0,0,0,@$_REQUEST['month_3'],@$_REQUEST['day_3'],@$_REQUEST['year_3']));         

 

And the code in the $query = "UPDATE `{$TABLE_PREFIX}accounts` SET section is:


student_1_dob          = '".mysql_escape($student_1_dob)."', 
student_2_dob          = '".mysql_escape($student_2_dob)."', 
student_3_dob          = '".mysql_escape($student_3_dob)."', 


In the form, in order to pre-populate the individual parts of the date field, Jason came up with the following code:

<tr> 
     <td>Date of Birth - Student 1</td> 
     <td><?php  
$lowestYear  1935;  
$highestYear 2008

//pre-populate date fields from the database 
  //get most current $CURRENT_USER 
  $CURRENT_USER _websiteLogin_getCurrentUser(); 
   
 
$_REQUEST['month_1']=date("n",strtotime($CURRENT_USER['student_1_dob'])); 
  
$_REQUEST['day_1']=date("j",strtotime($CURRENT_USER['student_1_dob'])); 
  
$_REQUEST['year_1']=date("Y",strtotime($CURRENT_USER['student_1_dob'])); 
?> 
Month:  
<select name="month_1">    
  <?php foreach(range(1,12) as $month_1): ?>       
   <option value="<?php echo $month_1;?>" <?php selectedIf($month_1,@$_REQUEST['month_1']);?>><?php echo
date("F",strtotime("0000-$month_1"));?></option>    
  <?php endforeach ?>    
</select> Day:    
<select name="day_1">    
  <?php foreach(range(1,31)as $day_1): ?>    
      <option value="<?php echo $day_1;?>" <?php selectedIf($day_1,@$_REQUEST['day_1']);?>><?php echo $day_1;?></option>
     
  <?php endforeach ?>    
</select> Year:    
<select name="year_1">    
   <?php foreach (range($lowestYear,$highestYear) as $year_1):?>    
       <option value="<?php echo $year_1;?>" <?php selectedIf($year_1,@$_REQUEST['year_1']);?>><?php echo
$year_1;?></option>    
   <?php endforeach?>    
</select>

   </tr>




<tr> 
     <td>Date of Birth - Student 2</td> 
     <td><?php  

//pre-populate date fields from the database 
  //get most current $CURRENT_USER 
  $CURRENT_USER _websiteLogin_getCurrentUser(); 
   
 
$_REQUEST['month_2']=date("n",strtotime($CURRENT_USER['student_2_dob'])); 
  
$_REQUEST['day_2']=date("j",strtotime($CURRENT_USER['student_2_dob'])); 
  
$_REQUEST['year_2']=date("Y",strtotime($CURRENT_USER['student_2_dob'])); 
?> 
Month:  
<select name="month_2">    
  <?php foreach(range(1,12) as $month_2): ?>       
   <option value="<?php echo $month_2;?>" <?php selectedIf($month_2,@$_REQUEST['month_2']);?>><?php echo
date("F",strtotime("0000-$month_2"));?></option>    
  <?php endforeach ?>    
</select> Day:    
<select name="day_2">    
  <?php foreach(range(1,31)as $day_2): ?>    
      <option value="<?php echo $day_2;?>" <?php selectedIf($day_2,@$_REQUEST['day_2']);?>><?php echo $day_2;?></option>
     
  <?php endforeach ?>    
</select> Year:    
<select name="year_2">    
   <?php foreach (range($lowestYear,$highestYear) as $year_2):?>    
       <option value="<?php echo $year_2;?>" <?php selectedIf($year_2,@$_REQUEST['year_2']);?>><?php echo
$year_2;?></option>    
   <?php endforeach?>    
</select>

   </tr>



And,


<tr> 
     <td>Date of Birth - Student 3</td> 
     <td><?php  

//pre-populate date fields from the database 
  //get most current $CURRENT_USER 
  $CURRENT_USER _websiteLogin_getCurrentUser(); 
   
 
$_REQUEST['month_3']=date("n",strtotime($CURRENT_USER['student_3_dob'])); 
  
$_REQUEST['day_3']=date("j",strtotime($CURRENT_USER['student_3_dob'])); 
  
$_REQUEST['year_3']=date("Y",strtotime($CURRENT_USER['student_3_dob'])); 
?> 
Month:  
<select name="month_3">    
  <?php foreach(range(1,12) as $month_3): ?>       
   <option value="<?php echo $month_3;?>" <?php selectedIf($month_3,@$_REQUEST['month_3']);?>><?php echo
date("F",strtotime("0000-$month_3"));?></option>    
  <?php endforeach ?>    
</select> Day:    
<select name="day_3">    
  <?php foreach(range(1,31)as $day_3): ?>    
      <option value="<?php echo $day_3;?>" <?php selectedIf($day_3,@$_REQUEST['day_3']);?>><?php echo $day_3;?></option>
     
  <?php endforeach ?>    
</select> Year:    
<select name="year_3">    
   <?php foreach (range($lowestYear,$highestYear) as $year_3):?>    
       <option value="<?php echo $year_3;?>" <?php selectedIf($year_3,@$_REQUEST['year_3']);?>><?php echo
$year_3;?></option>    
   <?php endforeach?>    
</select>

   </tr>


POPULATE A SIGNUP AND PROFILE REVISION FORM WITH EXISTING RADIO BUTTONS VALUE - Feb 18th, 2011

Let’s say you’ve got a radio button field called “interest” on your profile form with a number of possible
values and you want to echo the current value from  your user's record in their profile revision form.

It’s easy when you follow these 3 simple steps:

- Add a field to your User Accounts editor. (For this example
we'll call your field 'interest')

- Add this error checking code to your signup.php or profile.php viewer:

  if (!@$_REQUEST['interest']) { $errorsAndAlerts .= "You must select your interest!\n"; }


- Add this code to the list of mysql queries:

  interest = '".mysql_escape( $_REQUEST['interest'] )."',


- Add this user input field to your form:

  <tr>
   <td valign="top">Interest</td>
   <td>
     <?php $fieldname 'interest'?>
     <?php $idCounter 0?>
     <?php foreach (getListOptions('accounts'$fieldname) as $value => $label): ?>
       <?php $id "$fieldname." . ++$idCounter?>
       <input type="radio" name="<?php echo $fieldname ?>" id="<?php echo $id ?>"
             value="<?php echo htmlspecialchars($value?>" <?php checkedIf(@$_REQUEST[$fieldname], $value?> />
       <label for="<?php echo $id ?>"><?php echo htmlspecialchars($value?></label>

     <?php endforeach ?>
   </td>
  </tr>


- Before you go “live”, test that the form lets you save each possible value, displays the last saved
value, and that saved values are also displayed correctly in the User Accounts record.


PRE-POPULATING RADIO BUTTONS IN FORMS FROM A MASTER LIST - Jul 22nd, 2019

The trick here was to populate the list of values from the values available in separate master list, and use that list
to populate a radio button list field in the accounts section (some of the functions in this example require the Website
Membership plugin).

Here’s are the steps...

1) Create a multi-record section (called “my_values”) with a 'hidden’ check box and a text field called
“contents”

2) In the accounts section, create a radio button list field (called my_list_field) that gets its options from the MySQL
query: 

SELECT num, contents
  FROM `<?php echo $TABLE_PREFIX ?>my_values`
WHERE hidden = 0
ORDER BY dragSortOrder  ASC ;

3) Near the top of the viewer that contains your form, after the load records calls, add the following variable to the
list of variables: 
 
@$my_list_field = @$_REQUEST['my_list_field'] ;

4) After any error checking include the following in your $colsToValues array

@$colsToValues['my_list_field']         = $_REQUEST['my_list_field'] ;


5) to pre-populate your form from current user values in the accounts table

  // pre-populate form with current user values
  foreach ($CURRENT_USER as $name => $value) {
    if (array_key_exists($name, $_REQUEST)) { continue; }
    $_REQUEST[$name] = $value;
  }


5) In your form, to show a radio button list of all values that are not hidden in the "my_values" section, use the code:

<tr>
<td valign="top">My Available Values</td>
<td>
<?php $fieldname 'your_list_field'?>
<?php $idCounter 0?>
<?php foreach (getListOptions(accountsTable(), $fieldname) as $value => $label): ?>
<?php $id "$fieldname." . ++$idCounter?>
<input type="radio" name="<?php echo $fieldname ?>" id="<?php echo $id ?>"
 value="<?php echo htmlencode($value?>" <?php checkedIf(@$_REQUEST[$fieldname], $value?> />
<label for="<?php echo $id ?>"><?php echo htmlencode($label?></label>
<?php endforeach ?>
</td>
</tr> 


Another Approach (shows all values):


<?php $fieldname 'practice_country'?>
<?php $idCounter 0?>
<?php foreach (getListOptions(accountsTable(), $fieldname) as $value => $label): ?>
<?php $id "$fieldname." . ++$idCounter?>
<input id="<?php echo $id ?>" name="<?php echo $fieldname ?>" type="radio" value="<?php echo htmlencode($value?>" />
<label for="<?php echo $id ?>"><?php echo htmlencode($label?></label>

<?php endforeach ?>


<b>To show only values that are not hidden</b>:
Since getListOptions() will get all available options for a list field, if you want to only show countries that have
been selected by non-hidden records, here's a different approach.

<?php $fieldname 'practice_country'?>
<?php $tablename accountsTable(); ?>
<?php
  // get all table records
  list($tableRecords$tableRecordsMeta) = getRecords(array(
    
'tableName'     => $tablename,
    
'loadUploads'   => false,
    
'loadCreatedBy' => false,
    
'allowSearch'   => false,
  ));
  
  
// group records by target field to find option keys
  $tableRecordsByField array_groupBy($tableRecords$fieldname);
  
$keys                  array_keys($tableRecordsByField);
  
  
// create option list
  $listOptions = [];
  foreach(
$keys as $key) {
    if (!empty( 
$key )) { 
      
$listOptions$key ] = $tableRecordsByField$key ][ $fieldname.':label' ];
    }
  }
?>

This will create the variable $listOptions that should contain all of the currently in-use list values and labels for
the target table/field. This should let you then update your foreach to look like this:

<?php foreach ($listOptions as $value => $label): ?>
              <?php $id "$fieldname." . ++$idCounter?>
              <input id="<?php echo $id ?>" name="<?php echo $fieldname ?>" type="radio" value="<?php echo
htmlencode($value?>" <?php checkedIf(@$_REQUEST[$fieldname], $value?>/>
              <label for="<?php echo $id ?>"><?php echo htmlencode($label?></label>
              
              <?php endforeach ?>

Note that this will only work for single-value list fields, such as radio buttons or single dropdowns.


WORKING WITH SINGLE VALUE CHECK BOXES IN FORMS - Dec 23rd, 2015

Here's how to add a checkbox (called non_member) to a form and have it populate a single value check box field in your
multi record editor

(Of course, you'll need to have a single value check box field called non_member in your multi record editor)

Check boxes are handled a bit differently from text field, so here's the code you'd need.

First create a variable (makes it easier to keep track of your code)




$non_memberCheckBox = intval(@$_REQUEST['non_member']);



Then, in your INSERT INTO or UPDATE mysql_query:



non_member   = '".intval( $non_memberCheckBox )."',


Then in your form you would add:


<label for='non_member><span class="your_class">Non-Member:</span>&nbsp; </label>
      <input type = "checkbox" id="non_member" name="non_member" value = "1" <?php checkedIf(1,
@
$_REQUEST['non_member']);?> /> 


So that if the box is checked your non_member field will be populated with a value of "1" If not it will be populated
with "0"



Another iteration of this is when $colsToValues is used (like in the profile update and user signup form)

When a browser submits a form if a checkbox is not ticked it doesn't send a value at all as opposed to sending an empty
or negative value. 

To get around this issue, in the $colsToValues array, insert:


$colsToValues['non_member']    = (@$_REQUEST['non_member'])? '1' : '0' ;


Which sends a '0' when the box is unchecked.

In your form, your input field would look the same as above:


<input type = "checkbox" id="non_member" name="non_member" value = "1" <?php checkedIf(1, @$_REQUEST['non_member']);?>


WORKING WITH MULTI VALUE CHECK BOXES IN FORMS



WORKING WITH MULTI VALUE CHECK BOXES IN FORMS - Dec 29th, 2015

According to Jason Sauchuck, from Interactive Tools, Multi-value checklists and multi-value pull downs are both stored
the same way in CMS Builder,

In this example I'm populating the list of possible option values (num) and option labels (text) for the levels_of_care
field from another table

Using the basic approach in the website membership plugin's profile update form, and with some help fro Ross Fairbairn,
also from Interactive Tools, I was able to populate a multi value check box list field (levels_of_care in this example),
and update the list in my table (accounts in this case),use the following code in my form:
    
<tr>
              <td valign="top">Levels of Care</td>
              <td><?php $fieldname 'levels_of_care'?>
               <?php
if(is_array(@$_REQUEST[$fieldname])){
$fieldValues $_REQUEST[$fieldname];
}
else{
$fieldValues explode("\t",trim(@$_REQUEST[$fieldname],"\t"));
}
?>
                <?php $idCounter 0?>
                <?php foreach (getListOptions('accounts'$fieldname) as $value => $label): ?>
                <?php $id "$fieldname." . ++$idCounter?>
                <input type="checkbox" name="<?php echo $fieldname ?>[]" id="<?php echo $id ?>"
value="<?php echo htmlspecialchars($value?>" <?php if(in_array($value,$fieldValues)):?> checked="checked" <?php endif
?>/>
                <label for="<?php echo $id ?>"><?php echo htmlspecialchars($label?></label>
                
                
                <?php endforeach ?></td>
            </tr>


And this code in the colsToValues array:

  $colsToValues['levels_of_care'] = "\t" . implode("\t", $_REQUEST['levels_of_care']) . "\t";


CREATING DYNAMIC FORM FIELDS - Nov 10th, 2018

I was creating a front end form that allowed companies that had more than one location and wanted to list those
locations without cluttering up the form with lots of blank fields. 


Here’s how...

1) In the head of your form page add the following  javascript:

<script type="text/javascript">

function yesnoCheck1() {
    if (document.getElementById('yesCheck1').checked) {
        document.getElementById('ifYes1').style.display = 'block';
    }
    else document.getElementById('ifYes1').style.display = 'none';

}
function yesnoCheck2() {
    if (document.getElementById('yesCheck2').checked) {
        document.getElementById('ifYes2').style.display = 'block';
    }
    else document.getElementById('ifYes2').style.display = 'none';

}

function yesnoCheck3() {
    if (document.getElementById('yesCheck3').checked) {
        document.getElementById('ifYes3').style.display = 'block';
    }
    else document.getElementById('ifYes3').style.display = 'none';

}
</script>


Then in the form itself, add the following:

 <form>  
              <table align="center" >
                  <tr>
                    <td class="text_font" >Location 1 Street Address</td>
                    <td><input  type="text" name="location_street_address_1" value="<?php echo
ucfirst(htmlencode(@$_REQUEST['location_street_address_1'])); ?>"  size="50" /></td>
                  </tr>
                  <tr>
                    <td class="text_font" >Apt/Suite/Floor</td>
                    <td><input  type="text" name="location_suite_apartment_or_floor_1" value="<?php echo
ucfirst(htmlencode(@$_REQUEST['location_suite_apartment_or_floor_1'])); ?>" size="50" /></td>
                  </tr>
                  <tr>
                    <td class="text_font">City</td>
                    <td><input type="text" name="location_city_1" value="<?php echo
htmlspecialchars(@$_REQUEST['location_city_1']); ?>" size="50" /></td>
                  </tr>
                  <tr>
                    <td class="text_font">State</td>
                    <td><input type="text" name="location_state_1" value="<?php echo
htmlspecialchars(@$_REQUEST['location_state_1']); ?>" MAXLENGTH="1" size="50" /></td>
                  </tr>
                  <tr>
                    <td class="text_font">Zip Code</td>
                    <td><input type="text" name="location_zip_1" value="<?php echo
htmlspecialchars(@$_REQUEST['location_zip_1']); ?>" MAXLENGTH="10" size="50" /></td>
                  </tr>
                </table>
                <span class="text_font"><b>
                <hr />
                Add A Second Location?
                <input type="radio" onclick="javascript:yesnoCheck1();" name="yesno1" id="yesCheck1">
                
                <hr />
                </b></span>
                <div id="ifYes1" <?php if(@!$_REQUEST['location_street_address_2']):?>style="display:none" <?php endif
?>>
                  <table align="center" >
                    <tr>
                      <td class="text_font" >Location 2 Street Address</td>
                      <td><input  type="text" name="location_street_address_2" value="<?php echo
ucfirst(htmlencode(@$_REQUEST['location_street_address_2'])); ?>"  size="50" /></td>
                    </tr>
                    <tr>
                      <td class="text_font" >Apt/Suite/Floor</td>
                      <td><input  type="text" name="location_suite_apartment_or_floor_2" value="<?php echo
ucfirst(htmlencode(@$_REQUEST['location_suite_apartment_or_floor_2'])); ?>" size="50" /></td>
                    </tr>
                    <tr>
                      <td class="text_font">City</td>
                      <td><input type="text" name="location_city_2" value="<?php echo
htmlspecialchars(@$_REQUEST['location_city_2']); ?>" size="50" /></td>
                    </tr>
                    <tr>
                      <td class="text_font">State</td>
                      <td><input type="text" name="location_state_2" value="<?php echo
htmlspecialchars(@$_REQUEST['location_state_2']); ?>" MAXLENGTH="2" size="50" /></td>
                    </tr>
                    <tr>
                      <td class="text_font">Zip Code</td>
                      <td><input type="text" name="location_zip_2" value="<?php echo
htmlspecialchars(@$_REQUEST['location_zip_2']); ?>" MAXLENGTH="10" size="50" /></td>
                    </tr>
                  </table>
                  <span class="text_font"><b>
                  <hr />
                  Add A Third Location?
                  <input type="radio" onclick="javascript:yesnoCheck2();" name="yesno2" id="yesCheck2">
                  
                  <hr />
                  </b></span>
                  <div id="ifYes2" <?php if(@!$_REQUEST['location_street_address_3']):?>style="display:none" <?php endif
?>>
                    <table align="center" >
                      <tr>
                        <td class="text_font" >Location 3 Street Address</td>
                        <td><input  type="text" name="location_street_address_3" value="<?php echo
ucfirst(htmlencode(@$_REQUEST['location_street_address_3'])); ?>"  size="50" /></td>
                      </tr>
                      <tr>
                        <td class="text_font" >Apt/Suite/Floor</td>
                        <td><input  type="text" name="location_suite_apartment_or_floor_3" value="<?php echo
ucfirst(htmlencode(@$_REQUEST['location_suite_apartment_or_floor_3'])); ?>" size="50" /></td>
                      </tr>
                      <tr>
                        <td class="text_font">City</td>
                        <td><input type="text" name="location_city_3" value="<?php echo
htmlspecialchars(@$_REQUEST['location_city_3']); ?>" size="50" /></td>
                      </tr>
                      <tr>
                        <td class="text_font">State</td>
                        <td><input type="text" name="location_state_3" value="<?php echo
htmlspecialchars(@$_REQUEST['location_state_3']); ?>" MAXLENGTH="2" size="50" /></td>
                      </tr>
                      <tr>
                        <td class="text_font">Zip Code</td>
                        <td><input type="text" name="location_zip_3" value="<?php echo
htmlspecialchars(@$_REQUEST['location_zip_3']); ?>" MAXLENGTH="10" size="50" /></td>
                      </tr>
                    </table> 
                    </div>
                </div>
                </form>

To add new fields, at the end of each block of location entry fields there's an “Add another location?” radio button
that added another block of location entry fields.  

**To add more blocks of fields, add new functions to the javascript and add new blocks to the form. Just make sure to
add closing </div> tags and to increment the function numbers.

The <?php if(@!$_REQUEST['location_street_address_n']):?>style="display:none" <?php endif ?> code insures that if there
is data entered in that block, the block will remain visible for updating.


PASTE AWARE WORD COUNTER WITH WORD LIMITING FOR FORM TEXTAREA FIELDS - Dec 13th, 2018

Here’s a simple to implement word counter to use when you want to limit the length of the input to a front end html
form textarea field.

In the head of the web page insert the following:
(Change the number of words to suit your needs)


<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
 <script type="text/javascript"><!--
    
$(document).ready(function() {
  $("#word_count").on('keyup', function() {
    var words = this.value.match(/\S+/g).length;

    if (words > 200) {
      // Split the string on first 200 words and rejoin on spaces
      var trimmed = $(this).val().split(/\s+/, 200).join(" ");
      // Add a space at the end to make sure more typing creates new words
      $(this).val(trimmed + " ");
    }
    else {
      $('#display_count').text(words);
      $('#word_left').text(200-words);
    }
  });
}); 
// --></script>


Then, for the textarea field in the form, use something like:


<textarea name="your_name" id="word_count" COLS=50 ROWS=8></textarea>
                    
                    Total word Count : <span id="display_count">0</span> words. Words left : <span
id="word_left">200</span>
                    If you paste more than 200 words your entry will be truncated.


PRE-POPULATE A PULL DOWN FORM FIELD FROM THE VALUES IN A MASTER LIST - Jun 3rd, 2014

I had created a multi-record master list section called master_list with a field called title. I wanted to pre-populate
the options values in a form from the values in the title field of the records in that section.

At the top of my page I insert a load records call for the master_list table


 list($master_listRecords, $master_listMetaData) = getRecords(array(
    'tableName'   => 'master_list', 
  ));


In the body, before the form, I created an array out of the title field values in each record.


<?php 
 $names = array(); 
 foreach (
$master_listRecords as $record){ 
    
$names[$record['title']]=$record['title']; 
 } 
?>


In the form I inserted the following to loop through the array and populate the pull down field:


 <label for='exhibition_name'><span class="my_class">Exhibition Name:</span>
      </label>
<select  type="text" id="exhibition_name" name="exhibition_name"> 
<option value=""><span class="body-text-bold">Select an Exhibition Name</span></option> 
 
<?php foreach($names as $name): ?> 
   <option value="<?php echo $name;?>"><?php echo $name;?></option> 
<?php endforeach?> 
</select>


Here’s another approach using the record number as the option value for a record in a table called
‘email_signup_location’ that contains a field called ‘location’.

The Javascript retains the selected value in the form after submission so that if there are any errors thrown, the pull
down does not need to be manually repopulated. 


<select type="text"  id="source" name="source" width="200" style="width: 200px" >
<option value="" >Your Location</option>
                    <?php foreach(mysql_select("email_signup_location") as $email_signup_locationRecord): ?>
                    <option value="<?php echo $email_signup_locationRecord['num'];?>"><?php echo
$email_signup_locationRecord['location'];?></option>
                    <?php endforeach?>
</select>
<script type="text/javascript">
  document.getElementById('source').value = "<?php echo $_POST['source'];?>";
</script>

 


CREATING A LOGIN LOG TO SEE WHICH MEMBERS SIGN IN AND WHEN (UPDATED FOR MYSQLI) - Jul 22nd, 2019

Chris Waddell from Interactive Tools offered this approach:

NOTE: Records will only be added to the "Login Log" section for actual membership logins, not by logins to the CMSB
interface.

1) Create a Multi Record section called "Login Log". 

2) Modify it and remove all the fields except "num" and "createdDate" (note that you'll need to "Enable System Field
Editing" under the "Advanced Commands..." dropdown to remove some of the fields.) 

3) Add a List Field called "Who". Leave "Display As" set to "pull down".

Modify your "Who" field as follows:

List Options:                        Get options from database (advanced) 
 
  Section Tablename:                 accounts 
  Use this field for option values:  num 
  Use this field for option labels:  username
  
4) Create a list field for any other fields in the user account that you'd like to display. I have a First Name and a
Last Name field, so I've created a "First Name" list field and a "Last Name" list field as above. 

Each has the same list options except for the "Use This Field For Option Labels" which I changed to "first_name" and
"last_name".

If you want to add other fields to the log, just create more list fields as above, but change the "Use This Field For
Option Labels" as appropriate.

4) Now, make some more changes to your Login Log section, this time at the top of the page:

In the General tab, change "ListPage Fields" to "createdDate, who, first_name, last_name". 
Under the Viewer Urls tab, delete all the existing "Filename Fields" entries. 
Under the Searching tab, set "Search Fields" to "createdDate, who,  first_name, last_name". 
Finally, under the Sorting tab, set "Order By" to "createdDate DESC". Now click Save Details.

5) Now open up cmsAdmin/plugins/websiteMembership/websiteMembership.php in a text editor and find "redirect on success".
Add the following code immediately before "redirect on success":

   
  // CUSTOM CODE! add record to login_log updated for mysqli
  global $TABLE_PREFIX; 
 mysqli()->query(mysql_escapef("INSERT INTO {$TABLE_PREFIX}login_log SET
createdDate = NOW(), 
who = ?, last_name = ?,
 first_name = ?", 
$CURRENT_USER['num'], 
$CURRENT_USER['num'], 
$CURRENT_USER['num']))
or die("Mysql error adding login_log record: ". htmlspecialchars(mysqli()->error) . "\n"); 


If you don't want to log admins, us this code instead:


// CUSTOM CODE! add record to login_log updated for mysqli
 if (!@$CURRENT_USER['isAdmin'])       
 {global $TABLE_PREFIX;
mysqli()->query(mysql_escapef("INSERT INTO {$TABLE_PREFIX}login_log SET
createdDate = NOW(), 
 who = ?, last_name = ?,
 first_name = ?", 
$CURRENT_USER['num'], 
$CURRENT_USER['num'], 
$CURRENT_USER['num']))
or die("Mysql error adding login_log record: ". htmlspecialchars(mysqli()->error) . "\n");
 }


In the Custom Code add (or remove) a your_field = ?, and a $CURRENT_USER['num'], for each new field you want to
populate.

Now you can create a list viewer that your client can access and restrict viewer access to admins only.

Here's a simple example that you can style to match your site design.

At the top of your listing page:
NOTE: Don't forget to change the   $dirsToCheck = array('/PATH_TO_YOUR_SERVER/','','../','../../','../../../'); to match
your server path. (you can find this in the admin>code generator code for any section.

 

<?php header('Content-type: text/html; charset=utf-8'); ?>
<?php
  // load viewer library
  $libraryPath 'cmsAdmin/lib/viewer_functions.php';
  
$dirsToCheck = array('/PATH_TO_YOUR_SERVER/','','../','../../','../../../');
  foreach (
$dirsToCheck as $dir) { if (@include_once("$dir$libraryPath")) { break; }}
  if (!
function_exists('getRecords')) { die("Couldn't load viewer library, check filepath in sourcecode."); }

  
// load records
  list($login_logRecords$login_logMetaData) = getRecords(array(
    
'tableName'   => 'login_log',
  ));
  if (!
$CURRENT_USER['isAdmin']) { websiteLogin_redirectToLogin(); }
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Login Log</title>
</head>
<body>

<?php foreach ($login_logRecords as $record): ?>
<?php echo date("F jS  Y, g:i a "strtotime($record['createdDate'])) ?>

<?php echo $record['who:label'];?> - <?php echo $record['first_name:label'];?> <?php echo $record['last_:label'];?>
<hr/>
<?php endforeach ?>
</body>
</html>


AUTOMATIC RENEWALS USING THE WEBSITE MEMBERSHIP PLUGIN - Jan 31st, 2023

This is a rather long recipe, covering the process of automatically renewing memberships based on a specified membership
period.

There are a number of pieces to this puzzle:

Setting the expiration date when a member first signs up and creates their member record

Determining when a renewal has expired 

Directing the member to a renewal page for payment

After payment, updating the member record to reflect the “new” expiry date so the process can repeat automatically.

Automatically sending a renewal letter on expiration

SETTING THE INITIAL EXPIRATION DATE AND DETERMINING WHEN A RENEWAL HAS EXPIRED
Since my membership period is 1 year, when a member signs up, the “expiresDate” field in the accounts database is
automatically populated with a date that is 1 year from the date that the member record was created. I also set the
“neverExpires” checkbox to zero using this code in my sign-up form. (you can read more about sign-up form
modification elsewhere in this chapter).


expiresDate = (NOW() + INTERVAL 1 YEAR),
neverExpires     = '0',


NOTE: Although I could have used the neverExpires field, to deal with special memberships that should never expire, I
chose to create a “special_membership” check box field in the database.

REDIRECTING EXPIRED MEMBERS TO A RENEWAL PAGE
Thanks to some advice from Dave Edis from Interactive Tools, I inserted this code near the top of all of the “members
only” pages (after the login_redirect) to redirect members to a “renew_now” page if their membership had expired.


<?php if (!$CURRENT_USER) { websiteLogin_redirectToLogin(); } ?>

<?php
$isMembershipExpired strtotime($CURRENT_USER['expiresDate']) <= time(); 
$notSpecial = ($CURRENT_USER['special_membership'] == 0);
if (
$isMembershipExpired && $notSpecial) { redirectBrowserToURL("renew_now.php"); }
?>


The renewal page includes a PayPal link and I use the program “Linklok” to handle transaction verification and to
redirect the user to a hidden “thank you” page on the site after a successful payment.

THE RENEW_NOW.PHP CODE
At the top of the page:


<?php header('Content-type: text/html; charset=utf-8'); ?>
<?php 
 $libraryPath 'cmsAdmin/lib/viewer_functions.php';
  
$dirsToCheck = array('server_path_to_your_root_directory/','','../','../../','../../../');
  foreach (
$dirsToCheck as $dir) { if (@include_once("$dir$libraryPath")) { break; }}
  if (!
function_exists('getRecords')) { die("Couldn't load viewer library, check filepath in sourcecode."); }
 
?>

<?php if (!@$GLOBALS['WEBSITE_MEMBERSHIP_PLUGIN']) { die("You must activate the Website Membership plugin before you can
access this page."); } ?>



And in the body:


<div align="center">
<br /><h1><span class="Page-Titles">Thank's For</span><br />
<span class="Page-Titles"> 
Being a Subsciber!</span></h1>
 <h3><span class="X-Large-Text">Unfortunately, your subscription has expired,<br />
 and you'll need to renew your subscription to enjoy <br />
another full year of uninterupted access to the Cookbook</span>.</h3>
<h2><span class="X-Large-Text">Great News! </span></h2>

 <h3><span class="X-Large-Text">If you renew now with PayPal, <br />
</span><span class="X-Large-Text"> <br />
your special annual renewal rate is only <br />
</span></h3>
<h2><span class="X-Large-Text">$15.00 US!</span></h2>
<h3><span class="X-Large-Text">which is $40% off the current subscription rate. <br />
</span></h3>
<table width="75%" border="0" cellpadding="15">
<tr>
<td valign="top"><div align="center"> <img src="images/paypal-logo.jpg" alt="" height="99" width="100" border="0"
/></div></td>
<td valign="top"><p align="center"> </p>
<div align="center"> <a class="special" href="https://www.paypal.com/cgi-bin/webscr?amount=15.00
&amp;item_number=Your_Item_Number
&amp;item_name=Your Renewal
&amp;business=payments@your_site.com
&amp;cpp_header_image=http://www.your_site.com/images/CMSBPP.png
&amp;currency_code=USD&amp;lc=US&amp;add=1
&amp;cmd=_cart&amp;no_shipping=0
&amp;return=http://www.your_site.com/linklokipnret.php
&amp;notify_url=http://www.your_site.com/linklokipn.php
&amp;no_note=1&amp;bn value=PP-ShopCartBF&amp;">
<img src="/images/btn_buynowCC_LG.gif" /></a><br />
</div></td>
</tr>  <tr>
 <td align="center" valign="middle"><span class="Large-Text">Renew you subscription now, for only <s>$24.95</s>
</span><span class="X-Large-Text">$15.00</span></td>
</tr>
</table>


Linklok implementation proved to be easy.

The URL for Linklok is:

http://www.vibralogix.com/linklokipn/

If you should decide to purchase linklok, or any other Vibralogic programs, I'd appreciate your using my affiliate link:

http://www.shareasale.com/r.cfm?B=12671&U=520135&M=3826

Although the documentation outlines many ways to customize your installation, for a basic implementation there are only
two files required for linklok to operate: linklokipn.php and linklokipnret.php

linklokipn.php is the only file that you’ll need to customize, and all the user defined parameters appear at the top
of the file:

The information included in the $Products variable is: "unique_product_id,product name,product currency and minimum
price accepted,path to your hidden directory and the URL of your "your_update_page.php" page, leave this one set to
“0",the number of minutes until the link sent to the purchaser expires";


$Products[] = "Your_Product_ID,Your Product name,USD=99.95,soyerveortmvobd/your_update_page.php,0,1440";

// Setup admin and security variables
$LinklokURL = "http://www.your_site.com/linklokipn.php";      // URL of the linlok.php page on your site
$PaypalEmail = "payments@your_site.com";                        // PRIMARY Paypal email address
$SellerCompany = "Your Company";                             // Your company name
$SellerEmail = "info@your_site.com";                         // Your email address for order inquiries
$SellerURL = "http://www.your_site.com";                             // Your website URL
$LinkKey = "orange23";                                       // Encryption key for download links
$PDTtoken="12876Vdwerwrol_dmYMHfewrwurjdE3QsupkkvTo4Fv39-7y";       // Auto Return PDT token from paypal-profile-website
payment preferences

$DelayEchecks = "Y";                                         // Set to Y to delay eCheck orders until cleared.
$EmailTemplate = "";                                         // Optional Email Template in either .txt or .html format
$DownloadTemplate = "";                                      // Optional download page template
$ErrorTemplate = "";                                         // Optional Error page template
$CopyEmail = "any_email@your_provider.net";                                             // Receive copy of order emails.
email address or ""
$ManualPassword = "kookamonga";                                 // Password for manual order entry. "" to disable
$HTMLEmail = "Y";                                            // Set to Y to use HTML formatted emails or N to send in
plain-text
$Txnid = "";                                                 // Optional to stop possible multiple calls from IPN
$WarningTemplate = "";                                       // Check payment warning email template
$WarningTemplatePage= "";                                    // Check payment warning thankyou page template


UPDATING THE MEMBER’S RECORD (The soyerveortmvobd/your_update_page.php file)

Current versions of CMSB include password encryption, so matching an encrypted password with a submitted plain text
password is not possible. Fortunately, it is possible to use the built in login functionality to make the hidden update
function pretty straight forward:


<?php header('Content-type: text/html; charset=utf-8'); ?>
<?php
  // load viewer library
  $libraryPath 'cmsAdmin/lib/viewer_functions.php';
  
$dirsToCheck = array('/path_to_your_server/','','../','../../','../../../');
  foreach (
$dirsToCheck as $dir) { if (@include_once("$dir$libraryPath")) { break; }}
  if (!
function_exists('getRecords')) { die("Couldn't load viewer library, check filepath in sourcecode."); }
 if (!@
$GLOBALS['WEBSITE_MEMBERSHIP_PLUGIN']) { die("You must activate the Website Membership plugin before you can
access this page."); } 
 if (!
$CURRENT_USER) { websiteLogin_redirectToLogin(); }  ?>
<?php 
  mysqlStrictMode(false);
 
mysql_query("UPDATE `{$TABLE_PREFIX}accounts`    
    SET  expiresDate = NOW() + INTERVAL 1 YEAR    
   WHERE num = '".mysql_escape$CURRENT_USER['num'] )."'")  
       or die(
"MySQL Error:\n"htmlspecialchars(mysql_error()) . "\n");  
        
$userNum mysql_insert_id();  
    
// redirect after a successful update  
    header("Location: http://www.your_site.com/your_update_success_page.php"); 
 
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta name="robots" content="noindex,nofollow" />
</head>
<body>
</body>
</html>


AUTOMATING THE RENEWAL PROCESS WITH AUTOMATIC E-MAILS
Although my specific application did not require notification prior to membership expiration, you might want to send out
mailings a few weeks before a member’s account expires.

I did not implement this, but here are some preliminary manual steps you can use:

GENERATING EMAIL LISTS OF EXPIRING MEMBERS
Here’s the code for the top of your page:


<?php header('Content-type: text/html; charset=utf-8'); ?>
<?php
    // load viewer library
  $libraryPath 'cmsAdmin/lib/viewer_functions.php';
  
$dirsToCheck = array('/server_path_to_your_root_directory/','','../','../../','../../../');
  foreach (
$dirsToCheck as $dir) { if (@include_once("$dir$libraryPath")) { break; }}
  if (!
function_exists('getRecords')) { die("Couldn't load viewer library, check filepath in sourcecode."); }

// load records
  list($accountsRecords$accountsMetaData) = getRecords(array(
    
'tableName'   => 'accounts',
'where' => " '{$CURRENT_USER['isAdmin']}' ",    
  ));

?>


The code: 'where' => " '{$CURRENT_USER['isAdmin']}' ", prevents anyone who is not an admin from generating an email
list.

And in the body of the page:
The trick is to only list emails for those non-special members whose accounts have expired.
Using an if statement with a further modification of Dave’s Expired Account checking code solved that challenge:

 <hr />
<div align="left"> 
?php foreach ($accountsRecords as $record): ?>
<?php  $isMembershipExpired strtotime ($record['expiresDate']) <= time(); ?>
<?php if ($record['special_membership'] == '0' && $isMembershipExpired == '1'): ?>
<span class="your_class"><?php echo $record['email'?>;&nbsp;</span>
<?php endif ?>  
<?php endforeach ?>
</div>
<hr />


If you want to send emails to different lists of expired members here are some approaches:

GENERATE A LIST OF MEMBERS WHOSE ACCOUNTS HAVE RECENTLY EXPIRED 
You can compare the updatedDate value with the current date and only generate a list of those member records that have
expired and who fall between the two dates.

GENERATE A LIST OF EXPIRED MEMBERS THAT WERE SENT A LETTER AND DIDN’T RENEW
Here you’ll need a field that contains the last date that you sent out a letter so you can compare that date with
those members whose accounts expired before that date. (Yu can also generate a list of those who expired after that
date.) The Last Mailing Date can be in a single record editor if you have one. 

If your service provider limits the amount of email recipients that can be included in one email, you can break your
lists down into groups using the information in the recipe “BREAKING E-MAIL LISTS INTO GROUPS OF "N" ADDRESSES EACH”


DOUBLE OPT IN MAIL LIST SYSTEM IN CMSB (PHP7 AND GOOGLE RECAPTCHA) - Sep 7th, 2017

Here’s the updated double opt in email signup system for PHP7 and Google Recaptcha. I hope it proves helpful...

There are 4 pages that act on a section called email_signup: 

email_signup.php - the initial signup form that creates a record in the database with the hidden field checked and the
confirmed field unchecked. The page also sends a confirmation email to the email entered with a link that the recipient
can click on to confirm that they wanted to sign up.

confirm.php - the confirmation page that the person accesses through the link in their email to confirm that they wanted
to sign up.

unsubscribe.php - the form used to unsubscribe. As above a confirmation email is sent to the email entered, and no
action is taken until the recipient clicks on the link in the email.

unsubscribe_confirm.php - as above, the page that the person addresses through the link in their email to confirm that
they wanted to unsubscribe.

Here’s the .ini.php code for creating the email_signup section, email_signup,ini.php


<?php  if (!@$LOADSTRUCT) { die("This is not a program file."); }
return array (
  
'_detailPage' => '',
  
'_disableAdd' => '0',
  
'_disableErase' => '0',
  
'_disableModify' => '0',
  
'_disablePreview' => '1',
  
'_disableView' => '1',
  
'_filenameFields' => '',
  
'_hideRecordsFromDisabledAccounts' => '0',
  
'_indent' => '0',
  
'_listPage' => '',
  
'_maxRecords' => '',
  
'_maxRecordsPerUser' => '',
  
'_perPageDefault' => '1000',
  
'_previewPage' => '',
  
'_requiredPlugins' => '',
  
'_tableName' => 'email_signup',
  
'listPageFields' => 'last_name,first_name,email,hidden,confirmed,remove,source,createdDate,updatedDate',
  
'listPageOrder' => 'source DESC, confirmed DESC, last_name, first_name',
  
'listPageSearchFields' => '__ALL__',
  
'menuHidden' => '0',
  
'menuName' => 'Email Signup',
  
'menuOrder' => '0000000004',
  
'menuPrefixIcon' => '',
  
'menuType' => 'multi',
  
'num' => array(
    
'order' => 1,
    
'type' => 'none',
    
'label' => 'Record Number',
    
'isSystemField' => '1',
  ),
  
'createdDate' => array(
    
'order' => 2,
    
'type' => 'none',
    
'label' => 'Created',
    
'isSystemField' => '1',
  ),
  
'createdByUserNum' => array(
    
'order' => 3,
    
'type' => 'none',
    
'label' => 'Created By',
    
'isSystemField' => '1',
  ),
  
'updatedDate' => array(
    
'order' => 4,
    
'type' => 'none',
    
'label' => 'Last Updated',
    
'isSystemField' => '1',
  ),
  
'updatedByUserNum' => array(
    
'order' => 5,
    
'type' => 'none',
    
'label' => 'Last Updated By',
    
'isSystemField' => '1',
  ),
  
'hidden' => array(
    
'order' => 6,
    
'label' => 'Hidden',
    
'type' => 'checkbox',
    
'fieldPrefix' => '',
    
'checkedByDefault' => '0',
    
'description' => '',
    
'checkedValue' => 'Yes',
    
'uncheckedValue' => 'No',
  ),
  
'confirmed' => array(
    
'order' => 7,
    
'label' => 'Confirmed',
    
'type' => 'checkbox',
    
'fieldPrefix' => '',
    
'checkedByDefault' => '0',
    
'description' => '',
    
'checkedValue' => 'Yes',
    
'uncheckedValue' => 'No',
  ),
  
'remove' => array(
    
'order' => 8,
    
'label' => 'Remove',
    
'type' => 'checkbox',
    
'fieldPrefix' => '',
    
'checkedByDefault' => '0',
    
'description' => '',
    
'checkedValue' => 'Yes',
    
'uncheckedValue' => 'No',
  ),
  
'first_name' => array(
    
'order' => 9,
    
'label' => 'First Name',
    
'type' => 'textfield',
    
'defaultValue' => '',
    
'fieldPrefix' => '',
    
'description' => '',
    
'fieldWidth' => '',
    
'isPasswordField' => '0',
    
'isRequired' => '0',
    
'isUnique' => '0',
    
'minLength' => '',
    
'maxLength' => '',
    
'charsetRule' => '',
    
'charset' => '',
  ),
  
'last_name' => array(
    
'order' => 10,
    
'label' => 'Last Name',
    
'type' => 'textfield',
    
'defaultValue' => '',
    
'fieldPrefix' => '',
    
'description' => '',
    
'fieldWidth' => '',
    
'isPasswordField' => '0',
    
'isRequired' => '0',
    
'isUnique' => '0',
    
'minLength' => '',
    
'maxLength' => '',
    
'charsetRule' => '',
    
'charset' => '',
  ),
  
'email' => array(
    
'order' => 11,
    
'label' => 'Email',
    
'type' => 'textfield',
    
'defaultValue' => '',
    
'fieldPrefix' => '',
    
'description' => '',
    
'fieldWidth' => '',
    
'isPasswordField' => '0',
    
'isRequired' => '0',
    
'isUnique' => '0',
    
'minLength' => '',
    
'maxLength' => '',
    
'charsetRule' => '',
    
'charset' => '',
  ),
  
'source' => array(
    
'order' => 12,
    
'label' => 'Source',
    
'type' => 'list',
    
'fieldPrefix' => '',
    
'description' => '',
    
'isRequired' => '0',
    
'isUnique' => '0',
    
'listType' => 'pulldown',
    
'optionsType' => 'text',
    
'optionsText' => 'Exhibition
Meeting
Street Fair
Web Site',
  ),
);
?>


emailsignup.php

The code at the top of the page, above the head, after the records calls required for your site:


<?php
  // load records from 'email_signup'
  list($email_signupRecords$email_signupMetaData) = getRecords(array(
    
'tableName'   => 'email_signup',
    
'loadUploads' => true,
    
'allowSearch' => false,
  ));
?>
<?php $signup_email ' the_email_address_you_want_to_use_for_return_and_reply' ?>
<?php
if (@$_REQUEST['submit']) {
 
function 
validateGoogleCaptcha(){
    
    
$errorsAndAlerts "";

    if (!@
$_REQUEST['g-recaptcha-response'])     { $errorsAndAlerts .= "Please check the anti-spam 'I am not a robot'
checkbox!\n"
    
$showSignupForm true// don't change this value
    }
    else { 
      
// check recaptcha
      $postdata = array();
      
$postdata['secret']   = 'your Google Recaptcha secret code';
      
$postdata['response'] = @$_REQUEST['g-recaptcha-response'];
      
$postdata['remoteip'] = $_SERVER['REMOTE_ADDR'];
      
$url "https://www.google.com/recaptcha/api/siteverify?"http_build_query($postdata'''&');
      list(
$json$httpStatusCode$headers$request) = getPage($url5''true);
      
$recaptchaResponse json_decode($jsontrue);
      
      if (!
$recaptchaResponse['success']) {
        if (
is_array($recaptchaResponse['error-codes'])) { 
          if (
in_array('missing-input-secret'$recaptchaResponse['error-codes']))     { $errorsAndAlerts .= "There's a
problem with recaptcha, please let us know! (no secret)\n"; }
          if (
in_array('invalid-input-secret'$recaptchaResponse['error-codes']))     { $errorsAndAlerts .= "There's a
problem with recaptcha, please let us know! (invald secret)\n"; }
          if (
in_array('missing-input-response'$recaptchaResponse['error-codes']))   { $errorsAndAlerts .= "Please
fill out the recaptcha box!\n";
          
$showSignupForm true// do we need this line?
           }
          if (
in_array('invalid-input-response'$recaptchaResponse['error-codes']))   { $errorsAndAlerts .= "Please
fill out the recaptcha box again, your answer was incorrect!\n";
          
$showSignupForm true// do we need this line?
          }
        }
        if (!@
$errorsAndAlerts) { @$errorsAndAlerts .= "Invalid captcha response, please try again or contact us
directly and let us know."; }
        @
trigger_error("Failed recaptcha on signup form"E_USER_NOTICE);
      }
    }
    return 
$errorsAndAlerts;
  }
}
?>
<?php
// submit form
if (@$_REQUEST['submit']) {
 @
$errorsAndAlerts .= validateGoogleCaptcha();
  
// error checking

  if (!@$_REQUEST['first_name'])    { $errorsAndAlerts .= "Please enter your first name\n"; }
  if (!@
$_REQUEST['last_name'])  { $errorsAndAlerts .= "Please enter your last name\n"; }
    if (!@
$_REQUEST['email'])  { $errorsAndAlerts .= "Please enter your email address\n"; }

// email checking
    if ($_REQUEST['email'] || $_REQUEST['email2']) {
      if (!@
$_REQUEST['email'])                            { $errorsAndAlerts .= "Please enter your email address\n"; }
      elseif (!@
$_REQUEST['email2'])                            { $errorsAndAlerts .= "Please re-enter your email
address\n"; }
      elseif (
$_REQUEST['email'] != $_REQUEST['email2']) { $errorsAndAlerts .= "Sorry, the e mail addresses you entered
don't match!\n"; }
    }

 
// check for duplicate emails
    if (!$errorsAndAlerts) {
      
      
$count mysql_select_count_from('email_signup'"'".mysql_escape($_REQUEST['email'])."' IN (email)");
      if (
$count 0) { $errorsAndAlerts .= "That email address is already signed up, please choose another!\n"; }
    }

  
// turn off strict mysql error checking for: STRICT_ALL_TABLES
  mysqlStrictMode(false); // disable Mysql strict errors for when a field isn't defined below (can be caused when fields
are added later)

  
// add record
  if (!@$errorsAndAlerts) {
    
$tablename   'email_signup';
       
$colsToValues = array();
       
$colsToValues['createdDate=']     = 'NOW()';
       
$colsToValues['updatedDate=']     = 'NOW()';
       
$colsToValues['createdByUserNum'] = 0;
       
$colsToValues['updatedByUserNum'] = 0;
       
$colsToValues['first_name']         = $_REQUEST['first_name'];
       
$colsToValues['last_name']         = $_REQUEST['last_name'];
       
$colsToValues['email']            = $_REQUEST['email'];
       
$colsToValues['hidden'] = 1;
       
$hideMissingFieldErrors true;
       
$newRecordNum mysql_insert($tablename$colsToValues$hideMissingFieldErrors); 

    
// display thanks message and clear form
    $errorsAndAlerts "Thanks for submitting your information.  Before we can add your email address to our list,
you'll need to confirm your intent by clicking on the link in the email that you will receive shortly.  If you do not
see the email, check your spam folder.";

  
    
// send email to applicant    
    $to=$_REQUEST['email']; 
$subject 'Email List Signup Request';
$headers "From: $signup_email" "\r\n";
$headers .= "Reply-To: $signup_email" "\r\n";
$headers .= "MIME-Version: 1.0\r\n";
$headers .= "Content-Type: text/html; charset=ISO-8859-1\r\n";
$message '<html><body>';
$message .= '<table rules="all" style="border-color: #666;" cellpadding="10">';
$eml $_REQUEST['email'];
$message .= "<tr ><td><h2 align='center'>EMAIL LIST SIGNUP REQUEST</h2>There is just one more step to be included on our
email distribution list.To make sure that no one else signed you up for this list, please click on this link or paste it
into your browser.
 <a
href='http://your_site.com/confirmed.php?submit=1&confirmed=1&hidden=0&email=$eml'>http://your_site.com/confirmed.php?submit=1&confirmed=1&hidden=0&email=$eml</a></td></tr>";
$message .= "</table>";
$message .= "</body></html>";


// Send
if (mail($to,$subject,$message$headers))
{
 echo 
'Mail sent!';
} else
{
 echo 
'Error! Mail was not sent.';
};
  }

}

?>


And the active code in the body of the page:


 <table width="92%" border="0" align="center">
            <tr>
              <td valign="top"><form method="post" action="">
                  <input type="hidden" name="submit" value="1" />
                  <?php if (@$errorsAndAlerts): ?>
                  <div class="heading_font" align="left" style="color:#C00">
                    <?php echo $errorsAndAlerts?>
                    
                  </div>
                  <?php endif ?>
                  <table align="left"  border="0" cellspacing="0" cellpadding="2">
                    <tr>
                      <td class=" text_font" valign="top"><b>First Name</b></td>
                      <td><input type="text" name="first_name" value="<?php echo
htmlspecialchars(@$_REQUEST['first_name']) ?>" size="30" /></td>
                    </tr>
                    <tr>
                      <td class="text_font" valign="top"><b>Last Name</b></td>
                      <td><input type="text" name="last_name" value="<?php echo
htmlspecialchars(@$_REQUEST['last_name']) ?>" size="30" /></td>
                    </tr>
                    <tr>
                      <td class="text_font" valign="top"><b>Email Address</b></td>
                      <td><input type="text" name="email" value="<?php echo htmlspecialchars(@$_REQUEST['email']) ?>"
size="30" /></td>
                    </tr>
                    <tr>
                      <td class="text_font" valign="top"><b>Re-enter Your Email Address</b></td>
                      <td><input type="text" name="email2" value="<?php echo htmlspecialchars(@$_REQUEST['email2']) ?>"
size="30" /></td>
                    </tr>
                    <tr>
                      <td valign="top">&nbsp;</td>
                      <td>&nbsp;</td>
                    </tr>
                    <tr>
                      <td colspan="2" class="text_font" style=" font-weight: bold;" valign="top">Please check the "I'm
not a robot" box below before submitting.
                        
                        <div class="g-recaptcha" data-theme="light" data-sitekey="Your Google Recaptcha site
key"></div></td>
                    </tr>
                    <tr>
                      <td style="padding: 5px;"><input type="submit" name="add" value="Click To Submit &gt;&gt;" />
                        
                        </td>
                    </tr>
                  </table>
                </form></td>
            </tr>
          </table>


confirmed.php

The code at the top of the page, above the head, after the records calls required for your site:


<?php
// submit form
if (@$_REQUEST['submit']) {

  
// error checking
  $errorsAndAlerts "";
  if (!@
$_REQUEST['email'])  { $errorsAndAlerts .= "Please enter the email address you used when you signed up.\n"; }

   
// turn off strict mysql error checking for: STRICT_ALL_TABLES
  mysqlStrictMode(false); // disable Mysql strict errors for when a field isn't defined below (can be caused when fields
are added later)
 
 
// update user
   if (!$errorsAndAlerts) {
    
    
$emailExists        mysql_count('email_signup',         ['email' => $_REQUEST['email']]);
$emailConfirmed     mysql_count('email_signup', ['email' => $_REQUEST['email'], 'confirmed' => 1]);
$userNum            0;
if (
$emailExists && !$emailConfirmed) {
  
$updateNum    null;
  
$updateWhere  = [ 'email' => $_REQUEST['email'] ];
  
$colsToValues = [ 'hidden' => 0'confirmed' => '1''updatedDate=' => 'NOW()' ];
  
mysql_update('email_signup'$updateNum$updateWhere$colsToValues);
  
$userNum mysqli()->insert_id;
$errorsAndAlerts "Thanks, your email address has been succesfully added to our maillist";
    
}
    elseif(
$emailExists && $emailConfirmed){//email exists, but has already been confirmed
      $errorsAndAlerts.="That Email address has already been confirmed.If you'd like to sign up with another Email
address,<a  href='http://your_site.com/email_signup.php'><span class='heading_font' >CLICK ON THIS LINK</span></a>";
    }
    
     elseif (!
$emailExist){ //email does not exist in the database
    $errorsAndAlerts.="Sorry, that email address doesn't exist in the database.Please enter the same Email address that you
used when you signed up, and click on submit.";
      }
       
}}
  
?>


And the active code in the body of the page:


 <form method="post" action="">
        <input type="hidden" name="submit" value="1" />
        
        <table width="90%" border="0" cellpadding="5" cellspacing="0">
          <tr>
            <td valign="top">&<?php if (@$errorsAndAlerts): ?>
        <div class="text_font" align="left" style="color: #C00; font-weight: bold;">
          <?php echo $errorsAndAlerts?>
        </div>
        <?php endif ?></td>
            <td>&nbsp;</td>
          </tr>
          <tr>
            <td align="left" class="text_font" valign="top"><b>If you see an error above, it's probably because <input
type="text" name="email" value="<?php echo htmlspecialchars(@$_REQUEST['email']) ?>" size="30" /> is not the Email
address that you used when you signed up.
              
              Please change it and click on submit.</b></td>
            <td>&nbsp;</td>
          </tr>
          <tr>
            <td valign="top">&nbsp;</td>
            <td>&nbsp;</td>
          </tr>
        </table>
        <input type="submit" name="add" value="Click to Submit &gt;&gt;" />
      </form>
      <p align="left" class="text_font">
        
      If you no longer want to receive information about us, <a style="text-decoration:underline; color:#000"
href="http://www.yoursite.com/unsubscribe.php"><span class="text_font">CLICK/TAP HERE</span></a> to unsubscribe.</p>


unsubscribe.php

The code at the top of the page, above the head, after the records calls required for your site:


<?php
  // load records from 'email_signup'
  list($email_signupRecords$email_signupMetaData) = getRecords(array(
    
'tableName'   => 'email_signup',
    
'loadUploads' => true,
    
'allowSearch' => false,
  ));
?>
<?php $signup_email 'the_email_address_you_want_to_use_for_return_and_reply' // the email to use for return and
reply?>
<?php
if (@$_REQUEST['submit']) {
 
function 
validateGoogleCaptcha(){
    
    
$errorsAndAlerts "";

    if (!@
$_REQUEST['g-recaptcha-response'])     { $errorsAndAlerts .= "Please check the anti-spam 'I am not a robot'
checkbox!\n"
    
$showSignupForm true// don't change this value
    }
    else { 
      
// check recaptcha
      $postdata = array();
      
$postdata['secret']   = 'your Google Recaptcha secret code';
      
$postdata['response'] = @$_REQUEST['g-recaptcha-response'];
      
$postdata['remoteip'] = $_SERVER['REMOTE_ADDR'];
      
$url "https://www.google.com/recaptcha/api/siteverify?"http_build_query($postdata'''&');
      list(
$json$httpStatusCode$headers$request) = getPage($url5''true);
      
$recaptchaResponse json_decode($jsontrue);
      
      if (!
$recaptchaResponse['success']) {
        if (
is_array($recaptchaResponse['error-codes'])) { 
          if (
in_array('missing-input-secret'$recaptchaResponse['error-codes']))     { $errorsAndAlerts .= "There's a
problem with recaptcha, please let us know! (no secret)\n"; }
          if (
in_array('invalid-input-secret'$recaptchaResponse['error-codes']))     { $errorsAndAlerts .= "There's a
problem with recaptcha, please let us know! (invald secret)\n"; }
          if (
in_array('missing-input-response'$recaptchaResponse['error-codes']))   { $errorsAndAlerts .= "Please
fill out the recaptcha box!\n";
          
$showSignupForm true// do we need this line?
           }
          if (
in_array('invalid-input-response'$recaptchaResponse['error-codes']))   { $errorsAndAlerts .= "Please
fill out the recaptcha box again, your answer was incorrect!\n";
          
$showSignupForm true// do we need this line?
          }
        }
        if (!@
$errorsAndAlerts) { @$errorsAndAlerts .= "Invalid captcha response, please try again or contact us
directly and let us know."; }
        @
trigger_error("Failed recaptcha on signup form"E_USER_NOTICE);
      }
    }
    return 
$errorsAndAlerts;
  }
}
?>
<?php $redirect '0' // set a variable called $redirect to a value of 0 ?>
<?php if(@$_REQUEST['submit']):?>
<?php 
    $errorsAndAlerts "";
    
$errorsAndAlerts .= validateGoogleCaptcha();
    
 

  if (!@
$_REQUEST['email'])  { $errorsAndAlerts .= "Please enter your email address\n"; }

// email checking
    if ($_REQUEST['email'] || $_REQUEST['email2']) {
      if (!@
$_REQUEST['email'])                            { $errorsAndAlerts .= "Please enter your email address\n"; }
      elseif (!@
$_REQUEST['email2'])                            { $errorsAndAlerts .= "Please re-enter your email
address\n"; }
      elseif (
$_REQUEST['email'] != $_REQUEST['email2']) { $errorsAndAlerts .= "Sorry, the e mail addresses you entered
don't match!\n"; }
    }
    
 
// check for existing emails
   if (!$errorsAndAlerts) {
      
      
$count mysql_select_count_from('email_signup'"'".mysql_escape($_REQUEST['email'])."' IN (email)");
         if (
$count 1) { $redirect '1'// if no matching email address change the variable $redirect to a value of
1
         }
    }

  
// turn off strict mysql error checking for: STRICT_ALL_TABLES
  mysqlStrictMode(false); // disable Mysql strict errors for when a field isn't defined below (can be caused when fields
are added later
   
  
?>
<?php  
  // error checking
  if (!@$errorsAndAlerts && $redirect == '0') {
    

    
// display sorry message and clear form
    $errorsAndAlerts "We'll be sorry to see you go.To make sure that no one else is trying to remove your email
address from our list, you'll need to confirm your intent by clicking on the link in the email that you will receive
shortly.  If you don't see the email, check your spam folder.";

    
// send email to applicant    
    $to=$_REQUEST['email']; 
$subject 'Email List Removal Request';

$headers "From: $signup_email" "\r\n";
$headers .= "Reply-To: $signup_email" "\r\n";
$headers .= "MIME-Version: 1.0\r\n";
$headers .= "Content-Type: text/html; charset=ISO-8859-1\r\n";
$message '<html><body>';
$message .= '<table rules="all" style="border-color: #666;" cellpadding="10">';
$eml $_REQUEST['email'];
$message .= "<tr ><td><div align='left'><img src='http://www.your_site.com/images/email-masthead-400px.png'
style='border:hidden;'/></div><h2 align='center'>EMAIL LIST LIST REMOVAL REQUEST</h2>We're sorry to see you go.There's
just one more step to be removed from our email distribution list.To make sure that no one else is trying to remove you
from this list, please click on this link or paste it into your browser.
 <a
href=http:/your_site.com/unsubscribe_confirm.php?submit=1&remove_me=1&email=$eml'>http://your_site.com/unsubscribe_confirm.php?submit=1&remove=1&email=$eml</a></td></tr>";
$message .= "</table>";
$message .= "</body></html>";

// Send
if (mail($to,$subject,$message$headers))
{
 echo 
'Mail sent!';
} else
{
 echo 
'Error! Mail was not sent.';
};
  }
?>
<?php if ($redirect == '1'):?>
<?php

header("Location:http://your_site.com/your_unsubscribe_contact_page.php");

exit;
?>
<?php endif ?>
<?php endif?>


And the active code in the body of the page:


<table width="100%"   border="0" align="center" cellpadding="10">
        <tr>
          <td valign="top"><div align="center" class="heading_font">EMAIL LIST UNSUBSCRIBE
            </div>
            <div align="center" style="width:80%; text-align:left">
            <div align="center">
              <p class="heading_font">Please Use The Form Below
                To Unsubscribe From Our Email List </p>
            </div>
            <form method="post" action="">
              <input type="hidden" name="submit" value="1" />
              <?php if (@$errorsAndAlerts): ?>
              <div align="left" style="color: #C00; font-family: Verdana, Arial, Helvetica, sans-serif; font-weight:
bold; font-size: 15px;">
                <?php echo $errorsAndAlerts?>
                
              </div>
              <?php endif ?>
              <table align="left"  border="0" cellspacing="0" cellpadding="2">
                <tr>
                  <td class="Medium-Text" valign="top"><b>Enter The Email Address
                    To Be Removed</b></td>
                  <td><input type="text" name="email" value="<?php echo htmlspecialchars(@$_REQUEST['email']) ?>"
size="30" /></td>
                </tr>
                <tr>
                  <td class="Medium-Text" valign="top"><b>Re-enter The Email Address</b></td>
                  <td><input type="text" name="email2" value="<?php echo htmlspecialchars(@$_REQUEST['email2']) ?>"
size="30" /></td>
                </tr>
                <tr>
                  <td colspan="2" class="text_font" style="color: #<?php echo
$site_colorsRecord['menu_background_color'?>; font-weight: bold;" valign="top">Please check the "I'm not a robot" box
below before submitting.
                    
                    <div class="g-recaptcha" data-theme="light" data-sitekey="your_Google Recaptcha site
key"></div></td>
                </tr>
                <tr>
                  <td colspan="2" valign="top">
                    <input type="submit" name="submit" value="Click To Submit &gt;&gt;" /></td>
                </tr>
              </table>
            </form></td>
        </tr>
      </table>


unsubscribe_confirm.php

The code at the top of the page, above the head, after the records calls required for your site:


<?php
// submit form
if (@$_REQUEST['submit']) {

  
// error checking
  $errorsAndAlerts "";
  if (!@
$_REQUEST['email'])  { $errorsAndAlerts .= "Please enter the email address you used when you signed up.\n"; }

   
// turn off strict mysql error checking for: STRICT_ALL_TABLES
  mysqlStrictMode(false); // disable Mysql strict errors for when a field isn't defined below (can be caused when fields
are added later)
 
 
// update user
   if (!$errorsAndAlerts) {
    
    
$emailExists        mysql_count('email_signup',         ['email' => $_REQUEST['email']]); //check to ensure that
email exists in the table
$emailRemoved     mysql_count('email_signup', ['email' => $_REQUEST['email'], 'remove' => 1]); // check to ensure that
email exists and has not been removed yet
$userNum            0;
if (
$emailExists && !$emailRemoved) {
  
$updateNum    null;
  
$updateWhere  = [ 'email' => $_REQUEST['email'] ];
  
$colsToValues = [ 'hidden' => 1'remove' => '1''updatedDate=' => 'NOW()' ];
  
mysql_update('email_signup'$updateNum$updateWhere$colsToValues);
  
$userNum mysqli()->insert_id;
$errorsAndAlerts "Thanks, your email address has been successfully removed from our maillist.To sign up again, <a
style='text-decoration:underline; color:#C00;' href='http://www.your_site.com/email_signup.php'><span
class='text_font'><font color='#C00'>CLICK HERE</font></span></a> for our email sign up page.";
    
}
    elseif(
$emailExists && $emailRemoved){//email exists, but has already been removed
      $errorsAndAlerts.="That Email address has already been removed.To remove another address, <a
style='text-decoration:underline; color:#C00;' href='http://www.your_site.com/unsubscribe.php'><span
class='text_font'><font color='#C00'>CLICK HERE</font></span></a> to return to our unsubscribe page.";
    }
    
     elseif (!
$emailExist){ //email does not exist in the database
    $errorsAndAlerts.="Sorry, that email address doesn't exist in the database.>To be removed from our list, <a
style='text-decoration:underline; color:#C00;' href='http://www.your_site.com/unsubscribe.php'><span
class='text_font'><font color='#C00'>CLICK HERE</font></span></a> to return to our unsubscribe page and enter the email
address that you used when you signed up.";
      }
       
}}
  
?>


And the active code in the body of the page:


<div align="center" class="heading_font">
        <h2>Email List Unsubscribe Confirmation</h2>
      </div>
      <table width="900px"   border="0" align="center" cellpadding="10">
        <tr>
          <td class="heading_font" ><?php if (@$errorsAndAlerts): ?>
            <div align="left" style="color: #C00; font-family: Verdana, Arial, Helvetica, sans-serif; font-weight: bold;
font-size: 15px;">
              <?php echo $errorsAndAlerts?>
              
            </div>
            <?php endif ?>
            
            </td>
        </tr>
      </table>


UNSUBSCRIBE FROM YOUR SIGN-UP LIST AUTOMATICALLY - Mar 7th, 2013

This double opt out variation on the sign-up theme (See the recipe on DOUBLE OPT IN MAIL LIST IN CMSB ) automatically
removes a subscriber from your email list. It requires that you create a “remove” check box field in your sign-up
editor (mine is called newsletter_signup) and that you use an if statement to  check for the value of that field when
outputting your list. 

I.E.:


<?php if  ($record['hidden'] == && $record['confirmed'] == && $record['remove'] == 0): ?>
                   <?php echo $record['email'?>;&nbsp;
                  <?php endif ?>


There are 2 viewers required as well. An “unsubscribe” viewer and a “confirmation” viewer

When a subscriber wants to unsubscribe, they enter their email address in an “unsubscribe” form and to help prevent
spam, they’re required to fill in a “captcha” field. (See the recipe on IMPLEMENTING CAPTCHA ON THE WEBSITE
MEMBERSHIP PLUGIN 1.04+ SIGNUP FORM) 

The form sends an email to that email address, with a confirmation link that the subscriber has to click (or tap) on to
complete the unsubscribe process. 

The confirmation page uses error correction to see if the email address exists in the database and if it does, a
“remove” check box field is “checked” and a confirmation message is presented to the visitor along with a link
to the subscription form. If the box is already checked, an message to that effect is returned along with a link back to
the unsubscribe form.    

Here’s the active code for the unsubscribe viewer:
TOP OF THE PAGE
Note: change the path to your viewer and any required load records calls at the head of your page, and the domain name
in the // send email to applicant section.


<?php
include_once('captcha/captchac_lib.php');  
session_write_close ();

if (@
$_REQUEST['submit']) {
  
$errorsAndAlerts "";
    if (!@
$_REQUEST['Turing'])    {$errorsAndAlerts .= "To help prevent spam, you must enter the characters in the image
into the empty box below before submitting.\n";}
    else  
    {
         
$Turing_code $_REQUEST["Turing"];   
        if ( 
CheckCaptcha($Turing_code) !=)  {  $errorsAndAlerts .= "The characters you entered do not match those
displayed. Please try again.\n";}
    
    }
}
?>
<?php 
 // load viewer library
  $libraryPath 'cmsAdmin/lib/viewer_functions.php';
  
$dirsToCheck = array('/path_to_your_server/','','../','../../','../../../');
  foreach (
$dirsToCheck as $dir) { if (@include_once("$dir$libraryPath")) { break; }}
  if (!
function_exists('getRecords')) { die("Couldn't load viewer library, check filepath in sourcecode."); }

// any required load records calls go here

 ?>

<?php
// submit form
if (@$_REQUEST['submit']) {

  
// error checking

    if (!@$_REQUEST['email'])  { $errorsAndAlerts .= "Please enter your email address\n"; }

// email checking
    if ($_REQUEST['email'] || $_REQUEST['email2']) {
      if (!@
$_REQUEST['email'])                            { $errorsAndAlerts .= "Please enter your email address\n"; }
      elseif (!@
$_REQUEST['email2'])                            { $errorsAndAlerts .= "Please re-enter your email
address\n"; }
      elseif (
$_REQUEST['email'] != $_REQUEST['email2']) { $errorsAndAlerts .= "Sorry, the e mail addresses you entered
don't match!\n"; }
    }

 
// check fo rexisting emails
    if (!$errorsAndAlerts) {
      
      
$count mysql_select_count_from('newsletter_signup'"'".mysql_escape($_REQUEST['email'])."' IN (email)");
      if (
$count 1) { $errorsAndAlerts .= "We can't find that email address in our list. Please enter the email
address that you signed up with and try again.\n"; }
    }

  
// turn off strict mysql error checking for: STRICT_ALL_TABLES
  mysqlStrictMode(false); // disable Mysql strict errors for when a field isn't defined below (can be caused when fields
are added later)

  if (!@
$errorsAndAlerts) {

    
// display thanks message and clear form
    $errorsAndAlerts "We'll be sorry to see you go.To make sure that no one else is trying to remove your email
address from our list, you'll need to confirm your intent by clicking on the link in the email that you will receive
shortly.  If you don't see the email, check your spam folder.";

    
// send email to applicant    
    $sendto=$_REQUEST['email']; 
$subject='We\'re sorry to see you go.';
$header "From: newsletter@".$_SERVER["SERVER_NAME"]."\n";
$header .= "Content-Type: text/html; charset=iso-8859-1\n";
$message ' Thanks.We\'re sorry to see you go.There\'s just one more step to be removed from our newsletter
distribution list.To make sure that no one else is trying to remove you from this list, please click on this link or
paste it into your browser.
 http://www.your_domain.com/unsubscribe_confirm.php?submit=1&remove=1&email='.$_REQUEST['email'];'';

$_REQUEST = array();
// Send
if (mail($sendto,$subject,$message,$header"-fwebmaster@".$_SERVER["SERVER_NAME"]))
{
 echo 
'Mail sent!';
} else
{
 echo 
'Error! Mail was not sent.';
};
  }

}

?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">


BODY OF THE PAGE


<table    align="center" cellpadding="0">
     
        <tr>
    <td >
      <div align="center">
You'll receive an email from us to confirm your intention to unsubscribe.
To make sure that no one else is trying to remove you from our list, you'll need to click on the link in that email.

</div>
<table width="72%"  align="center">
  
  <tr>
    <td valign="top">
      
     <form method="post" action="">
<input type="hidden" name="submit" value="1" />



<?php if (@$errorsAndAlerts): ?>
  <div align="left" style="color: #FFFF00; font-family: Verdana, Arial, Helvetica, sans-serif; font-weight: bold;
font-size: 15px;">
    <?php echo $errorsAndAlerts?>
  </div>
<?php endif ?>


<table align="left"   cellspacing="0" cellpadding="2">
 <tr>
  <td valign="top"><b>Enter the email address that you'd like to removefrom our list</b></td>
  <td><input type="text" name="email" value="<?php echo htmlspecialchars(@$_REQUEST['email']) ?>" size="30" /></td>
</tr>
<tr>
  <td valign="top"><b>Re-enter the email address</b></td>
  <td><input type="text" name="email2" value="<?php echo htmlspecialchars(@$_REQUEST['email2']) ?>" size="30" /></td>
</tr>
<tr>
  <td valign="top">&nbsp;</td>
  <td>&nbsp;</td>
</tr>
<tr>
                     <td  valign="top"><b>CAPTCHA Image Verification</b>
                    
                     To help prevent spam, type the characters in the image into the empty box below  before submitting.
                     
                    <a href="/captcha/whatisturing.html"
onclick="window.open('/captcha/whatisturing.html','_blank','width=300, height=300, left=' + (screen.width-450) + ',
top=100');return false;">WHAT'S CAPTCHA?</a></td>
                     <td>
                     
                     <table cellpadding=5 cellspacing=0 >


<tr>
<td style="padding: 5px;" width="300" height="100"><img src="/captcha/captchac_code.php" id="captcha">
<a class="text_font" href="#" onclick=" document.getElementById('captcha').src = document.getElementById('captcha').src
+ '?' + (new Date()).getMilliseconds()">
</a></td>
</tr>
<tr>
  <td style="padding: 5px;"><input type="text" name="Turing" value="" maxlength="100" size="20" height="50" />
   
    <b>Enter Characters Here</b >
       </td>
</tr>
<tr>
  <td style="padding: 5px;"><input type="submit" name="add" value="Click To Submit &gt;&gt;" /><a class="text_font"
href="#" onclick=" document.getElementById('captcha').src = document.getElementById('captcha').src + '?' + (new
Date()).getMilliseconds()">HAVING TROUBLE READING
    THE CHARACTERS DISPLAYED?
    CLICK HERE TO CHANGE THEM.</a></td>
</tr>
          </table>
</td></tr>
</table></form></td></tr></table></td>
  </tr>
</table>


And here’s the code for the confirmation viewer (called unsubscribe_confirm.php)
NOTE: This approach assumes that all email addresses will always be drawn from your database. If your implementation
will be drawing email addresses form other sources in addition to the database, see the recipe called HANDLING
UNSUBSCRIBE REQUESTS FROM EMAILS NOT IN YOUR DATABASE

TOP OF THE PAGE


<?php header('Content-type: text/html; charset=utf-8'); ?>
<?php
  

  // load viewer library
  $libraryPath 'cmsAdmin/lib/viewer_functions.php';
  
$dirsToCheck = array('/path_to_your_server/','','../','../../','../../../');
  foreach (
$dirsToCheck as $dir) { if (@include_once("$dir$libraryPath")) { break; }}
  if (!
function_exists('getRecords')) { die("Couldn't load viewer library, check filepath in sourcecode."); }

// any required load records calls go here

 ?>
<?php
// submit form
if (@$_REQUEST['submit']) {

  
// error checking
  $errorsAndAlerts "";
  if (!@
$_REQUEST['email'])  { $errorsAndAlerts .= "Please enter the email address you used when you signed up.\n"; }

   
// turn off strict mysql error checking for: STRICT_ALL_TABLES
  mysqlStrictMode(false); // disable Mysql strict errors for when a field isn't defined below (can be caused when fields
are added later)
 
 
// update user
   if (!$errorsAndAlerts) {
    
      
$where "email='".mysql_escape($_REQUEST['email'])."'"
     
      if(
mysql_select_count_from('newsletter_signup',$where)){ //check to ensure that email exists in the table
    $where.=" AND remove='1'";
    if(!
mysql_select_count_from('newsletter_signup',$where)){ //email exists and has not been removed yet
        $query "UPDATE `{$TABLE_PREFIX}newsletter_signup` SET 
              remove           = '1', 
              updatedDate      = NOW() 
              WHERE email = '".mysql_escape$_REQUEST['email'] )."'"
        
mysql_query($query) or die("MySQL Error:\n"htmlspecialchars(mysql_error()) . "\n"); 
        
$userNum mysql_insert_id(); 
        
        
// on success 
        $errorsAndAlerts "Thanks, your email address has been successfully removed from our maillist.To sign up again, <a
href='http://www.your_domain.com/email_signup_web.php'><span style='color: #FFFF00; font-family: Verdana, Arial,
Helvetica, sans-serif; font-weight: bold; font-size: 15px;'>CLICK HERE</span></a> for our Newsletter subscription
page.";
    }
    else{
//email exists, but has already been removed
      $errorsAndAlerts.="That Email address has already been removed.To remove another address, <a
href='http://www.your_domain.com/unsubscribe.php'><span style='color: #FFFF00; font-family: Verdana, Arial, Helvetica,
sans-serif; font-weight: bold; font-size: 15px;'>CLICK HERE</span></a> to return to our unsubscribe page.";
    }
      }
      else{ 
//email does not exist in the database
    $errorsAndAlerts.="Sorry, that email address doesn't exist in the database.To be removed from out list, <a
href='http://www.your_domain.com/unsubscribe.php'><span style='color: #FFFF00; font-family: Verdana, Arial, Helvetica,
sans-serif; font-weight: bold; font-size: 15px;'>CLICK HERE</span></a> to return to our unsubscribe page and enter the
email address that you used when you signed up.";
      }
     
    }
  }

?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">


And in the Body:


<table width="40%"  align="center">
  
  <tr>
    <td valign="top">

<?php if (@$errorsAndAlerts): ?>
  <div align="left" style="color: #FFFF00; font-family: Verdana, Arial, Helvetica, sans-serif; font-weight: bold;
font-size: 15px;">
    <?php echo $errorsAndAlerts?>
  </div>
<?php endif ?>

</td>
  </tr>
</table>


HANDLING UNSUBSCRIBE REQUESTS FROM EMAILS NOT IN YOUR DATABASE - Aug 25th, 2014

There are situations where the subscriber that wants to unsubscribe may not have come from the email list or media
distribution list databases. If this fits your client’s situation, you might want to redirect subscribers with emails
not found to a contact form instead of just a "re-enter the email address you used when you signed up" message.

To accomplish this, you’ll have to change code in your unsubscribe form viewer and then create a contact form viewer.

THE UNSUBSCRIBE FORM VIEWER

In the code of  your unsubscribe form viewer:


1) insert this code immediately after your load records code block

<?php $redirect '0' // set a variable called $redirect to a value of 0 ?>


2) In the // check for existing emails section change: (change ‘newsletter_signup’ to the table name where you store
your list)


 if (!$errorsAndAlerts) {
    $count = mysql_select_count_from('newsletter_signup', "'".mysql_escape($_REQUEST['email'])."' IN (email)");
      if ($count < 1) { $errorsAndAlerts .= "We can't find that email address in our list. Please enter the email
address that you signed up with and try again. <br /> <br /> \n"; }
    }


To:


if (!$errorsAndAlerts) {
   $count = mysql_select_count_from('newsletter_signup', "'".mysql_escape($_REQUEST['email'])."' IN (email)");
      if ($count < 1) { $redirect = '1'; // if no matching email address change the variable $redirect to a value of 1
}
    }


3) To the // error checking if statement change:


if (!@$errorsAndAlerts) {


to this:


if (!@$errorsAndAlerts && $redirect == '0') // Sends email only if there are no errors and there's a matching email
address in the database
{


4) CAVEAT: Add this redirect code Before the DOCTYPE declaration to redirect the visitor to your contact form viewer if
there’s no matching email in the database. It must be before any HTML tag or you'll get errors.


<?php if ($redirect == '1'):?>
<?php

header("Location:your_contact_form_viewer.php");

exit;
?>
<?php endif ?>
 

 
THE CONTACT FORM VIEWER.

The form can be created in any email form generator. I’m using the free Forms Generator that was put together as a
SourceForge project (http://phpfmg.sourceforge.net/ ). There’s an implementation recipe called ANOTHER (FREE) PHP
FORMS GENERATOR THAT ALLOWS ATTACHMENTS

The active code in my viewer puts the form in an iframe, but you can use any form or method that works for your
application:

 <table border="0">
<tr>
<td> <div align="left" >
<span class="heading_font">You'll need to submit this contact form to let me knowthat you want to unsubscribe from my
email list.</span> 
<br /> <br />
<span class=" text_font">I'm sorry that you've decided not to receive my updates in the future.I hope that you'll let me
know why you're unsubscribing. <br /></span>
<br />
<iframe frameborder="none" src="http://www.my_site.com/contact/form.php" style="width:420px;height:420px;border:none;"
Allowtransparency="true"></iframe>
 </td>
</tr>        
</table> 


OFFERING PRIVATE EMAIL LIST CAPTURE TO YOUR MEMBERS - Jun 22nd, 2011

Want to allow your members to create independent, private, double opt in email lists.

It’s easier than you think.

Building on two recipes, LIMITING ACCESS TO RECORDS BY AUTHOR and DOUBLE OPT IN MAIL LIST IN CMSB, here’s how:

CONCEPT:
In my situation, the visitor was looking at the detail page of a record that was created by specific member. That page
also contained my email signup form.

The process I used was to query the createdByUserNum field of that detail page record, and use that value to populate
the createdByUserNum field of my “email_signup” table.

To display only the email addresses that belong to that member on an email list page, I restricted the records displayed
to only the email address records that were tagged with the member’s user number.

CODE CHANGES
First, create an email_signup table following he instructions in the DOUBLE OPT IN MAIL LIST IN CMSB recipe.

Then add the active error checking and execution code, and the email signup form to the detail page referenced above.

On that detail page, above the error correction and execution code add the following to define the variable: $owner :



<?php $owner $your_detail_page_tableRecord['createdByUserNum'?>


And in the //add record section, change



 createdByUserNum = '0', 


to:


 createdByUserNum = '$owner',


Now, each email_signup table record will be tagged with the user number of the member who created the record displayed
on the detail page.

The last part is to limit the email addresses displayed to only those email_signup records attributed to  that member.

Here are the simple changes to the email list display page described in the DOUBLE OPT IN MAIL LIST IN CMSB recipe.

In both foreach loops, add:



<?php if ($record['createdByUserNum']== @$CURRENT_USER['num']): ?>


Below the:



<?php if  ($record['hidden'] == && $record['confirmed'] == 1): ?>


And add an <?php endif ?> after each existing <?php endif ?> 

That should do it.


MERGE/PURGE NEWSLETTER MAIL LISTS WITH CMSB AND THE WEBSITE MEMBERSHIP PLUGIN - Aug 18th, 2011

In the recipe above, an organization has a newsletter signup form on their site that automatically populates a CMSB
multi record database called “Newsletter Signup”. (The Newsletter Signup database has only 4 fields: Hidden, First
Name, Last Name, and Email) 

When a subscriber joins the organization they use the membership module to automatically create a record in the
“accounts” database. After the new member has been approved (by checking an “approved” checkbox in the accounts
database) , they want to be able to remove their e-mail address from the Newsletter Signup e-mail list. They also want
to be able to create reports that show who joined after getting the newsletter, the date they subscribed, and the date
they joined. 

Jason Sauchuk from Interactive tools was up to the challenge.

He came up with the original code to compare the e-mail addresses in both the” Accounts” and the “Newsletter
Signup” database, and when there was a match, to hide the record in the Newsletter Signup database.

Here’s that code:

At the top of the page:



<?php header('Content-type: text/html; charset=utf-8'); ?>
<?php

  require_once "/path_to_your/cmsAdmin/lib/viewer_functions.php";

  list(
$newsletter_signupRecords$newsletter_signupMetaData) = getRecords(array(
    
'tableName'   => 'newsletter_signup',
    
  ));

?>



And in the body:



<?php foreach($newsletter_signupRecords as $record){ 
     
  
$where "email = '".mysql_escape($record['email'])."' AND approved=1";
   
  if(
mysql_select_count_from('accounts',$where)){ 
     
      
$query "UPDATE `{$TABLE_PREFIX}newsletter_signup` SET hidden = 1 WHERE num =".intval($record['num']) ;

     
mysql_query($query) or die("MySQL Error:".mysql_error()."<br />\n"); 
  }
  
}
?> 



When I added reporting code into the body, my first approach was to add this where statement to the top of the page:



list($newsletter_signupRecords, $newsletter_signupMetaData) = getRecords(array(
    'tableName'   => 'newsletter_signup',
     'where'    => " hidden = '0' ", 
    'orWhere'    => " hidden = '1' ", 
  ));



And this code below the existing body code:



MEMBERS WHO HAVE NOT YET JOINED  <br /> 
<?php foreach ($newsletter_signupRecords as $record): ?><?PHP if ($record['hidden'] == 0): ?> 
<?php echo $record['last_name'?>, <?php echo $record['first_name'?>
<br />
<?php echo $record['email'?>
<br />
Date first signed up: <?php echo date("D jS, M  Y "strtotime($record['createdDate'])) ?>
<hr>   <?php endif ?>
<?php endforeach ?>

<br /><br />MEMBERS WHO JOINED AFTER GETTING THE NEWSLETTER<br />
 <?php foreach ($newsletter_signupRecords as $record): ?><?PHP if ($record['hidden'] == 1): ?> 
<?php echo $record['last_name'?>, <?php echo $record['first_name'?>
<br />
<?php echo $record['email'?>
<br />
Date first signed up: <?php echo date("D jS, M  Y "strtotime($record['createdDate'])) ?>
<br />
Date joined: <?php echo date("D jS, M  Y "strtotime($record['updatedDate'])) ?>
<hr>   <?php endif ?>
<?php endforeach ?>



The reporting worked perfectly. The problem was that until the page was opened, and then refreshed, it did not reflect
the current changes to the information.

Again, Jason came to the rescue. He suggested that the easiest way to insure that the information was correct, was to
compile the report into 2 different variables while you're processing your records.  Then all you have to do is output
the results at the end.  This way you won't have to do a second database call to update the report.

Here’s the new code that he suggested for the body:



<?php $notConverted="";
      
$converted="";
?>

<?php foreach($newsletter_signupRecords as $record){ 
        
$where "email = '".mysql_escape($record['email'])."' AND approved=1"
   
  if(
mysql_select_count_from('accounts',$where)){ 
     
      
$converted.=$record['last_name'].",".$record['first_name']."<br />".$record['email']."<br />";
      
$query "UPDATE `{$TABLE_PREFIX}newsletter_signup` SET hidden = 1 WHERE num =".intval($record['num']) ;

     
mysql_query($query) or die("MySQL Error:".mysql_error()."<br />\n"); 
  }
  else{
    
$notConverted.=$record['last_name'].",".$record['first_name']."<br />".$record['email']."<br />";
  }
}
?>

Not Converted to Membership <br />
<?php echo $notConverted?>

Converted to Membership <br />
<?php echo $converted?>



IMPLEMENTING CAPTCHA ON THE WEBSITE MEMBERSHIP PLUGIN 1.04+ SIGNUP FORM - Aug 18th, 2011

NEW FOR MEMBERSHIP 1.04+ 

I wanted to use CAPTCHA validation on my membership signup form and although RE-CAPTCHA was pretty easy to implement, it
was a pretty intrusive addition to many web pages.  

I discovered another alternative in a program called Captcha Creator. http://www.captchacreator.com/  They’re based in
Romania and their support is a bit spottier then I’d like. (I’ve grown accustomed to the exceptional support offered
by Interactive Tools) But now that I’ve been able to implement their program, I can format the Captcha display to fit
my page design and it works like a charm. Again, their program is not free, and an unlimited use license costs $50.

Implementing the “out of the box” version of Captcha Creator on my membership signup form was throwing a bunch of
“session already started” errors. The programmers at Captcha Creator revised the way the code was implemented on my
page and also made some changes in the captchac_lib.php file and a few others.

*** After you’ve uploaded the “captcha” directory to your server, download the revised files from
http://www.thecmsbcookbook.com/downloads/captcha_creator.zip and replace the ones in your “captcha” directory with
those.

On my existing membership signup form I changed CAPTCHA CREATOR’s Installation Step 2 code to better suit my design.

Instead of: 



<table cellpadding=5 cellspacing=0 bgcolor="#E4F8E4">
<tr bgcolor="#AAD6AA">
<td colspan="2"><font color="#FFFFFF" face="Verdana" size="2"><b>Image Verification</b></font></td>
</tr>
<tr>
<td style="padding: 2px;" width="10"><img src="/captcha/captchac_code.php" id="captcha"></td>
<td valign="top"><font color="#000000">Please enter the text from the image</font> &nbsp; <br /> <input type="text"
name="Turing" value="" maxlength="100" size="10">
[ <a href="#" onclick=" document.getElementById('captcha').src = document.getElementById('captcha').src + '?' + (new
Date()).getMilliseconds()">Refresh Image</a> ] [ <a href="/captcha/whatisturing.html"
onClick="window.open('/captcha/whatisturing.html','_blank','width=400, height=300, left=' + (screen.width-450) + ',
top=100');return false;">What's This?</a> ]
</td>
</tr>
</table>



I used:



 <table>
<tr>
<td  valign="top"><span class="body-text-bold">CAPTCHA Image Verification</span><br /><br />
    <span class="body-text">To help reduce spam, please type the characters in the image into the blank box before you
click on submit.<br />
    </span> <br />                     
    <a href="/captcha/whatisturing.html" onclick="window.open('/captcha/whatisturing.html','_blank','width=300,
height=300, left=' + (screen.width-450) + ', top=100');return false;">What is CAPTCHA?</a></td>
<td>
<table cellpadding=5 cellspacing=0 >
<tr>
<td style="padding: 5px;" width="300" height="100"><img src="/captcha/captchac_code.php" id="captcha"><br />
<a href="#" onclick=" document.getElementById('captcha').src = document.getElementById('captcha').src + '?' + (new
Date()).getMilliseconds()">Click to change the displayed characters</a><br /></td>
</tr>
<tr>
  <td style="padding: 5px;"><input type="text" name="Turing" value="" maxlength="100" size="20" />
    <br />
    <br />    </td>
</tr>
</table></td></tr>
</table>


To make this all work in Membership 1.04+, at the top of the viewer I called the CAPTCHA program and checked for errors
with:



<?php
include_once('captcha/captchac_lib.php');  
session_write_close ();

if (@
$_REQUEST['submit']) {
  
$errorsAndAlerts "";
    if (!@
$_REQUEST['Turing'])    {$errorsAndAlerts .= "To help prevent spam, you must enter characters in the image into
the empty box below before submitting.<br /> <br /> \n";}
    else  
    {
         
$Turing_code $_REQUEST["Turing"];   
        if ( 
CheckCaptcha($Turing_code) !=)  {  $errorsAndAlerts .= "The characters you entered do not match those
displayed. Please try again.<br /> <br /> \n";}
    
    }
}
?>
 

And in the error checking code below I removed the clear errorsAndAlerts code line:


  $errorsAndAlerts = "";

So that it only appeared once at the top of the viewer code. (Otherwise it will clear the captcha error messages
__


Don't forget to use "save" or "submit" consistently throughout your viewer code. I think you can use either one but I've
settled on submit.
__


*** LEGACY FOR MEMBERSHIP 1.03 AND EARLIER



In the error checking section of my page after:



 //error checking
$errorsAndAlerts = ""; 

if (!@$_REQUEST['username'])                { $errorsAndAlerts .= "You must choose a username!<br />\n"; }



I added this code:



if (!@$_REQUEST['Turing'])    {$errorsAndAlerts .= "You must enter the CAPTCHA code!<br />\n";}
else  
{
     $Turing_code = $_REQUEST["Turing"];   
    if ( CheckCaptcha($Turing_code) !=1 )  {  $errorsAndAlerts .= "The CAPTCHA code you entered does not match. Please try
again.<br />\n";}
}



For CAPTCHA CREATOR’s Installation Step 3 I replaced this:



<?php
    include('captcha/captchac_lib.php');   
    
$Turing_code $_REQUEST["Turing"]; 
    if ( 
CheckCaptcha($Turing_code) !=)
    {
        echo 
"<b><font color=red>The Captcha Code you entered is invalid. Please press the Back button of your browser
and try again</font></b>";
    return 
1;
    }
?>



With this:



<?php
include_once('captcha/captchac_lib.php');  
session_write_close ();
?>


So the complete code at the top of my viewer, above the error checking section looks like this:



<?php
include_once('captcha/captchac_lib.php');  
session_write_close ();
?>
<?php 
require_once "cmsAdmin/lib/viewer_functions.php";
 
?>
 <?php if (!@$GLOBALS['WEBSITE_MEMBERSHIP_PLUGIN']) { die("You must activate the Website Membership plugin before you
can access this page."); } ?>


***(FOR V2.06 AND ABOVE SEE BELOW)***
_

NOTE: If you’ve updated to V2.06 you’ve probably discovered that the error checking at the top of the lib/init.php
file looks different, and pages using Captcha Creator are throwing errors again.

Well, thanks to CMSB User Djulia, the fix is in.

He discovered that when the function session_write_close (); is correctly written into the getRecords code at the top pf
your page, the errors disappear, without the need to modify the lib/init.php file.

Calling the Captcha program first, then closing the session, then calling the CMSB viewer_function is the implementation
that worked for me:



<?php
include_once('captcha/captchac_lib.php');  
session_write_close ();
?>
<?php 

require_once "cmsAdmin/lib/viewer_functions.php";
 
 list(
$common_informationRecords$common_informationMetaData) = getRecords(array(
    
'tableName'   => 'common_information',
    
'where'       => whereRecordNumberInUrl(1),
    
'limit'       => '1',
  ));
  
$common_informationRecord = @$common_informationRecords[0]; // get first record

 ?>
<?php include_once "/path_to_your/spambot-email-protector.php" ?>  


END LEGACY SECTION


IMPLEMENTING GOOGLE’S “I’M NOT A ROBOT”, NO CAPTCHA, RECAPTCHA V3 IN A FORM - Aug 6th, 2022

With a lot of help from Greg Thomas and Dave Edis, here’s how to implement the Google’s new, “I’m Not A
Robot”, No CAPTCHA, reCAPTCHA in a CMSB Form.

You can download 2 sample viewers from http://thecmsbcookbook.com/downloads/google-robot-captcha.zip

Both incorporate the code required to implement reCAPTCHA.

One contains a very simple form, and demonstrates a bare bones implementation.
The other is the Sample User Signup Form viewer that's generated by the Website Membership Plugin. This viewer
demonstrates a more complex implementation with error checking.

The 5 step implementation for both viewers is the same.

1) Log in to https://www.google.com/recaptcha/admin#list 
2) Register your web site. (if you want to get alerts from Google if they detect problems with your site, such as
misconfiguration errors or an increase in suspicious traffic, leave the “get alerts” checkbox checked.)
3) A Site Key and Secret Key will be generated, copy them to a safe (secret) place.
4) Enter the 2 Keys as noted in the viewer code.
5) Enter your “Server Path” in the load viewer library code at the top of the viewer code.

You should be good to go.

Right now there are only a few “Themes” available to change the look of the check box. They are data-theme="dark"
and data-theme="light", and are changed in the <div> where you entered your site key.

You can learn more about Google’s No CAPTCHA reCAPTCHA at: https://developers.google.com/recaptcha/


IMPLEMENTING ASYNC TO DELAY THE LOADING REQUIREMENT OF THE GOOGLE RECAPTCHA V3 PLUGIN - Aug 6th, 2022

In August 2022, user JeffC  was having a bit of an issue implementing async with the Google Recaptcha V3 plugin, which
was slowing down the loading of his web pages when not deferred.

According to Daniel Louwe, Technical Lead at Interactive Tools, 
It appears that the script provided in Google's docs is slightly inaccurate - at least for our usage here - so I've made
a small change and it appears to be working with async. For anyone else reading, here is the final updated script:

    <script async src="https://www.google.com/recaptcha/api.js?render=<?php echo
urlencode(recaptcha_settings('site_key')); ?>"></script>

    <script>
        
      if(typeof grecaptcha === 'undefined') {
        grecaptcha = {};
      }
      grecaptcha.ready = function(cb){
        if(typeof grecaptcha.execute === 'undefined') {
          // window.__grecaptcha_cfg is a global variable that stores reCAPTCHA's
          // configuration. By default, any functions listed in its 'fns' property
          // are automatically executed when reCAPTCHA loads.
          const c = '___grecaptcha_cfg';
          window[c] = window[c] || {};
          (window[c]['fns'] = window[c]['fns']||[]).push(cb);
        } else {
          cb();
        }
      }
    
      grecaptcha.ready(function(){

          grecaptcha.execute('<?php echo urlencode(recaptcha_settings('site_key')); ?>', {action: <?php echo
json_encode(recaptcha_settings('page')); ?>}).then(function(token) {

            document.getElementById("g-recaptcha-response").value = token;

          });

      });

    </script>


DEDUCTING A "REGISTRATION FEE" FROM THE FIRST PAYMENT ONLY - Feb 18th, 2011

My client wants to charge a registration fee to create a database record for new potential students. Then she wants to
refund the registration fee (only once) if a student signs up and pays for a class.

Here’s the logic and the code required:

1) When the parent of a potential student wants to register, they first pay a small registration fee. (The registration
fee value is pulled from a field in a single record editor called “common_information”)  On submission of the
application, a checkbox in the accounts database called “ registration fee refund” is automatically  set to ‘1"
with this code which is added to the series of mysql querys in the signup form, under  “mysql_query("INSERT INTO
`{$TABLE_PREFIX}accounts` SET”:


registration_fee_refund     = '1',


2) When a parent goes to the pay tuition page, if the “registration_fee_refund” checkbox =1, the tuition fee
displayed reflects the refund using this code:

First, a note telling the parent that their registration fee will be deducted from their tuition price.


<?PHP if ($CURRENT_USER['registration_fee_refund'] == 1): ?><br />
        *** Your ONE TIME registration fee refund is reflected in the tuition prices below. ***<br /> <?PHP endif ?>


Then deduct the registration fee from the normal tuition fee with this code:

Step A) define a variable for the deposit amount using a 2 decimal place number format:



 <?php $deposit_amount =  number_format($common_informationRecord['deposit_amount'],2); ?>


Step B) If applicable, subtract the deposit amount from the normal tuition amount (pulled from a field in a multi record
editor called “tuition_fees”.


<?php foreach ($tuition_feesRecords as $record): ?>
<?PHP if ($CURRENT_USER['registration_fee_refund'] == 1): ?><?php echo number_format(($record['normal_tuition_amount'] -
$deposit_amount),2?><?php else:?><?php echo number_format($record['normal_tuition_amount'] ,2?> <?PHP endif ?>
<?php endforeach ?>


3) After payment, the parent is automatically redirected to a “thank you” page. When the “thank you” page loads,
if value of the refund registration fee field value is “1", the field is automatically reset to “0" 



 <?php if ($CURRENT_USER ['registration_fee_refund'] == 1): ?>   
    
     <?php mysqlStrictMode(false); 
      
$query "UPDATE `{$TABLE_PREFIX}accounts` SET
       registration_fee_refund  = 0
                 WHERE num = '".mysql_escape$CURRENT_USER['num'] )."'";
      
mysql_query($query) or die("MySQL Error:<br />\n"htmlspecialchars(mysql_error()) . "\n");
      
$userNum mysql_insert_id();
     
   
?>  
 
<?php endif ?>


SHOWING MEMBER VIDEOS ON A MEMBER’S PAGE - Apr 23rd, 2011

A client of mine who runs a dance school wanted to be able to create personalized videos for her students. She wanted
each student who paid to have a video created to be able to see only their video.

Implementing this was made easier, because I was already using the “website membership” plugin and all students were
also “members”. 

NOTE:
I had created 2 new text fields in the “accounts” table, “last_name’ and “first_name” when I set up the CMSB
installation, so that I could address each student by their name. I’ll use these fields for this recipe also.

Here’s how:

1) First I added a “videos_for_sale” check box field to the “accounts” user account section.

2) Next I created a multi-record section called Videos For Sale”. Under the Viewer URL tab, set the detail page URL to
/videosdetail.php

Delete the “content” field and add 4 additional fields to the editor for a total of 5 input fields ("title" is the
5th)
    
    A) An “upload” field called “video_uploads”, restricted to one mp4 or mov upload of unlimited size, with no
thumbnails.
    B) An “upload” field called “background_image” for an optional placeholder image while the video is loading.
    C) A “text box” field called “description”
    D) A pull down “list” field 

The only field that’s a bit special is the list field. Instead of listing all the student’s names, I wanted to
restrict the pull down to show only the last name of those students (members) that had a video created for them (a check
mark in the “video_for_sale” check box in their account record). 

Set the List Options pull down to “Get options from MySQL query”
replace the sample code in the box below to this:


SELECT num, last_name 
  FROM '<?php echo $TABLE_PREFIX ?>accounts' 
 WHERE video_for_sale='1'


This will populate the pull down list with the last_names from only those records with a check mark (1) in the
“videos_for_sale” field in the “accounts” table.

After saving the new editor, check a few check boxes in the "accounts" records and create a few test records in the
"videos_for_sale" editor and upload a few test videos (preferably in .mp4 format)

NOTE: You may have to change the maximum upload and maximum post sizes in your web server's PHP.ini file to allow for
large uploads. (They're normally set to less than 10 Mb by default)



Now for the viewers.

On what I called the students page (students.php), include the following code. This will show a set of links to the
video if one exists for that member:

In the head of this page, after the "load viewer library" call, add this "load records" call:


// load records
  list($videos_for_saleRecords, $videos_for_saleMetaData) = getRecords(array(
    'tableName'   => 'videos_for_sale',
  ));


Note: If you are pulling information from a single record editor on this page as well, you’ll have to remove the  
'where'       => whereRecordNumberInUrl(1), line from the load records call.

And this error message call:


<?php if (!@$GLOBALS['WEBSITE_MEMBERSHIP_PLUGIN']) { die("You must activate the Website Membership plugin before you can
access this page."); } ?>


And in the body:


<!-- USER LOGIN FORM -->
<?php if (@$errorsAndAlerts): ?>
<div class="your_class">
<br />
<?php echo $errorsAndAlerts?>
<br />
</div>
<?php endif ?>

<?php if (!@$CURRENT_USER): ?>
<form action="?" method="post">
<input type="hidden" name="action" value="login" />

 <table width="400" border="0" align="center" cellpadding="2" cellspacing="0">
<tr>
<td class="your-class">Username</td>
<td><input type="text" name="username" value="<?php echo htmlspecialchars(@$_REQUEST['username']); ?>" size="20" /></td>
</tr>
<tr>
<td class="your_class">Password</td>
    <td><input type="password" name="password" value="<?php echo htmlspecialchars(@$_REQUEST['password']); ?>" size="20"
/></td>
</tr>
<tr>
<td colspan="2" align="center">
<br />
<input type="submit" name="submit" value="Login" />
</td>
</tr>
<tr>
<td colspan="2" align="left">
<br /> <br />
<a class="your_class" href="<?php echo $GLOBALS['WEBSITE_LOGIN_REMINDER_URL'?>">FORGOT YOUR PASSWORD? CLICK HERE</a>
<br /> <br />
<span class="your_class">If you'd like to become a dance school member</span><br />
<a class="your_class" href="http://www.your_site.com/becomeamember.php">CLICK HERE TO SIGN UP</a>
</td>
</tr>
</table>
  </form>
<?php endif ?>
<!-- /USER LOGIN FORM -->

<!-- WELCOME MESSAGE-->
<?php if (@$CURRENT_USER): ?>
<span class="your_class">Welcome <?php echo $CURRENT_USER['first_name']; ?>&nbsp;
<?php echo $CURRENT_USER['last_name']; ?>,</span>
<br /> <br />
<?php endif ?>
<!-- /WELCOME MESSAGE -->

<!-- VIDEO LINKS-->
<?php if (@$CURRENT_USER): ?>
<?php foreach ($videos_for_saleRecords as $record): ?><?php if ($CURRENT_USER['num'] == $record['videos_for_sale']): ?>
<br />
<a class="your_class" href="<?php foreach ($record['video_upload'] as $upload): ?><?php echo $upload['urlPath'?><?php
endforeach ?>">Download Your Personal<br />Training Video</a><br /> <br /><a class="your_class"
href="http://www.your_site.com/videosdetail.php?<?php echo $record['num']; ?>">Watch Your Personal<br />Training
Video</a>
<br /> <br />
<?php endif ?>
<!-- /VIDEO LINKS-->


And for the video detail page (videosdetail.php) where the student can view his or her video (using the free JWPlayer
Video player described in the recipe “FREE JWPLAYER HANDLES BOTH HTML5 AND FLASH”).

In the head of the page, after the "load viewer" library call, add this "load records" call:


 // load records
  list($videos_for_saleRecords, $videos_for_saleMetaData) = getRecords(array(
    'tableName'   => 'videos_for_sale',
    'where'       => whereRecordNumberInUrl(1),
    'limit'       => '1',
  ));
  $videos_for_saleRecord = @$videos_for_saleRecords[0]; // get first record


And this error message call:


<?php if (!@$GLOBALS['WEBSITE_MEMBERSHIP_PLUGIN']) { die("You must activate the Website Membership plugin before you can
access this page."); } ?>


In the body, where you want the video to appear:


<!-- USER LOGIN FORM -->
  <?php if (@$errorsAndAlerts): ?>
    <div class="heading-text-13"><br />
      <?php echo $errorsAndAlerts?><br />
    </div>
  <?php endif ?>

<?php if (!@$CURRENT_USER): ?>

<form action="?" method="post">
  <input type="hidden" name="action" value="login" />

  <table width="400" border="0" align="center" cellpadding="2" cellspacing="0">
   <tr>
     <td  class="heading-text-13" colspan="2">You must be logged in to access this page.</td></tr><tr>
    <td class="body-text-bold-9">Username</td>
    <td><input type="text" name="username" value="<?php echo htmlspecialchars(@$_REQUEST['username']); ?>" size="20"
/></td>
</tr>
<tr>
<td class="body-text-bold-9">Password</td>
<td><input type="password" name="password" value="<?php echo htmlspecialchars(@$_REQUEST['password']); ?>" size="20"
/></td>
</tr>
<tr>
<td colspan="2" align="center">
<br />
<input type="submit" name="submit" value="Login" />
 </td>
</tr>
<tr>
<td colspan="2" align="left">
<br /> <br />
<a class="special" href="<?php echo $GLOBALS['WEBSITE_LOGIN_REMINDER_URL'?>">FORGOT YOUR PASSWORD? CLICK HERE</a>
<br /> <br />
<span class="body-text-9">If you'd like to become a dance school member</span> 
<a class="special" href="http://www.your_site.com/becomeamember.php">CLICK HERE TO SIGN UP</a>
</td>
</tr>
</table>
</form>
<?php endif ?>

<!-- /USER LOGIN FORM -->

<!-- WELCOME MESSAGE -->
  <?php if (@$CURRENT_USER): ?>
  
<table width="70%" border="0" cellspacing="10" cellpadding="5">
<tr>
  <td valign="top" width="75%"><table width="100%" border="0" align="center" cellpadding="5" cellspacing="0">
      <tr>
        <td colspan="2"><?php foreach ($videos_for_saleRecords as $record): ?><?php if ($CURRENT_USER['num'] ==
$record['videos_for_sale']): ?>
          <span class="your_class">Welcome <?php echo $CURRENT_USER['first_name']; ?>&nbsp;<?php echo
$CURRENT_USER['last_name']; ?>,</span>
<br />
<span class="your_class">Enjoy your video.</span>
<br /> <br />
<!-- /WELCOME MESSAGE -->
</td>
</tr>
<tr>
<td>
<!-- VIDEO DOWNLOAD LINK-->
<a class="your_class" href="<?php foreach ($record['video_upload'] as $upload): ?><?php echo $upload['urlPath'?><?php
endforeach ?>">Click here if you'd prefer to download<br />your personal training video</a>
<br />
<!-- /VIDEO DOWNLOAD LINK-->
     <?PHP endif ?>
<?php endforeach ?>
<br />
</td>
</tr>
</table>
 <?php endif ?> 
    
<!-- DISPLAY THE VIDEO-->
 <?php if (@$CURRENT_USER): ?>
 
 <?php foreach ($videos_for_saleRecords as $record): ?>
 
 <?php if ($CURRENT_USER['num'] == $record['videos_for_sale']): ?>
<table align="center" width="90%" border="0" cellpadding="5">
<tr>
<td>
 <h2 align="center"><?php echo $record['title'?></h2>
<br />
</td>
</td>
<tr>
<td align="center">
<div id="container">&nbsp;</div>
<script type="text/javascript">
jwplayer("container").setup({file: "http://www.your_site.com<?php foreach ($record['video_upload'] as $upload): ?><?php
echo $upload['urlPath'?>
<?php endforeach ?>",
height: 420,
width: 510,
<?php if (@$record['background_image']): ?>image: "http://www.your_site.com<?php foreach ($record['background_image'] as
$upload): ?><?php echo $upload['thumbUrlPath2'?>"<?php endforeach ?>, <?php endif ?>
        autostart: true,
        modes: [
            { type: "html5" },
            { type: "flash", src: "player.swf" }
        ]

    });
</script>
 <!-- /DISPLAY THE VIDEO-->
<!-- DISPLAY THE VIDEO DESCRIPTION-->
<br /> <br />
</td>
</tr>
<tr>
<td class="your_class" align="left">
<?php echo $record['description'?>
</td>
</tr>
</table>
<!-- /DISPLAY THE VIDEO DESCRIPTION-->
<?php endif ?>
<?php endforeach ?>
<?php endif ?> 


That should do it. You can style the pages any way you’d like.




CUSTOMIZING THE DATE FORMAT IN THE CMSB LIST VIEW - Apr 30th, 2011

This free plugin, by Robin Brayer from Interactive Tools is for all of you who would rather see “Saturday, April 30,
2011" instead of “2011-04-30 09:44:46" for dates that are displayed in your records list view.

Just copy the code below and paste it into a blank PHP document, save it as listViewDateFormat.php and upload it to your
cmsAdmin/plugins folder. Then in the admin section, under plugins, activate your new plugin. 

You can customize exactly how your date displays by changing the line $dateFormat = "l, F, d, Y"; in the plugin,
following the guide in the Cookbook recipe called SHOW DATES ON YOUR PAGE


<?php


// register hooks
addFilter('listRow_displayValue''_lvdf_changeDateFormat'null4);

function 
_lvdf_changeDateFormat($displayValue$tableName$fieldname$record) {

  
$returnValue $displayValue;

  
//Should we even consider this field?
  if(!($displayValueTimeStamp = @strtotime($displayValue))) {
    return 
$returnValue
  }

// Determines the display format of the date field
  $dateFormat "l, F, d, Y";

  
$year substr($displayValue04);
  
$firstHyphen substr($displayValue41);

  
$month substr($displayValue52);
  
$secondHyphen substr($displayValue71);

  
$day substr($displayValue82);

  
//echo $year . " " . $firstHyphen . " " . $month . " " . $secondHyphen . " " . $day;

  if(is_numeric($year) && is_numeric($month) && is_numeric($day) && ($firstHyphen == "-") && ($secondHyphen == "-")) {
    
$displayValueTimeStamp strtotime($displayValue);
    if(
$displayValueTimeStamp 1) {
      
$returnValue date($dateFormat$displayValueTimeStamp);
    }
  }
  return 
$returnValue;
}

?>





REDIRECTING CURRENT USERS BASED ON THEIR MEMBER TYPE - Oct 5th, 2011

User John from JAM Graphics wanted to know if there was an easy way to redirect the retailer sales rep "members" to
different parts of a website on login.

Robin Brayer from Interactive Tools came up with this simple answer:

If you add a "type" menu to the accounts section, that data will automatically be added to the CURRENT_USER variable
($CURRENT_USER['type']). You can use that to verify a users type at the top of a page.

On login, you can set your website membership "WEBSITE_LOGIN_POST_LOGIN_URL" to direct to something like
"logindispatch.php". Then redirect users from there to the correct place. e.g. The code might look something like:


if($CURRENT_USER['type']=='retailer') { 
  redirectBrowserToURL('retailerpage.php'); 
}




SENDING EMAILS TO "MEMBERS" - Feb 7th, 2012

When Christopherb wanted to be able to send emails to all members using CMS Builder, Jason Sauchuk from Interactive
Tools had this suggestion.

According to Jason:

There's a function in CMS Builder called sendMessage() that lets you send emails.

What you can do is set up an HTML form that has 2 fields, one called "subject" and then one called "message". In the
action of this form, you can have the form submit to itself (ie, action = "?" ).

At the top of your script you can use this code:

    Code    

if (@$_REQUEST['subject'] && @$_REQUEST['message'] && $CURRENT_USER['isAdmin']) { 
   
  list($membersRecords, $membersMetaData) = getRecords(array( 
    'tableName'           =>       'accounts', 
    'allowSearch'         =>       false, 
  )); 
   
  $emailList = join(",", array_filter(array_pluck($memberRecords, 'email'))); 
   
  sendMessage(array( 
    'to'      =>  "to@email.com", 
    'from'    => "from@email.com", 
    'subject' => @$_REQUEST['subject'], 
    'text'    => @$_REQUEST['message'], 
    'headers' => array( 
           'BCC'  =>  $emailList, 
         ), 
  )); 
  
}


With the code && $CURRENT_USER['isAdmin'] included, , this code will only execute if the current user is an
administrator and both subject and message were given a value.

What the script does then is get all of the users in "accounts" and create a comma separated list of their emails. It
then sends out an email with all of these emails in it's BCC field. You'll need to change the value of the "to" and
"from" field to whatever emails you'd like to use.



SENDING EMAILS TO A SPECIFIC GROUP OF USERS - Aug 6th, 2012

If you're using an email form and sendmail to create and send emails, the manual way to add recipients to the list would
be something like this:
NOTE: 
I haven't checked this out so let me know if there are issues.


   'To'  => email1@example.com,email2@example.com


If all of your potential recipients have user accounts, you might also think about changing the To values to a variable
($emailList) that gets populated from user email addresses where there's a "notifications" check box checked in their
record.

Something like this:

First add a check box field called notifications to your user account editor.

Then, create your list from the accounts records. 



    <?php list($emailListRecords,) = getRecords(array(  
      
"tableName"         => "accounts",  
      
      
'where'      => "notifications = '1' AND hidden = '0'",
      
'orWhere'      => "notifications = '1' AND hidden = '1'",
    ));  
          
      
$emailList ""
       
      foreach( 
$emailListRecords as $record ) {  
          
        
$emailList.= $record['email'] .",";  
     }
?>

Then use the variable in your sendmail code, 



'To'      => $emailList,


Another way of listing all of the notification email addresses, say in a viewer,  would be:



<?php // Get email list of those members with notifications on, and access to the project
         
           $accounts  mysql_query_fetch_all_assoc("SELECT * FROM `{$TABLE_PREFIX}accounts` WHERE notifications = '1'"); 
      
$emailList join(","array_pluck($accounts'email'));
     
?>
     
     <?php echo $emailList ;?>  


ALPHABETICALLY DISPLAYING THE ENTRIES IN THE CREATEDBY SELECT USER PULL DOWN - Jan 3rd, 2014

Alphabetically displaying the entries in the createdBy select user pull down

To make it easier to re-assign author access only listings that were originally created by admins, I wanted to force the
entries createdBy select user pull down in a record to display in alphabetical order.

Dave Edis, senior developer at Interactive Tools came up with an easy, one line fix (and will be adding it to the code
starting with CMSB versions above 2.53).

In  cmsAdmin/lib/menu/default/common.php

Look for the code:

// show pulldown when editor clicks "change" beside "Created By" on edit page in CMS
function ajaxGetUsersAsPulldown() {
global $TABLE_PREFIX, $hasEditorAccess, $tableName;
if (!$hasEditorAccess) { return ''; } // must have section admin access

// get users with access to this section
$query = "SELECT u.num, u.username
FROM {$TABLE_PREFIX}accounts u
JOIN {$TABLE_PREFIX}_accesslist a ON u.num = a.userNum
WHERE a.accessLevel > 1 AND a.tableName IN ('all','$tableName')
$users = mysql_fetch($query);

And add:

ORDER BY username";

above the line, $users = mysql_fetch($query);


PICKING MEMBERSHIP RENEWAL DATE AND DURATION - Apr 20th, 2015

Instead of calculating an expiration date (expiresDate) based on the date that a member signed up, an organization
wanted to calculate the expiration date and membership duration (in years) from a fixed month. 

Further, they wanted anyone signing up after a particular cutoff month relative to the current month, to get an extra
year added to their membership, so that their initial membership period was never shorter than one year. 

Some of the other challenges were calculating a valid date for months that had 30 or 28 days vs those that had 31 days,
and generating a valid MySQL date from the calculation.

With Claire Ryan's and Greg Thomas' from Interactive Tools help, we came up with the following solution.

In a single record editor (mine was called ‘Organization Information’) there are 2 list fields, a text field, and a
check box . The list fields are called Start Month and Cutoff Month. Their values are:

1|January
2|February
3|March
4|April
5|May
6|June
7|July
8|August
9|September
10|October
11|November
12|December

NOTE: the format number|text allows the list to display the text in the pull down menu, while inserting the selected
value in numbers. 

The text field is called Renewal Duration and accepts 1 digit with a value from 1-9. With the description: Enter the
digit corresponding to the Renewal Duration in years (1-9).

The check box ( called ‘Valid for One Year From Date Joined’), with the description: “If renewal period begins on
date joined, check this box. Otherwise the 3 fields below will determine the renewal criteria.”

Then define a few variables:

<?php
@$curr_month date("n") ;
@
$start_month      str_pad(@$organization_informationRecord['start_month'], 2"0"STR_PAD_LEFT); 
@
$cutoff_month = @$organization_informationRecord['cutoff_month'] ;
@
$renewal_duration = @$organization_informationRecord['renewal_duration'] ;
@
$currentYear date('Y');
?>

After that, perform the calculations required and check for valid last day of the month :

 <?php
@$renewalYear = (intval(@$curr_month) >= intval(@$cutoff_month))? @$currentYear + (@$renewal_duration) :@ $currentYear +
(@
$renewal_duration 1) ;
@
$expiryMonth = ($organization_informationRecord['start_month'] -1);
  if(@
$expiryMonth 10) {
    @
$expiryMonth "0".@$expiryMonth;
  }
  if(@
$expiryMonth == 00) {
    @
$expiryMonth 12;
  }
   @
$testEndDate = @$renewalYear'-'.@$expiryMonth.'-01';
  @
$lastDayofMonth date('t'strtotime(@$testEndDate));
    if(@
$expiryMonth == 02) {
    @
$lastDayofMonth 28;
  }
  if(@
$expiryMonth == 04 || @$expiryMonth == 06 || @$expiryMonth == 09 || @$expiryMonth == 11) {
    @
$lastDayofMonth 30;
}
if(@
$expiryMonth == 01 || @$expiryMonth == 03 || @$expiryMonth == 05 || @$expiryMonth == 07 || @$expiryMonth == 08 ||
@
$expiryMonth == 10 || @$expiryMonth == 12 ) {
    @
$lastDayofMonth 31;
}
?>

And then create a valid MySQL date from the result:

<?php $adjustedMembershipExpiryDate $renewalYear'-'.$expiryMonth.'-'.$lastDayofMonth.' 00:00:00'?>


You can use the following to test your progress.

Current Month is: <?php echo date("n"); ?><br />
Test End Date is: <?php echo @$testEndDate ?><br />
Start Month is: <?php echo @$start_month ?><br />
Expiry Month is: <?php echo @$expiryMonth ?><br />
Last Day of Month: <?php echo @$lastDayofMonth ?><br />
Cutoff Month is: <?php echo @$cutoff_month ?><br />
Renewal Duration is:<?php echo @$organization_informationRecord['renewal_duration'?> years<br />
Current Year is: <?php echo date('Y'); ?><br />
Renewal Year is: <?php echo @$currentYear + (@$renewal_duration 1); ?><br />
Calculated $adjustedMembershipExpiryDate is: <?php echo @$adjustedMembershipExpiryDate ?> <br />

<?php if(@$curr_month >= @$cutoff_month): ?>
It's <?php echo @$cutoff_month ?> or after, so use renewal_duration  (<?php echo @$renewal_duration ?>) as year interval
added to current year.
<?php else:?>
It's before <?php echo @$cutoff_month ?>, so use renewal_duration -1 (<?php echo @$renewal_duration -1 ?>) as year
interval added to current year.
<?php endif ?>

The final step is to insert the $adjustedMembershipExpiryDate into the  mysql_query("INSERT INTO list. NOTE: the
apostrophes are so that the variable is recognized correctly.

 expiresDate      = '$adjustedMembershipExpiryDate',


Then, to allow for the, “membership from date joined” option, I surrounded the calculation code and the 
mysql_query("INSERT INTO code with:

<?php if ( $organization_informationRecord['valid_for_one_year_from_date_joined'] = ||
$organization_informationRecord['valid_for_one_year_from_date_joined'] = '' ):?>
... date calculation code and your  mysql_query("INSERT INTO code, including  expiresDate      =
'$adjustedMembershipExpiryDate', ...
 <?php elseif (@$organization_informationRecord['valid_for_one_year_from_date_joined'] == '1'):?>
...Your original mysql_query("INSERT INTO code, including expiresDate      = (NOW() + INTERVAL 1 YEAR),...
<?php endif ?>


MORE EXPIRATION DATE CODING OPTIONS - Oct 6th, 2016

Automatically updating an annual membership can get complicated because some organizations still prefer to use a
specific renewal date, and others allow the actual date that a member has signed up as the base date and annually update
from there.

The situation becomes more complex when a member has skipped a year and then decides to renew.

Here’s some really simple MySQL update code as well as some more sophisticated coding examples thanks to Dave Edis,
Senior developer at interactive Tools.

Example 1
This will simply add one year to the current expiration date:

<?php 
 mysqlStrictMode(false);
 
mysql_query("UPDATE `{$TABLE_PREFIX}accounts`    
        SET  expiresDate = expiresDate + INTERVAL 1 YEAR 
   WHERE num = '".mysql_escape$CURRENT_USER['num'] )."'")  
       or die(
"MySQL Error:\n"htmlspecialchars(mysql_error()) . "\n");  
        
$userNum mysql_insert_id(); 

?> 


Example 2
This will simply update the expiration date to one year from the actual date the membership is renewed:

<?php 
 mysqlStrictMode(false);
 
mysql_query("UPDATE `{$TABLE_PREFIX}accounts`    
        SET  expiresDate      = (NOW() + INTERVAL 1 YEAR)
   WHERE num = '".mysql_escape$CURRENT_USER['num'] )."'")  
       or die(
"MySQL Error:\n"htmlspecialchars(mysql_error()) . "\n");  
        
$userNum mysql_insert_id(); 

?>


Example 3 (thanks to Dave Edis)
This will update a member’s expiration date to 1 year from the current expiration date.
But, if the expiration date does not contain the previous year, then the new expiration date will
be the current year +1 (so that the record for a member who has skipped a renewal year does not
get updated to last year).

<?php 
 mysqlStrictMode(false);

$lastYear    date('Y'strtotime("-1 year"));
$expiryYear  date('Y'strtotime($CURRENT_USER['expiresDate']));
$currentYear date('Y');
if (
$expiryYear != $lastYear) { $newExpiryYear $currentYear 1; }
else                          { 
$newExpiryYear $expiryYear 1; }

// Update expiry to greater of: 1 year from now, 1 year from old expires date
$updateSet "expiresDate=DATE_FORMAT(expiresDate,'$newExpiryYear-%m-%d %T')";
$updateWhere "num = '".mysql_escape$CURRENT_USER['num'] )."'";
$updateQuery "UPDATE `{$TABLE_PREFIX}accounts` SET $updateSet WHERE $updateWhere";
mysql_query($updateQuery) or die("MySQL Error: "htmlspecialchars(mysql_error()) . "\n");
?>


Example 4 (thanks to Dave Edis)
This will update the expiration date to the greater of: 1 year from now, or 1 year from the old expiration date:

<?php 
 mysqlStrictMode(false);

$updateSet   "expiresDate = GREATEST(NOW(), expiresDate) + INTERVAL 1 YEAR";
$updateWhere "num = '".mysql_escape$CURRENT_USER['num'] )."'";
$updateQuery "UPDATE `{$TABLE_PREFIX}accounts` SET $updateSet WHERE $updateWhere";
mysql_query($updateQuery) or die("MySQL Error: "htmlspecialchars(mysql_error()) . "\n");
?>

CRON JOBS



CREATING A CRON JOB - Apr 10th, 2019

Cron is a time-based job scheduler in Unix-like computer operating systems. The name cron comes from the word "chronos",
Greek for "time". Crontab jobs allow users to schedule when a particular script is executed based on matching month,
day, hour, and minute.

I’m using a crontab job or “cron job” to schedule when a PHP “membership renewal reminder” script is run.

The PHP script, which is run once a day, compares the expiresDate with the current date for each member. If the number
of days until expiration matches a preset number, the member is sent an email reminding them that their membership is
about to expire.

Crontab jobs are pretty particular in how they’re set up, and ISPs differ in the way that you access and input the
information to set up a cron job, so you may have to get in touch with their tech support to get things operating as
they should. My ISP (IXWebHosting) sets up cron jobs in the ftp management section of their interface.

There’s specific syntax for entering the scheduling information into a cron job request. At the end of this recipe,
you’ll find the acceptable parameters from my web host IXWebHosting, taken straight from their “help” screen.

NOTE: You'll have to insure that the script that you're trying to run with the conjob has write permission 

CREATING AND RUNNING A CRON JOB
There are 3 pieces of information necessary for creating and running a cron job
1) the scheduling information
2) the path to your server’s php interpreter (obtained from your web host), and
3) the path to the php file to be executed

I needed to run my script once a day at 2 minutes after midnight, so my scheduling parameters are:
Minute: 2, Hour: 0, Day of month: 1-31, Month: 1-12, Day of week: 1-7

After you’ve set up all your scheduling parameters, either put both the path to your server’s php interpreter, and
the path to your PHP script in your crontab command line as in this example (Note: The path to your server’s php
interpreter will be different, so check with your web host, and don’t forget the space between the 2 segments.):

*** Note: if you have issues with sending mail using cron jobs, see the recipe under Bugs and Fixes called: "Cron issues
sending mail with CMSB 3.50  or earlier, and PHP 7.2 after a Bluehost server upgrade"



/hsphere/shared/php5/bin/php -q /your_server_path/mailtest.php


or, put the path to your server’s php interpreter at the very top of your php script with no blank spaces or lines
before it, like this (note: The path to your server’s PHP interpreter will be different, so check with your web
host.):


#!/hsphere/shared/php5/bin/php-cgi -q


And insert only the path to your php script in the command line, like this:


/your_server_path/mailtest.php


TESTING SENDMAIL
To test if your sendmail is working you can create a .php file called mailtest.php with this simple php sendmail test
script. When you refresh the page it should send the email.

<?php
$to "your_email@your_host.com";
$from "you@sendmail_test.com";
$subject "Testing Sendmail";
$txt "Testing Sendmail from PHP Script";
mail($to,$subject,$txt,"From: $from""-f$from");
?>


NOTE: You'll need to make sure that this file has a permission of 755 (read, write, execute) or the file may not run
when called by the cron manager (your cron daemon confirmation email will show an "access denied" error).

TESTING YOUR CRON JOB
Once you’re sure that sendmail is working, you can use the script to test your cronjob.

CRON JOB CONFIRMATION EMAILS
More than likely, your Cron Daemon will send a confirmation email to an address you choose and you can use the
information in that to debug your cron job and your scripts

Once you're sure everything is working, you can send the daemon emails to a non existent email address so that they will
not clutter up your inbox.


CRON ISSUES SENDING MAIL WITH CMSB 3.50 OR EARLIER, AND PHP 7.2 AFTER A BLUEHOST SERVER UPGRADE - May 28th, 2019

About a month ago, I moved an account to a new server at Bluehost, and I started having major problems with sending
emails using a cron job.

After weeks of playing around, many days waiting for Bluehost's senior level support, and hours of Daniel Loewe and Dave
Edis' time, The issue of why emails were never received when using a mailarray or an email template, is solved. (The
problem didn't affect phpmail)

Bluehost narrowed it down to Message-ID issues similar to: <65812fc901465e02d09689bdaf6a902f@swift.generated>.

Seems, according to Dave Edis, the senior guru at Interactive Tools, (as well as I understand it) if there's no
$_SERVER['SERVER_NAME'] set. See: https://forge.typo3.org/issues/24686, a header is generated by the swift mail program.

Daniel worked up a fix, and at least part of it will be rolled out in a future version of CMSB

Here's the fix that Daniel created.

1) Add your domain name to the end of your cron job (after a space, and no http or www)
2) Add the following code to the top of the cron.php file (above "//load viewer library" code) (i put mine after the
load viewer library code by mistake and it still worked)

 if (empty($_SERVER['HTTP_HOST']) && isset( $_SERVER['argv'][1])) {
 $_SERVER['SERVER_NAME'] = $_SERVER['argv'][1]; // set server name for SwiftMailer Message-ID in cron tasks //
(https://forge.typo3.org/issues/24686)
}

3) Add the same code to any page that addresses CMSB's sendMessage() function from outside the background task framework
(like mailarray or email template triggering scripts)

Daniel said, "The extra code should only be necessary when using CMSB's sendMessage() function. it's worth noting that
this fix will be in cron.php in a future CMSB version (either 3.51 or 3.52), so after that, it will only be applicable
for users setting up cron jobs that use sendMessage() outside of CMSB's background tasks or users on older versions."

Hope this save someone a bit of aggravation....


SENDING MEMBERSHIP REMINDER EMAILS USING A CRON JOB - Apr 10th, 2019

SENDING THE MEMBERSHIP RENEWAL REMINDER EMAILS
And once you’ve got your cron job working, you can substitute the PHP script that sends reminder emails to your
members. 

It uses the mail function built in to CMS Builder which seems to be more robust then using sendmail directly.

*** Note: if you have issues with sending mail using cron jobs, see the recipe under Bugs and Fixes called: "Cron issues
sending mail with CMSB 3.50 or earlier, and PHP 7.2 after a Bluehost server upgrade"

For reference, the basic implementation for the mail function is:


$message = 'This is a test message';
$mailArray = array(
'to' => 'example@example.com',
'from' => 'example@example.com',
'subject' => 'This is a subject',
'html' => $message
);
$errors = sendMessage($mailArray);
 

THE MEMBERSHIP RENEWAL REMINDER SCRIPT
Here’s the PHP script that I run once a day to send a renewal reminder emails to members: 
A) when their membership is 30 days from expiration, and 
B) when their membership is one week from expiration. 

To allow my clients to change both the reminder schedule and the text of the reminder emails that are sent, I created 2
text fields and 2 text boxes in a single record editor I named “Common Information”. The text fields are named
first_reminder_day and second_reminder_day, the text boxes are named first_reminder_message and second_reminder_message.

The script allows for html code to be added to the email so that the emails can be styled. It also includes code from
the recipe: “INCLUDING VARIABLES IN THE CONTENTS OF A TEXT BOX” so that the current annual renewal fee can be pulled
from another table called “become_a_member” and included in the “reminder message” fields.

NOTE: don’t forget to change the path to your server’s PHP interpreter and to revise the paths to your server and
any links. Also, make sure that this file has a permission of 755 (read, write, execute) or the file may not run when
called by the cronjob manager.



#!/hsphere/shared/php5/bin/php-cgi -q
<?php
  $libraryPath 'cmsAdmin/lib/viewer_functions.php';
 
$dirsToCheck = array('your_server_path/','','../','../../','../../../');
  foreach (
$dirsToCheck as $dir) { if (@include_once("$dir$libraryPath")) { break; }}
  if (!
function_exists('getRecords')) { die("Couldn't load viewer library, check filepath in sourcecode."); }

  list(
$common_informationRecords$common_informationMetaData) = getRecords(array(
    
'tableName'   => 'common_information',
    
'allowSearch' => '0',
    
'limit'       => '1',
  ));
  
$common_informationRecord = @$common_informationRecords[0]; // get first record
  
  list($accountsRecords$accountsMetaData) = getRecords(array(
    
'tableName'   => 'accounts',
  ));
  list(
$become_a_memberRecords$become_a_memberMetaData) = getRecords(array(
    
'tableName'   => 'become_a_member',
     
'allowSearch' => '0',
    
'limit'       => '1',
  ));
   
$become_a_memberRecord = @$become_a_memberRecords[0]; // get first record
  ?>
 <?php foreach($accountsRecords as $record) : ?>
 
<?php 
  $secondsOld time() - strtotime($record['expiresDate']); // seconds to or since expiration date
  $daysOld    intval($secondsOld/60/60/24); // converts seconts to days
  $first_reminder_day $common_informationRecord['first_reminder_day'] ;
  
$second_reminder_day $common_informationRecord['second_reminder_day'] ;
  
// replace the variable *regular_dues_amount* with the annual dues
  $first_reminder_message =
str_replace('*regular_dues_amount*',$become_a_memberRecord['regular_dues_amount'],$common_informationRecord['first_reminder_message']);
  
$second_reminder_message =
str_replace('*regular_dues_amount*',$become_a_memberRecord['regular_dues_amount'],$common_informationRecord['second_reminder_message']);
  
$member $record['first_name'] ;

?> 


 <?php ob_start(); // start capturing output ?>
   
      <?php $first_reminder_message wordwrap$first_reminder_message70); ?> 
      <?php echo $first_reminder_message ?>
      <?php $output ob_get_clean(); // stop capturing output  ?>
      
       <?php ob_start(); // start capturing output ?>
   
      <?php $second_reminder_message wordwrap$second_reminder_message70); ?> 
      <?php echo $second_reminder_message ?>
      <?php $output2 ob_get_clean(); // stop capturing output  ?>


<?php if((!@$record['neverExpires'] && @$record['lifetime_member'] == '0') && $record['remove_me'] == '0' && ($daysOld
== -$first_reminder_day)) :?>

 <?php   $the_to $record['email']; ?>

 <?php 
 
 $message = <<<EOF
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type content='text/html; charset=utf-8" />
<style type="text/css">
.special {font-family:Arial; color:  rgb(227,224,219); font-size: 1.0em; font-weight: bold; text-decoration: underline;}
.body-text {font-family:Arial; font-size: 1.0em;}
.body-text-yellow {font-family:Arial; color: rgb(255,255,0); font-size: 1.0em;}
.heading-text-yellow {font-family:Arial; color: rgb(255,255,0); font-size: 1.3em;}
</style>
</head>
<body bgcolor="#7C7164">
<table style='background-color: #7C7164;' width='100%' align='center' border='0' >
              <tr>
    <td align='left' >
    <table style='background-color: #7C7164;' width='70%' align='center' border='0' >
              <tr>
    <td align='left' >
 <div align='left'><img src='http://www.artistsofpalmbeachcounty.org/images/APBC-LOGO.png' width='800' height='183'
style='border:hidden'/></div>
<br /> <br />
 <div align='left' class='body-text'>
  Hello $member,
  <br /> <br />

 $output \r\n
<br /> <br />
  Best,
  <br /> <br />
 The Membership Committee <br />
 Artists of Palm Beach County\r\n</div>
  </td>
    </tr>
    </table>
    </td>
    </tr>
    </table>
  </body>
</html>
EOF;
 
$the_from  "membership@artistsofpalmbeachcounty.org";
 
$the_subject "Your Current Artists of Palm Beach County Membership will expire in $first_reminder_day days";

$mailArray = array( 
'to' => $the_to,
'from' => $the_from,
'subject' => $the_subject,
'html'         => $message 
); 
$errors =  sendMessage($mailArray);
// delay execution by .2 sec
usleep(200000);
// reset max_execution_time to 30 sec
set_time_limit(30);
?>
 
<?php endif ;?>

 <?php if((!@$record['neverExpires'] && @$record['lifetime_member'] == '0') && $record['remove_me'] == '0' && ($daysOld
== -$second_reminder_day)) :?>
<?php   $the_to $record['email']; ?>
 <?php 
 
 $message = <<<EOF
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type content='text/html; charset=utf-8" />
<style type="text/css">
.special {font-family:Arial; color:  rgb(227,224,219); font-size: 1.0em; font-weight: bold; text-decoration: underline;}
.body-text {font-family:Arial; font-size: 1.0em;}
.body-text-yellow {font-family:Arial; color: rgb(255,255,0); font-size: 1.0em;}
.heading-text-yellow {font-family:Arial; color: rgb(255,255,0); font-size: 1.3em;}
</style>
</head>
<body bgcolor="#7C7164">
<table style='background-color: #7C7164;' width='100%' align='center' border='0' >
              <tr>
    <td align='left' >
    <table style='background-color: #7C7164;' width='70%' align='center' border='0' >
              <tr>
    <td align='left' >
 <div align='left'><img src='http://www.artistsofpalmbeachcounty.org/images/APBC-LOGO.png' width='800' height='183'
style='border:hidden'/></div>
<br /> <br />
 <div align='left' class='body-text'>
  Hello $member,
  <br /> <br />

 $output2 \r\n
<br /> <br />
  Best,
  <br /> <br />
 The Membership Committee <br />
 Artists of Palm Beach County\r\n</div>
  </td>
    </tr>
    </table>
    </td>
    </tr>
    </table>
  </body>
</html>
EOF;
 
$the_from  "membership@artistsofpalmbeachcounty.org";
 
$the_subject "Your Current Artists of Palm Beach County Membership will expire in $second_reminder_day days";

$mailArray = array( 
'to' => $the_to,
'from' => $the_from,
'subject' => $the_subject,
'html'         => $message 
); 
$errors =  sendMessage($mailArray);
// delay execution by .2 sec
usleep(200000);
// reset max_execution_time to 30 sec
set_time_limit(30);
?>

<?php endif ;?>
<?php endforeach ?>


THE CONTENTS OF MY REMINDER MESSAGE TEXT BOXES

We wanted to let you know that your membership will expire in about a week.

To continue enjoying the benefits of membership, please <a href='http://www.your_site.com/paypal.php'   / >CLICK
HERE</a> to renew for another year for only $*regular_dues_amount* using PayPal.  

and

We wanted to let you know that your membership will expire in about a month.

To continue enjoying the benefits of membership, please <a href='http://www.your_site.com/paypal.php'   / >CLICK
HERE</a> to renew for another year for only $*regular_dues_amount* using PayPal.  

GOING FURTHER
For situations where the sending of  repetitive emails need to be avoided, Dave Edis from Interactive Tools suggested
setting up check boxes that the PHP script writes to when an email is sent and then including a compare in the if
statements to determine the check boxes value (1=sent, 0=not sent). he reminded that you'd also need to change the value
of those check boxes to "0" on renewal so the process could repeat.

If you're getting error messages in your Cron Daemon emails like these:



Notice: Undefined index:  SCRIPT_FILENAME in
/hsphere/local/home/apbcweb/artistsofpalmbeachcounty.org/cmsAdmin/plugins/createPDF/createPDF.php on line 22 
 
Notice: Undefined index:  HTTP_HOST in
/hsphere/local/home/apbcweb/artistsofpalmbeachcounty.org/cmsAdmin/plugins/createPDF/createPDF.php on line 23 
 
Notice: Undefined index:  SCRIPT_NAME in
/hsphere/local/home/apbcweb/artistsofpalmbeachcounty.org/cmsAdmin/plugins/createPDF/createPDF.php on line 23 
 
Notice: Undefined variable: _SESSION in
/hsphere/local/home/apbcweb/artistsofpalmbeachcounty.org/cmsAdmin/lib/login_functions.php on line 88 

 

Dave Edis offers a solution:

He says:

For the Undefined notices, a workaround might be to edit /lib/init.php and add this line just above "// define
constants"



error_reporting(E_ALL & ~E_NOTICE); // show all errors but notices 
 
// define constants

  
 
Cron Tab Help Document (IXWebhosting.com)

The format of a cron command is very much the V7 standard, with a number of upward-compatible extensions. Each line has
five time and date fields, followed by a user name if this is the system crontab file, followed by a command. Commands
are executed by cron(8) when the minute, hour, and month of year fields match the current time, and when at least one of
the two day fields (day of month, or day of week) match the current time (see ``Note'' below). Note that this means that
non-existent times, such as "missing hours" during daylight savings conversion, will never match, causing jobs scheduled
during the "missing times" not to be run. Similarly, times that occur more than once (again, during daylight savings
conversion) will cause matching jobs to be run twice.

cron(8) examines cron entries once every minute.

The time and date fields are:

       field          allowed values
       -----          --------------
       minute         0-59
       hour           0-23
       day of month   1-31
       month          1-12 (or names, see below)
       day of week    0-7 (0 or 7 is Sun, or use names)

A field may be an asterisk (*), which always stands for ``first-last''.

Ranges of numbers are allowed. Ranges are two numbers separated with a hyphen. The specified range is inclusive. For
example, 8-11 for an ``hours'' entry specifies execution at hours 8, 9, 10 and 11.
Lists are allowed. A list is a set of numbers (or ranges) separated by commas. Examples:
``1,2,5,9'', ``0-4,8-12''.

Step values can be used in conjunction with ranges. Following a range with ``/'' specifies skips of the number's value
through the range. For example, ``0-23/2'' can be used in the hours field to specify command execution every other hour
(the alternative in the V7 standard is ``0,2,4,6,8,10,12,14,16,18,20,22''). Steps are also permitted after an asterisk,
so if you want to say ``every two hours'', just use ``*/2''.

Names can also be used for the ``month'' and ``day of week'' fields. Use the first three letters of the particular day
or month (case doesn't matter). Ranges or lists of names are not allowed.

The ``sixth'' field (the rest of the line) specifies the command to be run. The entire command portion of the line, up
to a newline or % character, will be executed by /bin/sh or by the shell specified in the SHELL variable of the
cronfile. Percent-signs (%) in the command, unless escaped with backslash (\), will be changed into newline characters,
and all data after the first % will be sent to the command as standard input.

Note: The day of a command's execution can be specified by two fields -- day of month, and day of week. If both fields
are restricted (ie, aren't *), the command will be run when either field matches the current time. For example, ``30 4
1,15 * 5'' would cause a command to be run at 4:30 am on the 1st and 15th of each month, plus every Friday.


SENDING INFORMATION UPDATE REMINDER EMAILS USING A CRON JOB - Feb 12th, 2023

The information in an on-line directory is only as good as that input by the organizations that are listed. 
For that reason, my client wanted to hide any listing that has not been updated in either 6 month or a year intervals. 
They also wanted  to send emails to each listing organization 30 days before their update was due, another 10 days
before the update was due, and when their listing was automatically hidden form the search results.

This was accomplished by using a reminder email  PHP script that is called once a day by a cron job. 
My Cron job, executed at 5AM every day, looked like this:  

php -q /home3/mrqsygmy/public_html/reminder_email.php

The actual hiding of listings is directly implemented on the search page which is documented at the end of this recipe. 
   

THE INFORMATION UPDATE REMINDER SCRIPT
The script requires the following fields to be created in a single record editor (mine is called 'common_information'):
an 'are mandatory reminders active' checkbox called 'reminder' (checked = Yes, unchecked = No)
a mandatory update duration checkbox called ''mandatory_update_duration' (checked = one year, unchecked = 6 months)
a first reminder day text field called 'first_reminder_day'  (for 30 days or your interval)
a second reminder day text field called 'second_reminder_day'  (for 10 days or your interval)
a 'first reminder message' text box field called 'first_reminder_message' (for 30 days or your interval)
a 'second reminder message' text box field called 'second_reminder_message' (for 10 days or your interval)
a 'thiird reminder message 'text box field called 'third_reminder_message' (for hidden from directory message)
NOTE: *** It's important that your message includes that they need to log in to and RE-SAVE their account record.***
a 'send test emails only' checkbox called  'send_test_emails_only'  (checked = Yes (test email), unchecked = No (live)
a 'test email' text field called 'test_email' (for the email address where you'd like to recieve tests)
a main box background color' text field called 'main_box_background_color' (for formatting the color of the emails)

and a 'test date' date field in the Account record called 'test_date' (the live script depends on the field
'updatedDate' to determine when emails are due. The 'test_email'  field in the account record allows for testing
and a 'Hide This Account From Directory Searches and Email Lists' checkbox field in the Account record called 'notAdmin'


Here’s the PHP script that I run once a day to send a information update reminder emails to organizations: 

?php $libraryPath = 'cmsAdmin/lib/viewer_functions.php';
  $dirsToCheck = array('/home3/mrqsygmy/public_html/dbtproviders/','','../','../../','../../../');
  foreach ($dirsToCheck as $dir) { if (@include_once("$dir$libraryPath")) { break; }}
  if (!function_exists('getRecords')) { die("Couldn't load viewer library, check filepath in sourcecode."); }
 if (empty($_SERVER['HTTP_HOST']) && isset( $_SERVER['argv'][1])) {
$_SERVER['SERVER_NAME'] = $_SERVER['argv'][1]; // set server name for SwiftMailer Message-ID in cron tasks
(https://forge.typo3.org/issues/24686)
}
  // load records
       list($common_informationRecords, $common_informationMetaData) = getRecords(array(
    'tableName'   => 'common_information',
     'where'       => '',
     'allowSearch' => false,
    'limit'       => '1',
  ));
  $common_informationRecord = @$common_informationRecords[0]; // get first record 
<?php  list($accountsRecords$accountsMetaData) = getRecords(array(
    
'tableName'   => 'accounts',
  ));
?>
<?php $color $common_informationRecord['main_box_background_color'?>
<?php $currentUnixTime strtotime(date('M jS, Y')); ?>
<?php // $timeGone = 0 ?>
<?php @$first_reminder_day $common_informationRecord['first_reminder_day'] ;?>
<?php @$first_reminder_day_two =  @$first_reminder_day 86400 ?>
<?php @$second_reminder_day $common_informationRecord['second_reminder_day'] ;?>
<?php @$second_reminder_day_two =  @$second_reminder_day 86400 ?>
<?php // $timeGone = 0 ?>
<?php $emailCountTwoFour 0 ?>
<?php $emailCountOneThree 0 ?>
<?php foreach($accountsRecords as $record) : ?>
<?php $timeGone 0 ?>
<?php if ($common_informationRecord['send_test_emails_only'] == ):?><?php $sixMonths strtotime(date("M jS, Y",
strtotime($record['test_date'])) . " +6 month"?><?php elseif ($common_informationRecord['send_test_emails_only'] == 0
|| $common_informationRecord['send_test_emails_only'] == ''):?><?php $sixMonths strtotime(date("M jS, Y",
strtotime($record['updatedDate'])) . " +6 month"?><?php endif ?>
<?php $sixMonthsFirst = ($sixMonths $first_reminder_day_two?>
<?php $sixMonthsSecond $sixMonths $second_reminder_day_two ?>
<?php if ($common_informationRecord['send_test_emails_only'] == ):?><?php $twelveMonths strtotime(date("M jS, Y",
strtotime($record['test_date'])) . " +1 year"?><?php elseif ($common_informationRecord['send_test_emails_only'] == 0
|| $common_informationRecord['send_test_emails_only'] == ''):?><?php $twelveMonths strtotime(date("M jS, Y",
strtotime($record['updatedDate'])) . " +1 year"?><?php endif ?>
<?php $twelveMonthsFirst = ($twelveMonths $first_reminder_day_two?>
<?php $twelveMonthsSecond $twelveMonths $second_reminder_day_two ?>
<?php if ($sixMonthsFirst == $currentUnixTime ):?>
<?php $timeGone 1 ?>
<?php endif ?>
<?php if ($sixMonthsSecond == $currentUnixTime ):?>
<?php $timeGone 2 ?>
<?php endif ?>
<?php // elseif ($common_informationRecord['mandatory_update_duration']=='1'):?>
<?php if ($twelveMonthsFirst == $currentUnixTime ):?>
<?php $timeGone 3 ?>
<?php endif ?>
<?php if ($twelveMonthsSecond == $currentUnixTime ):?>
<?php $timeGone 4 ?>
<?php endif ?>
<?php  // endif?>
<?php if(!@$record['isAdmin'] && $common_informationRecord['reminder'] == 1): // live ?>
<?php  if($timeGone==|| $timeGone==3):  ?>
<?php 
  @$first_reminder_message $common_informationRecord['first_reminder_message']; // 10 calendar days
  @$second_reminder_message $common_informationRecord['second_reminder_message']; // 30 calendar days
   @$practiceName $record['practice_name'] ;
  @
$firstName $record['contact_first_name'] ;
    @
$lastName $record['contact_last_name'] ;?>
<?php if ($common_informationRecord['send_test_emails_only'] == ):?><?php $expires date("M jS, Y",
strtotime($record['test_date'])) ;?><?php elseif ($common_informationRecord['send_test_emails_only'] == ||
$common_informationRecord['send_test_emails_only'] == ''):?><?php $expires date("M jS, Y",
strtotime($record['updatedDate'])) ;?><?php endif ?>
<?php // ************** START SET PROVIDER VARIABLES AND OUTPUT **************************  ?>
<?php ob_start(); // start capturing output ?>
<?php $first_reminder_message wordwrap$first_reminder_message70); ?>
<?php echo $first_reminder_message ?>
<?php $output ob_get_clean(); // stop capturing output  ?>
<?php ob_start(); // start capturing output ?>
<?php $second_reminder_message wordwrap$second_reminder_message70); ?>
<?php echo $second_reminder_message ?>
<?php $output2 ob_get_clean(); // stop capturing output  ?>
<?php //************** END SET PROVIDER VARIABLES AND OUTPUT ************************** ?>
<?php  // **************DETERMINE PROVIDER SATUS ******************* ?>
<?php // Send Test or Real emails? ?>
<?php if ($common_informationRecord['send_test_emails_only'] == || $common_informationRecord['send_test_emails_only']
== 
''):?>
<?php   $the_to $record['email']; // live?>
<?php elseif ($common_informationRecord['send_test_emails_only'] == 1):?>
<?php @$the_to $common_informationRecord['test_email']; ?>
<?php endif ?>
<?php
$message = <<<EOF
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type content='text/html; charset=utf-8" />
<style type="text/css">
.special {font-family:Arial; color:  rgb(0,0,0); font-size: 1.0em; font-weight: bold; text-decoration: underline;}
.text_font {font-family:Arial; color: rgb(0,0,0); font-size: 1.0em;}
.heading_font {font-family:Arial; color: rgb(0,0,0); font-size: 1.3em;}
.heading_font_blue {font-family:Arial; color: rgb(0,0,255); font-size: 1.3em;}
</style>
</head>
<body bgcolor="#$color">
<table style='background-color: #$color;' width='100%' align='center' border='0' >
              <tr>
    <td align='left' >
    <table style='background-color: #$color;' width='70%' align='center' border='0' >
              <tr>
    <td align='left' >
 <div align='left'><img src='http://www.dbtproviders.com/images/DBT_email_masthead.png' style='border:hidden;
width:489px; height:88px;'/></div>
 <div align='left' class='text_font'>
  Hello $firstName $lastName,
  

 $output \r\n
  Best,
  
  The DBT Provider Directory Team
\r\n\r\n</div>
  </td>
    </tr>
    </table>
    </td>
    </tr>
    </table>
  </body>
</html>
EOF;

 
$the_from  "no-reply@dbtproviders.com";
 
$the_subject "A DBT Provider profile mandatory update for $practiceName is due about $first_reminder_day days from
now. You can update your profile at https://dbtproviders.com/provider_profile.php";

$mailArray = array( 
 
'to' => $the_to,
'from' => $the_from,
'subject' => $the_subject,
'html'         => $message 
); 
$errors =  sendMessage($mailArray);
// delay execution by .2 sec
usleep(200000);
// reset max_execution_time to 30 sec
set_time_limit(30);
?>
<?php endif ;?>
<?php endif // end test timegone 1 or 3?>
<?php  endforeach?>
<?php foreach($accountsRecords as $record) : ?>
<?php $timeGone 0 ?>
<?php if ($common_informationRecord['send_test_emails_only'] == ):?><?php $sixMonths strtotime(date("M jS, Y",
strtotime($record['test_date'])) . " +6 month")?><?php elseif ($common_informationRecord['send_test_emails_only'] == 0
|| $common_informationRecord['send_test_emails_only'] == ''):?><?php $sixMonths strtotime(date("M jS, Y",
strtotime($record['updatedDate'])) . " +6 month")?><?php endif ?>
<?php $sixMonthsFirst = ($sixMonths $first_reminder_day_two?>
<?php $sixMonthsSecond $sixMonths $second_reminder_day_two ?>
<?php if ($common_informationRecord['send_test_emails_only'] == ):?><?php $twelveMonths strtotime(date("M jS, Y",
strtotime($record['test_date'])) . " +1 year"?><?php elseif ($common_informationRecord['send_test_emails_only'] == 0
|| $common_informationRecord['send_test_emails_only'] == ''):?><?php $twelveMonths strtotime(date("M jS, Y",
strtotime($record['updatedDate'])) . " +1 year"?><?php endif ?>
<?php $twelveMonthsFirst = ($twelveMonths $first_reminder_day_two?>
<?php $twelveMonthsSecond $twelveMonths $second_reminder_day_two ?>
<?php if ($sixMonthsFirst == $currentUnixTime ):?>
<?php $timeGone 1 ?>
<?php $timeGone 5 ?>
<?php endif ?>
<?php if ($sixMonthsSecond == $currentUnixTime ):?>
<?php $timeGone 2 ?>
<?php $timeGone 5 ?>
<?php endif ?>
<?php if ($twelveMonthsFirst == $currentUnixTime ):?>
<?php $timeGone 3 ?>
<?php $timeGone 5 ?>
<?php endif ?>
<?php if ($twelveMonthsSecond == $currentUnixTime ):?>
<?php $timeGone 4 ?>
<?php $timeGone 5 ?>
<?php endif ?>
<?php if(!@$record['isAdmin']  && $common_informationRecord['reminder'] == 1):?>
<?php if($timeGone==|| $timeGone==4):?>
<?php 
  @$first_reminder_message $common_informationRecord['first_reminder_message']; // 10 calendar days
  @$second_reminder_message $common_informationRecord['second_reminder_message']; // 30 calendar days
   @$practiceName $record['practice_name'] ;
  @
$firstName $record['contact_first_name'] ;
    @
$lastName $record['contact_last_name'] ;?>
<?php if ($common_informationRecord['send_test_emails_only'] == ):?><?php $expires date("M jS, Y",
strtotime($record['test_date'])) ;?><?php elseif ($common_informationRecord['send_test_emails_only'] == ||
$common_informationRecord['send_test_emails_only'] == ''):?><?php $expires date("M jS, Y",
strtotime($record['updatedDate'])) ;?><?php endif ?>
<?php // ************** START SET PROVIDER VARIABLES AND OUTPUT **************************  ?>
<?php ob_start(); // start capturing output ?>
<?php $first_reminder_message wordwrap$first_reminder_message70); ?>
<?php echo $first_reminder_message ?>
<?php $output ob_get_clean(); // stop capturing output  ?>
<?php ob_start(); // start capturing output ?>
<?php $second_reminder_message wordwrap$second_reminder_message70); ?>
<?php echo $second_reminder_message ?>
<?php $output2 ob_get_clean(); // stop capturing output  ?>
<?php //************** END SET PROVIDER VARIABLES AND OUTPUT ************************** ?>
<?php  // **************DETERMINE PROVIDER SATUS ******************* ?>
<?php // Send Test or Real emails? ?>
<?php if ($common_informationRecord['send_test_emails_only'] == || $common_informationRecord['send_test_emails_only']
== 
''):?>
<?php   $the_toa $record['email']; // live?>
<?php elseif ($common_informationRecord['send_test_emails_only'] == 1):?>
<?php @$the_toa $common_informationRecord['test_email'];  ?>
<?php endif ?>
<?php 
 $message2 = <<<EOF
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type content='text/html; charset=utf-8" />
<style type="text/css">
.special {font-family:Arial; color:  rgb(0,0,0); font-size: 1.0em; font-weight: bold; text-decoration: underline;}
.text_font {font-family:Arial; font-size: 1.0em;}
.text_font {font-family:Arial; color: rgb(0,0,0); font-size: 1.0em;}
.heading_font_red {font-family:Arial; color: rgb(255,0,0); font-size: 1.3em;}
.heading_font {font-family:Arial; color: rgb(0,0,0); font-size: 1.3em;}
.heading_font_blue {font-family:Arial; color: rgb(0,0,255); font-size: 1.3em;}
</style>
</head>
<body bgcolor="#$color">
<table style='background-color: #$color;' width='100%' align='center' border='0' >
              <tr>
    <td align='left' >
    <table style='background-color: #$color;' width='70%' align='center' border='0' >
              <tr>
    <td align='left' >
 <div align='left'><img src='http://www.dbtproviders.com/images/DBT_email_masthead.png' style='border:hidden;
width:489px; height:88px;'/></div>
 <div align='left' class='text_font'>
  Hello $firstName $lastName,
  
<span class='heading_font_blue'>This is your final courtesy reminder.</span>
  
  <span class='heading_font'>You must verify/update the information in your DBT Provider profile within the next 10
days, or your listing will be hidden from the DBT Provider Directory.Your information is never deleted, and you can
reinstate your listing at any time by logging in to you provider profile at
https://dbtproviders.com/provider_profile.php and updating your information. </span>
 $output2 \r\n
  Best,
  
 The DBT Provider Directory Team
\r\n
 </div>
  </td>
    </tr>
    </table>
    </td>
    </tr>
    </table>
  </body>
</html>
EOF;

$the_from  "no-reply@dbtproviders.com";
 
$the_subject "A DBT Provider profile for $practiceName is due for a mandatory update about $second_reminder_day days
from now. You can update your profile at https://dbtproviders.com/provider_profile.php";

$mailArray = array( 
'to' => $the_toa,
'from' => $the_from,
'subject' => $the_subject,
'html'         => $message2 
); 
$errors =  sendMessage($mailArray);
// delay execution by .2 sec
usleep(200000);
// reset max_execution_time to 30 sec
set_time_limit(30);
?>
<?php endif ?>
<?php endif // end test timegone 2 or 4?>
<?php endforeach ?>
<?php foreach($accountsRecords as $record) : ?>
<?php $timeGone 0 ?>
<?php if ($common_informationRecord['send_test_emails_only'] == ):?><?php $sixMonths strtotime(date("M jS, Y",
strtotime($record['test_date'])) . " +6 month"?><?php elseif ($common_informationRecord['send_test_emails_only'] == 0
|| $common_informationRecord['send_test_emails_only'] == ''):?><?php $sixMonths strtotime(date("M jS, Y",
strtotime($record['updatedDate'])) . " +6 month"?><?php endif ?>
<?php $sixMonthsFirst = ($sixMonths $first_reminder_day_two?>
<?php $sixMonthsSecond $sixMonths $second_reminder_day_two ?>
<?php if ($common_informationRecord['send_test_emails_only'] == ):?><?php $twelveMonths strtotime(date("M jS, Y",
strtotime($record['test_date'])) . " +1 year")?><?php elseif ($common_informationRecord['send_test_emails_only'] == ||
$common_informationRecord['send_test_emails_only'] == ''):?><?php $sixMonths strtotime(date("M jS, Y",
strtotime($record['updatedDate'])) . " +6 month"?><?php endif ?>
<?php $twelveMonthsFirst = ($twelveMonths $first_reminder_day_two?>
<?php $twelveMonthsSecond $twelveMonths $second_reminder_day_two ?>
<?php if ($sixMonthsFirst == $currentUnixTime ):?>
<?php $timeGone 1 ?>
<?php $timeGone 5 ?>
<?php endif ?>
<?php if ($sixMonthsSecond == $currentUnixTime ):?>
<?php $timeGone 2 ?>
<?php $timeGone 5 ?>
<?php endif ?>
<?php if ($twelveMonthsFirst == $currentUnixTime ):?>
<?php $timeGone 3 ?>

<?php endif ?>
<?php if ($twelveMonthsSecond == $currentUnixTime ):?>
<?php $timeGone 4 ?>
<?php $timeGone 5 ?>
<?php endif ?>
<?php if ($sixMonths == $currentUnixTime ):?>
<?php $timeGone 5 ?>
<?php endif ?>
<?php if ($twelveMonths == $currentUnixTime ):?>
<?php $timeGone 6 ?>
<?php endif ?>
<?php  // endif?>
<?php if(!@$record['isAdmin']  && $common_informationRecord['reminder'] == 1):?>
<?php if($timeGone==|| $timeGone==6):?>
<?php 
  @$first_reminder_message $common_informationRecord['first_reminder_message']; // 10 calendar days
  @$second_reminder_message $common_informationRecord['second_reminder_message']; // 30 calendar days
   @$third_reminder_message $common_informationRecord['third_reminder_message']; // listing hidden
   @$practiceName $record['practice_name'] ;
  @
$firstName $record['contact_first_name'] ;
    @
$lastName $record['contact_last_name'] ;?>
<?php if ($common_informationRecord['send_test_emails_only'] == ):?><?php $expires date("M jS, Y",
strtotime($record['test_date']));?><?php elseif ($common_informationRecord['send_test_emails_only'] == ||
$common_informationRecord['send_test_emails_only'] == ''):?><?php $expires date("M jS, Y",
strtotime($record['updatedDate']));?><?php endif ?>
<?php // ************** START SET PROVIDER VARIABLES AND OUTPUT **************************  ?>
<?php ob_start(); // start capturing output ?>
<?php $first_reminder_message wordwrap$first_reminder_message70); ?>
<?php echo $first_reminder_message ?>
<?php $output ob_get_clean(); // stop capturing output  ?>
<?php ob_start(); // start capturing output ?>
<?php $second_reminder_message wordwrap$second_reminder_message70); ?>
<?php echo $second_reminder_message ?>
<?php $output2 ob_get_clean(); // stop capturing output  ?>
<?php ob_start(); // start capturing output ?>
<?php $third_reminder_message wordwrap$third_reminder_message70); ?>
<?php echo $third_reminder_message ?>
<?php $output3 ob_get_clean(); // stop capturing output  ?>
<?php //************** END SET PROVIDER VARIABLES AND OUTPUT ************************** ?>
<?php  // **************DETERMINE PROVIDER SATUS ******************* ?>
<?php // Send Test or Real emails? ?>
<?php if ($common_informationRecord['send_test_emails_only'] == || $common_informationRecord['send_test_emails_only']
== 
''):?>
<?php   $the_tob $record['email']; // live?>
<?php elseif ($common_informationRecord['send_test_emails_only'] == 1):?>
<?php @$the_tob $common_informationRecord['test_email'];  ?>
<?php endif ?>
<?php 
 $message3 = <<<EOF
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type content='text/html; charset=utf-8" />
<style type="text/css">
.special {font-family:Arial; color:  rgb(0,0,0); font-size: 1.0em; font-weight: bold; text-decoration: underline;}
.text_font {font-family:Arial; font-size: 1.0em;}
.text_font {font-family:Arial; color: rgb(0,0,0); font-size: 1.0em;}
.heading_font_red {font-family:Arial; color: rgb(255,0,0); font-size: 1.3em;}
.heading_font {font-family:Arial; color: rgb(0,0,0); font-size: 1.3em;}
.heading_font_blue {font-family:Arial; color: rgb(0,0,255); font-size: 1.3em;}
</style>
</head>
<body bgcolor="#$color">
<table style='background-color: #$color;' width='100%' align='center' border='0' >
              <tr>
    <td align='left' >
    <table style='background-color: #$color;' width='70%' align='center' border='0' >
              <tr>
    <td align='left' >
 <div align='left'><img src='http://www.dbtproviders.com/images/DBT_email_masthead.png' style='border:hidden;
width:489px; height:88px;'/></div>
 <div align='left' class='text_font'>
  Hello $firstName $lastName,
  
<span class='heading_font_blue'>Your Directory listing has been hidden from the search results in the DBT Provider
Directory.</span>
  
  <span class='heading_font'>Until you verify/update the information in your DBT Provider profile your listing will
remain hidden from the DBT Provider Directory.Your information is never deleted, and you can reinstate your listing at
any time by logging in to you provider profile at https://dbtproviders.com/provider_profile.php and updating your
information.</span>
 $output3 \r\n
  Best,
  
 The DBT Provider Directory Team
\r\n
 </div>
  </td>
    </tr>
    </table>
    </td>
    </tr>
    </table>
  </body>
</html>
EOF;

$the_from  "no-reply@dbtproviders.com";
  
$the_subject "The DBT Provider Directory Listing for $practiceName has been hidden from the Directory until  update
your profile at https://dbtproviders.com/provider_profile.php";

$mailArray = array( 
'to' => $the_tob,
'from' => $the_from,
'subject' => $the_subject,
'html'         => $message3 
); 
$errors =  sendMessage($mailArray);
// delay execution by .2 sec
usleep(200000);
// reset max_execution_time to 30 sec
set_time_limit(30);
?>
<?php endif ?>
<?php endif // end test timegone 2 or 4?>
<?php endforeach ?>
<?php
    $crontext "Reminder Run at ".date("r")."\n" ;
    
$folder substr($_SERVER['SCRIPT_FILENAME'],0,strrpos($_SERVER['SCRIPT_FILENAME'],"/")+1);
    
$filename $folder."cron_test.txt" ;
    
$fp fopen($filename,"a") or die("Open error!");
    
fwrite($fp$crontext) or die("Write error!");
    
fclose($fp);
    echo 
"Wrote to ".$filename."\n\n" ;
    
?>


The actual hiding of the listings is accomplished by the following code on the search page:


<?php // load records from 'accounts'
  list($accountsRecords$accountsMetaData) = getRecords(array(
    
'tableName'   => 'accounts',
    
'loadUploads' => true,
    
'allowSearch' => true,

  ));
  
// load records from ''common_information'
       list($common_informationRecords$common_informationMetaData) = getRecords(array(
    
'tableName'   => 'common_information',
     
'where'       => '',
     
'allowSearch' => false,
    
'limit'       => '1',
  ));
  
$common_informationRecord = @$common_informationRecords[0]; // get first record 
  ?>
<?php $currentUnixTime strtotime(date('M jS, Y')); ?>

<?php // if(@$_REQUEST['save']):?>
<?php foreach($accountsRecords as $record) : ?>
<?php $timeGone 0 ?>
<?php $sixMonths '0' ?>
<?php $twelveMonths '0' ?>
<?php if(!@$record['isAdmin'] && $common_informationRecord['reminder'] == 1): // live ?>

<?php if ($common_informationRecord['send_test_emails_only'] == ):?><?php $sixMonths strtotime(date("M jS, Y",
strtotime($record['test_date'])). " +6 month" ?><?php elseif ($common_informationRecord['send_test_emails_only'] == 0
|| $common_informationRecord['send_test_emails_only'] == ''):?><?php $sixMonths strtotime(date("M jS, Y",
strtotime($record['updatedDate'])). " +6 month" ?>
<?php endif ?>
<?php if ($common_informationRecord['send_test_emails_only'] == ):?><?php $twelveMonths strtotime(date("M jS, Y",
strtotime($record['test_date'])). " +1 year" ?><?php elseif ($common_informationRecord['send_test_emails_only'] == 0
|| $common_informationRecord['send_test_emails_only'] == ''):?><?php $twelveMonths strtotime(date("M jS, Y",
strtotime($record['updatedDate'])). " +1 year" ?><?php endif ?>
<?php endif ?>

<?php  if ($common_informationRecord['mandatory_update_duration']=='1'): // 12 months?>
<?php if ($twelveMonths >= $currentUnixTime ):?>
<?php $timeGone 2 ?>
<?php endif ?>
<?php endif ?>

<?php  if ($common_informationRecord['mandatory_update_duration']=='0' ||
$common_informationRecord['mandatory_update_duration']==''): // 6 months?>
<?php if ($sixMonths >= $currentUnixTime ):?>
<?php $timeGone 1 ?>
<?php endif ?>
<?php endif ?>

<?php if(!$record['isAdmin'] && !$record['notAdmin'] == '1' && ( $timeGone == 1) || ( $timeGone  == 2) &&
(!empty((int)
$record['test_date'] ) || !empty((int)$record['updatedDate'] ))):?>
<?php echo htmlencode($record['practice_name']) ?>

Test Date =  <?php echo date("M jS, Y "strtotime($record['test_date'])) // hide and remove  for live ?>
Current Date = <?php echo date("M jS, Y")  // hide and remove  for live ?>
Time Gone = <?php echo $timeGone  // hide and remove  for live ?>
<?php  endif ?>
<?php endforeach ?>

<?php // endif ?>



EXPLOITSCANNER SCHEDULING USING A CRON JOB - May 30th, 2012

After some fits and starts trying to get the script to work in a shared hosting environment, here’s what I
discovered...

Most shared hosting plans do not allow command line access, so scanning from the web seems like the only choice.

Most hosts time out their php execution script at 90 seconds so it's likely that your script will time out, as noted in
the readme file.

However, if you can run your exploitScanner as a cron job, you may be able to run it using the cron manager command line
and get around the timeout issue.

Here’s how it worked for me at IXWebHosting. (there's more info on setting up cron jobs and scheduling in the recipe
CREATING A CRON JOB)

At the very top of the xs.php script, I inserted the line (NOTE: Your path will probably be different)

#!/hsphere/shared/php5/bin/php -q

to call the php interpreter

On the cron manager command line I used: (NOTE: Enter your own path information here)

/path_to_your_server/xs.php -p /path_to_your_server/ -l xs.log

To call the xs.php script, tell it to start in the site's root directory, and to create a log file called xs.log in the
root directory

Don't be fooled into thinking that scanning is a quick process, my meager site took between 25 and 30 minutes to execute
and then create the cron report and log file.

So, you might want to do your testing on a directory that has only a few files and change to the root after you get
everything working.

The only change that I made in the xs.php file other than the php interpreter call is to change the time zone.

GOING FURTHER

Dave Edis from Interactive Tools offered: To suppress the listing of the number of scanned files in each directory when
the scanner is run from a cronjob you can just comment this line:

 print "Files Scanned: " .number_format($OPTIONS['filesScanned']). "\r";

the -p flag can be followed either by the full path to the directory that you want to scan, or by a "." (Without the
quotes). The "." means root directory. Don't forget to leave a space between the -p and the ".", IE: -p .

The -l flag only requires the file name of the log file after a space, IE: -l your_log_file.log

Your log file might look something like this.

Exploit Scanner v1.01 - Scans filesystem for web exploit patterns
--------------------------------------------------------------------------------
Matched Patterns: php(36), js(7), htaccess(6), filepath(2)
Scan started: Fri, 25 May 2012 09:35:01 -0400
Root dir: /hsphere/local/home/xyz/your_domain.com
Log file: your_log_file.log

/hsphere/local/home/xyz/your_domain.com/your_file.php (Score: 11)
  - PHP Code Match (Score 10): PHP gzinflate(base64_decode()) function pairing previously seen in exploit code (matched
string: gzinflate(base64_decode($stuff2))));)
  - PHP Code Match (Score 1): PHP variable being called as a function (matched string: " . $mysql_error()

Scanned: 235 dirs, 4,308 files (1 risks found)
Execute time: 127 minutes, 19 seconds

--------------------------------------------------------------------------------
* Note: Please send any files with false-positives or undetected-exploits to us.
 
An explanation of the various entries:

>Matched Patterns: php(36), js(7), htaccess(6), filepath(2)

These are the number of patterns being checked for different kinds of files. Note that often a single pattern will match
multiple exploits.

>Scan started: Thu, 24 May 2012 16:41:49 -0400

When the scan was started, useful if output is going to an email or log.

>Root dir: /hsphere/local/home/xyz/your_domain.com

The path being scanned, also useful for reference when output is going to an email or log.

If you scan from the command line you can output to a log file by adding -l yourlogfile.log in which case the filename
would be reflected here. 

>Log file: your_log_file.log

>/hsphere/local/home/xyz/your_domain.com/your_file.php (Score: 11)
The path to a scanned file with possible exploit vulnerability and the total score of those vulnerabilities
> - PHP Code Match (Score 10): PHP gzinflate(base64_decode()) function pairing previously seen in exploit code (matched
string: gzinflate(base64_decode($stuff2))));)
>  - PHP Code Match (Score 1): PHP variable being called as a function (matched string: " . $mysql_error()

These are the patterns that were discovered.

> Scanned: 235 dirs, 4,308 files (1 risks found)
 > Execute time: 127 minutes, 19 seconds

The number of directories and files scanned, and the time that it took for the entire scan.

WEBSITE MEMBERSHIP PLUGIN - USER EXAMPLES



SETTING UP PRIVATE AREAS FOR USERS - Aug 21st, 2011

User gsfriend said: I'm setting up a site for a custom home builder and they want a private area where customers can
login and see pictures of their home during the construction process and we as a place they can access documents that
relate to that customer only.

My question is, can the membership plugin be easily configured to display UNIQUE content to the current member logged
in? In my case, each customer would have their own unique files that I would only want them to have access to, not other
clients. 

Dave at Interactive Tools came back with:

“What the plugin does is make a global available on every page called $CURRENT_USER that has the record of the
currently logged in user.

So a simple implementation would be to have a section called "homes" with a field called "ownerUserNum" which was a
pulldown of customer names (with user num stored as the value in the database).”

Then in your homes viewer you could have something like:



'where' => " ownerUserNum = {$CURRENT_USER['num']} ",



And only homes assigned to the currently logged in user would be shown. 

Dave suggested setting up a multi record section called “Homes” with a pulldown field showing all users to the
"Homes" section like this:

Go to: Admin > Section Editors > Homes > Add Field
Field Label: Owner
Field Name: ownerUserNum
Field Type: list

Display As: Pulldown
List Options: Get options from database (advanced)
Section Tablename: Accounts
Values Field: num
Labels Field: fullname

Gsfriend added fields for Title, Name, Content, File Uploads for Images, etc., to the section.

The resulting viewer code was:



<?php header('Content-type: text/html; charset=utf-8'); ?> 
<?php if (!@$GLOBALS['WEBSITE_MEMBERSHIP_PLUGIN']) { die("You must activate the Website Membership plugin before you can
access this page."); } ?> 
<?php if (!$CURRENT_USER) { websiteLogin_redirectToLogin(); } ?>

<?php 
  require_once "/home/ekfriend/public_html/cms/lib/viewer_functions.php"
     
  list(
$homeownersRecords$homeownersMetaData) = getRecords(array( 
    
'tableName'   => 'homeowners'
'where' => " (ownerUserNum = '{$CURRENT_USER['num']}') OR '{$CURRENT_USER['isAdmin']}' ",  
    
'limit'       => '1'
  )); 
  
$homeownersRecord = @$homeownersRecords[0]; // get first record 
 
  list($settingsRecords$settingsMetaData) = getRecords(array( 
    
'tableName'   => 'settings'
    
'limit'       => '1'
  ));

 

If you wanted to also allow admins to view the recordsyou’d change the where statement to:



'where' => " (ownerUserNum = '{$CURRENT_USER['num']}') OR '{$CURRENT_USER['isAdmin']}' "



According to Jason Sauchuk of Interactive Tools:

If 
you're searching as part of a multi-select list, you need to use the LIKE operator and put tab characters before and
after your search option. Try this:

    

'where' => "ownerUserNum LIKE '%\t". intval($CURRENT_USER['num'])."\t%'",



LIST RECORDS ASSIGNED TO CURRENT USER ONLY - Aug 6th, 2010

S2media wanted to create a page where a project manager can log in to see only the projects that they’re assigned to. 

He went on to say, ”When a project is created in a section called “Projects”, I have a drop down of project
managers that I can assign it to.  The drop down pulls in a field "id" which is a field I added to the Accounts section.
There’s also a field called project_status which can be Quote, Active, Hold or Completed.

Both Jason Sauchuk and Dave Edis from Interactive Tools got involved in this one and they came up with the following:



list($projectsRecords, $projectsMetaData) = getRecords(array( 
    'tableName'   => 'projects', 
,
'where' => 'project_status="Active" AND project_manager= "'. $CURRENT_USER['id'] .'"' , 



While testing, you can add these to the code:



 'debugSql' => true,
  ));

<?php showme($projectsRecords); ?> 



LIST ONLY RECORDS WHERE CURRENT USER IS AUTHOR - Jul 24th, 2011

On a membership site the client wanted to display only those records that were authored by the current user from a table
called "subscription_pages".

The first step was to limit the records returned to only those where the Current user was the Author with a where clause
in the get records call:



list($subscription_pagesRecords, $subscription_pagesMetaData) = getRecords(array(
    'tableName'   => 'subscription_pages',
    'where' => 'createdByUserNum = "'. $CURRENT_USER['num'] .'"' ,
 
    
       )); 


Then for an added touch, I welcomed the user by name and added their subscription level to the greeting. 



Welcome <?php echo mysql_escape($CURRENT_USER['first_name']); ?> <?php echo mysql_escape($CURRENT_USER['last_name']);
?>, <?php if ($CURRENT_USER['allowedFolders'] == "entry_level"): ?>Entry<?PHP endif ?><?php if
(
$CURRENT_USER['allowedFolders'] == "silver"): ?>Silver<?PHP endif ?><?php if ($CURRENT_USER['allowedFolders'] ==
"gold"): ?>Gold<?PHP endif ?><?php if ($CURRENT_USER['allowedFolders'] == "platinum"): ?>Platinum<?PHP endif ?> level
subscriber.


Then where I wanted to display the user's choices:



 <?php foreach ($subscription_pagesRecords as $record): ?>
   <?php if ($record['createdByUserNum']== @$CURRENT_USER['num']): ?>

<!-- Records Created By User #:  <?php echo $record['createdByUserNum'?> Exist -  Current User is # : <?php echo 
@
$CURRENT_USER['num'?><br />-->
    
 <span class="medium">TO VIEW ALL OF YOUR LISTINGS - </span><a href="master_listing.php"><span
class="medium"><u><b>CLICK HERE</b></u></span></a>
   <br /> <br />
     <span class="medium">TO ADD LISTINGS OR MODIFY EXISTING LISTINGS - </span><a href="cmsAdmin/admin.php"><span
class="medium"><u><b>CLICK HERE</b></u></span></a>
  <br /> <br />
   <?php endif ?><?php break ; ?><?php endforeach ?>


And if the current user had not created any records:



 <?php if (!$subscription_pagesRecords): ?>
    <span class="medium">YOU HAVEN'T CREATED ANY LISTINGS YET. </span><a
href="http://96.0.19.10/cmsAdmin/admin.php"><span class="medium"><u><b>CLICK HERE</b></u></span></a> 
    <span class="medium">TO GET STARTED.</span>
<br /> <br />
<?php endif ?> 



LIST RECORDS WHERE AUTHOR OF RECORDS IN TABLE 1 RECORDS MATCH AUTHOR OF RECORDS IN TABLE 2 - Jul 24th, 2011

If a member-user uploaded a custom masthead, I needed to display that  masthead in a detail viewer. However, I wanted to
display each user's masthead only on records that they had created.

Here's how...

I already had a multi-record editor called subscription_pages that contained all of the records created by each
member-user.

In order to add custom mastheads:

1) Create a multi-record editor called "custom_branding" with only one record allowed per user. The only field in this
editor is an upload field for their custom masthead.

2) To the standard records calls:


 list($subscription_pagesRecords, $subscription_pagesMetaData) = getRecords(array(
    'tableName'   => 'subscription_pages',
    'where'       => whereRecordNumberInUrl(1),  
    'limit'       => '1',
    
  ));
  $subscription_pagesRecord = @$subscription_pagesRecords[0]; // get first record


Add the following to define the variable $owner (add this below the subscription_pages call or you'll get a undefined
variable error)

 
 $owner = $master_subscription_pagesRecord['createdByUserNum'];


Then change the where statement to restrict the records called to only those where the author's of the custom branding
record (the masthead) matches the subscription_pages records


 // load records
  list($custom_brandingRecords, $custom_brandingMetaData) = getRecords(array(
    'tableName'   => 'custom_branding',
'where' => 'createdByUserNum = "'. $owner .'"' , 
    'limit'       => '1',
  ));
  $custom_brandingRecord = @$custom_brandingRecords[0]; // get first record


Finally in the body where you want to display the custom masthead:


<?php if ($custom_brandingRecord['masthead'] && ($custom_brandingRecord['createdByUserNum'] == 
$master_subscription_pagesRecord['createdByUserNum'])) : ?><?php foreach ($custom_brandingRecord['masthead'] as
$upload): ?><img src="<?php echo $upload['thumbUrlPath'?>" width="<?php echo $upload['thumbWidth'?>" height="<?php
echo $upload['thumbHeight'?>" alt="" /><?php endforeach ?>
<?php else : ?> 
<img src="your_standard_masthead.jpg" />
<?PHP endif ?>


For testing purposes, you can add:


Custom Branding owner: <?php echo $custom_brandingRecord['createdByUserNum'?> <br /> Subscription Page owner: <?php
echo $master_subscription_pagesRecord['createdByUserNum'?>


CHECKING FOR PASSWORD STRENGTH - Aug 3rd, 2013

Thanks to user Zick and Greg Thomas from Interactive Tools for the following Password Strength checking suggestions for
the Website Membership Plugin. 

The code should be inserted after the NewPassword Checking code. You can change the password length to any number of
characters that you feel is appropriate.

For uniformity, you might also want to change the initial password generated in the “generate password” section of
the user-signup.php form to that number of characters matches. (See the recipe called CHANGING PASSWORD LENGTH) 



      if    (strlen($_REQUEST['newPassword1']) < 11 )                                        { $errorsAndAlerts .=
"Please enter 12 or more characters for your New Password\n"; }
      if    (!preg_match("#[a-z]+#", $_REQUEST['newPassword1']) )                            { $errorsAndAlerts .=
"Password must include at least one letter!\n"; }
      if    (!preg_match("#[A-Z]+#", $_REQUEST['newPassword1']) )                            { $errorsAndAlerts .=
"Password must include at least one CAPS!\n"; }
      if    (!preg_match("#\W+#", $_REQUEST['newPassword1']) )                                { $errorsAndAlerts .=
"Password must include at least one symbol!\n"; }
      $errorsAndAlerts .= getNewPasswordErrors(@$_REQUEST['newPassword1']); // only works in Version 2.52+)
_


According to Greg, the getNewPasswordErrors function added in Version 2.52, and implemented in Version 1.10 of the
Membership Plugin,  will return a string of errors, including checking if the password starts with spaces, and if the
password is in the 1000 most popular passwords. If there are no errors it will return an empty string.

If you’re using a prior version just comment out (or remove) the getNewPasswordErrors code.

HINT: Check the file /lib/login_password_blacklist.txt 

If you've got some passwords that you think should be included, just add them to the file and they won't be accepted. 


CHANGING PASSWORD LENGTH - Aug 27th, 2014

When a new user signs up for an account, a randomly generated 17 character temporary password is sent to them. That's
pretty secure, but if it's a bit unwieldy for your situation, you can easily change the password length to any amount of
characters.

In llder versions of the Website Membership plugin, in the user-signup.php file, look for the code:



$_REQUEST['password'] = substr(md5(uniqid(rand(), true)), 15);



or in newer versions Look in the Website Membership plugin itself, for:



$password = substr(md5(uniqid(mt_rand(), true)), 15);



According to Chris Waddell of Interactive Tools "The 15 in the code skips the first 15 characters of the 32 characters
returned by md5(), resulting in a 17 character string."

To set the length to 5 characters, try this:



$_REQUEST['password'] = substr(md5(uniqid(rand(), true)), 0, 27); // example output: c5560



or in newer versions



$password = substr(md5(uniqid(mt_rand(), true)), 0, 27); // example output: c5560



You can replace the 5 with any number you'd like up to 32. 

QR CODES



QRCODEGENERATOR PLUGIN - Dec 29th, 2018

Now for something completely different, and useful...

Robin Brayer of Interactive Tools has created a free QRCodeGenerator plugin that creates QR (Quick Response) codes from
URLs, text, Contact Information, email addresses and more.

You can download the latest version from the user submitted page at:

http://www.interactivetools.com/add-ons/?user_submitted=1

For those of you that are not up to speed on the many uses of QR codes, you can find out more on the CMSB Forum at:

http://www.interactivetools.com/iforum/Products_C2/CMS_Builder%3A_Plugins_%26_Add-ons_F40/QR_Code_Generator_P87895/


Or by Googling QR Codes

Implementation is really simple:

Just upload the QRCodeGenerator plugin to your cmsAdmin/plugins folder and then activate it through the admin>plugins
area if the admin interface.

There are examples included that show how to create QR codes from many types of information. 

You can read more about QR Codes at:

 http://code.google.com/apis/chart/docs/gallery/qr_codes.html

and an interesting blog at:

http://www.socialmediaexaminer.com/how-qr-codes-can-grow-your-business/


CREATING QR CODES FOR ALL THE RECORDS IN A SECTION - May 5th, 2011

I had a created a multi-record section with a record for each of an artist's client's works. 

The artist wanted to be able to generate a single page, three column viewer that showed the QR codes for the URL of the
detail page for each record in that section, along with some identifying information.

The QR images would then be used to create QR stickers for wall labels at an art exhibition.

The records all had an upload field called "image" and a "Title" field for the name of the work.

Here's the code I used  for the top of my viewer:

<?php
  // load viewer library
  $libraryPath 'cmsAdmin/lib/viewer_functions.php';
  
$dirsToCheck = array('','../','../../','../../../''../../../../''../../../../../');
  foreach (
$dirsToCheck as $dir) { if (@include_once("$dir$libraryPath")) { break; }}
  if (!
function_exists('getRecords')) { die("Couldn't load viewer library, check filepath in sourcecode."); }
  
  list(
$your_tablesRecords$your_tableMetaData) = getRecords(array(
    
'tableName'   => 'your_table',
  ));
  

?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">


And here's the code for the body:

<div align="center"><h2>HOME PAGE QR CODE</h2><?php
  echo qrCode(array(
    
'type' => 'url',
    
'size'   => '150'// height & width in pixels
    'url'  => 'http://your_site.com/'
  ));
?></div>
<br /> <br />
<div align="center"><h2>INDIVIDUAL IMAGE QR CODES<br /> <br />
<table align="center" width="1200">
  <tr><?php foreach ($your_tableRecords as $record): ?>
 <?php foreach ($record['image'] as $upload): ?>
 <?php $num $record['num'?>

    <td align="center" valign="bottom"><img src="<?php echo $upload['thumbUrlPath'?>" width="<?php echo
$upload['thumbWidth'?>" height="<?php echo $upload['thumbHeight']?>" />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 
<?php
  echo qrCode(array(
    
'type' => 'url',
    
'size'   => '150'// height & width in pixels
    'url'  => "http:/your_site.com/your_detail_page.php?$num"
  ));
?><br /><?php echo $record['title'?><br /><hr /><br />
</td>
<?php $maxCols=3; if (@++$count $maxCols == 0): ?></tr><tr><?php endif; ?> 
<?php endforeach ?>
<?php endforeach ?>
</tr>
</table>



Here's a trick from Robin Brayer that helped me get this to work.

To append the record number to the URL you first need to create a variable.  

I used: 

<?php $num $record['num'?>


Then you can append that variable to the URL in two ways.

1) Then you can concatenate the variable to the URL like this

 'url'  => 'http://www.elleschorrphotography.com/gallery5detail.php?' . $num  


or, 

2) If you use double quotes instead of single quotes, you can add the variable directly to the string, like this:

'url'  => "http://www.elleschorrphotography.com/gallery5detail.php?$num" 
 

BUGS AND FIXES



DON'T LIKE "SORRY, NO RECORDS WERE FOUND!" TO APPEAR AFTER CLEARING THE DEVELOPER, LOG? WELL, YOU'RE NOT ALONE. - Nov 11th, 2022

Unless you're Canadian, the word 'Sorry. probably appears strange when used in some cases.

For me it was that when I cleared the developer log table, the message "Sorry, no records were found!" came up instead
of something like, "Yay, no records were found!"

According to Dave Edis, Senior Developer at interactivetools.com: It's funny, saying "Sorry" is a bit of a Canadian
cultural stereotype.  Even the transit buses say "Sorry" when they're full or not in service!
https://dailyhive.com/vancouver/canadian-buses-apologize-sorry-photos 

However, if you edit /lib/languages/en.php you can provide a different translation even for the English version.  Just
search for "Sorry":

    'Sorry, no records were found!' => 'Sorry, no records were found!',
    'Sorry, the <b>search</b> returned no results!' => 'Sorry, the <b>search</b> returned no results!',

Just replace the second string with whatever text you want."

He was, "Sorry about that,  ;-)"




PATCHING CORE PROGRAM FILE BREAKS CMSB ADMIN PAGE FORMATTING - Jul 9th, 2020

I had to patch the code in a core program file (lib/common.php) for some specific functionality, but no matter what I
tried I came up with a trashed Admin page formatting. 

What was even more strange is that when I undid the code changes, and re-uploaded what should have been the unmodified
file, the badly formatted admin pages remained. 

Both Greg Thomas from Interactive Tools and I were at a loss to explain the issue until CMSB user Steve99  came up with
the solution.

He said: "In reference to "but the page is very strangely formatted"... I've seen that happen before when patching a
file. It ended up being the DreamWeaver html editor that corrupted the file.

Try replacing the modified file with the original packaged one, then perform the edits in another editor program such as
Notepad++ or Sublime Text and upload.

I was originally using DreamWeaver and when I used Notepad++ instead, the page formatting remained correct.

Now that I think about it, Dreamweaver also sometimes caused 'cannot send header' errors by inserting a hidden blank
character before the opening PHP tag. 

Moral... Use another text editor in these rare situations.


FILE UPLOAD ISSUES WITH THE NEW HTML UPLOADER AND FILE SIZE ISSUES IN V3.50 AND OTHER VERSIONS - Dec 16th, 2019

File Upload issues with the html uploader in V3.50 and possibly other versions

Issue 1) (from version 3.15 - Not tested in 3.50) .mov files could not be uploaded without throwing a 'forbidden file
type' error.

Daniel Loewe, a programmer from Interactive Tools said that was a related to an ongoing issue that we are aware of and
currently looking at solutions for.  He suggested adding the file type quicktime (the whole word) as an allowed 
filetype. That solved the error and I could upload a small test .mov file.

Issue 2) (from version 3.50) Attempts to upload large files (300mb) of any kind would throw an error similar to  ‘414
Request-URI Too Large" error from nginx/1.14.1.1'. or 'The requested URL's length exceeds the capacity limit for this
server'. Initially, I thought this to be related to the upload_max_filesize and post_max_size settings in my php.ini
file(s). (I actually found 2 php.ini files, a global one in the site root directory and the other in my cmsAdmin
directory. (You may have to ask your webhost to change the values in the Global php.ini file for your site)

I changed both the values in both files to 999M (999Mb) and thought that might solve the issue.

When I checked the values in the Admin>General tab they still showed an upload_max_filesize of 102M, and when I checked
the php.ini, using the link in the Admin>General tab under Server Info, the values were 102M local and 999M global.

Still, trying to upload a large (300mb) file threw the nginx error. 

Again Daniel came to the rescue. 

He said: 

“That nginx error is reported when a URL is requested that is too long for the server's configuration. In this case,
this appears to be a bug in how the HTML5 uploader is handling some errors. That's something that we'll have to look
into as a secondary issue, but hopefully sorting out your configuration problems will also clear this error up.

For PHP configuration, CMSB comes with a number of different default configuration files, as different server setups
will check different files/locations. These files have different values set to help locate which one is being loaded.
102MB indicates that it's checking the .user.ini file in your cmsAdmin directory for it’s values. Try changing the
values there.”

I change both of the values to 999M in the cmsAdmin>.user.ini file, and now large uploads are working as expected with
no errors.

Hope this helps someone.


HEADER BAR AND HTML SEPARATOR STYLING FIX (PRIOR TO V3.07) - Oct 24th, 2016

From CMSB version 3.0 through 3.06 styling in Header Bars has shown as html instead of actually styling the Header Bar.

Dave Edis, Senior Programmer at InteractiveTools  has offered a simple fix until Version 3.07 is released.

He said:

In /lib/admin_functions.php search for "to display even" and replace this line:

$attrs['label'] = $attrs['label'] !== "" ? htmlencode($attrs['label']): '&nbsp;'; // default to &nbsp; to display even
with empty string

With this line:

$attrs['label'] = $attrs['label'] !== "" ? $attrs['label'] : '&nbsp;'; // default to &nbsp; to display even with empty
string  


In /lib/menus/database/editTable_functions.php

To display up to 50 characters of text in an html separator in the field list instead of just --html--

In /lib/menus/database/editTable_functions.php

replace this block: 


if (@$field['type'] == 'separator') {
  if ( @$field['separatorHeader']) { echo "<div class='text-center' style='line-height: 200%'><b>---
{$field['separatorHeader']} ---</b></div>\n"; }
  else                             { echo "<div class='text-center'>--- {$field['separatorType']} ---</div>"; }
}

With this block: 

if (@$field['type'] == 'separator') {
  $htmlAsText = htmlencode(mb_strimwidth(strip_tags($field['separatorHTML']), 0, 50, "..."));
  if     ($field['separatorType'] == 'header bar') { echo "<div class='text-center' style='line-height: 200%'><b>---
{$field['separatorHeader']} ---</b></div>\n"; }
  elseif ($field['separatorType'] == 'html')       { echo "<div class='text-center' style='line-height: 200%'><b>---
$htmlAsText ---</b></div>\n"; }
  else                                             { echo "<div class='text-center'>--- {$field['separatorType']}
---</div>"; }
}


Dave said: We'll include that in V 3.07 and also enable the "Field Label" field so you can manually specify a field
label to be displayed in the field editor as well if desired.


CMSB SOMETIMES LOGS ME IN AUTOMATICALLY - Aug 6th, 2010

It’s not a bug. According to Dave Edis: Starting with about version 1.32, “We've switch the way login credentials
are stored so they are no longer removed when the browser is closed and are stored for up to 24 hours.  It used to be
stored until the browser was closed.”


STRANGE ERROR MESSAGES - Aug 6th, 2010

Ever consistently get a:
 
Warning: preg_match(): Unknown modifier '\' in /yourserverpath/cmsAdmin/lib/menus/default/save.php on line 132

when you try to save a record? Well, that’s a bug that has been fixed in the latest CMSB v1.34 and above.

It occurs when a non-escaped /appears in one of your “disallowed characters” fields. The / is ending that regexp
early and confusing it. (Search for regex in the cookbook to learn more about regular expressions).

If you don’t want to upgrade right now, you can fix the problem by backslashing the / to let the regex expression know
it's meant to be a character. As in:



!@#$%^&*()=_":;<>?\/\~`-



DEFAULT VALUE TEXT NOT COPIED TO RECORDS - Aug 6th, 2010

This was a bug in the first release version of CMSB1.34 which has been fixed.


GETTING ERRORS AFTER RESTORING A DATABASE? - Dec 31st, 2012

Dave Edis from Interactive Tools offered this possible fix. He said:

"Generally you need everything to match up, so the /data/schema/ folder (or the fields under the field editor) should be
the same as when the backup was made).

If you are getting errors because there is a field in the schema (created in the field editor) that doesn't exist in the
MySQL Database you can just click on "Section Editors" and it will automatically check and re-create any fields that are
missing from the MySQL."

CHAPTER 6 - SUBSCRIBER CONTRIBUTED RECIPES



CREATE A 10 STAR RATING SYSTEM IN CMSB - Dec 29th, 2018

It’s become quite common for hotels, attractions and other listings to include both reviews and star rating systems in
their listings.

Here’s how to create a star rating system for your site, using CMSB. It’s here courtesy of CMSB user thenetgirl, who
created it for a rental property client.

EDITORS
First, you’ll need to create 2 multi-record editors. They’re called “Vacation Rentals” and “Ratings”.

* You can create preformatted editors by upload the ,ini.php files in the download package below to your
cmsAdmin/data/schemaPresets folder and then creating the editors from
the "select presets" pulldown menu

The Vacation Rentals editor contains the listing information records for the individual properties. 

(Each record contains listing information for a single property. The “Property Code” field value in the “Vacation
Rentals” editor is used in a pull down list to populate the “Property Code” field in the “Ratings” editor.  )

The Ratings editor contains the customer submitted ratings for the individual properties.
(There can be multiple “Ratings” records for the same property. The “votes” are averaged and displayed in the
appropriate viewers. The VacationRentalsDetail viewer shows only the overall rating for the property and the MoreRatings
viewer shows detailed ratings for that property.

VIEWERS
There are 3 viewers that you’ll need to create: VacationRentalsList.php, VacationRentalsDetail.php, and
MoreRatings.php.

The Vacation Rentals viewers are standard list and detail pages for the property listings. Code to display the "Overall
Rating" and a link to the MoreRatings detailed ratings viewer are added to the VacationRentalsDetail page

The MoreRatings viewer is the one that shows the average of the ratings submitted for the particular property. Code to
display the detailed ratings is added to this viewer.

IMAGES
You’ll also have to create the “star” images to be displayed as the ratings. I’ve created some simple circle
gifs with the numbers 1-9 for this demonstration. (They are hard coded to be found in an images/stars folder in the
directory that contains your viewer files, so if you change the location, don’t forget to change the viewer coding.)

Rather the listing all the code here, you can download the ini.php* files the sample viewer files, and a folder
containing the 9 sample star gifs from:

http://thecmsbcookbook.com/downloads/ratings.zip

** DON’T FORGET TO CHANGE THE "LOAD VIEWER LIBRARY  - $dirsToCheck" PATH IN THE SAMPLE VIEWERS TO YOUR OWN SERVER
PATH.

Before you can see the rating system in operation, you’ll need to create some “property listings” records in the
Vacation Rentals editor and then some Ratings records for those properties in the Ratings editor. 

Once you’ve created a working system, you can customize the components for your specific needs.

You can also use the membership plugin to allow previously registered guests to post their own ratings and comments.


AN EVENTS CALENDAR USING CMSB - Sep 11th, 2020

Small change October 11, 2018 

After testing with PHP7 and CMSB 3.15, I had to move the header line <?php header('Content-type: text/html;
charset=utf-8'); ?> in caledar5.php to after the 'load viewer library' call, or I'd get the errors:


E_USER_ERROR: removePrefixedCookie: Can't remove cookie(loginsession), headers already sent! Output started in
/home3/ellescho/public_html/thecmsbcookbook/calendar5.php line 1.

/home3/ellescho/public_html/thecmsbcookbook/cmsAdmin/lib/common.php (line 757)
http://www.thecmsbcookbook.com/calendar5.php


  AND


E_WARNING: session_start(): Cannot send session cache limiter - headers already sent (output started at
/home3/ellescho/public_html/thecmsbcookbook/calendar5.php:1)

/home3/ellescho/public_html/thecmsbcookbook/cmsAdmin/lib/init.php (line 713)
http://www.thecmsbcookbook.com/calendar5.php


This recipe has been updated to reflect the latest code changes as of 2/2/2011 
The zip file was recompiled on April 9, 2011 
If you were having difficulties with single events, please re-download the zip file.

Zickey, who bills himself as “just a guy who uses CMS Builder” put together this really cool calendar application
and has been kind enough shared both the code and instructions with us. He asks that if you utilize the Zickey calendar,
you read and agree to the terms in the terms.txt document, which basically say that if you use the calendar, don’t
blame him for anything that doesn’t work the way you expect it to.

To implement the Zickey calendar, you’ll need:

A multi record editor called “events”
A viewer called “calendar5.php”
A detail viewer called “events”detail.php”
The “calendar5.css” file

You can download all the required viewer files, the.css file, the events.ini.php file*, the terms.txt file, and a copy
of Zickey’s help file, instructions.rtf from:

http://www.thecmsbcookbook.com/downloads/zickey_calendar5.zip

You can see an operating calendar example at 

http://www.thecmsbcookbook.com/calendar5.php

The calendar contains sample events spanning September and October 2020.

***To make the calendar load the event entries from your site, the calendar5.php  $dirsToCheck = array code on line 5
must be updated with your own path. You'll also need to modify the eventsDetail.php file the same way. If needed, you
can find the path by opening the events editor in the CMS Builder "Code Generator".   

Note: The eventsDetail.php viewer is for example only and taken directly from the CMSB code generator.

*** to create your own events editor, upload the events,ini.php file to your cmsAdmin/data/schemaPresets folder and then
create the editor from the "select presets" pulldown menu

Setup is extremely simple:
 
1) create the “events” editor and create some test events.
2) Change the “l $dirsToCheck = array” paths and upload the calendar.php, and eventsDetail.php viewers to your
server
3) Upload the calendar5.css file to your css directory

That’s it, the calendar should be fully functional.

The calendar uses a “maxwords” function to limit the amount of words shown on the calendar in the optional event
description field. If you’re unfamiliar with the maxwords function you can learn more about it in the recipe,
“LIMITING THE NUMBER OF WORDS SHOWN IN A PAGE”.

**** The Zickey calendar is a collaborative project, so if you make any useful changes, or add any functionality, please
post your changes and  additions to:

http://www.interactivetools.com/iforum/Products_C2/CMS_Builder_F35/P82903

As always, after you’ve got the example working, you can add any required fields and style the pages the way you’d
like.


ATTENDANCE TRACKING SYSTEM - Dec 29th, 2018

A client of mine needed to create an attendance tracking system for a group of 2 lectures in a series. They needed to be
able to have their attendees log in on site using a tablet, have welcome emails sent to first time attendees with an
express sign in ID (record number) and generate email lists based on either attendees to the lecture series, attendees
to a specific lecture on a specific date, or everyone who ever attended a lecture.

The solution uses 3 editors. 

A multi-record editor called “Attendance Listings”, with the following fields.

First Name (text field)
Last Name (text field)
Email Address (text field)
Lecture Name (list field values pulled from the Lecture Names database)
Attended (text field populated with the month, year and lecture code of the last attended lecture)

A multi-record editor called Lecture Names with only one field. (In my case there are 4 records; Lecture 1 (record # 1),
Lecture 2 (record # 2), Category 1 (record # 3),  and Category 2 (record # 4)

And a single record editor called “Attendee Messages”, with text fields called welcome_email_message_1 and
welcome_email_message_2

THEORY
When an attendee fills out and submits the sign in form with either their Attendee ID number (record number) for express
sign in, or their first name, last name and a valid email address, the “Attendance Listings” database is checked for
a matching ID number or a matching email address.

If neither match is found, a new record is created with first_name, last_name and email_address field values, the
“Lecture 1" check box is checked in the lecture_name field and the current month, year and lecture code are inserted
into the “Attended” field. In addition, a welcome email is sent to the attendee with their Attendee ID number, which
can be used for future “Express” sign ins.

If a matching Attendee ID number is found the record is updated by appending the current month, year and lecture code to
the “Attended” field and, if required, the appropriate additional lecture name check box is checked.

If a matching email address is found and there are no other errors, the record is updated by updating their first and
last name where necessary, and appending the current month, year and lecture code  to the “Attended” field. If
required, the appropriate additional lecture name check box is checked..

The report viewer allows series administrators to generate email lists for all those who attended a particular lecture
series, those who attended a particular lecture, or those who belong to a specific category (media, etc.).  The lecture
date queries are automatically generated, so that their format is consistent.

For simplicity, I decided to create a separate sign in form for each lecture series (one set is attached as an example)

You can download the sign in form for the Lecture 1 series, the report viewer, and the php.ini files for the 3 editors
involved from:

 http://www.thecmsbcookbook.com/downloads/attendance.zip

I’ve sanitized the viewers and .ini files to be more generic, so if you come across any issues, email me with what you
find and I’ll see if I can resolve them.

GOING FURTHER
There are still 2 issues to resolve:

1) Modifying the code so that any number of lectures can be automatically added and tracked. (I don’t mind generating
a separate viewer for each new lecture, but I’d like to do that automatically as well).

2) Generating the welcome email in a more dependable way (right now I’m getting around the fact that a new record
needs to be generated before its record number can be captured, by delaying the code for .5 seconds with a
usleep(500000); command).

Any help would be appreciated.


MOBILE PHONES

As I come across insights regarding developing/re purposing web sites for mobile phones, I'll list them here. 

If you've got any favorites to share, contact me and I'll add them as well.


DETECTING MOBILE PHONES AND TABLETS - Jan 31st, 2023

If you can't detect if a visitor is using a mobile phone, you can't feed them information that's tailored to them.

Here's an article that suggests some code to accomplish the task. 

NOTE: There's a specific implementation for using this with the Website Membership Plugin (through v 1.05) later in this
recipe.

http://azure.ironie.org/478-php-mobile-phone-detection

And here's the code that they suggest:

The function:


<?php
    function mobile_detection ()
{
    if (isset(
$_SERVER['HTTP_X_WAP_PROFILE']) || isset($_SERVER['HTTP_PROFILE']))
        return 
true;
 if (isset (
$_SERVER['HTTP_ACCEPT']))
    {
        
$accept strtolower($_SERVER['HTTP_ACCEPT']);
        if (
strpos($accept'wap') !== false)
            return 
true;
    }

    if (isset (
$_SERVER['HTTP_USER_AGENT']))
    {
        if (
strpos ($_SERVER['HTTP_USER_AGENT'], 'Mobile') !== false)
            return 
true;

        if (
strpos ($_SERVER['HTTP_USER_AGENT'], 'Opera Mini') !== false)
            return 
true;
            
    if (
strpos ($_SERVER['HTTP_USER_AGENT'], 'iPad') !== false)
            return 
true;
    
if (
strpos ($_SERVER['HTTP_USER_AGENT'], 'iPhone') !== false)
            return 
true;
            
            if (
strpos ($_SERVER['HTTP_USER_AGENT'], 'Tablet') !== false)
            return 
true;
    }
    
    return 
false;
}
    
?>


And the implementation


<?php if (!isset ($_SESSION['mobile']))
  
$_SESSION['mobile'] = mobile_detection();
if (
$_SESSION['mobile'] == true)
  echo 
'<link rel="stylesheet" type="text/css" href="style/version-mobile.css" />
<meta name="HandheldFriendly" content="true" />
<meta name="viewport" content="width=device-width, user-scalable=yes, initial-scale=1.0, maximum-scale=3.0,
minimum-scale=0.25" />';

?>


You can use this concept to replace any code.  Here's an implementation to replace a flash .swf masthead with a .png

*** NOTE: this implementation t works with the Website Membership Plugin through V1.05 ***
Currently, if Website Membership Plugin V1.06 is used, redirect to login does not redirect the user to the calling page.
If anyone solves this, please pass the information on so I can update this recipe.


<?php if (mobile_detection()) : ?>

<img src="images/masthead.png" width="800" height="183" />

<?PHP else: ?>

<script type='text/javascript'>
AC_FL_RunContent(
'codebase','http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,0,0','height','180','width','100%','pluginspage',
'http://www.macromedia.com/go/getflashplayer','src','images/masthead','wmode','transparent','quality','best','play','true','movie','images/masthead'
);
</script>
 
<noscript>
<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"
codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,0,0" height="180"
width="100%">
                                <param name="movie" value="images/masthead.swf" />
                                <param name="quality" value="best" />
                                <param name="play" value="true" />
                                <param name="wmode" value="transparent" />
                                <embed height="188" pluginspage="http://www.macromedia.com/go/getflashplayer" src="images/masthead.swf"
type="application/x-shockwave-flash" width="100%" wmode="transparent" quality="best" play="true"></embed> 
      </object></noscript>
                            
<?PHP endif ?>  
 

The article goes further to describe the reasons for this particular code, 

Here's an implementation that chooses between viewers:


<?php $_SESSION['mobile'] = mobile_detection();  ?>
                    <?php if (!isset ($_SESSION['mobile']) OR $_SESSION['mobile'] == true) :
?>your_file_for_tablet.php?<?php echo $record['num'?><?php else: ?>tour_file_for computer.php?<?php echo
$record['num'?><? endif?>"><span class="sub_heading_font"> Your Text</span></a>
 

There's also an article describing the meta tags used for mobile phones at: 

http://learnthemobileweb.com/2009/07/mobile-meta-tags/

CHAPTER 7 - OFF THE TOPIC

TRICKS



EASY EXPANDABLE SECTIONS ON YOUR WEB PAGES - Nov 5th, 2022

Need to create 1 or more expandable sections on your pages?
Here's how, using just a few html 5 tags, <details> and <summary> ...

On a Detail Page


<details>
    <summary style="text-decoration:underline">Click/Tap for an expanded version</summary>

    <p class="text_font"><?php echo $yourRecord['your_field']?></p>
</details>


Or on a list page


 <?php foreach ($your_tableRecords as $record): ?>
<details>
    <summary style="text-decoration:underline">Click/Tap for more about <?php echo $record['title']?></summary>
    <?php echo $record['your_description_field']?>
</details>
<?php endforeach ?>

BEST PRACTICES



403 ERROR WHEN TRYING TO INSTALL CMSB - Jul 26th, 2022

User w2kd discovered this when trying to activate CMSB running php 7.4 and folders set to 755 and files 666 file
permissions.

According to Daniel Louwe, Technical Lead at interactive Tools:

Some webservers will not open files with certain permission configurations - in particular they will prevent access to
files that are too permissive. In this case, having the permissions set to 666 allows anyone to write to the file, which
was likely the cause of the issue. Tightening the permissions to 644 (only the owner of the file has write access)
satisfies the webserver's rules.

Further reading on Linux file permissions: https://linuxize.com/post/chmod-command-in-linux/


UPGRADING BLUEHOST FROM SHARED TO PRO HOSTING AND SSL - Jun 10th, 2018

Things learned when upgrading from shared hosting to pro hosting with a dedicated IP address and implementing SSL
certificates on Bluehost.

In the original setup on shared hosting all site files for both example.com and example.org were in a sub-directory
called 'example' in the 'public_html' folder and the .htaccess file in the 'example' sub-directory redirected the
domains to point to the appropriate index file.

The original code for the .htaccess file in the 'example' sub-directory was:

# Use PHP5.4 Single php.ini as default

AddHandler application/x-httpd-php54s .php

RewriteEngine on

RewriteCond %{HTTP_HOST} ^(www.)?example.com$

RewriteCond %{REQUEST_URI} !^/example/

RewriteCond %{REQUEST_FILENAME} !-f

RewriteCond %{REQUEST_FILENAME} !-d

RewriteRule ^(.*)$ /example/$1

RewriteCond %{HTTP_HOST} ^(www.)?example.com$

RewriteRule ^(/)?$ example/index.php [L] 


RewriteEngine on

RewriteCond %{HTTP_HOST} ^(www.)?example.org$

RewriteCond %{REQUEST_URI} !^/example/

RewriteCond %{REQUEST_FILENAME} !-f

RewriteCond %{REQUEST_FILENAME} !-d

RewriteRule ^(.*)$ /example/$1

RewriteCond %{HTTP_HOST} ^(www.)?example.org$

RewriteRule ^(/)?$ example/index.php [L] 

Here are the changes that were required after upgrading to pro hosting and SSL certificates:

1) I needed to re-assign database permissions to users, since they were wiped during transfer to pro hosting.

Since I still wanted both .com and .org to point to the same 'example' sub-directory,

1) my client needed to obtain 2 SSL certificates.

2) 2 separate, and different, .htaccess files were required. One in the 'public_html' directory and one in the 'example'
sub-directory.

In the 'public_html' .htaccess file, I added the following code to turn on the HTTPS requirement (don’t know if it was
actually necessary, but it seemed to make things work, and doesn’t seem to break anything):

RewriteEngine On
RewriteCond %{HTTPS} !=on
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]

In addition, since the .htaccess file in the 'public_html' directory now only redirects the .com (primary domain) to the
'example' sub-directory, the entire code block for the .org was commented out.

# Use PHP5.4 Single php.ini as default

AddHandler application/x-httpd-php54s .php

RewriteEngine On
RewriteCond %{HTTPS} !=on
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]

RewriteEngine on

RewriteCond %{HTTP_HOST} ^(www.)?example.com$

RewriteCond %{REQUEST_URI} !^/example/

RewriteCond %{REQUEST_FILENAME} !-f

RewriteCond %{REQUEST_FILENAME} !-d

RewriteRule ^(.*)$ /example/$1

RewriteCond %{HTTP_HOST} ^(www.)?example.com$

RewriteRule ^(/)?$ example/index.php [L] 

#RewriteEngine on

#RewriteCond %{HTTP_HOST} ^(www.)?example.org$

#RewriteCond %{REQUEST_URI} !^/example/

#RewriteCond %{REQUEST_FILENAME} !-f

#RewriteCond %{REQUEST_FILENAME} !-d

#RewriteRule ^(.*)$ /example/$1

#RewriteCond %{HTTP_HOST} ^(www.)?example.org$

#RewriteRule ^(/)?$ example/index.php [L] 

The .htaccess file in the 'example' sub-directory redirected both .com and .org to the appropriate index file. (Note the
line that was commented out (#) of the original shared hosting .htaccess file code)

# Use PHP5.4 Single php.ini as default
AddHandler application/x-httpd-php54s .php

RewriteEngine on
RewriteCond %{HTTP_HOST} ^(www.)?example.com$
RewriteCond %{REQUEST_URI} !^/example/
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
# RewriteRule ^(.*)$ /example/$1
RewriteCond %{HTTP_HOST} ^(www.)?example.com$
RewriteRule ^(/)?$ example/index.php [L] 

RewriteEngine on
RewriteCond %{HTTP_HOST} ^(www.)?example.org$
RewriteCond %{REQUEST_URI} !^/example/
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
# RewriteRule ^(.*)$ /example/$1
RewriteCond %{HTTP_HOST} ^(www.)?example.org$
RewriteRule ^(/)?$ example/index.php [L] 



ENCRYPTING E-MAIL ADDRESSES AND HYPERLINKS WITH THE SPAMBOTEMAILPROTECTOR PLUGIN - Dec 29th, 2018

OK, so I’m slow to adopt things for just the sake of doing it. You know, if it ain’t broke, don’t fix it.

Well, this time it was “broke” so I didn’t have any choice.

I had been using the “emailcode.class”email encryption scheme described later in this recipe and was really happy
with the results. Yes, it meant encoding the e-mail addresses in a special way, but once I got the hang of it, it was
pretty easy.

The problem appeared when I tried to email a web page containing an encrypted email address (using IE and the
File>Send>Page by Email feature) and the addresses in the email either didn’t work or didn’t show up at all.  

Enter the Spambot Email Protector plugin from Interactive Tools. 

Problem is that the latest version (2.00) of the spambotEmailProtector plugin solved an earlier problem where only the
first email address on a page was encrypted. 

However, it created another problem because it broke links like PayPal Buttons that rely on an email address for
payments and such.

Until all of these issues are resolved in a new version of the plugin, Robin Brayer from Interactive Tools came up with
an interim solution that allows you to turn off the spambot protector on a particular page.

You can download his beta version of the spambot protector plugin from:

http://thecmsbcookbook.com/downloads/spambotEmailProtector.zip

If you don't want the spambot plugin to run on a particular viewer, add this line to the top of the viewer code. 
 
Note: it need to be the top, above any includes.



$GLOBALS['SEP_DISABLED'] = 1;



There are also some changes that you might want to make in the settings at the top of the page to fix other issues that
you find:

Near the top of the spambot-email-protector.php you’ll find a list of Encoding Options, change this:




$GLOBALS['SEP_ENCODING_METHODS']['jsLocation']  = true;



To this:



$GLOBALS['SEP_ENCODING_METHODS']['jsLocation']  = false;



I also found that unless I changed this: 



$GLOBALS['SEP_ENCODING_METHODS']['bdo']         = true;



To this:



$GLOBALS['SEP_ENCODING_METHODS']['bdo']         = false;



When a page was sent by the above method, the email addresses appeared backwards.

Other options may cause other strange results, and you can't break the plugin, so experiment until you find the set of
options that works for you.

That’s it.  An added benefit is that any standard email format or hyperlink on the page is automatically encrypted, so
it’s easy to encrypt emails and web addresses that are pulled from the database without any special formatting. 

And, the encryption works on any web page that uses PHP, whether it's part of a CMSB managed site or not.

The only requirement is that you insert this line of code at the head of your page, after the /viewer_functions.php call
if it's a CMSB viewer:




<?php include_once "/your_path_to/spambot-email-protector.php" ?>



If you need to find the path, just call the spambot-email-protector.php file from your browser and the path will be
listed. 

Well worth the $39.95. You can purchase the SPAMBOT EMAIL PROTECTOR at:

http://www.interactivetools.com/order/




Even though I've opted to use the SPAMBOT EMAIL PROTECTOR, there’s another method that’s worth discussing. A big
thanks to Djulia for unearthing this and refining it for use with CMSB.

Copies of the files discussed below can all be downloaded from:

http://www.thecmsbcookbook.com/downloads/email_encryption.zip 

In a multi-record CMSB database that contained the e-mail field “e-mail”, here’s the usual unencrypted code to
pull an e-mail address from a record:


<a href=”<?PHP echo $record['e_mail'?>”>Email</a>
_

    
The encryption method, the unrefined version of which is documented at:

       http://aspirine.org/emailcode.php_en.html  

requires the file “emailcode.class.php” exists in the location entered in the “require” statement below . ***
Djulia found a bug in the original file, so use the one that you download from 

        http://www.jkwebdesigns.com/email_encryption.zip

This is the code that must get inserted at the beginning of the body section of your web page:


<?PHP 
    require 'emailcode.class.php';
    
$emailcode = new ClassEmailcode();?>



On a list page insert this code where you want the encrypted e-mail address to appear (inside an appropriate foreach
loop):


<?PHP echo $emailcode->emailgetencode($record['e_mail'],'VISIBLE TEXT','Your mail subject','YOURCLASS','xhtml'); ?>



For a detail page, you’d use this code:


<?PHP echo $emailcode->emailgetencode($your_tableRecord['e_mail'],'VISIBLE TEXT','Your mail
subject','YOURCLASS','xhtml'); ?>



(To include a “subject” line in the generated e-mail message,  include 'Your mail subject', as the 3rd parameter in
the statement. If you don’t want a subject line, take out the text, but leave the ‘’”.)

If you want to encode a single e-mail address on a page or one that’s not pulled from the database, insert this code
where you want the encrypted e-mail address to appear:


<?PHP echo $emailcode->emailgetencode('yourmail@yourprovider.com','VISIBLE TEXT','Your mail
subject','YOURCLASS','xhtml');
?>



IF YOU WANT TO USE AN IMAGE INSTEAD OF TEXT FOR YOUR LINK

You can use:


<?PHP echo $emailcode->emailgetencode($record['e_mail'],'<img border="0" src="images/yourimage.jpg">','Your mail
subject','YOURCLASS','xhtml'); ?>


 (.jpg and .gif are OK too)

IF YOU WANT TO PULL THE IMAGE FROM YOUR DATABASE 

Since you cannot put PHP tags inside a string inside a PHP call. Here's an alternate approach which was provided by
Chris from Interactive Tools:

The following code will find the first image in the e-mail_logo field and generate an <img> tag for it, or use the
string "You can click here to e-mail us" if no image is available, then pass that off to $emailcode->emailgetencode().


<?php 
$visibleText "You can click here to e-mail us"
foreach (
$yourRecord['e-mail_logo'] as $upload): 
if (
$upload['isImage']): 
$visibleText "<img src=".$upload['thumbUrlPath']." width=".$upload['width']." height=".$upload['height']."  border='0'
alt='' />"
break; 
endif; 
endforeach; 
echo 
$emailcode->emailgetencode($common_informationRecord['contact_e_mail'],$visibleText,'','special','xhtml'); 
?>


They may be hard to see, but don’t forget the periods which are the concatenation operator in PHP and which joins two
string together.


CREATING PRINTER FRIENDLY PAGES - Aug 6th, 2010

Thanks to Kevin B. for this find. 

He said: I have found the best way to make your website 'Printer Friendly' is to create a CSS stylesheet for printing.
This has worked very well for the sites I have designed. You create ONE stylesheet as opposed to TWO versions of EACH
web page or document you want to be able to print and worrying about linking to the second 'print version'. You set the
elements which you do not want to be printed (say your menu or background, etc.) to 'display:none'

Try a Google search on 'css print styles' and you will find tons of info. A great starting point would be here:

       http://www.alistapart.com/stories/goingtoprint/

Jake offered another solution. He said:
Another technique to set up a printer-friendly page in CMS Builder would be to create a new viewer page with a
scaled-back design to accommodate printing. You can link to this printer-friendly page from your regular page by
creating a link like this, so that the page's record number is passed along:



<a href="/path/to/printer_friendly.php<?PHP echo $record['num'?>">Printer-friendly Page</a>



PASSWORD PROTECT YOUR WEB PAGE CONTENTS WITH ONE LINE OF CODE - Aug 6th, 2010

If you’re not using the Membership Module, you can still password protect your pages. Thanks to Djulia for
recommending:

       http://www.zubrag.com/

They’ve got a bunch of free PHP based scripts to add functionality to your project. 


MAILTO ERRORS WHEN USING COMMAS TO SEPARATE MULTIPLE E-MAIL ADDRESSES - Aug 6th, 2010

Thanks to rcrofoot for this one:

It seems that while the specification for Internet Message Formatting RFC2822, 

       http://www.faqs.org/rfcs/rfc2822 

says that commas are the standard, Outlook (2003, 2007) by default, Microsoft Office Outlook 2003 and Microsoft Office
Outlook 2007 do not recognize the comma as an e-mail address separator. (Thanks again Mr. Bill)

You can follow these steps to configure Outlook to recognize the comma as a valid e-mail address separator:

1. On the Tools menu, click Options.
2. Click E-Mail Options, and then click Advanced E-Mail Options.
3. Under When sending a message, click to select the Allow comma as address separator check box.

Note You can still use a semi-colon (;) to separate e-mail addresses when you click to select the Allow comma as address
separator check box.


USING FORMS INSTEAD OF MAILTO FOR CONTACT (GOOD) - Aug 6th, 2010

To help eliminate some of the issues that occur for users of some web based e-mail services when they try to access a
mailto tag on your pages, you might consider using forms.

There are some spam issues that come with the use of forms, but this script, which was found by kevbarker, a member of
the Interactive Tools community support team, attempts to protect from most of them. It also includes a number of other
nice features.  Remember, no guarantees, but you may find it helpful.

        http://green-beast.com/gbcf-v3/

It’s not extremely flexible, but it does the job.


USING FORMS TO GO - Aug 4th, 2012

FormsToGo ( http://bebosoft.com/products/formstogo/overview ) at $30 turns out to be an extremely flexible program for
generating (form mail) scripts from your forms with a long list of available features. It supports multiple recipients,
popup error messaging to handle form errors and lots more. I also found their tech support extremely responsive.

Here are a few things that I've learned that can make using FormsToGo a bit easier.

NOTE: All changes are in the generated php file and will have to be re-done each time you re-generate the file.

1) Their built in Captcha implementation is case sensitive. To make it case insensitive they suggested searching the
following:

NOTE: $FTGcaptcha will change depending on what you've called your captcha field. Mine was called 'verify' so I searched
for $FTGverify.



$FTGcaptcha = DoStripSlashes( $_POST['captcha'] ); 


and add the strtoupper function to force the entry to upper case :



$FTGcaptcha = strtoupper( DoStripSlashes( $_POST['captcha'] ) ); 


2) I wanted to implement HTML Error checking on my form (any errors pop up on attempted submission and the for data does
not get cleared) but but couldn't get it to work without the following:

Search for:



$codeHtmlForm = ProcessPHPFile($fileHtmlForm);


and change it to:



$codeHtmlForm = file_get_contents('http://www.your_domain.com/your_form.php');




 .



SELECTING A FORM'S EMAIL RECIPIENTS USING FIELDS IN THEIR ACCOUNT RECORD (A FORMS TO GO MODIFICATION) - Jun 26th, 2015

The initial purpose of this recipe is to be able to control an organization’s board of director positions, committee
memberships and easily designate who the chairpersons of committees are and who is to receive email for the various
positions and committees.

To make the board of director positions and committees flexible, I created a multi record editor called “Board Of
Director Positions”. The records in this section have one text field called “Position” and a dragSortOrder field.
NOTE: The records in “Board Of Director Positions” are where the options in the form below come from.

The organization wanted to allow a member to hold more than one position on the board of directors and/or to sit on up
to 3 committees.

So, in the “User Accounts” section there are 3 sets of 3 Board Of Director fields.

A list field for position (1, 2 and 3), a check box for the chairperson of each, and a check box for the designated
email recipient for each.
 
The Board of Director Position pull down lists fields (board_of_director_position_1, board_of_director_position_2, and
board_of_director_position_3) get their options from the database  “board_of_director_positions” with Option Values
from the “num” field, and Option Labels from the Positions Field.

The check box fields (board_of_director_position_1_chair, board_of_director_position_2_chair,
board_of_director_position_3_chair),  signify that this person is the chairperson.

The check box fields (board_of_director_position_1_email, board_of_director_position_2_email,
board_of_director_position_3_email) designate the person who gets email for the board position or committee.

I created the basic form, and then used Forms To Go from Bebosoft.com to create the PHP code required to hide email
address values, route email messages, send confirmations, include captcha and generate error checking code.

I then modified the code to pull the appropriate position and email information from the records in the “User
Accounts” section.

I had a bit of trouble getting the code to generate a set of variables until Claire Ryan from Interactive Tools came to
the rescue and suggested the use of a double $ ($$) to create the variables required.

She also shared the idea of including a variable dump to see if variables were actually being generated in response to
an if statement. The following, which checked the President position and it’s associated variable:

The original static code for each position (President is only an example) was in the format:



if ($FTGreason == "President") {
     
  $emailTo = $president;
  
  $emailFrom = FilterCChars("$FTGemail");
   
  $emailHeader = "From: $emailFrom\n"
   . "MIME-Version: 1.0\n"
   . "Content-type: text/plain; charset=\"UTF-8\"\n"
   . "Content-transfer-encoding: 8bit\n";
   
  mail($emailTo, $emailSubject, $emailBody, $emailHeader);
  
 }



She said: “Just before you get to the check if($FTGreason == "President"), add the following code to see if the
variables are being processed correctly:”


var_dump ($FTGreason);
var_dump ("President");
var_dump ($president);
die; 



Claire went on to say: “What you should see is some information like string(9) "President". The var_dump will show you
what FTGreason actually is just before it hits this check, and it won't pass and execute the email code if it doesn't
match what you expect it to be. This is by far the most common reason that if statements stop working, and I've been
caught by it so many times it's the first thing I look at when troubleshooting.

The die statement will obviously kill the script so it'll output the var_dump without processing any further.”

OK, back to the recipe code.

***DON'T FORGET TO CHANGE ALL REFERENCES TO GENERIC PATHS AND DOMAIN NAMES TO MATCH YOUR OWN***
*** IF YOU USE NO-REPLY@YOUR_DOMAIN.COM AS THE RETURN EMAIL ADDRESS< AND DON'T SET UP A REAL EMAIL ACCOUNT FOR THAT
ADDRESS AT YOUR DOMAIN, SOME PROVIDERS MAY BLOCK YOUR EMAILS***

Here’s the code for the form that the visitor sees:  ( *** Name the contact form page "contact_form.php" *** ) 


 <div align="left"><span class="body-text-bold">USE THIS FORM TO SEND A MESSAGE DIRECTLY TO THE PEOPLE WHO CAN ANSWER
YOUR QUESTION:</span>
                            
                            <div class="error-text-yellow-italic" align="left"><!--VALIDATIONERROR--></div>
<?php
$names = array();
foreach (
$board_of_director_positionsRecords as $record){
$names[$record['position']]=$record['position'];
}
?>
        <form method="post" action="http://www.your_domain.com/master_form_php.php">

<table width="80%" border="0" cellpadding="15">
  <tr >
    <td width="40%" align="left" valign="middle"><label class="body-text-bold">Who would you like to
contact?</label></td>
    <td  style="text-align:left" width="60%" align="left" valign="middle"><select name="reason"> 
<option value="" >...Select...</option>
<?php foreach($names as $name): ?>
<option value="<?php echo $name;?>"><?php echo $name;?></option>
<?php endforeach?>
</select></td>
  </tr>
  <tr>
    <td width="40%" align="left" valign="middle" class="body-text-bold">What's yourFirst Name</td>
    <td style="text-align:left" align="left" valign="middle"><input class="text" type="text" name="first_name"
id="first_name" value="" /></td>
  </tr>
  <tr>
    <td width="40%" align="left" valign="middle" class="body-text-bold">Your Last Name</td>
    <td style="text-align:left" align="left" valign="middle"><input class="text" type="text" name="last_name"
id="last_name" value="" /></td>
  </tr>
  <tr>
    <td width="40%" align="left" valign="middle" class="body-text-bold">Your e-mail</td>
    <td  style="text-align:left" align="left" valign="middle"><input class="text" type="text" name="email" id="email"
value="" /></td>
  </tr>
  <tr>
    <td width="40%" align="left" valign="middle" class="body-text-bold">Re-enter your e-mail</td>
    <td  style="text-align:left" align="left" valign="middle"><input class="text" type="text" name="email2" id="email2"
value="" /></td>
  </tr>
  <tr>
    <td width="460%" align="left" valign="middle" class="body-text-bold">What's your message</td>
    <td  style="text-align:left" align="left" valign="middle"><textarea class="textarea" name="message" cols="60"
rows="6" id="message"></textarea></td>
  </tr>
  <tr>
  <td width="40%">
  <span class="body-text">To help prove that you're a human, 
  please enter the characters into the blank box</span>
  </td>
  <td  style="text-align:left" align="left" valign="middle"><img
src="http://www.your_domain.com/master_form_php.php?formstogoimgflt=yes" height="40" width="160"/>&nbsp; &nbsp;
    <input type="text" name="verify" id="verify" width="200" value="" /></td></tr>
  <tr>
  <td colspan="2" align="left" valign="middle"><input type="submit" name="form_submitted" value="Submit Your Message"
/></td>
  </tr>
</table>
</form>
</div>


And Here’s the complete code for the PHP page that processes the form, including vcode to accomodate more than one and
0 selected recipients for a position or committee.: ( ***Name the PHP page "master_form_php.php"*** ) 

NOTE : I've set up a test field called "board_backup_email" in a single record editor called "Common Information" to
contain the backup email address used in case there is no email address chosen for a particular committee or position.
If you choose not to do this, you'll need to replace the board_backup_email field call with a hard coded address.

NOTE: It's tempting to add comments to a script, but in this case, make sure that they are at the end of an existing
line of code and NOT in a new line. 

Extra lines in this case can break the page and cause errors that will keep it from working (like cannot modify header
errors, etc.).



<?php
$GLOBALS['SEP_DISABLED'] = 1?>
<?PHP
// library 
  $libraryPath 'cmsAdmin/lib/viewer_functions.php';
  
$dirsToCheck = array('/path_to_your_server/','','../','../../','../../../');
  foreach (
$dirsToCheck as $dir) { if (@include_once("$dir$libraryPath")) { break; }}
  if (!
function_exists('getRecords')) { die("Couldn't load viewer library, check filepath in sourcecode."); }
   list(
$board_of_director_positionsRecords$board_of_director_positionsMetaData) = getRecords(array(
    
'tableName'   => 'board_of_director_positions',
   ));
     
$directorposition array_filter(array_pluck($board_of_director_positionsRecords'position')); 
   list(
$accountsRecords$accountsMetaData) = getRecords(array(
    
'tableName'   => 'accounts',
'where' => "board_of_director_position_1_email  LIKE '%1%' OR  board_of_director_position_2_email  LIKE '%1%' OR
board_of_director_position_3_email  LIKE '%1%' "
  ));
  list(
$common_informationRecords$common_informationMetaData) = getRecords(array(
    
'tableName'   => 'common_information',
    
'allowSearch' => '0',
    
'limit'       => '1',
  ));
  
$common_informationRecord = @$common_informationRecords[0]; // get first record
    ?>
<?php foreach ($directorposition as $actual_position): ?>
<?php foreach ($accountsRecords as $record): ?>
<?php if ($record['board_of_director_position_1:label'] == $actual_position ||
$record['board_of_director_position_2:label'] == $actual_position || $record['board_of_director_position_3:label'] ==
$actual_position): // is there a position selected  ?>
<?php $formatted_position strtolower($actual_position); // format the position ?>
<?PHP $formatted_position preg_replace('/\s+/''_'$formatted_position ); ?>
<?PHP $formatted_position preg_replace('/-/''_'$formatted_position ); ?>
<?php if ($record['board_of_director_position_1:label'] == $actual_position && $actual_position &&
$record['board_of_director_position_1_email'] == '1'): // is this records email address the one to use  ?>
<?php $email_to_use $record['email']?>
<?php endif ?>
<?php if ($record['board_of_director_position_2:label'] == $actual_position && $actual_position &&
$record['board_of_director_position_2_email'] == '1'): ?>
<?php  $email_to_use $record['email']?>
<?php endif ?>
<?php if ($record['board_of_director_position_3:label'] == $actual_position && $actual_position &&
$record['board_of_director_position_3_email'] == '1'): ?>
<?php $email_to_use $record['email']?>
<?php endif ?>
<?php $$formatted_position $email_to_use// (the $$ is not a typo, it create a variable using the name stored in
$formatted_position and assign it the value in $email_to_use.)?>
<?php endif ?>
<?php endforeach ?>
<?php endforeach ?>
<?php
define('kOptional'true);
define('kMandatory'false);

define('kStringRangeFrom'1);
define('kStringRangeTo'2);
define('kStringRangeBetween'3);
        
define('kYes''yes');
define('kNo''no');
error_reporting(E_ERROR E_WARNING E_PARSE);
ini_set('track_errors'true);

function 
CaptchaGenerator() {

 if ( (!
function_exists('imagejpeg')) && (!function_exists('imagepng')) ) {
  exit;
 }

 
$im imagecreate(100,40);

 
$white imagecolorallocate($im255255255);
 
$black imagecolorallocate($im000);
 
$gray imagecolorallocate($im150150150);

 
imagerectangle($im002539$gray);
 
imagerectangle($im2505039$gray);
 
imagerectangle($im5007539$gray);
 
imagerectangle($im7509939$gray);

 
imageline($im002539$gray);
 
imageline($im2505039$gray);
 
imageline($im5007539$gray);
 
imageline($im7509939$gray);

 
imageline($im039250$gray);
 
imageline($im2539500$gray);
 
imageline($im5039750$gray);
 
imageline($im7539990$gray);

 
$c1 rand(6590);
 
$c2 rand(6590);
 
$c3 rand(6590);
 
$c4 rand(6590);
 
$c5 rand(6590);

 
$textOut chr($c1) . ' ' chr($c2) . ' ' chr($c3) . ' ' chr($c4) . ' ' chr($c5);
 
$textCaptcha chr($c1) . chr($c2) . chr($c3) . chr($c4) . chr($c5);

 
$a imagestring($im51113$textOut$black);

 
$fileName substr(md5($textCaptcha), 012);

 
$captchaDir 'verify';

 if ( !
is_dir$captchaDir ) ) {
  echo 
'<html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"
/><title>Error</title></head><body>Access Code Validation Error: directory &quot;verify&quot; not found. Script will
quit now.</body></html>';
  exit;
 }

 if ( !
is_writable$captchaDir ) ) {
  echo 
'<html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"
/><title>Error</title></head><body>Access Code Validation Error: directory &quot;verify&quot; is not writeable. Script
will quit now.</body></html>';
  exit;
 }

 
$handle opendir$captchaDir );

 while ( 
$captchaFile readdir($handle) ) {
  if ( ( 
substr($captchaFile01) != '.' ) && ( substr($captchaFile01) != '_' ) && ( !is_dir'verify' '/' .
$captchaFile ) ) ) {
   if ( ( 
filemtime'verify' '/' $captchaFile ) + 900 ) < time() ) {
    
unlink'verify' '/' $captchaFile );
   }
  }
 }

 
closedir$handle );

 
$handle = @fopen'verify' '/' $fileName'w' );

 if ( !
$handle ) {
  echo 
'<html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"
/><title>Error</title></head><body>Access Code Validation Error: unable to create captcha control file. Script will quit
now.</body></html>';
  exit;
 }
 
 
fclose($handle);

 if (
imagetypes() & IMG_JPG) {
  
header('Content-type: image/jpeg');
  
imagejpeg($im);
 } elseif (
imagetypes() & IMG_PNG) {
  
header('Content-type: image/png');
  
imagepng($im);
 }
 exit;

}

function 
DoStripSlashes($fieldValue)  { 
// temporary fix for PHP6 compatibility - magic quotes deprecated in PHP6
 if ( function_exists'get_magic_quotes_gpc' ) && get_magic_quotes_gpc() ) { 
  if (
is_array($fieldValue) ) { 
   return 
array_map('DoStripSlashes'$fieldValue); 
  } else { 
   return 
trim(stripslashes($fieldValue)); 
  } 
 } else { 
  return 
$fieldValue
 } 
}

function 
FilterCChars($theString) {
 return 
preg_replace('/[\x00-\x1F]/'''$theString);
}

function 
ProcessTextField(&$codeHtmlForm$fieldName$fieldValue) {

 
$tagPattern '/(<input[^>]+name=[\'\"]?\Q' $fieldName '\E[\'\"\s]+[^>]*>)/i';
 
preg_match($tagPattern$codeHtmlForm$matches);

 
$htmlTag $matches[1];
 
$valuePattern '/value=[\'\"]?[^\'\"]*[\'\"]+/i';
 
$replacementPattern 'value="' $fieldValue '" ';
 
 if (
preg_match($valuePattern$htmlTag)) {
  
$htmlTagToReplace preg_replace($valuePattern$replacementPattern$htmlTag);
 } else {
  
$valuePattern '/([^>\/]*)([\/]?>)/';
  
$replacementPattern '\1 value="' $fieldValue '" \2';
  
$htmlTagToReplace preg_replace($valuePattern$replacementPattern$htmlTag);
 }

 
$codeHtmlForm preg_replace($tagPattern$htmlTagToReplace$codeHtmlForm);

}

function 
ProcessTextArea(&$codeHtmlForm$fieldName$fieldValue) {

 
$tagPattern '/(<textarea[^>]+name=[\'\"]?\Q' $fieldName '\E[\'\"\s]+[^>]*)>(.*?)(<\/textarea>)/is';
 
$replacementPattern '\1>' $fieldValue '\3';

 
$codeHtmlForm preg_replace($tagPattern$replacementPattern$codeHtmlForm);

}

function 
ProcessSelect(&$codeHtmlForm$fieldName$fieldValues) {

 
# Search the select tag
 $selectPattern '/(<select[^>]+name=[\'\"]?\Q' $fieldName '\E(\[\]|)[\'\"\s]+[^>]*>.*?<\/select>)/is';
 
$numMatches preg_match($selectPattern$codeHtmlForm$selectMatches);
 
 
$originalSelectTag $selectMatches[1];
 
 
# Remove the selected option tags
 $selectedPattern '/(<option[^>]+)([\s]+selected="selected"|[\s]+selected)([^>]*)>/i';
 
$replacementPattern '\1\3>';
 
$modifiedSelectTag preg_replace($selectedPattern$replacementPattern$originalSelectTag);

 
# Find the tags that must be checked 
 if (!is_array($fieldValues)) {
  if (
strlen($fieldValues) > 0) {
   
$fieldValues = array($fieldValues);
  } else {
   return;
  }
 }

 foreach (
$fieldValues as $fieldValue) {
  
# Find the options that must be selected
  $optionPattern '/(<option[^>]+value=[\'\"]?\Q' $fieldValue '\E[\'\"\s]+[^>]*)(>)/i';
  
$replacementPattern '\1 selected="selected"\2';
  
$modifiedSelectTag preg_replace($optionPattern$replacementPattern$modifiedSelectTag);
 }
 
 
# Replace the code in the form
 $codeHtmlForm preg_replace($selectPattern$modifiedSelectTag$codeHtmlForm);

}


function 
ProcessPHPFile($PHPFile) {
 
 
ob_start();
 
 if (
file_exists($PHPFile)) {
  require 
$PHPFile;
 } else {
  echo 
'<html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"
/><title>Error</title></head><body>Forms To Go - Error: Unable to load HTML form: ' $PHPFile '</body></html>';
  exit;
 }
 
 return 
ob_get_clean();
}

function 
CheckString($value$low$high$mode$limitAlpha$limitNumbers$limitEmptySpaces$limitExtraChars,
$optional) {

 
$regEx '';

 if (
$limitAlpha == kYes) {
  
$regExp 'A-Za-z';
 }
 
 if (
$limitNumbers == kYes) {
  
$regExp .= '0-9'
 }
 
 if (
$limitEmptySpaces == kYes) {
  
$regExp .= ' '
 }

 if (
strlen($limitExtraChars) > 0) {
 
  
$search = array('\\''['']''-''$''.''*''('')''?''+''^''{''}''|''/');
  
$replace = array('\\\\''\[''\]''\-''\$''\.''\*''\(''\)''\?''\+''\^''\{''\}''\|''\/');

  
$regExp .= str_replace($search$replace$limitExtraChars);

 }

 if ( (
strlen($regExp) > 0) && (strlen($value) > 0) ){
  if (
preg_match('/[^' $regExp ']/'$value)) {
   return 
false;
  }
 }

 if ( (
strlen($value) == 0) && ($optional === kOptional) ) {
  return 
true;
 } elseif ( (
strlen($value) >= $low) && ($mode == kStringRangeFrom) ) {
  return 
true;
 } elseif ( (
strlen($value) <= $high) && ($mode == kStringRangeTo) ) {
  return 
true;
 } elseif ( (
strlen($value) >= $low) && (strlen($value) <= $high) && ($mode == kStringRangeBetween) ) {
  return 
true;
 } else {
  return 
false;
 }

}


function 
CheckEmail($email$optional) {
 if ( (
strlen($email) == 0) && ($optional === kOptional) ) {
  return 
true;
  } elseif (
preg_match("/^([\w\!\#$\%\&\'\*\+\-\/\=\?\^\`{\|\}\~]+\.)*[\w\!\#$\%\&\'\*\+\-\/\=\?\^\`{\|\}\~]+@((((([a-z0-9]{1}[a-z0-9\-]{0,62}[a-z0-9]{1})|[a-z])\.)+[a-z]{2,6})|(\d{1,3}\.){3}\d{1,3}(\:\d{1,5})?)$/i",
$email) == ) {
  return 
true;
 } else {
  return 
false;
 }
}


function 
CheckEqualTo($original$repeated) {
 if (
$original == $repeated) {
  return 
true;
 } else {
  return 
false;
 }
}


function 
CheckFTGCaptcha($accessCode) {

 
$captchaDir 'verify';

 if ( !
is_dir$captchaDir ) ) {
  echo 
'<html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"
/><title>Error</title></head><body>Access Code Validation Error: directory &quot;verify&quot; not found. Script will
quit now.</body></html>';
  exit;
 }

 
$handle opendir$captchaDir );

 
$fileAccessCode substrmd5$accessCode ), 012 );

 while ( 
$captchaFile readdir$handle ) ) {
  if ( 
substr$captchaFile0) != '.' ) {
   if ( 
$fileAccessCode == $captchaFile ) {
    return 
true;
   }
  }
 }
 return 
false;
}

function 
DeleteCaptcha($accessCode) {

 
$captchaDir 'verify';

 if ( !
is_dir$captchaDir ) ) {
  echo 
'<html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"
/><title>Error</title></head><body>Access Code Validation Error: directory &quot;verify&quot; not found. Script will
quit now.</body></html>';
  exit;
 }

 
$handle opendir$captchaDir );

 
$fileAccessCode substrmd5$accessCode ), 012 );

 while ( 
$captchaFile readdir$handle ) ) {
  if ( ( 
substr$captchaFile0) != '.' ) && ( substr$captchaFile0) != '_' ) && ( !is_dir'verify' '/' .
$captchaFile ) ) ) {
   if ( 
$fileAccessCode == $captchaFile ) {
    
unlink'verify' '/' $captchaFile );
    return;
   }
  }
 }

}



if (isset(
$_SERVER['HTTP_X_FORWARDED_FOR'])) {
 
$clientIP $_SERVER['HTTP_X_FORWARDED_FOR'];
} else {
 
$clientIP $_SERVER['REMOTE_ADDR'];
}

if ( isset(
$_GET['formstogoimgflt']) ) {
 
CaptchaGenerator();
 exit;
}

$FTGreason DoStripSlashes$_POST['reason'] );
$FTGfirst_name DoStripSlashes$_POST['first_name'] );
$FTGlast_name DoStripSlashes$_POST['last_name'] );
$FTGemail DoStripSlashes$_POST['email'] );
$FTGemail2 DoStripSlashes$_POST['email2'] );
$FTGmessage DoStripSlashes$_POST['message'] );
$FTGverify DoStripSlashes$_POST['verify'] );



$validationFailed false;

# Fields Validations


if (!CheckString($FTGreason10kStringRangeFromkNokNokNo''kMandatory)) {
 
$FTGErrorMessage['reason'] = 'Tell us why you\'re contacting us so we can route your message correctly.';
 
$validationFailed true;
}

if (!
CheckString($FTGfirst_name136kStringRangeBetweenkNokNokNo''kMandatory)) {
 
$FTGErrorMessage['first_name'] = 'Please enter your First Name';
 
$validationFailed true;
}

if (!
CheckString($FTGlast_name136kStringRangeBetweenkNokNokNo''kMandatory)) {
 
$FTGErrorMessage['last_name'] = 'Please enter your Last Name';
 
$validationFailed true;
}

if (!
CheckEmail($FTGemailkMandatory)) {
 
$FTGErrorMessage['email'] = 'You\'ll need to enter your e-mail address before you can send this.';
 
$validationFailed true;
}

if (!
CheckEqualTo($FTGemail2$FTGemail)) {
 
$FTGErrorMessage['email2'] = 'Your E-mail addresses don\'t match.';
 
$validationFailed true;
}

if (!
CheckFTGCaptcha($FTGverify)) {
 
$FTGErrorMessage['verify'] = 'Your input didn\'t match the (CASE SENSITIVE) characters displayed, please try again.';
 
$validationFailed true;
}



# Display HTML form with filled values

if ($validationFailed === true) {

 
$fileHtmlForm 'http://your_domain.com/your_contact_form.php';
 
 
$codeHtmlForm file_get_contents($fileHtmlForm);


 
ProcessSelect($codeHtmlForm'reason'$FTGreason);
 
ProcessTextField($codeHtmlForm'first_name'$FTGfirst_name);
 
ProcessTextField($codeHtmlForm'last_name'$FTGlast_name);
 
ProcessTextField($codeHtmlForm'email'$FTGemail);
 
ProcessTextField($codeHtmlForm'email2'$FTGemail2);
 
ProcessTextArea($codeHtmlForm'message'$FTGmessage);


 
$errorList = @implode("\n"$FTGErrorMessage);
 
$codeHtmlForm str_replace('<!--VALIDATIONERROR-->'$errorList$codeHtmlForm);

 
$codeHtmlForm str_replace('<!--FIELDVALUE:reason-->'$FTGreason$codeHtmlForm);
 
$codeHtmlForm str_replace('<!--FIELDVALUE:first_name-->'$FTGfirst_name$codeHtmlForm);
 
$codeHtmlForm str_replace('<!--FIELDVALUE:last_name-->'$FTGlast_name$codeHtmlForm);
 
$codeHtmlForm str_replace('<!--FIELDVALUE:email-->'$FTGemail$codeHtmlForm);
 
$codeHtmlForm str_replace('<!--FIELDVALUE:email2-->'$FTGemail2$codeHtmlForm);
 
$codeHtmlForm str_replace('<!--FIELDVALUE:message-->'$FTGmessage$codeHtmlForm);
 
$codeHtmlForm str_replace('<!--FIELDVALUE:verify-->'$FTGverify$codeHtmlForm);
 
$codeHtmlForm str_replace('<!--ERRORMSG:reason-->'$FTGErrorMessage['reason'], $codeHtmlForm);
 
$codeHtmlForm str_replace('<!--ERRORMSG:first_name-->'$FTGErrorMessage['first_name'], $codeHtmlForm);
 
$codeHtmlForm str_replace('<!--ERRORMSG:last_name-->'$FTGErrorMessage['last_name'], $codeHtmlForm);
 
$codeHtmlForm str_replace('<!--ERRORMSG:email-->'$FTGErrorMessage['email'], $codeHtmlForm);
 
$codeHtmlForm str_replace('<!--ERRORMSG:email2-->'$FTGErrorMessage['email2'], $codeHtmlForm);
 
$codeHtmlForm str_replace('<!--ERRORMSG:verify-->'$FTGErrorMessage['verify'], $codeHtmlForm);
 echo 
$codeHtmlForm;
}
if ( 
$validationFailed === false ) {
 
# Email to Form Owner
  $emailSubject FilterCChars("$FTGreason");
  
$emailBody "You've received a message submitted via the\n"
  "Organization Help Desk\n"
  "\n"
  "The person submitting this information is: $FTGfirst_name $FTGlast_name\n"
  "\n"
  "The message was sent from: $FTGemail\n"
  "\n"
  "\n"
  "Here's what it said:\n"
  "$FTGmessage\n"
  "\n"
  "This message was sent from IP address: $clientIP on " date('m/d/y') . " at " date('h:i:s A') . "\n"
  "\n"
  "";
  
  
  
  
  
foreach (
$accountsRecords as $record): //Populate mail destination for each position or committee (the $$ is not a typo,
it creates a variable using the name stored in $position_email).
if ((
$record['board_of_director_position_1'] && $record['board_of_director_position_1_email'] == ) &&
$record['board_of_director_position_1:label'] == $FTGreason) {
 
$position_1_email strtolower($record['email']); // format the position 
 $position_1_email preg_replace('/\s+/''_'$position_1_email ); 
 
$position_1_email preg_replace('/-/''_'$position_1_email ); 
$output1 .= $position_1_email',' ;
     } ;
    if ((
$record['board_of_director_position_2'] && $record['board_of_director_position_2_email'] == ) &&
$record['board_of_director_position_2:label'] == $FTGreason ) {
 
$position_2_email strtolower($record['email']); // format the position 
 $position_2_email preg_replace('/\s+/''_'$position_2_email ); 
 
$position_2_email preg_replace('/-/''_'$position_2_email ); 
$output2 .= $position_2_email',' ;
    } ;
    if ((
$record['board_of_director_position_3'] && $record['board_of_director_position_3_email'] == &&
$record['board_of_director_position_1:label'] == $FTGreason)) {
 
$position_3_email strtolower($record['email']); // format the position 
 $position_3_email preg_replace('/\s+/''_'$position_3_email ); 
 
$position_3_email preg_replace('/-/''_'$position_3_email ); 
$output3 .= $position_3_email',' ;
    } ;
    endforeach ;
$emailTo $output1.$output2.$output3 ;
  
$emailTo rtrim($emailTo,','); // remove trailing comma
  $emailback $common_informationRecord['board_backup_email']; // "common_information" is a single record editor
  if ($emailTo == ''){$emailTo $emailback ;};
  
$emailFrom FilterCChars("$FTGemail");
  
$emailHeader "From: $emailFrom\n"
   "MIME-Version: 1.0\n"
   "Content-type: text/plain; charset=\"UTF-8\"\n"
   "Content-transfer-encoding: 8bit\n";
 
mail($emailTo$emailSubject$emailBody$emailHeader);
  
$confEmailTo FilterCChars($FTGemail); // Confirmation Email to User
   $confEmailSubject FilterCChars("Your Message To The Organization");
  
$confEmailBody chunk_splitbase64_encode"Hello $FTGfirst_name $FTGlast_name,\n"
  "\n"
  "Thanks for caring enough to contact us.\n"
  "\n"
  "You sent a message to the $FTGreason\n"
  "\n"
  "You sent your message from: $FTGemail\n"
  "\n"
  "Here is what it said:\n"
  "$FTGmessage\n"
  "\n"
  "They've received your message and will get back to you shortly.\n"
  "\n"
  "Best,\n"
  "\n"
  "The Organzation\n"
  "\n"
  "" ) );
  
$confEmailHeader "From: no-reply@your_domain.com\n"
  "MIME-Version: 1.0\n"
  "Content-type: text/plain; charset=\"UTF-8\"\n"
  "Content-transfer-encoding: base64\n";
  
mail($confEmailTo$confEmailSubject$confEmailBody$confEmailHeader);
 
DeleteCaptcha($FTGverify);
# Redirect user to success page
header("Location: http://www.your_domain.com/contactsuccess.php");
}
?>


SELECTING A CONTACT FORM'S EMAIL RECIPIENTS USING FIELDS IN THEIR ACCOUNT RECORD (AN ALL CMSB IMPLEMENTATION) - Jun 28th, 2015

The initial purpose of this recipe is to be able to control an organization’s board of director positions, committee
memberships and easily designate who the chairpersons of committees are and who is to receive email for the various
positions and committees.

This implementation (thanks to a lot of help from Ross Fairbairn from Interactive Tools) uses the built in mail
functions in CMSB and their associated Email Templates, and a single viewer to accomplish the task. 

To make the board of director positions and committees flexible, I created a multi record editor called “Board Of
Director Positions”. The records in this section have one text field called “Position” and a dragSortOrder field.
NOTE: The records in “Board Of Director Positions” are where the options in the form below come from.

The organization wanted to allow a member to hold more than one position on the board of directors and/or to sit on up
to 3 committees.

So, in the “User Accounts” section there are 3 sets of 3 Board Of Director fields.

A list field for position (1, 2 and 3), a check box for the chairperson of each, and a check box for the designated
email recipient for each.
 
The Board of Director Position pull down lists fields (board_of_director_position_1, board_of_director_position_2, and
board_of_director_position_3) get their options from the database  “board_of_director_positions” with Option Values
from the “num” field, and Option Labels from the Positions Field.

The check box fields (board_of_director_position_1_chair, board_of_director_position_2_chair,
board_of_director_position_3_chair),  signify that this person is the chairperson.

The check box fields (board_of_director_position_1_email, board_of_director_position_2_email,
board_of_director_position_3_email) designate the person who gets email for the board position or committee.

This implementation uses 2 Email Templates, one for the message that’s sent to the recipient
(BOARD-OF-DIRECTOR-CONTACT), and one for a confirmation message to the sender (BOARD-OF-DIRECTOR-CONTACT-CONFIRMATION)

Each of these has placeholder variables set up for contact.firstName, contact.lastName, contact.email,  contact.message,
board.position, and bod.email.

Here’s the basic setup for the Templates.

BOARD-OF-DIRECTOR-CONTACT

From: your_from_email@your_site.com
Reply To: #contact.email# 
To: #bod.email#

Subject: A Message For The  #board.position#

Message HTML:

Hello #board.position#,

You've received a message from #contact.firstName# #contact.lastName#

Here's what it said:

#contact.message#

You can contact them via: #contact.email#

Or just reply to this email

Thanks,

This message was sent from IP Address: #server.remote_addr#


BOARD-OF-DIRECTOR-CONTACT-CONFIRMATION

From: your_from_email@your_site.com
Reply To: your_reply_email@your_site.com
To: #contact.email#

Subject: A Copy Of Your Message To The #board.position#

Message HTML:

Hello #contact.firstName# #contact.lastName#,

Thanks for caring enough to contact the #board.position#.

Here's a copy of your message:

#contact.message#

Your message has been received and you should get a response shortly

Best,


***DON'T FORGET TO CHANGE ALL REFERENCES TO GENERIC PATHS TO MATCH YOUR OWN***
*** IF YOU USE NO-REPLY@YOUR_DOMAIN.COM AS THE RETURN EMAIL ADDRESS, AND DON'T SET UP A REAL EMAIL ACCOUNT FOR THAT
ADDRESS AT YOUR DOMAIN, SOME PROVIDERS MAY BLOCK YOUR EMAILS***

You can set the email properties in Admin>General to log only for testing and debugging and then set to either send only
or send and log after the form is working the way you want it to. 

Here’s the code for the viewer (without CAPTCHA) The Code with Google’s New  “I’M NOT A ROBOT”, NO CAPTCHA,
RECAPTCHA is below, You’ll need to format it to match the look of your site.


 <?php header('Content-type: text/html; charset=utf-8'); ?>
<?php
  
   // load viewer library
  $libraryPath 'cmsAdmin/lib/viewer_functions.php';
 
$dirsToCheck = array('/path_to_your_server/','','../','../../','../../../');
  foreach (
$dirsToCheck as $dir) { if (@include_once("$dir$libraryPath")) { break; }}
  if (!
function_exists('getRecords')) { die("Couldn't load viewer library, check filepath in sourcecode."); }
 
?>
<?php
    // load records
  list($board_of_director_positionsRecords$board_of_director_positionsMetaData) = getRecords(array(
    
'tableName'   => 'board_of_director_positions',
  ));

?>
<?php  $errorsAndAlerts "";
  
  
// form submit
  if (@$_REQUEST['formSubmit']) {
    
    
// error checking
    if (!@$_REQUEST['reason'])                         { $errorsAndAlerts .= "You must select someone to contact\n"; }
    if (!@
$_REQUEST['first_name'])                     { $errorsAndAlerts .= "You must enter a first name\n"; }
    if (!@
$_REQUEST['last_name'])                     { $errorsAndAlerts .= "You must enter a last name\n"; }
    
    if (!@
$_REQUEST['email'])                               { $errorsAndAlerts .= "You must enter your email!\n"; }
    else if(!
isValidEmail(@$_REQUEST['email']))             { $errorsAndAlerts .= "Please enter a valid email (example:
user@example.com)\n"; }
    else if (@
$_REQUEST['email'] != @$_REQUEST['email2'])    { $errorsAndAlerts .= "Your emails must match\n"; }
    
    if (!@
$_REQUEST['message'])                     { $errorsAndAlerts .= "You must enter a message\n"; }

    
// send emails    
    if (!$errorsAndAlerts) {
      
     
// look up email address (3 fields for board_of_director_position)
      
    $customWhere "(board_of_director_position_1 = '"mysql_escape($_REQUEST['reason']) ."' AND
board_of_director_position_1_email = '1') OR (board_of_director_position_2 = '"mysql_escape($_REQUEST['reason']) ."'
AND board_of_director_position_2_email = '1') OR (board_of_director_position_3 = '"mysql_escape($_REQUEST['reason'])
.
"' AND board_of_director_position_3_email = '1')";
        
     
$accountRecords mysql_select("accounts"$customWhere );
      
    
$bodEmail "";
      foreach (
$accountRecords as $accountRecord) { 
    
$bodEmail         .= @$accountRecord['email'] .", ";
      }
      
      
$bodEmail substr($bodEmail0, -2);
      
      
// look up bod position
      $bodPositionRecord     mysql_get("board_of_director_positions"mysql_escape($_REQUEST['reason']));
      
$bodPosition         $bodPositionRecord['position'];
      
      
// no email supplied for this account
      if (!$bodEmail || $bodEmail == "") {
    
$commonInformationRecord     mysql_get("common_information"1);
    
$bodEmail             $commonInformationRecord['board_backup_email'];
      }
      
      
// send email to BOD member 
      $emailHeaders emailTemplate_loadFromDB(array(
    
'template_id'  => 'BOARD-OF-DIRECTOR-CONTACT',
    
'placeholders' => array(
      
'contact.firstName'     => $_REQUEST['first_name'],
      
'contact.lastName'      => $_REQUEST['last_name'],
      
'contact.email'     => $_REQUEST['email'],
      
'contact.message'     => $_REQUEST['message'],
      
'board.position'    => $bodPosition,
      
'bod.email'         => $bodEmail,
      
      )));
      
$mailErrors   sendMessage($emailHeaders);
      if (
$mailErrors) { alert("Mail Error: $mailErrors"); }
      
      
// send confirmation email to sender
      $emailHeaders emailTemplate_loadFromDB(array(
    
'template_id'  => 'BOARD-OF-DIRECTOR-CONTACT-CONFIRMATION',
    
'placeholders' => array(
      
'contact.firstName'     => $_REQUEST['first_name'],
      
'contact.lastName'      => $_REQUEST['last_name'],
      
'contact.email'     => $_REQUEST['email'],
      
'contact.message'     => $_REQUEST['message'],
       
'board.position'    => $bodPosition,
      
'bod.email'         => $bodEmail,
      
      )));
      
$mailErrors   sendMessage($emailHeaders);
      if (
$mailErrors) { alert("Mail Error: $mailErrors"); }
      
      if (!
$mailErrors) {
    
$_REQUEST = array();
    
$errorsAndAlerts "
              <div class='heading_font' align='center'>THANKS FOR CONTACTING US
                
              </div>
              <div align='center'> 
                
                <div class='text_font' align='left'><b>Your Message has been sent successfuly.
                  
                  Thanks for caring enough to contact us.
                  
                  We'll get back to you shortly. 
                  
                  Best, 
                  
                The Organization</b></div>
              </div>
            </div>";
      }
      
    }
  }
   
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Welcome To Our Contact Form</title>
<META NAME="ROBOTS" CONTENT="NOINDEX, NOFOLLOW">
<meta name="viewport" content="width=device-width, target-densitydpi=device-dpi, initial-scale=1">
</head>

<body >
<table align="center" width="100%" border="0" cellspacing="0" cellpadding="20">
  <tr>
    <td valign="top" align="left"  ><div align="left">
      
      <div align="center"><b>CONTACT OUR BOARD OF DIRECTORS
        AND COMMITTEE CHAIRS</b>
        
      </div>
      
      <div align="left">
        <?php if (@$errorsAndAlerts == ""): ?>
        <<b>USE THIS FORM TO SEND A MESSAGE DIRECTLY TO THE PERSON WHO CAN ANSWER YOUR QUESTION:</b>
        <?php endif ?>
        
        
        <?php if (@$errorsAndAlerts): ?>
        <div style="color: #FF0000; font-weight: bold; font-size: 16px; font-family: arial;">
          <?php echo $errorsAndAlerts?>
          
        </div>
        <?php endif ?>
        <?php if (@$errorsAndAlerts == ""): ?>
        <form method="post" action="?">
          <input type="hidden" name="formSubmit" value="1">
          <table width="95%" border="0" cellpadding="15">
            <tr >
              <td width="40%" align="left" valign="middle"><label><b>Who would you
                  like to contact?</b></label></td>
              <td  style="text-align:left" width="60%" align="left" valign="middle"><?php $reason =
htmlspecialchars(@$_REQUEST['reason']); ?>
                <select name="reason">
                  <option value="" >...Select...</option>
                  <?php foreach($board_of_director_positionsRecords as $bod): ?>
                  <option value="<?php echo $bod['num'];?>"  <?php selectedIf($reason,$bod['num']) ?>     ><?php echo
$bod['position'];?></option>
                  <?php endforeach?>
                </select></td>
            </tr>
            <tr>
              <td width="40%" align="left" valign="middle" ><b>What's your
                First Name</b></td>
              <td style="text-align:left" align="left" valign="middle"><input class="text" type="text" name="first_name"
id="first_name" value="<?php echo @$_REQUEST['first_name']; ?>" /></td>
            </tr>
            <tr>
              <td width="40%" align="left" valign="middle" ><b>Your Last Name</b></td>
              <td style="text-align:left" align="left" valign="middle"><input class="text" type="text" name="last_name"
id="last_name" value="<?php echo @$_REQUEST['last_name']; ?>" /></td>
            </tr>
            <tr>
              <td width="40%" align="left" valign="middle" ><b>Your e-mail</b></td>
              <td  style="text-align:left" align="left" valign="middle"><input class="text" type="text" name="email"
id="email" value="<?php echo @$_REQUEST['email']; ?>" /></td>
            </tr>
            <tr>
              <td width="40%" align="left" valign="middle"><b>Re-enter your e-mail</b></td>
              <td  style="text-align:left" align="left" valign="middle"><input class="text" type="text" name="email2"
id="email2" value="<?php echo @$_REQUEST['email2']; ?>" /></td>
            </tr>
            <tr>
              <td width="460%" align="left" valign="middle"><b>What's your message</b></td>
              <td  style="text-align:left" align="left" valign="middle"><textarea class="textarea" name="message"
cols="60" rows="6" id="message"><?php echo @$_REQUEST['message']; ?></textarea></td>
            </tr>
            <tr>
              <td colspan="2" align="left" valign="middle"><input type="submit" name="form_submitted" value="Submit Your
Message" /></td>
            </tr>
          </table>
        </form>
        <?php endif ?>
      </div></td>
  </tr>
</table>
</body>
</html>


WITH GOOGLE'S  “I’M NOT A ROBOT”, NO CAPTCHA, RECAPTCHA


<?php header('Content-type: text/html; charset=utf-8'); ?>
<?php
   // load viewer library
  $libraryPath 'cmsAdmin/lib/viewer_functions.php';
 
$dirsToCheck = array('/path_to_your_server/','','../','../../','../../../');
  foreach (
$dirsToCheck as $dir) { if (@include_once("$dir$libraryPath")) { break; }}
  if (!
function_exists('getRecords')) { die("Couldn't load viewer library, check filepath in sourcecode."); }
  
?>
<?php
    // load records
  list($board_of_director_positionsRecords$board_of_director_positionsMetaData) = getRecords(array(
    
'tableName'   => 'board_of_director_positions',
  ));

?>
<?php
  function validateGoogleCaptcha(){
    
    
$errorsAndAlerts "";

    if (!@
$_REQUEST['g-recaptcha-response'])     { $errorsAndAlerts .= "Please check the anti-spam 'I am not a robot'
checkbox!\n"
    
$showSignupForm true// don't change this value
    }
    else { 
      
// check recaptcha
      $postdata = array();
      
$postdata['secret']   = 'YOUR SECRET GOOGLE CAPTCHA KEY GOES HERE';
      
$postdata['response'] = @$_REQUEST['g-recaptcha-response'];
      
$postdata['remoteip'] = $_SERVER['REMOTE_ADDR'];
      
$url "https://www.google.com/recaptcha/api/siteverify?"http_build_query($postdata'''&');
      list(
$json$httpStatusCode$headers$request) = getPage($url5''true);
      
$recaptchaResponse json_decode($jsontrue);
      
      if (!
$recaptchaResponse['success']) {
        if (
is_array($recaptchaResponse['error-codes'])) { 
          if (
in_array('missing-input-secret'$recaptchaResponse['error-codes']))     { $errorsAndAlerts .= "There's a
problem with recaptcha, please let us know! (no secret)\n"; }
          if (
in_array('invalid-input-secret'$recaptchaResponse['error-codes']))     { $errorsAndAlerts .= "There's a
problem with recaptcha, please let us know! (invald secret)\n"; }
          if (
in_array('missing-input-response'$recaptchaResponse['error-codes']))   { $errorsAndAlerts .= "Please
fill out the recaptcha box!\n";
          
$showSignupForm true// do we need this line?
           }
          if (
in_array('invalid-input-response'$recaptchaResponse['error-codes']))   { $errorsAndAlerts .= "Please
fill out the recaptcha box again, your answer was incorrect!\n";
          
$showSignupForm true// do we need this line?
          }
        }
        if (!
$errorsAndAlerts) { $errorsAndAlerts .= "Invalid captcha response, please try again or contact us directly
and let us know."; }
        @
trigger_error("Failed recaptcha on signup form"E_USER_NOTICE);
      }
    }
    return 
$errorsAndAlerts;
  }
?>
<?php  $errorsAndAlerts "";
 
$showSignupForm true
  
// form submit
  if (@$_REQUEST['formSubmit']) {
    
$errorsAndAlerts .= validateGoogleCaptcha();  
    
// error checking
    if (!@$_REQUEST['reason'])                         { $errorsAndAlerts .= "You must select someone to contact\n"; }
    if (!@
$_REQUEST['first_name'])                     { $errorsAndAlerts .= "You must enter a first name\n"; }
    if (!@
$_REQUEST['last_name'])                     { $errorsAndAlerts .= "You must enter a last name\n"; }
    
    if (!@
$_REQUEST['email'])                               { $errorsAndAlerts .= "You must enter your email!\n"; }
    else if(!
isValidEmail(@$_REQUEST['email']))             { $errorsAndAlerts .= "Please enter a valid email (example:
user@example.com)\n"; }
    else if (@
$_REQUEST['email'] != @$_REQUEST['email2'])    { $errorsAndAlerts .= "Your emails must match\n"; }
    
    if (!@
$_REQUEST['message'])                     { $errorsAndAlerts .= "You must enter a message\n"; }

    
// send emails    
    if (!$errorsAndAlerts) {
      
$showSignupForm false;  
      
     
// look up email address (3 fields for board_of_director_position)
      
    $customWhere "(board_of_director_position_1 = '"mysql_escape($_REQUEST['reason']) ."' AND
board_of_director_position_1_email = '1') OR (board_of_director_position_2 = '"mysql_escape($_REQUEST['reason']) ."'
AND board_of_director_position_2_email = '1') OR (board_of_director_position_3 = '"mysql_escape($_REQUEST['reason'])
.
"' AND board_of_director_position_3_email = '1')";
        
     
$accountRecords mysql_select("accounts"$customWhere );
      
    
$bodEmail "";
      foreach (
$accountRecords as $accountRecord) { 
    
$bodEmail         .= @$accountRecord['email'] .", ";
      }
      
      
$bodEmail substr($bodEmail0, -2);
      
      
// look up bod position
      $bodPositionRecord     mysql_get("board_of_director_positions"mysql_escape($_REQUEST['reason']));
      
$bodPosition         $bodPositionRecord['position'];
      
      
// no email supplied for this account
      if (!$bodEmail || $bodEmail == "") {
    
$commonInformationRecord     mysql_get("common_information"1);
    
$bodEmail             $commonInformationRecord['board_backup_email'];
      }
      
      
// send email to BOD member 
      $emailHeaders emailTemplate_loadFromDB(array(
    
'template_id'  => 'BOARD-OF-DIRECTOR-CONTACT',
    
'placeholders' => array(
      
'contact.firstName'     => $_REQUEST['first_name'],
      
'contact.lastName'      => $_REQUEST['last_name'],
      
'contact.email'     => $_REQUEST['email'],
      
'contact.message'     => $_REQUEST['message'],
      
'board.position'    => $bodPosition,
      
'bod.email'         => $bodEmail,
      
      )));
      
$mailErrors   sendMessage($emailHeaders);
      if (
$mailErrors) { alert("Mail Error: $mailErrors"); }
      
      
// send confirmation email to sender
      $emailHeaders emailTemplate_loadFromDB(array(
    
'template_id'  => 'BOARD-OF-DIRECTOR-CONTACT-CONFIRMATION',
    
'placeholders' => array(
      
'contact.firstName'     => $_REQUEST['first_name'],
      
'contact.lastName'      => $_REQUEST['last_name'],
      
'contact.email'     => $_REQUEST['email'],
      
'contact.message'     => $_REQUEST['message'],
       
'board.position'    => $bodPosition,
      
'bod.email'         => $bodEmail,
      
      )));
      
$mailErrors   sendMessage($emailHeaders);
      if (
$mailErrors) { alert("Mail Error: $mailErrors"); }
      
      if (!
$mailErrors) {
    
$_REQUEST = array();
    
$errorsAndAlerts "
              <div align='center'>THANKS FOR CONTACTING US
                
              </div>
              <div align='center'> 
                
                <div align='left'><b>Your Message has been sent successfuly.
                  
                  Thanks for caring enough to contact us.
                  
                  We'll get back to you shortly. 
                  
                  Best, 
                  
                  The Organization</b></div>
              </div>
            </div>";
      }
      
    }
  }
   
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Welcome to<?php echo $organization_informationRecord['organization_name'?></title>
<script src="https://www.google.com/recaptcha/api.js" async defer></script>
<META NAME="ROBOTS" CONTENT="NOINDEX, NOFOLLOW">
<meta name="viewport" content="width=device-width, target-densitydpi=device-dpi, initial-scale=1">
</head>
<body >
<table align="center" width="100%" border="0" cellspacing="0" cellpadding="20">
  <tr>
    <td valign="top" align="left"  ><div align="left">
      
      <div align="center"><b>CONTACT OUR BOARD OF DIRECTORS
        AND COMMITTEE CHAIRS</b>
        
      </div>
      
      <div align="left">
        <?php if ($showSignupForm): ?>
        <b>USE THIS FORM TO SEND A MESSAGE DIRECTLY TO THE PERSON WHO CAN ANSWER YOUR QUESTION:</b>
        <?php  endif ?>
        
        
        <?php if (@$errorsAndAlerts ): ?>
        <div style="color: #FF0000; font-weight: bold; font-size: 16px; font-family: arial;">
          <?php echo $errorsAndAlerts?>
          
        </div>
        <?php endif ?>
        <?php if ($showSignupForm == "true"): ?>
        <form method="post" action="?">
          <input type="hidden" name="formSubmit" value="1">
          <table width="95%" border="0" cellpadding="15">
            <tr >
              <td width="40%" align="left" valign="middle"><label ><b>Who would you 
                  like to contact?</b></label></td>
              <td  style="text-align:left" width="60%" align="left" valign="middle"><?php $reason =
htmlspecialchars(@$_REQUEST['reason']); ?>
                <select name="reason">
                  <option value="" >...Select...</option>
                  <?php foreach($board_of_director_positionsRecords as $bod): ?>
                  <option value="<?php echo $bod['num'];?>"  <?php selectedIf($reason,$bod['num']) ?>     ><?php echo
$bod['position'];?></option>
                  <?php endforeach?>
                </select></td>
            </tr>
            <tr>
              <td width="40%" align="left" valign="middle" ><b>What's your
                First Name</b></td>
              <td style="text-align:left" align="left" valign="middle"><input class="text" type="text" name="first_name"
id="first_name" value="<?php echo @$_REQUEST['first_name']; ?>" /></td>
            </tr>
            <tr>
              <td width="40%" align="left" valign="middle" ><b>Your Last Name</b></td>
              <td style="text-align:left" align="left" valign="middle"><input class="text" type="text" name="last_name"
id="last_name" value="<?php echo @$_REQUEST['last_name']; ?>" /></td>
            </tr>
            <tr>
              <td width="40%" align="left" valign="middle"><b>Your e-mail</b></td>
              <td  style="text-align:left" align="left" valign="middle"><input class="text" type="text" name="email"
id="email" value="<?php echo @$_REQUEST['email']; ?>" /></td>
            </tr>
            <tr>
              <td width="40%" align="left" valign="middle" ><b>Re-enter your e-mail</b></td>
              <td  style="text-align:left" align="left" valign="middle"><input class="text" type="text" name="email2"
id="email2" value="<?php echo @$_REQUEST['email2']; ?>" /></td>
            </tr>
            <tr>
              <td width="460%" align="left" valign="middle" ><b>What's your message</b></td>
              <td  style="text-align:left" align="left" valign="middle"><textarea class="textarea" name="message"
cols="60" rows="6" id="message"><?php echo @$_REQUEST['message']; ?></textarea></td>
            </tr>
            <tr>
              <td colspan="2" style=" font-weight: bold;" valign="top">Please check the "I'm not a robot" box below
before submitting.
                
                <div class="g-recaptcha" data-theme="light" data-sitekey="YOUR GOOGLE CAPTCHA SITE KEY GOES
HERE"></div></td>
            </tr>
            <tr>
              <td colspan="2" align="left" valign="middle"><input type="submit" name="form_submitted" value="Submit Your
Message" /></td>
            </tr>
          </table>
        </form>
        <?php  endif ?>
      </div></td>
  </tr>
</table>
</body>
</html>


A QUESTION BASED CONTACT FORM WITH CUSTOM RESPONSES (AN ALL CMSB IMPLEMENTATION) - Jun 28th, 2015

After I'd implemented the Board of Directors contact form (See "Selecting A Form's Email Recipients Using Fields In
Their Account Record (A CMSB implementation)"  my cient wanted to use the same approach to send emails from the contact
page based on the question that the sender wanted to ask.

I created a multi-record section called "Contact Page Questions" that had 2 text fields called "Question" and "Response"

In an existing single record section called "Common Information", I added 2 text fields called "Contact Page Backup
Email" and "Contact Page Generic Reply"

You'll also need to create 2 new Email Templates. CONTACT-PAGE-QUESTION-CONTACT and CONTACT-PAGE-QUESTION-CONFIRMATION

The CONTACT-PAGE-QUESTION-CONTACT template is set up like this:

From: your_from_email@your_site.com (use a real email address, I set up no-reply@my_domain.com)
Reply-To: #contact.email#
To: #bod.email#
Subject: A Message Regarding:  "#board.position#"
Message:

Hello,

You've received a message from #contact.firstName# #contact.lastName#, regarding: "#board.position#".

Here's what it said:

#contact.message#

You can contact them via: #contact.email#

Or just reply to this email.

Thanks,

The  Help Desk

This message was sent from IP Address: #server.remote_addr#

The CONTACT-PAGE-QUESTION-CONFIRMATION template is set up like this:

From: your_from_email@your_site.com (use a real email address, I set up no-reply@my_domain.com)
Reply To: your_reply_email@your_site.com (use a real email address, I used no-reply@my_domain.com here too)
To: #contact.email#
Subject: A Message Regarding:  "#board.position#"
Message:

Hello #contact.firstName# #contact.lastName#,

Thanks for caring enough to contact us regarding: "#board.position#".

Here's a copy of your message:

#contact.message#

Your message has been received and you should get a response shortly

Best,

The  Help Team


Here's the code that I came up with for the viewer (you'll have to format it to match your site design):


<?php header('Content-type: text/html; charset=utf-8'); ?>
<?php
  
   // load viewer library
  $libraryPath 'cmsAdmin/lib/viewer_functions.php';
 
$dirsToCheck = array('/path_to_your_server/','','../','../../','../../../');
  foreach (
$dirsToCheck as $dir) { if (@include_once("$dir$libraryPath")) { break; }}
 if (!
function_exists('getRecords')) { die("Couldn't load viewer library, check filepath in sourcecode."); }
   
?>
<?PHP //  With multiple email addresses and 3 Question Fields, form hidden after submit, Captcha, custom replies?>
<?php
    // load records
  list($contact_page_questionsRecords$contact_page_questionsMetaData) = getRecords(array(
    
'tableName'   => 'contact_page_questions',
  ));

?>
<?php  $errorsAndAlerts "";
?>
<?php
  function validateGoogleCaptcha(){
    
    
$errorsAndAlerts "";

    if (!@
$_REQUEST['g-recaptcha-response'])     { $errorsAndAlerts .= "Please check the anti-spam 'I am not a robot'
checkbox!\n"
    
$showSignupForm true// don't change this value
    }
    else { 
      
// check recaptcha
      $postdata = array();
      
$postdata['secret']   = 'YOUR SECRET GOOGLE CAPTCHA KEY GOES HERE';
      
$postdata['response'] = @$_REQUEST['g-recaptcha-response'];
      
$postdata['remoteip'] = $_SERVER['REMOTE_ADDR'];
      
$url "https://www.google.com/recaptcha/api/siteverify?"http_build_query($postdata'''&');
      list(
$json$httpStatusCode$headers$request) = getPage($url5''true);
      
$recaptchaResponse json_decode($jsontrue);
      
      if (!
$recaptchaResponse['success']) {
        if (
is_array($recaptchaResponse['error-codes'])) { 
          if (
in_array('missing-input-secret'$recaptchaResponse['error-codes']))     { $errorsAndAlerts .= "There's a
problem with recaptcha, please let us know! (no secret)\n"; }
          if (
in_array('invalid-input-secret'$recaptchaResponse['error-codes']))     { $errorsAndAlerts .= "There's a
problem with recaptcha, please let us know! (invald secret)\n"; }
          if (
in_array('missing-input-response'$recaptchaResponse['error-codes']))   { $errorsAndAlerts .= "Please
fill out the recaptcha box!\n";
          
$showSignupForm true// do we need this line?
           }
          if (
in_array('invalid-input-response'$recaptchaResponse['error-codes']))   { $errorsAndAlerts .= "Please
fill out the recaptcha box again, your answer was incorrect!\n";
          
$showSignupForm true// do we need this line?
          }
        }
        if (!
$errorsAndAlerts) { $errorsAndAlerts .= "Invalid captcha response, please try again or contact us directly
and let us know."; }
        @
trigger_error("Failed recaptcha on signup form"E_USER_NOTICE);
      }
    }
    return 
$errorsAndAlerts;
  }
?>
<?php  $errorsAndAlerts "";
 
$showSignupForm true
  
// form submit
  if (@$_REQUEST['formSubmit']) {
    
$errorsAndAlerts .= validateGoogleCaptcha();     
    
// error checking
    if (!@$_REQUEST['reason'])                         { $errorsAndAlerts .= "You must select a question for your contact\n"; }
    if (!@
$_REQUEST['first_name'])                     { $errorsAndAlerts .= "You must enter a first name\n"; }
    if (!@
$_REQUEST['last_name'])                     { $errorsAndAlerts .= "You must enter a last name\n"; }
    
    if (!@
$_REQUEST['email'])                               { $errorsAndAlerts .= "You must enter your email!\n"; }
    else if(!
isValidEmail(@$_REQUEST['email']))             { $errorsAndAlerts .= "Please enter a valid email (example:
user@example.com)\n"; }
    else if (@
$_REQUEST['email'] != @$_REQUEST['email2'])    { $errorsAndAlerts .= "Your emails must match\n"; }
    
    if (!@
$_REQUEST['message'])                     { $errorsAndAlerts .= "You must enter a message\n"; }

    
// send emails    
    if (!$errorsAndAlerts) {
        
     
$showSignupForm false;      


   
// look up email address (3 fields for contact_page_question_email_assignment)
      
    $customWhere2 "(contact_page_question_email_assignment_1 = '"mysql_escape($_REQUEST['reason']) ."') OR
(contact_page_question_email_assignment_2 = '"mysql_escape($_REQUEST['reason']) ."') OR
(contact_page_question_email_assignment_3 = '"mysql_escape($_REQUEST['reason']) ."')";
        
     
$accountRecords mysql_select("accounts"$customWhere2 );
      
    
$questionEmail "";
      foreach (
$accountRecords as $accountRecord) { 
    
$questionEmail         .= @$accountRecord['email'] .", ";
      }
      
      
$questionEmail substr($questionEmail0, -2);
      
        
//  no email supplied for this account
      if (!$questionEmail || $questionEmail == "") {
$commonInformationRecord     mysql_get("common_information"1);
    
$questionEmail             $commonInformationRecord['contact_page_backup_email'];
      }
      
      
// look up contact Page Questions
      $emailQuestionRecord     mysql_get("contact_page_questions"mysql_escape($_REQUEST['reason']));
      
$bodPosition         $emailQuestionRecord['question'];
    
    
    
//Look Up Custom Reply
      
       $customError ="";
     
      foreach (
$contact_page_questionsRecords as $record) { 
     if (@
$record['question'] == $bodPosition
    {
@
$customError $record['response'];
      }     }
      
  
//  no custom reply
      if (!$customError || $customError == "") {
$commonInformationRecord     mysql_get("common_information"1);
    
$customError             $commonInformationRecord['contact_page_generic_reply'];
      }
    
   
    
      
// send email to BOD member 
      $emailHeaders emailTemplate_loadFromDB(array(
    
'template_id'  => 'CONTACT-PAGE-QUESTION-CONTACT',
    
'placeholders' => array(
      
'contact.firstName'     => $_REQUEST['first_name'],
      
'contact.lastName'      => $_REQUEST['last_name'],
      
'contact.email'     => $_REQUEST['email'],
      
'contact.message'     => $_REQUEST['message'],
      
'board.position'    => $bodPosition,
      
'bod.email'         => $questionEmail,
      
      )));
      
$mailErrors   sendMessage($emailHeaders);
      if (
$mailErrors) { alert("Mail Error: $mailErrors"); }
      
      
// send confirmation email to sender
      $emailHeaders emailTemplate_loadFromDB(array(
    
'template_id'  => 'CONTACT-PAGE-QUESTION-CONFIRMATION',
    
'placeholders' => array(
      
'contact.firstName'     => $_REQUEST['first_name'],
      
'contact.lastName'      => $_REQUEST['last_name'],
      
'contact.email'     => $_REQUEST['email'],
      
'contact.message'     => $_REQUEST['message'],
       
'board.position'    => $bodPosition,
      
'bod.email'         => $questionEmail,
      
      )));
      
$mailErrors   sendMessage($emailHeaders);
      if (
$mailErrors) { alert("Mail Error: $mailErrors"); }
      
      if (!
$mailErrors) {
    
$_REQUEST = array();
    
$errorsAndAlerts "
              <div class='heading_font' align='center'>THANKS FOR CONTACTING US
                
              </div>
              <div align='center'> 
                
                <div class='text_font' align='left'><b>Your Message has been sent successfully.
                  
                  Thanks for caring enough to contact us.
                  
                 $customError
                  
                  Best, 
                  
                  The N.A.W.A.Florida Chapter</b></div>
              </div>
            </div>";
      }
      
    }
  }
   
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<script src="https://www.google.com/recaptcha/api.js" async defer></script>
<title>Welcome</title>
<META NAME="ROBOTS" CONTENT="NOINDEX, NOFOLLOW">
<meta name="viewport" content="width=device-width, target-densitydpi=device-dpi, initial-scale=1">
</head>

<body >
<table align="center" width="100%" border="0" cellspacing="0" cellpadding="20">
        <tr>
          <td valign="top" align="left"  ><div align="left">
            
            <div align="center"><b>CONTACT US...</b>
            
            
            </div>
            
            <div align="left">
              <?php if (@$errorsAndAlerts == ""): ?>
              <b>USE THIS FORM TO SEND A MESSAGE DIRECTLY TO THE PERSON WHO CAN ANSWER YOUR QUESTION:</b>
              <?php endif ?>
              
              
              <?php if (@$errorsAndAlerts): ?>
              <div style="color: #FF0000; font-weight: bold; font-size: 16px; font-family: arial;">
                <?php echo $errorsAndAlerts?>
                
              </div>
              <?php endif ?>
              <?php if ($showSignupForm == "true"): ?>
              <form method="post" action="?">
                <input type="hidden" name="formSubmit" value="1">
                <table width="95%" border="0" cellpadding="15">
                  <tr >
                    <td width="40%" align="left" valign="middle"><label ><b>Tell us your reason for contacting
us.</b></label></td>
                    <td  style="text-align:left" width="60%" align="left" valign="middle"><?php $reason =
htmlspecialchars(@$_REQUEST['reason']); ?>
                      <select name="reason">
                        <option value="" >...Select...</option>
                        <?php foreach($contact_page_questionsRecords as $question): ?>
                        <option value="<?php echo $question['num'];?>"  <?php selectedIf($reason,$question['num']) ?>   
 ><?php echo $question['question'];?></option>
                        <?php endforeach?>
                      </select></td>
                  </tr>
                  <tr>
                    <td width="40%" align="left" valign="middle" ><b>What's your
                      First Name</b></td>
                    <td style="text-align:left" align="left" valign="middle"><input class="text" type="text"
name="first_name" id="first_name" value="<?php echo @$_REQUEST['first_name']; ?>" /></td>
                  </tr>
                  <tr>
                    <td width="40%" align="left" valign="middle" ><b>Your Last Name</b></td>
                    <td style="text-align:left" align="left" valign="middle"><input class="text" type="text"
name="last_name" id="last_name" value="<?php echo @$_REQUEST['last_name']; ?>" /></td>
                  </tr>
                  <tr>
                    <td width="40%" align="left" valign="middle" ><b>Your e-mail</b></td>
                    <td  style="text-align:left" align="left" valign="middle"><input class="text" type="text"
name="email" id="email" value="<?php echo @$_REQUEST['email']; ?>" /></td>
                  </tr>
                  <tr>
                    <td width="40%" align="left" valign="middle" ><b>Re-enter your e-mail</b></td>
                    <td  style="text-align:left" align="left" valign="middle"><input class="text" type="text"
name="email2" id="email2" value="<?php echo @$_REQUEST['email2']; ?>" /></td>
                  </tr>
                  <tr>
                    <td width="460%" align="left" valign="middle" ><b>What's your message</b></td>
                    <td  style="text-align:left" align="left" valign="middle"><textarea class="textarea" name="message"
cols="60" rows="6" id="message"><?php echo @$_REQUEST['message']; ?></textarea></td>
                  </tr>
                  <tr>
                    <td colspan="2" style=" font-weight: bold;" valign="top">Please check the "I'm not a robot" box
below before submitting.
                      
                      <div class="g-recaptcha" data-theme="light" data-sitekey="YOUR GOOGLE CAPTCHA SITE KEY GOES
HERE"></div></td>
                  </tr>
                  <tr>
                    <td colspan="2" align="left" valign="middle"><input type="submit" name="form_submitted"
value="Submit Your Message" /></td>
                  </tr>
                </table>
              </form>
              <?php endif ?>
            </div>
            </td>
        </tr>
      </table>
</body>
</html>




CREATING A FORM THAT WILL E-MAIL AN ATTACHMENT WITH THE FORM DATA (BETTER) - Aug 6th, 2010

Forms are easy and many of you have been using them for quite a while. The trick comes in when you are trying to allow
the sender to attach a document or image to the submitted form. Here’s a simple set of PHP scripts that will
accomplish the task. 

This one’s based on the post by bokeh at:

      http://www.webdeveloper.com/forum/showthread.php?p=344578 

If security is an issue, you may want to password protect this page from being accessed by unauthorized users. 

You can cut and paste the code below into new documents or you can download the set from:

       http://www.thecmsbcookbook.com/downloads/forms1.zip

The first one is the form that you’ll use to collect the information. You can call this anything appropriate. Like
eventform.php This one is set up to collect event submission data for an arts organization, so you’ll have to modify
it for your use. 

The second is the PHP document that actually handles the form submission mail2.php

CHANGES TO EMAIL2.PHP

The first  thing to change is the e-mail address of the recipient ($to) on line 5 of mail2.php and the “from” e-mail
on line 189.

After that, if you change the form fields in eventform.php you’ll have to mirror those changes in the mail.php file on
line 7, and in the ifelse” statement that currently starts on line 97 (the check that all required fields are filled
in before submission)

Near the end of the document on line 187, you’ll find the text of the confirmation e-mail that is sent to the e-mail
address entered on the form. And on line 189 you’ll find the field that is copied into the “RE:” of that e-mail
and the “from” address.

On line 191 you’ll find the text of the confirmation web page that appears after a successful form submission. 

If you want to change the error message that comes up if the e-mail addresses entered do not match, that’s found on
line 194

EVENTFORM.PHP



<form action="email2.php" enctype="multipart/form-data" method="post" onsubmit="
        document.getElementById('container0').style.display='';
        document.getElementById('container2').style.display='none';
        progress();
        return true;">
<table width="90%" border="0">
                
 </td>
</tr>
 <tr>
 <td width="17%">
<label for="name">Member's<br />Full Name: <span style="color: red;">*</span></label></td>
<td width="83%">
<input  id="name" type="text" size="50" name="name" value="<?php print "$name"?>" />
</td>
</tr>
 <tr>
<td>
<label for="email">Member's<br />E-mail address: <span style="color:red;">*</span></label>
</td>
<td>
<input  id="email" type="text" name="email" size="50" value="<?php print "$email"?>" />
</td>
</tr>
<tr>
<td>
<label for="confirm_email">Confirm e-mail: <span style="color: red;">*</span></label>
</td>
<td>
<input  id="confirm_email" type="text" name="confirm_email" size="50" value="<?php print "$confirm_email"?>" /></td>
</tr>
 <tr>
<td><label for="event_title">Event Title: <span style="color: red;">*</span></label>
</td>
<td><input  id="event_title" type="text" name="event_title" size="50" value="<?php print "$event_title"?>" /></td>
</tr>
 <tr>
<td>
<label for="venue_name">Venue Name: <span style="color: red;">*</span></label>
</td>
<td>
<input  id="venue_name" type="text" name="venue_name" size="50" value="<?php print "$venue_name"?>" />
</td>
</tr>
 <tr>
<td>
<label for="venue_location">Venue Address and Location: <span style="color: red;">*</span></label>
</td>
<td>
<textarea  id="venue_location" name="venue_location" cols="38" rows="5"><?php print "$venue_location"?></textarea>
</td>
</tr>
<tr>
<td>
<label for="venue_url">Venue Web Address:</label>
</td>
<td><input  id="venue_url" type="text" name="venue_url" size="50" value="<?php print "$venue_url"?>" />
</td>
</tr>
<tr>
<td>
<label for="start_date">Start Date: <span style="color: red;">*</span></label>
</td>
<td>
<input  id="start_date" type="text" name="start_date" size="50" value="<?php print "$start_date"?>" />
</td>
</tr>
<tr>
<td><label for="end_date">End Date:</label>
</td>
<td>
<input  id="end_date" type="text" name="end_date" size="50" value="<?php print "$end_date"?>" />
</td>
</tr>
<tr>
<td>
<label for="reception_date">Reception Date:</label>
</td>
<td>
<input  id="reception_date" type="text" size="50" name="reception_date" value="<?php print "$reception_date"?>" />
</td>
</tr>
<tr>
<td><label for="reception_time">Reception Time:</label>
</td>
<td><input  id="reception_time" type="text" name="reception_time" size="50" value="<?php print "$reception_time"?>" />
</td>
 </tr>
 <tr>
<td>
<label for="description">Event Description: <span style="color: red;">*</span></label>
</td>
<td>
<textarea  id="description" name="description" cols="38" rows="5"><?php print "$description"?></textarea>
</td>
</tr>
<tr>
<td>
&nbsp;
</td>
<td>
<label for="fileatt">Attach an image of your work that will be in the event:</label><br /><input id="fileatt"
type="file" name="fileatt"  size="50" />
<br />This image may be included in the listing and should be no larger than 1MB.
</td>
</tr>
<tr>
<td colspan="2"><input  type="hidden" name="hidden"  value="1" /></td>
</tr>
<tr>
<td>
<label for="submit"><span style="color: red;">*</span> Required fields.</label>
</td>
<td>
<input id="submit" type="submit" value="Send" />
</td>
</tr>
</table>
</form>



The second file is called email2.php



<?php

ob_start();

$to 'your@email.com';

$keys = array('name''email''confirm_email''event_title''venue_name''venue_location''venue_url''start
date''end_date''reception_date',  'reception_time''description''hidden');
foreach(
$keys as $key)
{
    $
$key = isset($_POST[$key]) ? $_POST[$key] : null ;


print (
'
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
     <meta http-equiv="content-type" content="text/html; charset=iso-8859-1" />
       <title>Email</title>
<script type="text/javascript">

function progress(){
intWidth = parseInt(document.getElementById("container1").style.width) + 1;
if(intWidth <= 400){
     document.getElementById("container1").style.width = intWidth+"px";
}else{
     document.getElementById("container1").style.width = 0;
}
     setTimeout("progress()",300);
}

</script>
</head>

<body>
');

//Make sure email and confirm email are the same
if (!empty ($hidden)) {
    if (
$email == $confirm_email) {
    }else{
    
$email '';
    
$confirm_email '';
    }
}


//Do a reg_ex check on the email
if (!empty ($hidden)) {    
   
$regexp "^([_a-z0-9-]+)(\.[_a-z0-9-]+)*@([a-z0-9-]+)(\.[a-z0-9-]+)*(\.[a-z]{2,4})$";
   if (
eregi($regexp$email))
   {
   }else{
       
$email '';
    
$confirm_email '';
   }
}
// End of email checking

if (empty ($hidden)) {

print (
'<div id="container2">
<h1 id="content_h1"><a name="text">    E-mail </a></h1> <p>Please use the following form to e-mail us:</p>

');

include (
"form.php");    
    
print (
'</div>

<div id="container0" style="display: none;">

<p style="font-size: 15pt; font-family: sans-serif; color:#fd6700; background:#fff;">
    Loading...
    </p>

<div id="container1" style="width:0px; height:5px; background-color:#fd6700; margin-top:0px; text-align: left;"></div>

<p>Please be patient while your data is processed. This may take a few moments especially if you are uploading a
file.</p>

</div>
');

}

// Check to see if all required fields are filled in correctly.

if (!empty ($hidden)) {
    
    if (
$_FILES['fileatt']['error'] == 1){
        print (
'<h2 id="content_h1"><a name="text">There\'s a problem with the imamge that you\'re trying to upload</a> 

                <p>The maximum file size that can be uploaded using this form is 2 megabytes.
                </p></h2>');
               }elseif ( (empty (
$name))) {
        print (
'<h2 id="content_h1"><a name="text">You must fill in the "Member\'s Full Name" field.</a><p>All required fields
must be filled in before you can submit this form.</p> </h2>'
        );
    
                }elseif ( (empty (
$event_title))) {
        print (
'<h2 id="content_h1"><a name="text">You must fill in the "Event Title" field.</a><p>All required fields must be
filled in before you can submit this form. </p>  </h2>
                ');
                }elseif ( (empty (
$venue_name))) {
        print (
'<h2 id="content_h1"><a name="text">You must fill in the "Venue Name" field.</a><p>All required fields must be
filled in before you can submit this form. </p> </h2> 
                ');}elseif ( (empty ($venue_location))) {
        print (
'<h2 id="content_h1"><a name="text">You must fill in the "Venue Address" field.</a><p>All required fields must
be filled in before you can submit this form. </p> </h2> 
                ');
                }elseif ( (empty (
$start_date))) {
        print (
'<h2 id="content_h1"><a name="text">You must fill in the "Start Date" field.</a><p>All required fields must be
filled in before you can submit this form. </p></h2>  
                ');
                }elseif ( (empty (
$description))) {
        print (
'<h2 id="content_h1"><a name="text">You must fill in the "Event Description" field.</a></h1><p>All required
fields must be filled in before you can submit this form. </p></h2>  
                ');
    }elseif ( (!empty (
$name)) && (!empty ($email)) && (!empty ($venue_name))&& (!empty ($start_date))&& (!empty
(
$description))&& (!empty ($event_title))) {
    
    
// Get html message content
$form_data "<p>This email is from <span class=\"bold\">$name</span> \n\n ";
$form_data .= "<p><b>Email Address:</b>$email</p><p><b>Event Title:</b>$event_title</p><p><b>Venue
Name:</b>$venue_name</p><p><b>Venue Location:</b>$venue_location</p><p><b>Venue URL:</b>$venue_url</p><p><b>Event Start
Date</b>$start_date</p><p><b>Event End Date</b>$end_date</p><p><b>Reception Date</b>$reception_date</p><p><b>Reception
Time</b>$reception_time</p><p><b>Event Description</b>$description</p> ";

$message =         "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \n" .
                
"    \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\"> \n" .
                
"<html xmlns=\"http://www.w3.org/1999/xhtml\"> \n" .
                
"<head> \n" .
                
"  <meta http-equiv=\"content-type\" content= \n" .
                
"  \"text/html; charset=iso-8859-1\" /> \n" .
                
"<style type=\"text/css\"> \n" .
                
"body {    font-size: 9pt; font-family:  verdana, sans-serif;     color: #000; background:#fff; }  \n" .
                
".bold { font-weight: bold; }  \n" .
                
"</style>  \n" .
                
"</head> \n" .
                
"<body>$form_data \n" .
                
"</body> \n" .
                
"</html> \n\n";

// Obtain file upload vars
$fileatt      $_FILES['fileatt']['tmp_name'];
$fileatt_type $_FILES['fileatt']['type'];
$fileatt_name $_FILES['fileatt']['name'];


$headers "From: $from";

if (
is_uploaded_file($fileatt)) {
// Read the file to be attached ('rb' = read binary)
$file fopen($fileatt,'rb');
$data fread($file,filesize($fileatt));
fclose($file);

// Generate a boundary string
$semi_rand md5(time());
$mime_boundary "==Multipart_Boundary_x{$semi_rand}x";

// Add the headers for a file attachment
$headers .= "\nMIME-Version: 1.0\n" .
             
"Content-Type: multipart/mixed;\n" .
             
" boundary=\"{$mime_boundary}\"";

// Add a multipart boundary above the html message
$message "This is a multi-part message in MIME format.\n\n" .
            
"--{$mime_boundary}\n" .
            
"Content-Type: text/html; charset=\"iso-8859-1\"\n" .
            
"Content-Transfer-Encoding: 7bit\n\n" .
            
$message "\n\n";
                        

// Base64 encode the file data
$data chunk_split(base64_encode($data));

//We now have everything we need to write the portion of the message that contains the file attachment. Here's the code:

// Add file attachment to the message
$message .= "--{$mime_boundary}\n" .
             
"Content-Type: {$fileatt_type};\n" .
             
" name=\"{$fileatt_name}\"\n" .
             
"Content-Disposition: attachment;\n" .
             
" filename=\"{$fileatt_name}\"\n" .
             
"Content-Transfer-Encoding: base64\n\n" .
             
$data "\n\n" .
             
"--{$mime_boundary}--\n";
}else{
     
// Generate a boundary string
$semi_rand md5(time());
$mime_boundary "==Multipart_Boundary_x{$semi_rand}x";

// Add the headers for a file attachment
$headers .= "\nMIME-Version: 1.0\n" .
             
"Content-Type: multipart/mixed;\n" .
             
" boundary=\"{$mime_boundary}\"";

// Add a multipart boundary above the html message
$message "This is a multi-part message in MIME format.\n\n" .
            
"--{$mime_boundary}\n" .
            
"Content-Type: text/html; charset=\"iso-8859-1\"\n" .
            
"Content-Transfer-Encoding: 7bit\n\n" .
            
$message "\n\n";
}                        


//That completes the modifications necessary to accommodate a file attachment. We can now send the message with a quick
call to mail:

// Send the message
mail($to$subject$message$headers);

$body "Dear $name, \n\nThank you for your event submission. We'll post the information as soon as
possible.\n\nCommunications Committee" ;

mail ($email"Re: $event_title"$body'From:your@email.com');

print (
'<h1 id="content_h1"><a name="text">            Thank you             </a></h1> <p>Thanks for submitting your
event information. Don\'t forget to send us information about any new events that you\'re in. We\'ll post as many as we
can.</p><p>Communications Committee</p>');

}else{
    print (
'<h2 id="content_h1"><a name="text">Please check your e-mail addresses to make sure they match and then
resubmit the form.</a>  <p>Thank you.</p></h2>');
    include (
"form.php");    
}
}

// This is the end of the insert

print ('

  </div>
</body>
</html>
');

ob_end_flush();

?> 



ANOTHER (FREE) PHP (CONTACT) FORMS GENERATOR THAT ALLOWS ATTACHMENTS (BEST) - Jun 10th, 2019

For those of you that are looking for an easy to use and very flexible PHP based forms generator that supports google
reCaptcha, and that offers lots of sophistication, you might want to look at the Forms Generator put together as a
SourceForge project by Raymond Leung 

I use this with Google recaptcha as a contact form on all my sites.

       http://phpfmg.sourceforge.net/

Forms are set up on line and downloaded to put up on your server. Forms can be modified any time and will support
multiple uploads, reCaptcha, and other security measures. As of May 2010 the form also supports unlimited levels of
dependent drop downs (If you choose “Canada” from the “countries” drop down menu, a list of Provinces will be
displayed in another drop down. If you choose “United States” then a list of States will appear.) 

Your form data is stored on Raymond’s server for a few days after it’s created, but after that you can still modify
your form because all of your data is stored in the files that you download and then upload to your own server.  

Your user name for admin access is the e-mail address in the form, so if you change the e-mail address, your user name
will change. You can always find your current user name and password in the first few lines of the admin.php file code. 

But... Nothing is perfect.

If you’re going to insert your form into your page using an iframe here are some hints.

In IE (even the latest version IE8) if you want your page background to show through an iframe, you’ll need to add
allowtransparency=”true” to the iframe tag:


<iframe src="http://www.jkwebdesigns.com/vanishingflorida/contact/contactform.php" width="450" height="425"
frameborder="0"   Allowtransparency="true">
</iframe>


And you’ll need to add background-color: transparent; to the body css of the file you’re including in the iframe.
(in the case of formmail-maker, you'll find the css code in form.lib.php. See below.).

One Caveat: If you modify the form through the on-line interface and download the files again, you’ll have to make
these changes again. This goes for the PayPal donation ID required to remove the copyright link as well.

To change the text color of your form labels, In FORM.PHP search for the CSS:


body{
    font-family : Verdana, Arial, Helvetica, sans-serif;
    font-size : 13px;
    color : #474747;
    background-color: transparent;


And change the color to suit your needs.

You can search for other “color” entries and customize your form even more. 

ALSO… Based on some confusion about how captcha security works among people who were filling out the form, I made one
change that you might want to include in the generator code.

In FORM.PHP Search for:


<li class='field_block' id='phpfmg_captcha_div'>


Change the entire <div> to:


<div class='col_label'><label class='form_field'>Security Code:</label> <label class='form_required' >*</label><br
/><b>Copy The 4 Characters Into The Empty Box Below</b></label> <br /> </div><div class='col_field'>
    <?php phpfmg_show_captcha(); ?>
    </div>


You may also want to change “reload image” to” reload characters” for the captcha image. If you do, in
FORM.LIB.PHP search for:

Reload PHP FormMail Generator Security Image

and change the text of the link and the color of the text to suit your needs.

If you’ve used this program before and had a problem with the $ character, it’s been fixed and should give you no
more trouble. Raymond said that the revised captcha notation may also be included into the generator code in the future.
Until then, if you modify the form through the on-line interface and download the files again, you’ll have to make
this change again as well.
_______________________________________________________

GOOGLE RECAPTCHA
Only a few changes required to implement Google reCaptcha

In form.php, if you’ve changed the ‘phpfmg_captcha_div’ as I did:

Change this:


<li class='field_block' id='phpfmg_captcha_div'>
    <div class='col_label'>
 <label class='form_field'>Security Code: <label class='form_required' >*</label><b>Copy The 4
Characters Into The Empty Box Below</b></label></div>
<div class='col_field'>
    <?php phpfmg_show_captcha(); ?>
    </div>
</li>


With this (the original code):

<li class='field_block' id='phpfmg_captcha_div'>
    <div class='col_label'></div><div class='col_field'>
    <?php phpfmg_show_captcha(); ?>
    </div>
</li>


Then in form.lib.php
search for 'RECAP_SITE_KEY' and enter both the site key and secret key that you got when you registered your site on
Google’s recaptcha page.

https://www.google.com/recaptcha/admin

If you’d rather not allow hotlinking to your form, search for PHPFMG_ANTI_HOTLINKING and change it’s value to
‘Y’. (It's counter intuitive, but it's an anti hotlinking switch, so 'Y' turns off hotlinking)


SHOWING HYPERLINK ICONS AUTOMATICALLY WITH CSS - Aug 6th, 2010

Here’s something that was featured in an Interactive Tools newsletter a long time ago but may have some interest.

It describes how to display little icons next to hyperlinks that signify if that link will take you offsite, open a
popup, or link to a file (as opposed to another html page). Here's how to do it using CSS in a way that's supported in
IE7, Firefox, and Safari. The CSS tags automatically search out occurrences of the specific extensions and display an
icon accordingly.

Using a PDF as an example

Add this to your CSS:



a[href $='.pdf'] { 
   padding-right: 18px;
   background: transparent url(icon_pdf.gif) no-repeat center right;
}



and instead of just the link, 

check this out 

you’ll get:  

check this out  (followed by the pdf icon you uploaded)

The same is true for many other extensions. Just replace the pdf with the ones you want.

You can use this for e-mail links that begin with mailto with:



a[href ^="mailto:"] {
   padding-right: 20px;
   background: transparent url(icon_mail.gif) no-repeat center right;
}


And for popup windows and many other uses.

You can find the original article at:

        http://www.askthecssguy.com/2006/12/showing_hyperlink_cues_with_cs_1.html

Where there’s also a link to a zip file of various icons.


STYLING HOVER POPUPS WITH CSS AND PULLING THE POPUP TEXT FROM CMSB - Aug 6th, 2010

That’s what nikkijones wanted to do, have popups with additional information displayed to the right of a menu of links
when a visitor hovered over them.

It turns out to be pretty easy once you see how it works.

To give it a try, create a multi-record editor called popups with 2 fields Title and Content and create a few
records.(Pretty Basic)

Now create a list page viewer (popups.php) for that editor and put these CSS styles in the head section:



<style type="text/css">
  #popup { 
    color: #000; 
    background-color: #ffffff; 
    font-family: arial; 

 
#popup a, #popup a:visited { 
position: relative; 
display: block; 
width: 100px; 
line-height: 14px; 
text-align: left; 
padding: 0 0px; 
margin: 0; 
border: 0px solid #666; 
text-decoration: none; 
font-size: 1em; 
font-weight: bold; 

 
#popup a span {display: none;} 
 
#popup a:hover { background-color: #00ffff; } 
 
 
#popup a:hover    { 
color: #f00; background-color: #00ffff; 
text-indent: 0;  

 
#popup a:hover span{ 
display: block; 
position: absolute; 
top: 0px; 
left: 170px; 
width: 320px; 
margin: 0px; 
padding: 10px; 
color: #ffffff; 
font-weight: normal; 
background: #222222; 
text-align: left; 
border: 5px solid #666; 

</style>



The body code that pulls the popup test from the “content” field is:



<div id="popup"> 
<?php foreach ($popupsRecords as $record): ?> 
     <a href="<?php echo $record['_link'?>"> 
      <font color="#000000">  
      <?php echo $record['title'?></font> 
     <span> 
      <?php echo $record['content'?></span></a> 
    <?php endforeach; ?> 
 </div> 



You can style the popups to suit your needs (I doubt that you’ll like my sample sizes and color scheme .)


DYNAMIC MENU LINK COLOR CHANGE ON THE ACTIVE PAGE USING CSS - Jan 27th, 2015

When I accessed a web page I wanted to highlight the navigation menu entry corresponded to that page.

The only wrinkle was that I was pulling the navigation menu entries from a multi-record table called navigation_menu
that had a link text field called entry and another for the URL called URL. Further, I was including the navigation menu
code (contained in a file called _mainnavmenu.php) on all my pages with the following code:


<?php include ("_mainnavmenu.php"); ?>

That meant that I’d have to change the css value that I wanted to change on each page on the fly (my client wanted to
change the navigation menu entry text color for the active page).

With a lot of assistance from Claire Ryan, a developer on the Interactive Tools team, we came up with this simple and
elegant solution.

I created a class value in my CSS style sheet for the active class like this:


.active {
    color: #FF0000;
}


My original mainnavmenu.php looked like this:


<ul >
 <?php foreach ($navigation_menuRecords as $record): ?>
<li ><a class="special " href="<?php echo strtolower($record['url']) ?>"<?php echo strtoupper$record['entry'] )
?></a></li>
 <?php endforeach ?>
  <li style="height:50px;">&nbsp;</li>
</ul>


I added the dynamic class by changing that code to:


<ul >
 <?php foreach ($navigation_menuRecords as $record): ?>
<li ><a class="special " href="<?php echo strtolower($record['url']) ?>"><span class="<?php echo
stripos($_SERVER['SCRIPT_NAME'], $record['url']) !== false 'active' 'inactive'?>"><?php echo strtoupper(
$record['entry'] ) ?></span></a></li>
 <?php endforeach ?>
  <li style="height:50px;">&nbsp;</li>
</ul>


The stripos looks for a (case insensitive) match for the $record [‘url’] value in the $_SERVER['SCRIPT_NAME'] (the
path to the file that is being accessed on the server) and if one is found it returns active, if not, it returns
inactive. 

Since there is no class defined for the value “inactive”, the color is changed if the value of the class is
“active”..


HELPING CLIENTS PICK PAGE BACKGROUND COLORS - Mar 18th, 2011

A client asked if I could allow their visitors to pick a background color for the page that they were viewing and
here’s what I found. I’ve also used a modified version of this to allow clients to choose the color they wanted for
page backgrounds. (Well actually <div> backgrounds that fill the whole page).

When you mouse over a color, the background changes and stays that color until another color is chosen.

You can see a sample of this effect at:

       www.thecmsbcookbook.com/colors.php

Here’s how it’s done:

First, create a multi-record section editor called "colors", with 2 fields. A “Color Hex Code” text field and a
“Color Image” upload field and make them both required fields. Set the Color Image” upload field thumbnails to
around 20 x 20 pixels

On your viewer page, add an id=“theContainer” to the Body tag.



<body id="theContainer">



Insert the following code where you want your color table to appear:



<table>
  <tr><?php foreach ($colorsRecords as $record): ?>
    <td ><a href="#" onmouseover="document.getElementById('theContainer').style.backgroundColor = '#<?php echo
$record['color_hex_code'?>'; return false;"><?php foreach ($record['color_image'] as $upload): ?>
          
            <img src="<?php echo $upload['thumbUrlPath'?>" width="<?php echo $upload['thumbWidth'?>" height="<?php
echo $upload['thumbHeight'?>" alt='#<?php echo $record['color_hex_code'?>' />

        <?php endforeach ?></a> </td><?php endforeach ?>
  </tr>

</table>



You can substitute Div tags with different id names instead of the body tag and of course you can style 
your color table any way you want to.

You can change the action to require clicking on a color, by changing "onmouseover" to "onclick".


HELPING CLIENT'S PICK CSS PROPERTY VALUES - Dec 29th, 2018

I needed to allow my client to pick the background color of a div with the id "bar".

With a bit of help from Jason Sauchuk from Interactive Tools I came up with this:

The basic script to display the user's input was:


<script type="text/javascript"> 
function changeText2(){ 
    var userInput = document.getElementById('userInput').value; 
    document.getElementById('new-text').innerHTML = userInput; 

</script>


And the basic input form:


<form>The text you entered is <b id='new-text'></b>  
<input type='text' id='userInput' value='Enter Value Here' />
<input type='button' onclick='changeText2()' value='Enter new text and Click'/></form>


And here’s the CSS Value I needed to change:


#bar {background-color: #new hex color value here ;
more css;
more css;
 }


First I removed the property background-color: #new-color here ;  from the CSS

Then I changed the script code to:


<script type="text/javascript">  
function changeText2(){  
    $('#bar').css('background-color', $('#userInput').val());  
}  
</script>


And the input form to:


<form>Change The Header Color <b id='new-color'></b>  
<input type='text' id='userInput' value='Enter Value Here' />
<input type='button' onclick='changeText2()' value='Enter # plus 6 digit Hex Color and Click'/></form>

Hex color values are always preceded by a #. If you don't want to have to add the # manually before the 6 digits each
time, you can use:


<script type="text/javascript">  
function changeText2(){  
      $('#bar').css('background-color', "#" + $('#userInput').val());
}  
</script>


If you're actually using this for choosing colors, you can add this simple on-line color picker URL to the form 

http://www.colorpicker.com/


OPEN MULTIPLE BROWSER TABS WITH ONE CLICK - Jul 3rd, 2011

Ever want to open multiple tabs with one click, say a program and it's help document at the same time?

Here's one approach using javascript:

May not work in all browsers (IE 9 has issues) 




<a href="javascript:open_wins()">click</a>

<script>
function open_wins(){
window.open("http://www.google.com");
window.open("http://www.jkwebdesigns.com");
window.open("http://www.your_site.com");
window.open("http://www.thecmsbcookbook.com");
}
</script>

_


CUSTOM 404 PAGE FOR YOUR SITE - Dec 29th, 2018

First thing you’ll need is a new 404 page for your site. it's just a web page. The page can be html, php or any other
standard page. Upload the page to your root directory.

The second is to modify the .htaccess file in your root directory to include a directive to look for the new file, like
this:


ErrorDocument 404 /your_new_404_file.php


If you don't have an .htaccess file in your root directory you can create one in a text editor with the code above on a
single line. Save it with the name .htaccess (don't forget the period at the front of the file name), and upload the
file to your root directory

Some suggestions:

Style your page to have the same look and feel as your web site (including navigation).
Include a search box to help visitors find what they are looking for, or include links to your site map, or both.
Use friendly verbiage, no “tech” speak.
Include a contact form or a link to one, so that your visitor can easily report a broken link to you.

Make sure that your webserver returns an actual 404 HTTP status code when a missing page is requested. (check with your
web host if you're not sure) 

Include <meta name="robots" content="noindex, nofollow"> in the head section of your new 404 page 

Here’s a link to Google’s multi-lingual, enhanced 404 widget to embed a search box on your custom 404 page and
provide users with useful information to help them find the information they need.

http://support.google.com/webmasters/bin/answer.py?hl=en&answer=136085 


DEALING WITH IE'S "COMPATIBILITY" MODE - Aug 11th, 2012

If IE8 and 9's "Compatibility" mode is breaking your site design you can add this as the first line in the head of your
viewer to force IE not to enter compatibility mode.



<meta http-equiv="X-UA-Compatible" content="IE=8;IE=9" />



There's more about this topic in articles at: http://www.alistapart.com/articles/beyonddoctype and
http://hsivonen.iki.fi/doctype/

EMAIL



EMAILS FROM PAYPAL NOT BEING FORWARDED - Oct 23rd, 2016

Many of my clients use an email address like payments@myclientdomain.com as their primary PayPal email address, and I've
been using simple email forwarding to forward email that's sent to payments@myclientdomain.com to a series of real email
addresses. 

Recently, my clients began complaining that PayPal emails (like payment confirmations, etc) were not being forwarded to
them.

According to tech support at Bluehost, it seems that in an effort to further guard against spammers, PayPal has recently
decided to set their
emails (like payment confirmations, etc.) so that they cannot be forwarded. (I didn’t know they could do that)

If you need to have these PayPal emails sent to multiple recipients there are a few possibilities.

Google Apps for Work is possibly the most comprehensive solution but might be overkill for your situation.

If you're clients are using a Gmail account to access their email, here's a link to a Google help doc on How to access
other email accounts with Gmail: 
https://support.google.com/mail/answer/21289?hl=en

And here's the Bluehost tutorial on the same topic:
https://my.bluehost.com/cgi/help/gmail

And of course if you're using an email client like Outlook, you can always add a new account for
payments@myclientdomain.com


TESTING THE PHP MAIL() AND MAILARAY() FUNCTIONS - Aug 23rd, 2014

Here are 2 simple scripts for testing your server's PHP mail() and one to test CMSB's built in mailArray function:

CAVEAT: Make sure that the 'from' address and the 'to' address are real, or your mailserver may kick your email back
with no specific error messages. I had a .com instead of a .org as the TLD in an admin email address, and since there
was no .com domain, the password change email, supposedly coming from the admin email address, was kicked back by the
server as invalid and never got delivered. I guess that applies to those noreply@noreply.com email return addresses as
well. 



<?php
$message '';

if (isset(
$_POST['email']) && !empty($_POST['email'])){
  if (
mail($_POST['email'], $_POST['subject'], $_POST['body'], '')){
    
$message "Email has been sent to <b>".$_POST['email']."</b>.";
  }else{
    
$message "Failed sending message to <b>".$_POST['email']."</b>.";
  }
}else{
  if (isset(
$_POST['submit'])){
    
$message "No email address specified!";
  }
}

if (!empty(
$message)){
  
$message .= "n";
}
?>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>
      Mail test 3
    </title>
  </head>
  <body>
    <?php echo $message?>
    <form method="post" action="">
      <table>
        <tr>
          <td>
            e-mail
          </td>
          <td>
            <input name="email" value="<?php if (isset($_POST['email'])
            && !empty(
$_POST['email'])) echo $_POST['email']; ?>">
          </td>
        </tr>
        <tr>
          <td>
            subject
          </td>
          <td>
            <input name="subject">
          </td>
        </tr>
        <tr>
          <td>
            message
          </td>
          <td>
            <textarea name="body"></textarea>
          </td>
        </tr>
        <tr>
          <td>&nbsp;
            
          </td>
          <td>
            <input type="submit" value="send" name="submit">
          </td>
        </tr>
      </table>
    </form>
  </body>
</html>

 



<?php 
if ( function_exists'mail' ) )
{
    echo 
'mail() is available';
}
else
{
    echo 
'mail() has been disabled';

?>
<?php

$msg="";
if(isset(
$_POST['submit']))
{
    

    
$from_add "someone@a_real_domain.com"

    
$to_add "you@your_domain.com"//<-- put your email address here

    $subject "Test Subject";
    
$message "Test Message 4";
    
    
$headers "From: $from_add \r\n";
    
$headers .= "Reply-To: $from_add \r\n";
    
$headers .= "Return-Path: $from_add\r\n";
    
$headers .= "X-Mailer: PHP \r\n";
    
    
    if(
mail($to_add,$subject,$message,$headers)) 
    {
        
$msg "Mail sent OK";
    } 
    else 
    {
        
$msg "Error sending email!";
    }
}
?>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> 
<html>
<head>
    <title>Test form to email</title>
</head>

<body>
<?php echo $msg ?>
<p>
<form action='<?php echo htmlentities($_SERVER['PHP_SELF']); ?>' method='post'>
<input type='submit' name='submit' value='Submit'>
</form>
</p>


</body>
</html>

 



<?php  
  
  
  // load viewer library
  $libraryPath 'cmsAdmin/lib/viewer_functions.php';
  
$dirsToCheck = array('','../','../../','../../../','../../../../');
  foreach (
$dirsToCheck as $dir) { if (@include_once("$dir$libraryPath")) { break; }}
  if (!
function_exists('getRecords')) { die("Couldn't load viewer library, check filepath in sourcecode."); }
  
?>
  
  <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>mailArray test2</title>
</head>

<body>   

<?php
$message 'This is a test message';
$mailArray = array(
'to' => 'you@your_domain.com',
'from' => 'someone@a_real_domain.com',
'subject' => 'mailarray test 2',
'html' => $message
);
$errors sendMessage($mailArray);
?>

</body>
</html>

 


USING THE BUILT IN "MAILARRAY" FUNCTION TO SEND EMAILS - Aug 23rd, 2014

Using the mailArray function built in to CMS Builder seems to be more robust then using sendmail directly.

CAVEAT: Make sure that the 'from' address and the 'to' address are real, or your mailserver may kick your email back
with no specific error message. I had a .com instead of a .org as the TLD in an admin email address, and since there was
no .com domain, the password change email, supposedly coming from the admin email address, was kicked back by the server
as invalid and never got delivered. I guess that applies to those noreply@noreply.com email return addresses as well.  

The basic implementation for the mailArray function is:


<?php 
$message 'This is a test message';
$mailArray = array(
'to' => 'example@example.com',
'from' => 'example@example.com',
'subject' => 'This is a subject',
'html' => $message
);
$errors sendMessage($mailArray);
?>
 

If you're pulling the to: value from  a multi-record editor, you may want to delay execution of the individual emails
and reset the PHP max_execution time. If so, insert this before the $errors code above



// delay execution by .2 sec
usleep(200000);
// reset max_execution_time to 30 sec
set_time_limit(30);
 

To make sure that the maximum length of a line in your email does not exceed 70 characters (a stipulation in the PHP
email function spec), you may want to include this code just after the $message variable:



<?php $message wordwrap$message70); ?> 
     


SENDING EMAILS FROM A MULTI-RECORD EDITOR USING MAILARRAY FUNCTION - Nov 16th, 2012

Recent versions of CMS Builder have a built in mailArray email function that takes care of many of the issues
encountered when using the PHP mail( ) function alone.

If you use this function in CMS Builder 2.17 (or later)  it will log your messages for you if you have the feature
enabled.

Beginning with CMS Builder version 2.50, there are many other new email related features available, including templates
and cron job management.
 
This is the basic code required to use the mailAray function.   
 


$message = 'This is a test message'; 
$mailArray = array( 
  'to'         => 'example@example.com', 
  'from'     => 'example@example.com', 
  'subject'     => 'This is a subject', 
  'html'         => $message 
); 
$errors =  sendMessage($mailArray);


To use this function to send emails to recipients in a multi-record table, you could use something like:



<?php //reset counter so emails sent can be counted
$email_count '' ?>
<?php foreach($your_tableRecords as $record) : ?>
<?php $the_to $record['email']; ?>
<?php $first_name $record['first_name'] ; ?>
<?php $last_name $record['last_name'] ; ?>
<?php ob_start(); // start capturing output ?>
<?php foreach ($your_tableRecords as $record): ?>
<?php if($the_to == $record['email']) : ?>
<?php $recnum $record['num'?>
<?php endif; ?>
<?php endforeach ?>
<?php $output ob_get_clean(); // stop capturing output  ?>
<?php $variable1 "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi vulputate, est vitae convallis
hendrerit, risus nulla iaculis urna, viverra euismod tortor sem eget felis.
      
Aliquam erat volutpat. Maecenas ac purus justo. Vivamus tortor leo, tincidunt sit amet rhoncus bibendum, faucibus vitae
neque. 

Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Duis id velit sed leo dignissim
hendrerit id sit amet ligula. Nam iaculis, mi suscipit varius dignissim, ligula lectus eleifend est, vitae sollicitudin
orci arcu facilisis tortor. Vivamus adipiscing diam vitae ipsum adipiscing euismod." ?>
<?php //limit line length to no more than 70 characters
$variable1 wordwrap($variable170); ?>
<?php $email_count++ ; ?>
<?php  
 $message = <<<EOF
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type content='text/html; charset=utf-8" />
<style type="text/css">
.body-text {font-family:Arial; font-size: 1.0em;}
</style>
</head>
<body >
<div align='left'><img src='http://www.your_domain.com/email_open.php?num=$recnum' height='1' width='1' alt='' /><img
src='http://www.your_domain.com/images/LOGO.png' width='800' height='183' style='border:hidden'/></div>
 <div align='left' class='body_text'>
Hello $first_name $last_name,

 $disclaimer \r\n

$output \r\n

Please be aware that metus id nibh porttitor faucibus. Integer nec urna risus, in cursus enim. Nulla hendrerit, sem non
hendrerit molestie, dui urna tempus dui, sit amet bibendum orci tortor ac metus. Proin ac scelerisque neque.\r\n
</div>
</body>
</html>
EOF;
$the_from  "you@your_domain.com";
$the_subject "This is the email subject";

$mailArray = array( 
'to' => $the_to,
'from' => $the_from,
'subject' => $the_subject,
'html'         => $message 
); 
$errors =  sendMessage($mailArray);
// delay execution by .1 sec (optional if sending anomilies occer, try this)
usleep(100000);
// reset max_execution_time to 30 sec (or your value from php.ini)
set_time_limit(30);
?>
<?php endforeach ?>
<?php if ($email_count && !$email_count == '0' ):?>
<span class="your_class">*** </span>You have sent <?php echo $email_count ?> emails.
<?php endif; ?>



TRACK IF AN EMAIL WAS OPENED BY RECIPIENT - Dec 29th, 2018

If you're sending emails to a list from a series of records in a multi-record editor using CMSB's built in $mailArray
function, it’s easier then you think to track which emails were opened and which were not. (Because of the
multiplicity of email clients in cyberspace, it’s not a perfect solution but it can be pretty accurate.)

First, in the editor that contains your email addresses, you’ll need to create a check box field called ‘opened’.
It’s also handy to insert that field in the ListPage Fields so you can get a quick overview of the field’s status.

Now you’ll need to create a simple viewer ( I called mine email_open.php) that will update the ‘opened’ field to a
value of ‘1' when accessed. It also updates the updateDate to the date and time that the email was opened to
facilitate reporting. (I used the record number from the record to make sure that the right record gets updated.)


<?php 
  $libraryPath 'cmsAdmin/lib/viewer_functions.php';
 
$dirsToCheck = array('/path/to/your/viewer_functions.php/','','../','../../','../../../');
  foreach (
$dirsToCheck as $dir) { if (@include_once("$dir$libraryPath")) { break; }}
  if (!
function_exists('getRecords')) { die("Couldn't load viewer library, check filepath in sourcecode."); }
  
?>
 <?php
$numAsInt intval( @$_GET['num'] ); 
$query "UPDATE {$TABLE_PREFIX}client_uploads SET opened = '1', updatedDate = NOW() WHERE num = $numAsInt"
mysql_query($query) or die("MySQL Error:\n"htmlspecialchars(mysql_error()) . "\n");
$userNum mysql_insert_id();
exit;        
?>


NOTE: Dave Edis from Interactive Tools suggested, “You need to always escape or sanitize user input to prevent hackers
from tricking the script into running their own MySQL. Instead of calling mysql_real_escape_string() or a related
function I call intval() which forces the value to be a number and accomplishes the same thing." 

Then somewhere in the foreach loop that loops through the records that contain your email address, you’ll need to
define the variable $recnum.


<?php $recnum $record['num'?>


Then in the message body or header, you’ll need to insert an img tag that instead accesses your email_open.php file :


<img src='http://www.your_domain.com/email_open.php?num=$recnum' height='1' width='1' alt='' />


Since some recipients have images turned off in their email clients, one possibility (untested) is to use a bgsound tag
as a fallback The bgsound tag is proprietary to Microsoft so it only works on Outlook and IE.


<bgsound src='http://www.your_domain.com/email_open.php?num=$recnum ' volume='-10000'?/>


There’s also the HTML5 tag (also untested) 


<audio autoplay='autoplay'><source  src='http://www.your_domain.com/email_open.php?num=$recnum' type='audio/mpeg'>
</audio>


You can add additional criteria to define the record to be modified by adding those criteria to the end of the url after
$recnum (separated by &amp; ) and to the MySQL $query where statement separated by appropriate operators.

You can also implement this concept in the emailOnApproved plugin by adding:

Building on the concept in the recipe above, you can implement this idea for emails sent by the emailonapproved plugin.

Just add:


<img src='http://www.your_domain.com/email_open.php?num={$_REQUEST['num']} height='1' width='1' alt='' />


to the plugin's message area. The other files in this recipe can remain the same.

Note: If you are drawing on new records for each mailing, you won’t need to reset the ‘opened’ field for each
mailing, however, if you are drawing on the same records each time, you’ll need to reset the ‘opened’ field to a
value of ‘0’ on each record.  Fortunately you can use the Field Resetter plugin from the Cookbook recipe called
“RESET THE VALUE OF A FIELD IN ALL RECORDS OR IN FIELDS IN MULTIPLE TABLES” to accomplish this with one click.


AN OPENED EMAILS REPORT VIEWER - Nov 16th, 2012

Assuming that you have a first_name’,  a last_name’, and an 'opened' check box field in your table, here’s a
simple viewer that will list the recipients of emails that were opened and when they were opened, those recipients that
did not appear to open their emails, and an email list to contact them. 


<span class="your_heading_class">THESE EMAILS WERE OPENED</span>
<?php foreach ($your_tableRecords as $record): ?>
<?php if ($record['opened']==1):?>
<span class="your_body_class">
<?php echo @date("l F jS, Y - g:i a"strtotime($record['updatedDate'])) ?> - <?php echo $record['last_name'?>, <?php
echo $record['first_name'?> 
</span>
<?php endif ?>
<?php endforeach ?>
<hr />
<span class="your_heading_class">THESE EMAILS WERE NOT OPENED</span>
<?php foreach ($your_tableRecords as $record): ?>
<?php if ($record['opened']==0):?>
<span class="your_body_class">
<?php echo $record['last_name'?>, <?php echo $record['first_name'?>
</span>
<?php endif ?>
<?php endforeach ?>
<hr />
<span class="your_heading_class">EMAIL LIST FOR EMAILS THAT WERE NOT OPENED</span>
<?php foreach ($your_tableRecords as $record): ?>
<?php if ($record['opened']==0):?>
<span class="your_body_class"><?php echo $record['email'?>;&nbsp;</span> 
<?php endif ?>
<?php endforeach ?>


USING PHPLIST TO MANAGE AND SEND BULK EMAIL CAMPAIGNS - Jan 26th, 2019

As more and more ISPs clamp down on the rules for sending bulk emails, making sure that an email campaign is delivered
to multiple recipients has become more of a challenge.

I've been successfully using the open source PHPList 
https://www.phplist.org/download-phplist/
 to handle the job.

The only downside is that if you are currently managing captured email addresses using CMS Builder you'll have to paste
any email lists and  remove request lists from CMSB into PHPList, but that is a really quick procedure.

Or, if it works for you, you can let PHPList handle the whole process of capturing email addresses and remove requests
automatically

PHPList also does a commendable job of sending either web pages or WYSIWYG documents to recipients as emails, bounced
email management, click tracking for links, and it has many other features.

In fact, all the email management features that we've asked for on the forum seem to be incorporated into PHPlist, and
it's free and open source.

Below is a set of instructions on how to set up and use use PHPList together with cron jobs to handle automatic
scheduling of both single, and automatically repeated email campaigns (great for things like monthly newsletters).

That's followed by some User Level help documentation
___

(ADMIN -PROGRAMMERS) SETTING UP PHPLIST FOR CAMPAIGN EXECUTION AND BOUNCED MANAGEMENT USING CRON JOBS

Step 1 through 4 are changes to the basic config.php file, step 5 and 6 are about setting up the cron jobs, Step 7
setting up your listbounces@your_domain.com  mailbox Step 8 is about testing the result.

Step 1) Since My Web Host limits the amount of emails sent in one hour to 150, and allowing 25 for possible overhead,
125 per hour can safely be sent, so set the:

MAILQUEUE_BATCH_SIZE to 125 - 125 messages per batch, 
MAILQUEUE_BATCH_PERIOD to 3600 - 1 batch per hour,
MAILQUEUE_THROTTLE to 1 - one second between messages,
MAILQUEUE_AUTOTHROTTLE - to 0 (off ).
(Don’t add these to the config file now, they'll be added in step 3)

Step 2) set define MANUALLY_PROCESS_QUEUE to 0 (off)

Step 3) Add the following to the config.php file (no values need to be changed for 125 emails per batch)

define ("MANUALLY_PROCESS_QUEUE",0);
define('MAX_PROCESS_MESSAGE',999);
define("MAILQUEUE_BATCH_SIZE",125); 
define("MAILQUEUE_BATCH_PERIOD",3600);
define('MAILQUEUE_THROTTLE',1); 
define('MAILQUEUE_AUTOTHROTTLE',0);
define('USE_DOMAIN_THROTTLE',0);
define('DOMAIN_BATCH_SIZE',1);
define('DOMAIN_BATCH_PERIOD',120);
define('DOMAIN_AUTO_THROTTLE',0);
define('MAX_PROCESSQUEUE_TIME',0);
define("PLUGIN_ROOTDIR","/path-to-your/phplistplugins");

NOTE: Plugins are in the phplistplugins directory 

Step 4) To handle automatic processing of bounced emails, uncomment the:
$message_envelope = 'listbounces@your_domain.com';

Set the:
define MANUALLY_PROCESS_BOUNCES to 0 (off),
$bounce_mailbox_host = 'localhost'; to the actual incoming mailserver address: (mail.your_domain.com) 
$bounce_mailbox_user = 'popuser'; to the actual email username: (listbounces@your_domain.com)
$bounce_mailbox_password = 'password'; (SoMePasSwoRD is entered here).

Step 5) Set up a cron job for processing cued campaigns:

/usr/bin/php-cli -q /path-to-your-installation/lists/admin/index.php -p processqueue -c
/path-to-your-installation/lists/config/config.php

Set the repeat execution time value to every few minutes for testing, and then to a reasonable for the campaign density.

Step 6) Set up a Cron job to handle bounced emails (Your path may be different then the sample code) Look at
CMSB>Admin>Background Tasks for a path specific to your server:

/usr/bin/php-cli -q path-to-your-installation/lists/admin/index.php -p processbounces -c
/path-to-your-installation/lists/config/config.php  

Again, set the repeat execution time value to every few minutes for testing, and then to once per hour.

Step 7) Setting up a listbounces@your_domain.com mailbox:

Email Accounts in the your cPanel, add a mailbox called listbounces@your_domain.com with the password: SoMePasSwoRD (or
one of your choosing)

Step 8) Testing

Add your email to the cron job reporting so that you see the results of any tests.

Create a test campaign with a few email addresses that you control. 

Complete all the steps required to send a campaign but, if you’ve previously sent campaigns manually, you notice that
there is no longer a “process queue” button to click, because the cron job will start sending your campaign
automatically.

Once the process is working correctly it is no longer necessary to keep the browser window open or even to keep the
computer on to send a campaign or process bounces.

Once all is working as it should, you can reset the cron job execution times to a more reasonable frequency (once per
hour) and remove your email address from the cron job reporting. >/dev/null 2>&1 can also be added to the end of the
cron command line to prohibit sending reporting emails for specific cron jobs. 

Issues? Email Dan Waterloo at dan@phplistsupport.com (phplistsupport.com is a private company and is not a free service)
___

SETTING UP AND USING THE USE_REPETITION FUNCTION TO SCHEDULE THE AUTOMATIC RE-SENDING OF THE SAME CAMPAIGN TO THE SAME
SUBSCRIBER LIST. 
(Great for sending a web page with dynamic content like a newsletter, weekly specials, etc.)

Enabling the option to re-send the same campaign in the future is as simple as adding define('USE_REPETITION',1);  to
your config.php file.

Once enabled, when you create a new campaign, 2 new fields will be added to the scheduling tab, “Repeat Campaign
Every” and “Repeat Until”.

When these are set, and the campaign is sent (or cued if you’re using a cron job), PHPList will automatically create a
new message with the same content using those repetition settings. The embargo of the message will be automatically
increased to accommodate the repetition settings you choose.

If you are using a cron job to automatically trigger the sending of cued campaigns, the campaign will be not be sent
more often than the repetition rate set in the “Repeat Campaign Every” setting of that campaign.

NOTE: If you go back to edit your campaign in any way, it will be considered a “draft” and will not be automatically
sent until it has been re-cued or re-sent.

*Exclusion of times and dates*

You can globally tell PHPList NOT to send (ie re-schedule) messages at certain times, like weekends or holidays by
adding the following code to your config.php file.

# exclude dates for repetition
$repeat_exclude = array(
  array("format" => "%a", "values" => array("Sun","Sat")),
  array("format" => "%d-%m-%Y","values" => array("31-01-2004","01-01-2005")),
);
## end
You can add as many entries as you like using the following format:

array("format" => [format], "values" => array([list of values])),

The bits between [ and ] can be chosen as you like it, but they have to conform to the following rules:

[format] needs to be a Mysql data format string. There’s more information at:

http://dev.mysql.com/doc/refman/5.0/en/date-and-time-functions.html#function_date-format

[list of values] needs to be a list of values that this format can return, which then, if matched with the new
“embargo” for a message will not create a message for that date, but instead increases the “repetition value” to
find the next one in a row.

So for example for excluding the weekend you use array(“format” ? ”%a”, “values” ?
array(“Sun”,”Sat”)), and for excluding specific dates you can use array(“format” ?
”%d-%m-%Y”,”values” ? array(“31-01-2004”,”28-08-2004”)), 

___

USER INSTRUCTIONS:

USING PHPLIST TO SEND OUT EMAIL CAMPAIGNS

PHPlist allows you to send out large numbers of emails at one time.

A) Open <a target="_blank"
href="http://www.your_domain.com/your-email-list.php"><u>http://www.your_domain.com/your-email-list.php</u></a> and copy
the desired list of email addresses. (CTL-C)

B) Log in to the PHPList admin page at:


<a target="_blank" href="http://www.your_domain.com/lists/admin/"><u>http://www.your_domain.com/lists/admin/</u></a>


1) From the subscriber menu, choose “Subscriber Lists”
2) Click on the little garbage can icon to delete the existing “Salon” list
3) Add a new list called “Salon” and Save
4) Click on “Add Some Subscribers”
5) Paste the copied list (CTL-P) into the empty box and click on “Import Emails”
6) Either “Import Some More Emails” (add your email address to the list) if required, or 
7) Choose "Send a Campaign” from the" Campaigns" menu, and click on "Start a New Campaign"
8) Fill in the “Subject” and the “From” Field
9) Check the “Send a Web Page” button and fill in the URL of the E-Blast Web Page
10) Delete any code in the “Footer” box, click “Next” 
11) Leave HTML checked and fill in your email address in the “Send Test” box and click on “Send Test”
12) If the test is received and all is well, click “NEXT”, decide when to send the campaign (or leave the defaults
to send immediately) and click “NEXT” again.
NOTE: If you want to automatically repeat sending this particular campaign (I.E. A Monthly Newsletter) you would select
"every 4 weeks" from the “Repeat Campaign Every” field and as far in the future as required from the “Repeat
Until” field. This specific campaign will automatically be repeatedly sent based on the parameters you enter. 
13) Select the list to send to (Salon) and click “NEXT”
14) Check the “Reset Click Statistics” box and click “Send Campaign”. The campaign will automatically start
processing at the top of the next hour.

NOTE: You can safely close your browser and/or turn off your computer after you've finished step 14. You will receive a
number of progress emails to let you know the start and end of the mailing process.

*** AFTER  A SPECIFIC REPEATING CAMPAIGN HAS BEEN SENT THE FIRST TIME, <u>DO NOT</u> DELETE THE EMAIL LIST ASSOCIATED
WITH THAT CAMPAIGN.

INSTEAD, FOLLOW THE STEPS IN "ADDING SUBSCRIBERS TO A PHPLIST EMAIL LIST" TO UPDATE THE ASSOCIATED EMAIL LIST.

YOU DO NOT NEED TO CREATE THAT NEW SPECIFIC REPEATING CAMPAIGN AGAIN***

NOTE: "Click Tracking" is enabled for your campaigns. This means that you can view detailed statistics about which links
in a campaign email have been clicked on by each of the email recipients.

WARNING: When managing campaigns, if you delete a "sent" campaign, the campaign's click tracking data will also be
deleted, and links in the campaign emails will no longer work.

There's more information at: http://www.phplist.com/support
___

ADDING SUBSCRIBERS TO A PHPLIST EMAIL LIST

After you've created a master email list or have sent out a repeating campaign for the first time, you only need to add
email addresses in the email list.

***NEVER DELETE AN EMAIL LIST THAT IS USED IN A REPEATING CAMPAIGN***

TO ADD NEW EMAIL ADDRESSES TO AN EMAIL LIST:

A) Open <a target="_blank"
href="http://www.your_domain.com/your-email-list.php"><u>http://www.your_domain.com/your-email-list.php</u></a> and copy
the desired list of email addresses. (CTL-C or CMD-C)

B) Log in to the PHPList admin page at:


<a target="_blank" href="http://www.your_domain.com/lists/admin/"><u>http://www.your_domain.com/lists/admin/</u></a>


1) From the subscriber menu, choose “Import Emails”
2) Check the check box for the appropriate list
3) Check the “skip email addresses that are not valid” check box
3) Paste the copied list (CTL-P or CMD-P) into the empty box.
4) Click on "Import Emails".
4) Your new emails will be added to the list and will automatically receive the repeating campaign, you can now close
your browser tab.
___

DELETING SUBSCRIBERS FROM ALL PHPLIST EMAIL LISTS

There are times when a subscriber will want to opt out of receiving email blasts and newsletters from you and they exist
on a list that you are not allowed to delete (like a repeating NEWSLETTER list).

To remove subscribers.

A) Open 


http://www.your_domain.com/your-email-list.php


and copy the list of email addresses to be removed. (CTL-C or CMD-C)

B) Log in to the PHPList admin page at:


<a target="_blank" href="http://www.your_domain.com/lists/admin/"><u>http://www.your_domain.com/lists/admin/</u></a>


1) From the "Subscriber" menu choose "suppression list"
2) *** Un-check the "make suppression permanent" checkbox*** 
3) Paste the email addresses in the empty box (CTL-P or CMD-P)
4) Click Continue 

That's it, your done. Any duplicates will be ignored

NOTE: If you don't un-check the  "make suppression permanent" you will cause the subscriber to be blacklisted on
PHPList, and that adds another step to reinstating them if ever required.

Added Jan, 25, 2019: If your campaigns don't seem to be sent out as planned, check the scheduling tab and the embargoed
until field. If it is not set to the top of the current hour then you may have a server time issue. You can install the
timezone plugin to correct the time difference. It'a available from the clone or download button at:
https://github.com/bramley/phplist-plugin-timezone


SOCIAL MEDIA SHARE LINKS THAT WORK IN HTML EMAILS SENT BY PHPLIST - Nov 17th, 2022

The problem was that almost all of the social media share link generators use JavaScript, and JavaScript doesn't work at
all with most email clients.

I found the original unmodified html code at https://www.niftybuttons.com/

And found the share link formats at: https://www.websiteplanet.com/webtools/sharelink/

I had to create the images, since I couldn’t find a matching set of share images on-line. You can download the set I
created from http://thecmsbcookbook.com/downloads/share-buttons.zip

A good place to start to find Social Media icon images for making your own buttons is http://Icon8.com

The detail page is a detail page for a multi-record section. The active code for that page, which (finally) works on the
greater majority of email clients when sent through PHPList is below.

The following code is in a file called _eblast_share.php which is inserted in the full detail page using  <?php  include
(
"_eblast_share.php"); ?>.

The title="" atribute shows the text in the atribute as a popup when a link is rolled over.


<div align="center"><span class="salon_text_font" style="color:#FFF">Please Share This Announcement With Others</span>
<a
href="https://www.facebook.com/sharer/sharer.php?u=http://www.southfloridaartsalons.com/eblastdetail-salon-annc.php?<?php
echo($salon_e_blastsRecord['num']); ?>" target="_blank" rel="noopener noreferrer" title="Share On Facebook"
style="text-decoration:none;border:0;width:92px;height:60px;padding:6px;margin:6px;"><img src="
http://www.southfloridaartsalons.com/images/facebook-share-button.png"  width="92" height="60" /></a>

<a href="https://twitter.com/intent/tweet?url=http://www.southfloridaartsalons.com/eblastdetail-salon-annc.php?<?php
echo($salon_e_blastsRecord['num']); ?>" target="_blank" rel="noopener noreferrer" title="Share On Twitter"
style="text-decoration:none;border:0;width:92px;height:60px;padding:6px;margin:6px;"><img src="
http://www.southfloridaartsalons.com/images/twitter-share-button.png" width="92" height="60" /></a>

<a href="mailto:info@example.com?&subject=I&#39;d Like to Share This Interesting South Florida Art Salon With
You&body=http://www.southfloridaartsalons.com/eblastdetail-salon-annc.php?<?php echo($salon_e_blastsRecord['num']);
?>%0A" target="_blank" rel="noopener noreferrer" title="Share By Email"
style="text-decoration:none;border:0;width:92px;height:60px;padding:6px;margin:6px;"><img src="
http://www.southfloridaartsalons.com/images/email-sharing-button.png" width="92" height="60" /></a>

<a
href="https://www.linkedin.com/shareArticle?mini=true&url=http://www.southfloridaartsalons.com/eblastdetail-salon-annc.php?<?php
echo($salon_e_blastsRecord['num']); ?>" target="_blank" rel="noopener noreferrer" title="Share On Linkedin"
style="text-decoration:none;border:0;width:92px;height:60px;padding:6px;margin:5px;"><img src="
http://www.southfloridaartsalons.com/images/linkedin-share-buttton.png" width="92" height="60" /></a>

<a
href="https://pinterest.com/pin/create/button/?url=http://www.southfloridaartsalons.com/eblastdetail-salon-annc.php?<?php
echo($salon_e_blastsRecord['num']); ?>" target="_blank" rel="noopener noreferrer" title="Share On Pinterest"
style="text-decoration:none;border:0;width:92px;height:60px;padding:6px;margin:6px;"><img src="
http://www.southfloridaartsalons.com/images/pinterest-share-button.png" width="92" height="60" /></a>
</div>


And the full detail page code: (Sorry, but there are a lot of if statements for various conditions that are unique to my
application...) Take note of where there is inline css asnd where the css is between the <style type="text/css"> and the
</style> tag.

<?php header('Content-type: text/html; charset=utf-8'); ?>
<?php
  /* STEP 1: LOAD RECORDS - Copy this PHP code block near the TOP of your page */
  require_once "/home2/ngpymrmy/public_html/sofloartsalons/cmsAdmin/lib/viewer_functions.php";

   list(
$salon_e_blastsRecords$salon_e_blastsMetaData) = getRecords(array(
    
'tableName'   => 'salon_e_blasts',
    
'where'       => whereRecordNumberInUrl(1),
    
'limit'       => '1',
  ));
  
$salon_e_blastsRecord = @$salon_e_blastsRecords[0]; // get first record
  
  list($common_informationRecords$common_informationMetaData) = getRecords(array(
    
'tableName'   => 'common_information',
    
'allowSearch' => '0',
    
'limit'       => '1',
  ));
  
$common_informationRecord = @$common_informationRecords[0]; // get first record
  
  // load records
  list($google_font_namesRecords$google_font_namesMetaData) = getRecords(array(
    
'tableName'   => 'google_font_names',
  ));
  
  list(
$fontsRecords$fontsMetaData) = getRecords(array(
    
'tableName'   => 'fonts',
'limit'       => '1',

  ));
  
$fontsRecord = @$fontsRecords[0]; // get first record
  
  // show error message if no matching record is found
  if (!$salon_e_blastsRecord) {
    
header("HTTP/1.0 404 Not Found");
    print 
"Record not found!";
    exit;
  }

?>
<?php $masterurl $common_informationRecord['master_url']?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<link href='http://fonts.googleapis.com/css?family=Noto+Sans:400,700|Montserrat|Karla|Istok+Web|Oxygen' rel='stylesheet'
type='text/css'>
<meta name="robots" content="noindex,nofollow" />
<meta http-equiv="content-type" content="text/html;charset=utf-8" />
<meta name="color-scheme" content="light dark">
<meta name="supported-color-schemes" content="only">
<meta name="viewport" content="width=device-width, target-densitydpi=device-dpi, initial-scale=1">
<title>WELCOME &nbsp; TO &nbsp; SOUTH &nbsp; FLORIDA &nbsp; ART &nbsp; SALONS - E-BLASTS</title>
<script src="<?php echo $masterurl ?>/Scripts/AC_RunActiveContent.js" type="text/javascript"></script>
<link rel="stylesheet" type="text/css" href="<?php echo $masterurl ?>/css/fonts.css.php" />
<link rel="stylesheet" type="text/css" href="http://fonts.googleapis.com/css?family=<?php 
    $output ''
    foreach (
$google_font_namesRecords as $record) { $record['name'] = preg_replace("/[, ]/""+"$record['name']);
      
$output .= $record['name'] . "|"
    } 
    
$output rtrim($output,"|"); // remove trailing pipe 
    print $output
?>">
<style type="text/css">
body { background-color: #0e2071; background-image: url(http://southfloridaartsalons.com/<?php foreach
(
$common_informationRecord['all_page_backgound_image'] as $upload): ?><?php echo $upload['urlPath'?><?php endforeach
?>); background-repeat: repeat;  }
@media (prefers-color-scheme: dark) {
        .darkmode {background-color: #0e2071; !important; }
        .darkmode p { color: #ffffff !important; }
}
@media (prefers-color-scheme: light) {
        .darkmode {background-color: #0e2071; !important; }
        .darkmode p { color: #ffffff !important; }
}
a:link {color: #ffffff; text-decoration:none;}
a:visited {color: #ffffff; text-decoration:none;}
a:active {color: #ffffff; }
a:hover {color: #ffffff; text-decoration:underline;}

a {
 color: #<?php echo $common_informationRecord['link_color'?>;
    text-decoration: none;
}
a:hover {
color: #<?php echo $common_informationRecord['hover_color'?>;
    text-decoration: underline;
}
a.special:link {
color: #<?php echo $common_informationRecord['link_color'?>;
    text-decoration: none;
}
a.special:visited {
color: #<?php echo $common_informationRecord['link_color'?>;
    text-decoration: none;
}
a.special:hover {
color: #<?php echo $common_informationRecord['hover_color'?>;
    text-decoration: underline;
}
a.special:active {
color: #<?php echo $common_informationRecord['link_color'?>;
    text-decoration: underline;
}
.copyright {
color: #<?php echo $common_informationRecord['text_color'?>;
}
img {
    border: none;
}
.salon_masthead_font {
    font-family: "'Montserrat'", sans-serif;
<?php if ($fontsRecord['salon_masthead_font_size']): ?>font-size:<?php echo $fontsRecord['salon_masthead_font_size']
?>em;
<?php else: ?>font-size: 1.6em;
<?php endif ?> <?php if ($fontsRecord['salon_masthead_font_color']): ?>color:#<?php echo
$fontsRecord['salon_masthead_font_color'?>;
<?php else: ?>color:#000000;
<?php endif ?> <?php if ($fontsRecord['salon_masthead_font_style']): ?>font-style:<?php echo
$fontsRecord['salon_masthead_font_style'?>;
<?php else: ?>font-style: normal;
<?php endif ?>
}
.salon_navigation_font {
    font-family: "'Montserrat'", sans-serif;
<?php if ($fontsRecord['salon_navigation_font_size']): ?>font-size:<?php echo $fontsRecord['salon_navigation_font_size']
?>em;
<?php else: ?>font-size: 1.6em;
<?php endif ?> <?php if ($fontsRecord['salon_navigation_font_color']): ?>color:#<?php echo
$fontsRecord['salon_navigation_font_color'?>;
<?php else: ?>color:#000000;
<?php endif ?> <?php if ($fontsRecord['salon_navigation_font_style']): ?>font-style:<?php echo
$fontsRecord['salon_navigation_font_style'?>;
<?php else: ?>font-style: normal;
<?php endif ?>
}
.salon_heading_font {
    font-family: "'Montserrat'", sans-serif;
<?php if ($fontsRecord['salon_heading_font_size']): ?>font-size:<?php echo $fontsRecord['salon_heading_font_size'?>em;
<?php else: ?>font-size: 1.5em;
<?php endif ?> <?php if ($fontsRecord['salon_heading_font_color']): ?>color:#<?php echo
$fontsRecord['salon_heading_font_color'?>;
<?php else: ?>color:#000000;
<?php endif ?> <?php if ($fontsRecord['salon_heading_font_style']): ?>font-style:<?php echo
$fontsRecord['salon_heading_font_style'?>;
<?php else: ?>font-style: normal;
<?php endif ?>
}
.salon_sub_heading_font {
    font-family: "'Montserrat'", sans-serif;
<?php if ($fontsRecord['salon_sub_heading_font_size']): ?>font-size:<?php echo
$fontsRecord['salon_sub_heading_font_size'?>em;
<?php else: ?>font-size: 1.1em;
<?php endif ?> <?php if ($fontsRecord['salon_sub_heading_font_color']): ?>color:#<?php echo
$fontsRecord['salon_sub_heading_font_color'?>;
<?php else: ?>color:#000000;
<?php endif ?> <?php if ($fontsRecord['salon_sub_heading_font_style']): ?>font-style:<?php echo
$fontsRecord['salon_sub_heading_font_style'?>;
<?php else: ?>font-style: normal;
<?php endif ?>
}
.salon_sub_heading_font_bold_underline {
    font-family: "'Montserrat'", sans-serif;
<?php if ($fontsRecord['salon_sub_heading_font_size']): ?>font-size:<?php echo
$fontsRecord['salon_sub_heading_font_bold_underline_size'?>em;
<?php else: ?>font-size: 1.1em;
<?php endif ?> <?php if ($fontsRecord['salon_sub_heading_font_color']): ?>color:#<?php echo
$fontsRecord['salon_sub_heading_font_bold_underline_color'?>;
<?php else: ?>color:#000000;
<?php endif ?> <?php if ($fontsRecord['salon_sub_heading_font_style']): ?>font-style:<?php echo
$fontsRecord['salon_sub_heading_font_bold_underline_style'?>;
<?php else: ?>font-style: normal;
text-decoration: underline;
 font-weight: bold;
<?php endif ?>
}

.salon_sub_heading_font_underline {
    font-family: "'Montserrat'", sans-serif;
<?php if ($fontsRecord['salon_sub_heading_font_size']): ?>font-size:<?php echo
$fontsRecord['salon_sub_heading_font_underline_size'?>em;
<?php else: ?>font-size: 1.1em;
<?php endif ?> <?php if ($fontsRecord['salon_sub_heading_font_color']): ?>color:#<?php echo
$fontsRecord['salon_sub_heading_font_underline_color'?>;
<?php else: ?>color:#000000;
<?php endif ?> <?php if ($fontsRecord['salon_sub_heading_font_style']): ?>font-style:<?php echo
$fontsRecord['salon_sub_heading_underline_font_style'?>;
<?php else: ?>font-style: normal;
text-decoration: underline;
<?php endif ?>
}
.salon_text_font {
    font-family: "'Montserrat'", sans-serif;
<?php if ($fontsRecord['salon_text_font_size']): ?>font-size:<?php echo $fontsRecord['salon_text_font_size'?>em;
<?php else: ?>font-size: .9em;
<?php endif ?> <?php if ($fontsRecord['salon_text_font_color']): ?>color:#<?php echo
$fontsRecord['salon_text_font_color'?>;
<?php else: ?>color:#000000;
<?php endif ?> <?php if ($fontsRecord['salon_text_font_style']): ?>font-style:<?php echo
$fontsRecord['salon_text_font_style'?>;
<?php else: ?>font-style: normal;
<?php endif ?>
}
.salon_small_text_font {
    font-family: "'Montserrat'", sans-serif;
<?php if ($fontsRecord['salon_small_text_font_size']): ?>font-size:<?php echo $fontsRecord['salon_small_text_font_size']
?>em;
<?php else: ?>font-size: .75em;
<?php endif ?> <?php if ($fontsRecord['salon_small_text_font_color']): ?>color:#<?php echo
$fontsRecord['salon_small_text_font_color'?>;
<?php else: ?>color:#000000;
<?php endif ?> <?php if ($fontsRecord['salon_small_text_font_style']): ?>font-style:<?php echo
$fontsRecord['salon_small_text_font_style'?>;
<?php else: ?>font-style: normal;
<?php endif ?>
}
.salon_tiny_text_font {
    font-family: "'Montserrat'", sans-serif;
<?php if ($fontsRecord['salon_tiny_text_font_size']): ?>font-size:<?php echo $fontsRecord['salon_tiny_text_font_size']
?>em;
<?php else: ?>font-size: .7em;
<?php endif ?> <?php if ($fontsRecord['salon_tiny_text_font_color']): ?>color:#<?php echo
$fontsRecord['salon_tiny_text_font_color'?>;
<?php else: ?>color:#000000;
<?php endif ?> <?php if ($fontsRecord['salon_tiny_text_font_style']): ?>font-style:<?php echo
$fontsRecord['salon_tiny_text_font_style'?>;
<?php else: ?>font-style: normal;
<?php endif ?>
}
.disclaimer {
    font-family: "'Montserrat'", sans-serif;
    text-decoration: none;
    border: none;
color:#<?php if ($fontsRecord['font_color']): ?><?php echo $fontsRecord['font_color'?><?php else: ?>000000<?php endif
?>;
}
a.disclaimer:link {
    font-family: "'Montserrat'", sans-serif;
    text-decoration: none;
    border: none;
color:#<?php if ($fontsRecord['font_color']): ?><?php echo $fontsRecord['font_color'?><?php else: ?>000000<?php endif
?>;
}
a.disclaimer:visited {
    font-family: "'Montserrat'", sans-serif;
    text-decoration: none;
    border: none;
color:#<?php if ($fontsRecord['font_color']): ?><?php echo $fontsRecord['font_color'?><?php else: ?>000000<?php endif
?>;
}
a.disclaimer:hover {
    font-family: "'Montserrat'", sans-serif;
    text-decoration: underline;
    border: none;
color:#<?php if ($fontsRecord['font_rollover_color']): ?><?php echo $fontsRecord['font_rollover_color'?><?php else:
?>000000<?php endif ?>;
}
a.disclaimer:active {
    font-family: "'Montserrat'", sans-serif;
    text-decoration: none;
    border: none;
color:#<?php if ($fontsRecord['font_color']): ?><?php echo $fontsRecord['font_color'?><?php else: ?>000000<?php endif
?>;
}


</style>

</head>

<body bgcolor: #0e2071; style="background-image: url(http://southfloridaartsalons.com/images/email-bg.png);
background-repeat:repeat;" >
<table height="1000" width="100%"  style="background-image: url(http://southfloridaartsalons.com/images/email-bg.png);
background-repeat:repeat;" >
  <tr>
    <td><table  align="center" width="80%" border="0" cellpadding="10" >
        <tr>
          <td colspan="2" align="center"><?php if ($common_informationRecord['salon_eblast_page_masthead']): ?>
            <?php foreach ($common_informationRecord['salon_eblast_detail_masthead'] as $upload): ?>
            <img src="<?php echo $masterurl ?><?php  echo $upload['urlPath'?>" alt="<?php $info1 =
htmlspecialchars($upload['info1']); ?>
<?php echo ucwords(strtolower($info1)); ?>" width="<?php echo $upload['width'?>" height="<?php echo $upload['height']
?>" border="0" />
            <?php endforeach ?>
            <?php endif; ?></td>
        </tr>
        <tr>
          <td colspan="2"  align="center" valign="top"><table width="100%" border="0" cellpadding="0">
              <?php if($salon_e_blastsRecord['virtual_salon'] == || $salon_e_blastsRecord['virtual_salon'] == ""):?>
              <?php else:?>
              <tr>
                <td><span class="salon_heading_font" style="color:#FFD800">VIRTUAL ART SALONS ON ZOOM
                 </span>
                      
                  <span class="salon_heading_font" style="color:#FFD800">First Tuesdays - October Through May - 7:00 -
9:00 PM</span>
                 <span class="salon_heading_font" style="color:#FFD800">Look for us on</span> <a
href="https://www.instagram.com/south_florida_art_salons/" target="_blank"><span class="salon_heading_font"
style="text-decoration:underline; color:#FFD800">Instagram</span></a> <span class="salon_heading_font"
style="color:#FFD800">and</span> <a href="https://www.facebook.com/SouthFloridaArtSalons/" target="_blank"><span
class="salon_heading_font" style="text-decoration:underline; color:#FFD800">Facebook</span></a><span
class="salon_heading_font" style="color:#FFD800">, on our</span> <a href="http://www.southfloridaartsalons.com"
target="_blank"><span class="salon_heading_font" style="text-decoration:underline; color:#FFD800">New
Website</span></a><span class="salon_heading_font" style="color:#FFD800">, and on </span><a
href="https://www.youtube.com/channel/UCTezgwpkvDHjHQz82wwU2dg" target="_blank"><span class="salon_heading_font"
style="text-decoration:underline; color:#FFD800">YouTube</span></a>
                  <span class="salon_sub_heading_font" style="color:#FFD800">(** Check our</span> <a
href="http://www.southfloridaartsalons.com/index.php" target="_blank"><span class="salon_sub_heading_font"
style="text-decoration:underline; color:#FFD800">Salon schedule</span></a> <span class="salon_sub_heading_font"
style="color:#FFD800"> for any changes **)</span></td>
              </tr>
              <?php endif ?>
            </table></td>
        </tr>
        <tr>
          <td colspan="2" width="28%" align="right" valign="top"><span class="salon_sub_heading_font" 
style="color:#FFD800"><font color="#FFD800">Having trouble reading this e-mail?</font></span>
            <a href="<?php echo $masterurl ?>/eblastdetail-salon-annc.php?<?php echo
htmlencode($salon_e_blastsRecord['num']) ?>"><span class="salon_sub_heading_font" style="text-decoration:underline;
color:#FFD800">CLICK/TAP HERE TO VIEW IN A BROWSER</span></a></td>
        </tr>"
        <tr>
          <td colspan="2" align="center" ><table width="80%" border="0" cellpadding="0">
              <tr>
                <td colspan="3" align="center" valign="top"><div align="center">
                    <?php if ($salon_e_blastsRecord['special_announcement'] ||
$salon_e_blastsRecord['special_announcement_text'] ): ?>
                    <span class="salon_heading_font" font color="#FFD800">
                    <?php $special_announcement = ($salon_e_blastsRecord['special_announcement']); ?>
                    <b><?php echo strtoupper($special_announcement); ?></b></span>
                    <?php if ($salon_e_blastsRecord['special_announcement_text']): ?>
                    
                    <span class="salon_sub_heading_font" style="color:#FFFFFF"><?php
echo($salon_e_blastsRecord['special_announcement_text']); ?></span>
                    <?php endif; ?>
                    <?php endif; ?>
                    
                    
                    
                    <?php if ($salon_e_blastsRecord['presentation_title']): ?>
                    <span class="salon_heading_font" style="color:#FFD800"><b><?php echo strtoupper(date("l, F jS, Y",
strtotime($salon_e_blastsRecord['presentation_date']))) ?></b></span> 
                    <?php $presenter_s_name = ($salon_e_blastsRecord['presenter_s_name']); ?>
                    
                    <?php $presentation_title = ($salon_e_blastsRecord['presentation_title']); ?>
                    <span class="salon_masthead_font" style="color:#FFF"><b><?php echo strtoupper($presentation_title);
?></b></span>
                    
                    <?php if($presenter_s_name):?>
                    <span class="salon_sub_heading_font"style="color:#FFF">PRESENTED BY</span>
                    
                    <span class="salon_heading_font" style="color:#FFF"><?php echo strtoupper($presenter_s_name);
?></span>
                    
                    <?php endif ?>
                    <?php endif; ?>
                  </div></td>
              </tr>
              <tr>
                <?php if ($salon_e_blastsRecord['image_1']): ?>
                <td width="33%" align="center" valign="top"><a href="<?php echo $masterurl ?><?php foreach
(
$salon_e_blastsRecord['image_1'] as $upload): ?><?php echo $upload['urlPath'?><?php endforeach ?>" >
                  <?php foreach ($salon_e_blastsRecord['image_1'] as $upload): ?>
                  <img src="<?php echo $masterurl ?><?php echo $upload['thumbUrlPath2'?>" width="<?php echo
$upload['thumbWidth2'?>" height="<?php echo $upload['thumbHeight2'?>" alt="" /></a>
                  <span class="salon_text_font" style="color:#FFF"><b>
                  <?php if ($upload['info1']): ?>
                  <?php echo $upload['info1']; ?>
                  <?php endif; ?>
                  <?php if ($upload['info2']): ?>
                  <?php echo $upload['info2']; ?>
                  <?php endif; ?>
                  </b></span>
                  <?php endforeach ?></td>
                <?php endif; ?>
                <?php if ($salon_e_blastsRecord['image_2']): ?>
                <td width="34%" align="center" valign="top"><a href="<?php echo $masterurl ?><?php foreach
(
$salon_e_blastsRecord['image_2'] as $upload): ?><?php echo $upload['urlPath'?><?php endforeach ?>" >
                  <?php foreach ($salon_e_blastsRecord['image_2'] as $upload): ?>
                  <img src="<?php echo $masterurl ?><?php echo $upload['thumbUrlPath2'?>" width="<?php echo
$upload['thumbWidth2'?>" height="<?php echo $upload['thumbHeight2'?>" alt="" /></a>
                  <span class="salon_text_font" style="color:#FFF"><b>
                  <?php if ($upload['info1']): ?>
                  <?php echo $upload['info1']; ?>
                  <?php endif; ?>
                  <?php if ($upload['info2']): ?>
                  <?php echo $upload['info2']; ?>
                  <?php endif; ?>
                  </b></span>
                  <?php endforeach ?></td>
                <?php endif; ?>
                <?php if ($salon_e_blastsRecord['image_3']): ?>
                <td width="33%" align="center" valign="top"><a href="<?php echo $masterurl ?><?php foreach
(
$salon_e_blastsRecord['image_3'] as $upload): ?><?php echo $upload['urlPath'?><?php endforeach ?>" >
                  <?php foreach ($salon_e_blastsRecord['image_3'] as $upload): ?>
                  <img src="<?php echo $masterurl ?><?php echo $upload['thumbUrlPath2'?>" width="<?php echo
$upload['thumbWidth2'?>" height="<?php echo $upload['thumbHeight2'?>" alt="" /></a>
                  <span class="salon_text_font" style="color:#FFF"><b>
                  <?php if ($upload['info1']): ?>
                  <?php echo $upload['info1']; ?>
                  <?php endif; ?>
                  <?php if ($upload['info2']): ?>
                  <?php echo $upload['info2']; ?>
                  <?php endif; ?>
                  </b></span>
                  <?php endforeach ?></td>
                <?php endif; ?>
              </tr>
              <?php if ($salon_e_blastsRecord['image_1']): ?>
              <tr>
                <td colspan="3" align="center" valign="top" class="salon_text_font">Click/Tap on any thumbnail for a
larger image.
                  </td>
              </tr>
              <?php endif; ?>
              <tr>
                <td colspan="3" align="left" valign="top">
                  <?php if ($salon_e_blastsRecord['announcement_lead']): ?>
                  <span class="salon_text_font" style="color:#FFF"><?php echo $salon_e_blastsRecord['announcement_lead']
?></span>
                  
                  <?php endif; ?>
                  <?php if ($salon_e_blastsRecord['announcement_content']): ?>
                  <span class="salon_text_font" style="color:#FFF"><?php echo
$salon_e_blastsRecord['announcement_content'?></span>
                  
                  <?php endif; ?>
                  <?php if ($salon_e_blastsRecord['presenter_s_web_site_or_social_media_link']): ?>
                  <?php if(!preg_match("/^https:\/\//i",
$salon_e_blastsRecord['presenter_s_web_site_or_social_media_link'] )):?>
                  <?PHP  if  (!preg_match("/^http:\/\//i",
$salon_e_blastsRecord['presenter_s_web_site_or_social_media_link'])) {
 
$salon_e_blastsRecord['presenter_s_web_site_or_social_media_link'] = "http://" .
$salon_e_blastsRecord['presenter_s_web_site_or_social_media_link'];   } ?>
                  <?PHP endif ?>
                  <a class="special" target="_blank" href="<?php echo
$salon_e_blastsRecord['presenter_s_web_site_or_social_media_link'?>"><span class="salon_text_font" style="color:#FFF">
                  <?php $presenter_s_web_site_or_social_media_link_text =
(
$salon_e_blastsRecord['presenter_s_web_site_or_social_media_link_text']); ?>
                  <b style="text-decoration:underline;"><?php echo
strtoupper($presenter_s_web_site_or_social_media_link_text); ?></b></span></a> 
                  
                  <?php endif; ?>
                  <?php if ($salon_e_blastsRecord['2nd_presenter_s_web_site_or_social_media_link']): ?>
                  <?php if(!preg_match("/^https:\/\//i",
$salon_e_blastsRecord['2nd_presenter_s_web_site_or_social_media_link'] )):?>
                  <?PHP  if  (!preg_match("/^http:\/\//i",
$salon_e_blastsRecord['2nd_presenter_s_web_site_or_social_media_link'])) {
 
$salon_e_blastsRecord['2nd_presenter_s_web_site_or_social_media_link'] = "http://" .
$salon_e_blastsRecord['2nd_presenter_s_web_site_or_social_media_link'];   } ?>
                  <?PHP endif ?>
                  <a class="special" target="_blank" href="<?php echo
$salon_e_blastsRecord['2nd_presenter_s_web_site_or_social_media_link'?>"><span class="salon_text_font"
style="color:#FFF">
                  <?php $second_presenter_s_web_site_or_social_media_link_text =
(
$salon_e_blastsRecord['2nd_presenter_s_web_site_or_social_media_link_text']); ?>
                  <b style="text-decoration:underline;"><?php echo
strtoupper($second_presenter_s_web_site_or_social_media_link_text); ?></b></span></a> 
                  
                  <?php endif; ?></td>
              </tr>
              <tr>
                <td colspan="3" align="center" valign="top" class="salon_text_font"><?php
if($salon_e_blastsRecord['attendance_fee']): ?>
                  There's a $<?php echo $salon_e_blastsRecord['attendance_fee'?> fee to attend.
                  <?php elseif($salon_e_blastsRecord['donation_required']): ?>
                  $<?php echo $salon_e_blastsRecord['donation_required'?> donation at the door.
                  <?php elseif($salon_e_blastsRecord['donation_text']): ?>
                  <?php echo $salon_e_blastsRecord['donation_text'?>
                  <?php else: ?>
                  
                  <?php endif; ?></td>
              </tr>
              <?php if ($salon_e_blastsRecord['snacks_and_share_tag']): ?>
              <tr>
                <td colspan="3"><span class="salon_text_font" style="color:#FFF"><?php echo
$salon_e_blastsRecord['snacks_and_share_tag'?></span>
                  </td>
              </tr>
              <?php endif; ?>
              <!--<?php /* if ($salon_e_blastsRecord['full_schedule_url']): ?>
              <tr>
                <td colspan="3" align="center" valign="top" class="salon_text_font"><?php
if(!preg_match("/^https:\/\//i", $salon_e_blastsRecord['full_schedule_url'] )):?>
                  <?PHP  if  (!preg_match("/^http:\/\//i", $salon_e_blastsRecord['full_schedule_url'])) {
 $salon_e_blastsRecord['full_schedule_url'] = "http://" . $salon_e_blastsRecord['full_schedule_url'];   } ?>
                  <?PHP endif ?>
                  <a class="special" target="_blank" href="<?php echo $salon_e_blastsRecord['full_schedule_url']
?>"><span class="salon_text_font">
                  <?php $full_schedule_link_text = ($salon_e_blastsRecord['full_schedule_link_text']); ?>
                  <b><?php echo strtoupper($full_schedule_link_text); ?></b></span></a> 
                  
                  <?php if(!preg_match("/^https:\/\//i", $salon_e_blastsRecord['armory_schedule_url'] )):?>
                  <?PHP  if  (!preg_match("/^http:\/\//i", $salon_e_blastsRecord['armory_schedule_url'])) {
 $salon_e_blastsRecord['armory_schedule_url'] = "http://" . $salon_e_blastsRecord['armory_schedule_url'];   } ?>
                  <?PHP endif ?>
                  <a class="special" target="_blank" href="<?php echo $salon_e_blastsRecord['armory_schedule_url']
?>"><span class="salon_text_font">
                  <?php $armory_schedule_link_text = ($salon_e_blastsRecord['armory_schedule_link_text']); ?>
                  <b><?php echo strtoupper($armory_schedule_link_text);  ?></b></span></a> 
                  </td>
              </tr>/ 
              <?php endif; */?>-->
              <tr>
                <td colspan="3" align="center" valign="top" class="salon_text_font"><span class="salon_text_font"
style="text-align:center"><b><span style="color:#FFF">A FULL SCHEDULE AND VIDEOS OF PAST SALON PRESENTATIONS ARE NOW
AVAILABLE</span></b> </span><a class="special" target="_blank" href="<?php echo $masterurl ?>/index.php#A"><u><b><span
class="salon_text_font" style="color:#FFF">ON OUR HOME PAGE</span></b></u></a></td>
              </tr>
              <tr>
                <?php if ($salon_e_blastsRecord['image_4']): ?>
                <td width="33%" align="center" valign="top">
                  <a href="<?php echo $masterurl ?><?php foreach ($salon_e_blastsRecord['image_4'] as $upload): ?><?php
echo $upload['urlPath'?><?php endforeach ?>" >
                  <?php foreach ($salon_e_blastsRecord['image_4'] as $upload): ?>
                  <img src="<?php echo $masterurl ?><?php echo $upload['thumbUrlPath2'?>" width="<?php echo
$upload['thumbWidth2'?>" height="<?php echo $upload['thumbHeight2'?>" alt="" /></a>
                  <span class="salon_text_font" style="color:#FFF"><b>
                  <?php if ($upload['info1']): ?>
                  <?php echo $upload['info1']; ?>
                  <?php endif; ?>
                  <?php if ($upload['info2']): ?>
                  <?php echo $upload['info2']; ?>
                  <?php endif; ?>
                  </b></span>
                  <?php endforeach ?></td>
                <?php endif; ?>
                <?php if ($salon_e_blastsRecord['image_5']): ?>
                <td width="34%" align="center" valign="top">
                  <a href="<?php echo $masterurl ?><?php foreach ($salon_e_blastsRecord['image_5'] as $upload): ?><?php
echo $upload['urlPath'?><?php endforeach ?>" >
                  <?php foreach ($salon_e_blastsRecord['image_5'] as $upload): ?>
                  <img src="<?php echo $masterurl ?><?php echo $upload['thumbUrlPath2'?>" width="<?php echo
$upload['thumbWidth2'?>" height="<?php echo $upload['thumbHeight2'?>" alt="" /></a>
                  <span class="salon_text_font" style="color:#FFF"><b>
                  <?php if ($upload['info1']): ?>
                  <?php echo $upload['info1']; ?>
                  <?php endif; ?>
                  <?php if ($upload['info2']): ?>
                  <?php echo $upload['info2']; ?>
                  <?php endif; ?>
                  </b></span>
                  <?php endforeach ?></td>
                <?php endif; ?>
                <?php if ($salon_e_blastsRecord['image_6']): ?>
                <td width="33%" align="center" valign="top">
                  <a href="<?php echo $masterurl ?><?php foreach ($salon_e_blastsRecord['image_6'] as $upload): ?><?php
echo $upload['urlPath'?><?php endforeach ?>" >
                  <?php foreach ($salon_e_blastsRecord['image_6'] as $upload): ?>
                  <img src="<?php echo $masterurl ?><?php echo $upload['thumbUrlPath2'?>" width="<?php echo
$upload['thumbWidth2'?>" height="<?php echo $upload['thumbHeight2'?>" alt="" /></a>
                  <span class="salon_text_font" style="color:#FFF"><b>
                  <?php if ($upload['info1']): ?>
                  <?php echo $upload['info1']; ?>
                  <?php endif; ?>
                  <?php if ($upload['info2']): ?>
                  <?php echo $upload['info2']; ?>
                  <?php endif; ?>
                  </b></span>
                  <?php endforeach ?></td>
                <?php endif; ?>
              </tr>
            </table></td>
        </tr>
        <tr>
          <td colspan="3"><?php if ($salon_e_blastsRecord['announcement_art_salon_description']): ?>
            <span class="salon_text_font" style="color:#FFF"><?php echo
$salon_e_blastsRecord['announcement_art_salon_description'?></span>
           
            <?php endif; ?></td>
        </tr>
        <tr>
          <td align="left" colspan="2" class="salon_text_font" style="color:#FFF"><?php if
(
$salon_e_blastsRecord['travel_directions']): ?>
            <b>TRAVEL DIRECTIONS:</b>
            <?php echo $salon_e_blastsRecord['travel_directions'?>
            
            <?php endif; ?></td>
        </tr>
        <?php include ("_eblast_share.php");?>
        <tr>
          <td align="left" colspan="2" class="salon_small_text_font" style="color:#FFF">You're receiving this eBlast
because you are on the South Florida Art Salon's Distribution list. If you feel that you've received this eBlast in
error, please <a target="_blank" href="http://southfloridaartsalons.com/unsubscribe.php"><span class="salon_text_font"
style="text-decoration:underline; color:#FFF">CLICK/TAP HERE</span></a> to unsubscribe.</td>
        </tr>
      </table></td>
  </tr>
</table>
</body>
</html>



PHPLIST CACHING OLD PAGES FIX - Nov 8th, 2019



I had an issue where PHPList was delivering old versions of web pages in emails even though the web pages displayed the
new data.

To fix the issue I added the following to my .htaccess file:


 # DISABLE CACHING
<IfModule mod_headers.c>
    Header set Cache-Control "no-cache, no-store, must-revalidate"
    Header set Pragma "no-cache"
    Header set Expires 0
</IfModule>

<FilesMatch "\.(css|flv|gif|htm|html|php|ico|jpe|jpeg|jpg|js|mp3|mp4|png|pdf|swf|txt)$">
    <IfModule mod_expires.c>
        ExpiresActive Off
    </IfModule>
    <IfModule mod_headers.c>
        FileETag None
        Header unset ETag
        Header unset Pragma
        Header unset Cache-Control
        Header unset Last-Modified
        Header set Pragma "no-cache"
        Header set Cache-Control "max-age=0, no-cache, no-store, must-revalidate"
        Header set Expires "Thu, 1 Jan 1970 00:00:00 GMT"
    </IfModule>
</FilesMatch>



E-MAIL CSS ISSUES WHEN SENDING A WEB PAGE AS AN EMAIL - Jun 12th, 2014

Mailchimp to the rescue.

According to Mailchimp:

"Because some email clients strip out <head> and <style> tags from emails, it's best to have your CSS written inline
when sending campaigns. We know that writing inline CSS is time consuming and repetitive, so we've built this (free)
conversion tool to automatically inline your email's CSS.

It's a good idea to leave the CSS in the original <style> tag as a backup, but we can optionally strip that out for you.
And if your email is responsive and uses media queries, don't worry - the inliner tool will leave those styles alone.

I tried it and found only a few caveats:

1) The tool (neatly) replaces most occurrences of < with & lt; most occurrences of > with & gt; and many occurrences of
" with & quot; The good news is that you can do a few quick find and replaces and you're back in business.

2) The tool may not work or play well with things like Google Fonts or other web fonts.

3) There may be some other small PHP errors, some dealing with capitalization, so check for them.

However, at the cost of one click, it's pretty darn good.

<a target="_blank"
href="http://templates.mailchimp.com/resources/inline-css/">http://templates.mailchimp.com/resources/inline-css</a>


USING BACKGROUND IMAGES IN EMAILS - Dec 29th, 2018

*** If anyone has improvements to this recipe, please let me know. ***

A web page that I was sending as an email had a background image that was to cover the full email window. 

Since my page was potentially long, the background image I used was a 1920px x 1920px .jpg image that I kept under 150kb
by lowering the quality.

It took a bit of fiddling to get that background image to display correctly since every email client handles code a bit
differently. 

To display the background image in Outlook variants I found a neat code generator courtesy of Campaign Monitor: 

http://backgrounds.cm/

It generated this code, which gets inserted just after the opening body tag (change the 2 urls and the backup background
color to suit):

*** If you use the generator, note the addition below of style="background-repeat:no-repeat" to the table cell that was
not in the generated code.


<div style="background-color:#00102a;">
  <!--[if gte mso 9]>
  <v:background xmlns:v="urn:schemas-microsoft-com:vml" fill="t">
    <v:fill type="tile" src="http://www.your_site.com/images/bg.jpg" color="#00102a"/>
  </v:background>
  <![endif]-->
  <table height="100%" width="100%" cellpadding="0" cellspacing="0" border="0">
    <tr>
      <td valign="top" align="left" background="http://your_site.com/images/bg.jpg" style="background-repeat:no-repeat">


And this code just before the closing body tag:


      </td>
    </tr>
  </table>
</div>


For the web page display I added the following css to the head section of the page:


<style type="text/css">
body {
    background-image: url("http://www.your_site.com/images/bg.jpg");
    background-repeat: no-repeat;
    background-color: #00102a;
}
</style>


USING CMSB EMAIL TEMPLATES - Nov 14th, 2016

Email Templates are really easy to use, but they can seem pretty confusing , so with the help of Ross Fairbain from
Interactive Tools, here's a simple recipe to help understand the process.

Let's say that you want to send an email to specific individuals each time a form is submitted on your site (or for any
other purpose).

He said (edited): 

For my example, I'll assume that you have a simple email form on your site that lets visitors email comments to you. 
The form has 3 fields. Name, Email and Comment.

Something like:


<form method="post" action="?">
  <input type="hidden" name="save" value="1" />
  <table border="0" cellspacing="0" cellpadding="2">
   <tr>
    <td>Name</td>
    <td><input type="text" name="name" value="<?php echo htmlencode(@$_REQUEST['name']); ?>" size="50" /></td>
   </tr>
   <tr>
    <td>Email</td>
    <td><input type="text" name="email" value="<?php echo htmlencode(@$_REQUEST['email']); ?>" size="50" /></td>
   </tr>
   <tr>
    <td>Comment</td>
    <td><textarea name="comment" COLS=50 ROWS=6><?php echo htmlspecialchars(@$_REQUEST['comment']); ?></textarea></td>
   </tr>
   <tr>
    <td colspan="2" align="center">
      <input class="button" type="submit" name="submit" value="Send Us Your Comments &gt;&gt;" />
    </td>
   </tr>
  </table>
  </form>


The first thing we'll look at is the code you need on your web page (the one with the form):


$emailHeaders = emailTemplate_loadFromDB(array(
    'template_id'  => 'COMMENT-TO-ADMIN',
    'placeholders' => array(
      'user.name'     => $_REQUEST['name'],
      'user.email'    => $_REQUEST['email'],
      'user.comment'  => $_REQUEST['comment'],

  )));
$mailErrors   = sendMessage($emailHeaders);


You can put this after any error checking you're doing when the form is submitted. Just make sure that it appears before
any code that clears your form values, like:


$_REQUEST    = array(); // clear form values


Next, you need to log into CMS Builder and create a new email template. ***Make sure you do this in the Email Templates
section under CMS Setup (or under Admin if prior to V 3).***

Here's an example of how you might fill in the fields in the Email Template:


Template ID =>  COMMENT-TO-ADMIN
Description  => This email gets sent to site admin each time the comment form is submitted
From              => #user.email#
To                   => #settings.adminEmail#

Subject                  => New Comment From #user.name#
Message HTML   => #user.comment#


And then leave everything else as it is.

Things to note here:

1. You can see that anything set as a "placeholder" in PHP is available in the email template with the hash marks. Ex:
'user.name' in PHP becomes #user.name# in the template.

2. Placeholder names are completely customization. Ex: 'user.name' could just be 'name', or' first_name'. Sometimes
you'll want to have a lot of placeholders and naming them specifically helps organize things. No spaces are allowed so
just use . or _ or - instead.

3. There are several placeholders available to every email template. Ex: #settings.adminEmail#. You'll see a full list
of those in the email template under the "Instructions" heading in your new email template.

4. Placeholders can be variables that pull information from your database. For example, they can define the To and CC
email addresses in the template. So, for example,  if you have a particular position in your organization that handles
these types of comment submissions, you can  create a variable on your form page like:


$service_rep = strtolower($organization_informationRecord['service_rep_email']); // a field in a single record editor on
your site called "Organization Information"


then in the placeholders array, create the place holder  


'service_rep'     => $service_rep,


Then use #service_rep# as the "to" address in the email template.

You can also use plain email addresses, like someone@mydomain.com in the "to", "CC", and "BCC" Fields in the template.

JUST IDEAS



PROTECTING YOUR CONTENT, HIDING YOUR CODE - Mar 23rd, 2012

If you want to disable the viewing your source code by right clicking on your page, you can add oncontextmenu="return
false" to the head tag.

To prevent dragging and copying text on your site you can add onselectstart="return false" 

And finally if you want to stop visitors from dragging images or other objects from your page to their desktop, add
ondragstart="return false" 

These can be included in any block level element tags and will act on that content only.

These are not perfect solutions because the developer tools in most browsers will still allow access to the information,
but it will stop the less technically savvy visitors, and certainly slow down the others.

HANDY PROGRAMS



HELP FINALLY KILL IE6 (DOWN TO 1% OF USERS IN 2012) - Aug 11th, 2012

You can show an upgrade reminder to anyone still using IE6 with this simple code in the body of your viewer. Modify it
to suite your needs.

To change the IE version, just change the number. 

You can also insert an lt to modify the limiting parameter to "less than" I.E.7  I.E: <!--[if lt IE 7]> or insert an lte
for "less than or equal to", or a gt for "greater than", or a gte for "greater than or equal to".



<!--[if IE 6]>
<h3 align="center" class="your_class">We've noticed that you're still using Internet Explorer 6, which is a very old
(2006) and outdated browser.</h3><h3 align="center" class="your_class">To enjoy a much richer surfing experience, you
might want to update to a more modern, <a
href='http://windows.microsoft.com/en-US/internet-explorer/products/ie/home'><span class="your_class"><u>Internet
Explorer</u></span></a> or <a href='http://www.firefox.com' target='_blank'><span
class="your_class"><u>Firefox</u></span></a> browser free of charge.</h3>
   <![endif]-->



IS THERE A LIST OF USEFUL PROGRAMS? - Jul 9th, 2020

Over the years I’ve used a few programs that have helped me and may be of interest to you. 

There’s a complete list of them, along with other utilities, cheat sheets, articles, and more, including all of the
ones that have been listed in the Interactive Tools newsletter, at:

       http://thecmsbcookbook.com/resources.php

TROUBLESHOOTING



ISSUES WITH IFRAMES OR SIMILAR CODING NOT DISPLAYING CONTENTS (SOLVED) - Feb 21st, 2019

If you’ve got iframes or similar coding on your site that reference either external and/or internal urls and they’re
not displaying their contents, the simple answer might be to check if the referenced URLs are https:// and not just
http://

Also, if you’re using a master URL field and have recently changed to using an SSL certificate, make sure that
you’ve updated that field to reflect the change as well.

Hope that helps someone avoid some needless frustration.


TRANSPARENCY ISSUES WITH .PNG THUMBNAILS IN V.2.61 AND V.2.62 - Jul 25th, 2014

If some of the transparent areas of your png thumbnails are rendering as black when uploading them in V. 2.61 (and V.
2.62), Dave Edis, from Interactive Tools offered the following fix:

He said: 

I've added the code to maintain transparencies to the fast image resizing code, so you should be able to get the
benefits of both now.

The file should be uploaded to your CMS Builder "Lib" folder. Back up the old image_functions.php file just in case.

You can download the fix file from:

http://www.thecmsbcookbook.com/downloads/image_functions-2.61-fix.zip


DEBUGGING - Jan 5th, 2014

Dave Edis from Interactive Tools expert advice saved the day once again. He said:
Anytime a MySQL query isn't working the first thing to do is back up and print out the variables you're passing to it
(or the query itself). 

Try this debug code: (change the section and field names to your own)



print "<xmp>Product Record: ";
print_r($productsRecord);
print "</xmp>";



or



print "<xmp>my_field: ";
print_r($productsRecord)['my_field'];
print "</xmp>";



Another trick is to add this option which displays the SQL being executed:



'debugSql' => true,



If you’re getting an "undefined index' error, that means that field could not be found in the record. You can try
displaying all the fields in the record with this code to see what fields are available (or where the missing ones are):



<xmp><?PHP print_r($record); ?></xmp> 



To this list, Jason Shautuck added:



<?php showme($record['recipe']);exit; ?> 

Or if you're looking for th value of a variable, 

<?php showme($your_variable);exit; ?> 


He said: This will just output the content of that variable and then stop execution.


DEBUGGING CSS - Oct 7th, 2013

Greg Thomas from Interactive Tools shared this idea for tracking down misbehaving formatting:

He said, "When I'm having trouble tracking down where an item is getting a certain CSS rule from, I use Chromes
developer tools to track it down. 

If you right click on the some of the incorrectly formatted text in Chrome and then select inspect element, the
developer tools will pop up. 

On the right hand side of the developer tools is a list of all of the CSS rules that apply to the current item, and any
that are being overwritten will be crossed out. It will also show you which CSS file  the rule has come from."

Greg's right, and you can do the same in FireFox (and maybe even in IE)


SOLVING PHP FILE UPLOAD SIZE ISSUES - Aug 6th, 2010

According to Dave Edis at Interactive Tools:

It could be that your host has a limit on the upload file size that’s lower than the one that you’ve set in your
Admin’s General Settings.

If so, can you click on "Show server upload limits" link below: Admin > General Settings > Upload Folder URL. Donna from
Interactive Tools reminds us that this functionality was implemented in version 1.19 (another reason to upgrade any
really old versions)

This should show you your web hosts limits. You can contact your host and ask them to allow for larger uploads. On some
servers it means uploading or changing values in a php.ini file (usually in your cgi-bin directory). On others it may
require working with .htaccess files. In any event, most reputable hosts will be happy to help you with this. 

For (much) more information about your servers configuration, you can also get phpinfo details with this search added to
your admin.php URL



http://yourURL/yourcmsAdminfolder/admin.php?menu=admin&action=phpinfo




Or just download a phpinfo script and check these values:

file_uploads
max_input_time
post_max_size
upload_max_filesize 

post_max_size limits how much data can be submitted through a form (forms use the method called "GET" or "POST" to
submit data). And upload_max_filesize limits the max size of an upload file itself.

They do seem to overlap a bit in functionality. That happens in PHP a fair bit. :) The key thing is to make sure they're
both large enough to allow your uploads.

Here are links to some PHP docs:

       http://www.php.net/manual/en/ini.core.php#ini.post-max-size

       http://www.php.net/manual/en/ini.core.php#ini.upload-max-filesize


DREAMWEAVER CS3 LIBRARY ITEM QUIRK UNMASKED - Aug 6th, 2010

As many of us have discovered, correctly managing php code in library items is not one of Dreamweaver’s strong points.

In a library item, I had the code: 

<a href="<?php echo $record['books_available_on_line_url'?>"><?php echo $record['books_available_at'?></a>

But when it was rendered in the documents that used the code, it came out:

<a href="<?php echo $record['Library/books_available_on_line_url'?>"><?php echo $record['books_available_at'?></a>

The solution was to define a variable in the library item: 



<?php $link_online_books $record['books_available_on_line_url']; ?>



And then echo that variable in the library code that followed instead of using the original link code.



<a href="<?php echo $link_online_books2?>"><?php echo $record['books_available_on_line_at'?></a>



This way, Dreamweaver CS3 no longer recognizes the link in the same way and does not “manage” it.


CAN’T SEND MAIL FROM THE E-MAIL BOX AT YOUR WEB HOST? - Aug 6th, 2010

You may be experiencing ISP port 25 blocking.

If you’ve been blocked, you can only SEND email OUT through your Internet Service Provider's outbound (SMTP) mail
servers. If you try to use any other mail servers except theirs, (The mail server at your domain registrar or web host)
you’ll get a “can not connect” error message.
 
Bell System DSL, Charter Cable, Cox Cable and other ISPs are implementing "port 25 blocking", in an effort to reduce
spam. These are not temporary changes, so you’ll have to work around the issue.

The solution is to change your OUTBOUND SMTP mail server, under "Account Server Settings".

In your mail program, change the outgoing server listed in your e-mail account settings to your ISP’s outgoing e-mail
server and then under “advanced” or “other options” > “outgoing server”, check ”my outgoing server
requires authentication” and enter your ISP’s e-mail username and password. That should get you running again.

You can continue receiving email at your domain name with your current settings. You do not need to change anything to
continue receiving email at your domain name. And you will not usually have your display name and e-mail address
changed. An exception to that is, if you’re using one of the free e-mail services like Hotmail, they may change the
displayed information. 

Outlook 2010 Update - I don’t profess to understand it, but in Outlook 2010, all of my outbound settings needed to be
reset to my Inbound server settings with the exception of the outbound port which needed to be reset to Port 587
(advanced tab). 


CHECKING YOUR E-MAIL SERVER WHEN THINGS DON’T WORK - Aug 6th, 2010

This is a way to determine if the e-mail  issues that your having are server related or local (entry errors, firewalls,
etc.)
For those of you new to telnet: (If you’re running Vista, you’ll have to enable telenet services (see below)

Windows users in a cmd window (start, run, cmd, enter) type:

telnet mail.yourdomain.com 110 (110 is the outgoing email port) and press enter

(Mac users you’ll find the telnet screen in a terminal window (Finder, Go, Utilities, Terminal)

If an OK is returned, you are logged on to your e-mail server. Now enter:

user yourfullemailaddress@yourserver.com and press enter

If an OK is returned, You are logged on to your e-mail account. Now enter:
pass yourpassword (replacing “yourpassword” with your password)

If an OK is returned, You have logged into to your e-mail account. Now enter:
list and press enter.

You should see a list of the messages that are on the server, if any. Message numbers only.

If  errors are returned on any of the above steps, type tracert mail.yourdomain.com and press enter

You should get a list of the hops that it takes to get a message from your machine to your e-mail server.

If any of the hops are listed with an error message, this will help to determine where the issues are.


NOT RECEIVING CERTAIN E-MAILS? CHECK THIS FIRST - May 24th, 2011

I’m passing this on in the hope that it will save some frustration. 

I use Bellsouth (now ATT) as my service provider for both DSL and mail service. 

I also use Easylist on many of my client’s sites to develop their e-mail lists. Easylist is a double opt in program
that sends a confirmation e-mail to the prospective recipient. They must responded to that e-mail before their e-mail
address is added to the list.

When setting up Easylist for a new client I discovered that I couldn’t receive the opt in e-mails on my Bellsouth
account. I checked some of my other client’s installations and found that although the program had been working
flawlessly for years,  I now couldn’t receive e-mail from any of them either. 

After a long and frustrating investigation I determined that the program itself was operating correctly, I could receive
the confirmation e-mails through other e-mail accounts, like gmail and that other Bellsouth accounts could receive them
as well.

The “obvious” conclusion? I was being blocked from getting these e-mails by my ISP.

After few more weeks of frustration I was able to obtain a relevant e-mail log from my webhost, which after a half dozen
support calls to Bellsouth I was able to  pass on to their e-mail support team.

After another half dozen calls, I discovered that in the interest of my safety, when Bellsouth (now ATT) moved to using
Yahoo as their portal, they also changed the default setting of their Web Mail spam filter to "on", so that it stopped
what THEY considered spam from ever leaving their server. Hence all of those confirmation e-mails were never downloaded
to either my Outlook in box or junk mail box. 

Up to that point the default had always been “off” and the change in their policy was not evident to me since I
never use their Web Mail interface.

So the mystery was solved, I turned off their spam filter again and sorted out the few hundred e-mails in the Web Mail
spam folder.

The moral,... Check your Web Mail spam filter settings. It might save you needless hours of frustration.


THINK YOUR SERVER IS RUNNING SLOW? - Aug 6th, 2010

You can display the total execution time on your web page with this code:



<?php showExecuteSeconds() ?> seconds 



Or for a more verbose description of both connection success or failure and the time it took to connect, here’s a
script Dave Edis of Interactive Tools wrote that times your MySQL connection speed.

Paste the following into a blank PHP doc, change the hostname, username and password to your information,  and upload it
to the root directory of your server (where you normally run your .PHP docs from) and open the file in your browser.



<?PHP

// MYSQL INFO
$hostname "localhost";
$username "root";
$password "";

// get start time
$START_TIME microtime();

// enable error checking
if (!defined('E_STRICT')) { define('E_STRICT'2048); } // define E_STRICT for PHP < 5
error_reporting(E_ALL E_STRICT);  // display all errors
ini_set('display_errors''1');
ini_set('display_startup_errors''1');

// timer function
function showExecuteSeconds() {
  
$endTime   microtime();

  list(
$start_msec$start_sec) = explode(" "$GLOBALS['START_TIME']);  // START_TIME is set in init.php
  list($end_msec$end_sec)     = explode(" "$endTime);

  
$diff_sec  intval($end_sec) - intval($start_sec);
  
$diff_msec floatval($end_msec) - floatval($start_msec);
  
$totalTime floatval$diff_sec ) + $diff_msec;
  
$value     sprintf("%0.2f"$totalTime);

  print 
"Total Time: $value seconds<br /><br />\n";
}

//
print "<h1>Testing MySQL Speed</h1>";
showExecuteSeconds();

// connect to mysql
print "Connecting to MySQL...<br />";
mysql_connect($hostname$username$password) or die("Error connecting to MySQL: " mysql_error());
showExecuteSeconds();

//
print "Done";

?>



PHP FILE CRASHES DREAMWEAVER CS3 - Aug 6th, 2010

Well, I thought it was a PHP coding issue until Donna from Interactive Tools came to the rescue...

She found an obscure thread on a Google forum from 2007 that addressed this type of issue and it turns out that it’s
more than likely a Dreamweaver/Windows bug. The thread offered a fix that worked for me but, as always, no guarantees.

Here’s the link to the thread:

        http://groups.google.com/group/macromedia.dreamweaver/browse_thread/thread/e493f48243a14b28/2fa3ea8a3c34ba5b

And here’s what they said:

There are a series of reports about CS3 crashing when PHP code is placed in a “select” element. It doesn’t seem to
happen to Mac users, only Windows, and here’s the fix they suggested.

1. Close Dreamweaver

2. Set your clock to the correct time if it isn't already set

3. Delete the file WinFileCache*.dat from (XP)  C:\Documents and Settings\[Your username]\Application
Data\Adobe\Dreamweaver9\Configuration
or (Vista) “C:\Users\<username>\AppData\Roaming\Adobe\Dreamweaver 9\Configuration

4. Start Dreamweaver and the problem should go away.

This is a hidden file so you’ll have to go into Folder options in your control panel and check the “Show hidden and
system files” to find it.


DISPLAYING AN IP ADDRESS IN YOUR VIEWER - Aug 6th, 2010

Just insert:



<?php echo getenv('REMOTE_ADDR'); ?>



Where you want the IP Address to appear.


FINDING YOUR IP ADDRESS - Aug 6th, 2010

If you need to find out what your IP address is, an easy way is to log on to: 

      http://whatsmyip.net/


HIJACKED SEARCH ENGINE FIX FOR FIREFOX - May 13th, 2011

Ever since I upgraded to FF 4, and even though my default search engine is set to Google, when I enter a search term in
the URL location bar my search ends up being hijacked by  Yahoo's search engine. 

Your result may be different, but if your searches are not being handled by your default search engine, here’s the fix
that worked for me.

1. Type about:config in the Firefox URL location bar and press Enter

2. Type the word “keyword” (no double quotes) in the "Filter" text box and you will see an entry for keyword.URL.
There’s a good chance that it's value will contain the culprit search engine’s URL.

3. Double-click on keyword.URL and change the value to:


 
http://www.google.com/search?&q=



That should fix the problem, until some other program changes it again.



SOMOTO.COM

I also had a recurring issue with pages redirecting to Somoto.com and no matter what I did, I couldn’t keep it from
recurring. 
Even when I did a search for the keword Somoto in the Filter text box of about:config, and deleted or changed all the
somoto result values, they kept on coming back.

After trying a number of options that I found on line, here’s what worked.

BTW, This process did not delete any of my stored passwords, but you might want to make a backup of all your preferences
and values just to be on the safe side. (Although one of them probably creates all the Somoto values as well) You can
create a backup by following the instructions at the end of this recipe.

Killing Somoto... 

1) First I made a backup of my bookmarks alone. (bookmarks>show all bookmarks>import and backup menu>backup) 

2) Then I started Firefox in the Safe mode (Hold down the "shift" key while starting the program).

3) Then I reset all the options to their default values by checking all the boxes available and clicking on “make
changes and restart”

4) After a restart, I imported my latest backup of my bookmarks, reset a few default values, and the problem was solved.

BACKING UP YOUR PREFERENCES
On the Help menu in the Firefox menu bar click on "Troubleshooting Information". This will take you to the
"about:support" information page.  
(you can also visit this page by typing about:support into the Firefox address bar)

Under "Application Basics", click on the button that reads, "Open Containing Folder" next to Profile Directory.

Then either copy the entire ??????.default folder, or just the files that you think you'll need from this list:

NOTE: If you use the entire folder, be sure to change the ?????? in the old folder name to exactly match the numbers
that appear in the new default name or the new install of Firefox won’t work.
 
bookmarks.html - Bookmarks.
prefs.js - Current user preferences.
user.js - User-added preferences (overrides prefs.js every startup).
cookies.txt - Cookies.
hostperm.1 - Cookies permissions, image permissions, and extension website install permissions list.
signons.txt - Saved passwords. Requires the "key3.db" file to work.
key3.db - Supplements password file.
cert8.db - Needed for key3.db file.




ONE OF YOUR INSTALLED BROWSERS WILL NOT CONNECT TO THE INTERNET - May 6th, 2011

If this is happening to you, or if a program that needs an internet connection is being denied access, your firewall is
a good place to look for a solution to the problem.

Sorry I can't be specific as to the exact approach here because there are so many security programs in use, and so many
variables in their firewalls.

If you look at the permissions granted by your firewall program for that particular program, you'll probably find that
it is set to some version of "blocked" for that program.

Just change the permission for that program to the same permission level as your working programs and that should take
care of the problem.


TESTING MYSQL CONNECTION LIMITS - Jul 22nd, 2019

Dave Edis of Interactive Tools offered this code to find out your database server's actual limits. Save it as a php
document and run it on your server.

The code has been updated for MySQLi with the help of Steve from Interactive Tools.


 <?php

print "<h1>MySQL Connection Limit Test</h1>\n";
// display errors
error_reporting(E_ALL); // display all errors
ini_set('display_errors''1');
ini_set('display_startup_errors''1');
ini_set('html_errors''0'); // don't output html links in error messages

// mysql info
$hostname "localhost";
$username "database user name";
$password "database password";
$database_name "database_name";

// connect
$DBH = @mysqli-connect($hostname$username$password$database_name);
if (!
$DBH) { print "Program was unable to connect to the MySQL on '$hostname'.\nThe reason given was: " .
htmlspecialchars(mysqli-error()); }
$mysqlVersion 
// get MySQL Version and connection limits
$query "SELECT @@max_connections, @@max_user_connections";
$mysqlVersion preg-replace("/[^0-9\.]/"''mysqli-get_server_info($DBH));
$result mysqli()->query($query) or die("MySQL Error: "htmlspecialchars(mysqli_error()) . "\n");
list(
$maxConnections$maxUserConnections) = mysqli-fetch_row($result);
if (
is_resource($result)) { mysqli_free-result($result); }


// show version and limits
print "MySQL Version: $mysqlVersion" ."\n";
print 
"MySQL Max Connections: $maxConnections <a
href='http://dev.mysql.com/doc/refman/5.0/en/server-system-variables.html#sysvar_max_connections'> (max_connections
documentation)</a> " "\n";
print 
"MySQL Max User Connections: $maxUserConnections <a
href='http://dev.mysql.com/doc/refman/5.0/en/server-system-variables.html#sysvar_max_user_connections'>
(max_user_connections documentation)</a> " "\n";
print 
"<p>Done!";

?>

CHAPTER 8 - WYSIWYG EDITOR



CHANGING LANGUAGES IN THE TINYMCE EDITOR (FOR CMSB VERSIONS PRIOR TO 1.16) - Aug 6th, 2010

When Gleert wanted to know how to change the language displayed in the TinyMCE WYSIWYG editor used in CMSB, Dave came to
the rescue. He said:

Download your language file from this url: http://tinymce.moxiecode.com/download_i18n.php. Check the checkbox and then
click download. Don't download the XML file, that won't work.
Next, unzip the file you download to the /tinymce3/ folder.
Next, create a backup and then edit /lib/menus/default/edit_functions.php. Search for "tinyMCE.init" and add the text in
red. Be sure to use your language code, not just "es" unless it's Spanish.



tinyMCE.init({  
  mode : "exact",  
  language : "es", 
  theme : "advanced",



Finally, backup and edit this file /lib/menus/default/edit.php, search for "languages" and change the language code in
red:



themes : 'advanced', 
languages : 'es', 
disk_cache : true, 



Then reload (and clear your browser cache if needed) and the WYSIWYG should be in the new language.

Thanks Dave, as always...


ADD SPELL CHECK TO YOUR CMS BUILDER WYSIWYG EDITOR - OLDER VERSIONS - Dec 23rd, 2012

SPELLCHECK ADDED TO WYSIWYG EDITOR (CMSB v1.35 and below)

The latest versions of CMSB use the styles in /css/wysiwyg.css for the drop down styles in the WYSIWYG editor, so all
you have to do is add the CSS styles that you want available to this file. See the next recipe for how to implement this
in v1.36.

In older versions, you may have to edit the /lib/wysiwyg.php

Search for:



 "theme_advanced_buttons1"



Remove the "formatselect, fontsizeselect" buttons and replace with "styleselect". This will use a pull down of style
classes instead of the format and font size pull downs. Like this:



theme_advanced_buttons1 : "styleselect, bold, (... your buttons here ...), fullscreen",



Then just add the classes you want available to this file: /css/wysiwyg.css

Next, make sure your viewer pages also load a .css file that has the same classes in it. Something like this:

<link rel="stylesheet" type="text/css" href="css/style.css" /> 

Open your “cmsAdmin/lib/wysiwyg.php” file.

Search for:



 "theme_advanced_buttons1"



That line specifies which buttons to display. You can find a complete button list here:

       http://wiki.moxiecode.com/index.php/TinyMCE:Control_reference

Remove the "formatselect, fontsizeselect" buttons and replace with "styleselect". This will use a pull down of style
classes instead of the format and font size pull downs. Like this:



theme_advanced_buttons1 : "styleselect, bold, (... your buttons here ...), fullscreen",




Then just add the classes you want available to this file: /css/wysiwyg.css

Next, make sure your viewer pages also load a .css file that has the same classes in it. Something like this:




<link rel="stylesheet" type="text/css" href="css/style.css" />




Later Versions of the WYSIWYG editor do not need the following.

In your .CSS file, add the following styles or the editor will not function in some browsers.



body.mceContentBody {
  background:#FFFFFF;
}

body.mceContentBody, .mceContentBody td, .mceContentBody pre {
  font-family: Arial, Helvetica, sans-serif;
  font-size: 12px;
  color: #000000;
}



ADD MULTILINGUAL SPELL CHECK TO YOUR WYSIWYG EDITOR (CMSB 1.30 +) - Dec 23rd, 2012

This add-in was located and shared by CMSB user MOONWALK. 

1) Download and unpack the contents of the Zip File at
www.thecmsbcookbook.com/downloads/tinymce_spellchecker_php_2_0_6_1.zip

2) Upload the spellcheck folder to the plugins folder of your tinymce3 folder (cmsAdmin/tinymce3/plugins)

3) Download the new wysiwyg.php file at www.thecmsbcookbook.com/downloads/wysiwyg.zip

4) Unpack the wysiwyg.php from the zip file.

5) Make a backup of the existing wysiwyg.php file in your lib folder and replace the original file with the new one you
just downloaded. (cmsAdmin/lib)

That’s it, you now have a toggle spellcheck icon on the tinymce application toolbar with multilingual  support. 

One cautionary note, you’ll have to repeat step 4 whenever you upgrade your CMSB Installation, until spellcheck
becomes standard issue for the tinymce editor.

GoogleSpell is already set by TinyMCE as the default spell checker. To choose a spell checker other than GoogleSpell,
open the config.php file you just unzipped to the spellchecker folder and un-comment the spell checker engine that you
wish to use.


ADD SPELL CHECK TO YOUR CMS BUILDER WYSIWYG EDITOR (CMSB 1.35 +) - Dec 23rd, 2012

A new spellcheck button has been added to the WYSIWYG editor. Dave notes that “You’ll need openssl or curl PHP
extensions to use this (most servers have those by default).”


A BETTER SPELL CHECKING FOR THE WYSIWYG EDITOR - Dec 29th, 2018

I think that everyone can agree that the spellchecker plugin that comes with the Tinymce editor leaves much to be
desired.

The PHPSpellChecker at www.phpspellcheck.com might be a replacement worth investigating.

I haven't tried it myself (none of my sites use the WYSIWYG editor) but Jacob Mellor  the developer offered the
following when asked about integration.

  References:

•    http://www.tinymce.com/tryit/custom_toolbar_button.php
•    http://www.phpspellcheck.com/Javascript-AJAX-Spell-Check-API


<script src="/phpspellcheck/include.js' type='text/javascript'></script>

tinyMCE.init({
///
    setup : function(ed) {
        // Add a custom button
        ed.addButton('mybutton', {

           title : 'PHP Spell Check',
           image : '/phpspellcheck/themes/buttons/example.gif',
            onclick : function() {
var mySpell = new LiveSpellInstance();

mySpell.Fields = "EDITORS";
mySpell.checkInWindow();
            }
        });
    }
});


</script>


MAKE THE STYLES FROM YOUR EXTERNAL CSS STYLE SHEET AVAILABLE IN THE WYSIWYG EDITOR - OLDER VERSIONS - Aug 6th, 2010

According to Damon from Interactive Tools:

You can add a custom styles drop down to the WYSIWYG editor with these steps:

1. Open both these files:

/cmsAdmin/lib/wysiwyg.css
/cmsAdmin/lib/wysiwyg.php

- make a backup of both before making any changes.
2. In the wysiwyg.php file, search for:



theme_advanced_buttons1 : "formatselect



and change it to:



theme_advanced_buttons1 : "styleselect, formatselect



3. In the wysiwyg.css file, on line 31, add classes to have them appear in your 'styleselect' drop down. This will allow
your users to add classes and visually see the effect in the editor.
4. Add the same custom styles to the css file that is linked to your published pages. 


MAKE THE STYLES FROM YOUR EXTERNAL CSS STYLE SHEET AVAILABLE IN THE WYSIWYG EDITOR - NEWER VERSIONS - Jul 4th, 2011

Many of these functions can now be added in /cmsAdmin/lib/wysiwyg.css 


LIMITING YOUR CLIENT’S OPTIONS IN THE TINYMCE EDITOR - Aug 6th, 2010

There are times when you’ll want to use a WYSIWYG editor (easy addition of tables, links, etc.) but want to limit the
amount of styling available to your clients. This is easier than you think. 

1. Open the file:

/cmsAdmin/lib/wysiwyg.php

- make a backup before making any changes.

2
. Search for:

theme_advanced_buttons1 : "formatselect


The whole code block is actually: 



 theme_advanced_buttons1 :
"formatselect,fontsizeselect,bold,italic,underline,|,justifyleft,justifycenter,justifyright,|,bullist,numlist,|,outdent,indent,|,removeformat,fullscreen",
          theme_advanced_buttons2 :
"forecolor,backcolor,|,link,unlink,anchor,|,hr,image,media,table,|,pastetext,pasteword,|,code,|,spellchecker"



This is where menu functions are enabled. If you want to disable one of the functions, just delete it from the list
(don’t forget to delete the comma also). You can remove the <!– temporarily to display the code more clearly.


REMOVING THE&LT;P&GT; TAGS INSERTED BY THE TINYMCE EDITOR - Aug 6th, 2010

CMSB uses the TinyMCE WYSIWYG editor and therefore is pretty much stuck with the concepts that it uses.

If using a textbox or a text field won’t work for you and you’re using the WYSIWYG, here’s an idea that Dave at
Interactive tools came up with in response to the issue of <p> tags.



<!-- remove leading <p>, do this _before_ displaying field value -->
<?PHP $record['yourfieldname'] = preg_replace("/^\s*<p>/i"""$record['yourfieldname'] ); ?>



DISPLAYING HTML CODE IN A WYSIWYG FIELD - May 14th, 2011

Not the perfect solution, but <textarea> your code...</textarea> shows the code as code.

Try this in the HTML of the WYSIWYG editor as an example:


<p>Here's some code</p> 
<p><textarea style="width: 162px; height: 24px;"> < span class="blue"></textarea></p> 
<p>And Here's some more text after the code block. </p>


You can re-size the code block by dragging it's borders or if that fails, adjust the height and width of the style in
the html. 

CHAPTER 9 - TUTORIALS



PLUGIN TUTORIAL - Dec 29th, 2018

Glen, from CanadianDomainRegistry.ca has generously offered to allow me to include a link to his tutorial on Plugins as
part of the Cookbook.

You can access that tutorial at:

http://www.canadiandomainregistry.ca/cmsDocs/cmsPublic/

While you're there, note that the Table of Contents on this page is created on the fly using JavaScript.


FIXING AND SUPPRESING PHP ERRORS - May 25th, 2017

Starting with CMSB V.2.64, finding PHP errors on a site has become much easier thanks to the addition of a global error
logging section.

Many of these errors have gone (happily) undetected by both you and your client.

This will be a growing list of fixes for the most commonly found errors. If you have any additions or corrections,
please share them  with me via the contact page, or on the CMSB Forum in post:

https://www.interactivetools.com/forum/forum-posts.php?postNum=2236347

Here's a few to start

***NOTE: Based on personal experience, make sure that your page works as planned after you attempt to suppress errors.
Otherwise you may be in for some unexpected surprises.***

"Invalid argument supplied for foreach()" in website membership, uncomment the array it was referencing at the top and
just blanked out the fields like this: 

//$GLOBALS['WEBSITE_LOGIN_REQUIRED_FIELDS'] = array('agree_tos','agree_legal');
$GLOBALS['WEBSITE_LOGIN_REQUIRED_FIELDS'] = array(); 

Another way to do that is put an if around the foreach: 
if ($array) { 
  foreach ($array as $item) { 
    // ...
  }
}

If you get an undefined or unknown index in an array, you can add an @ in front of the arrays:

if (!@$higher_exceptions[$word]) {$word = strtolower($word);}
if ((!@$lower_exceptions[$word]) || ($word == $firstElement) || ($word == $lastElement) ) {$word = ucfirst($word);}


Same for split: 
$words = @split(" ", $instruction);

In general, you can add the error-suppression operator @ in front of @php_functions(), @$variables and
@$array['lookups']. 

If you're using the not operator, !, as in !$your_variable, you should put the @ after the ! operator, like this: 
!@$your_variable.

Dave Edis, at Interactive Tool explains:

@!$_REQUEST['save']. processes from right to left: 

    $_REQUEST['save'] - check for value of array element with key of 'save', If it's not defined display a warning
    @ If a warning was produced by the last operation don't show it
    ! (NOT) check if the last returned value was boolean false (and an undefined array index would return null which
evaluates as false)

VS: 

    $_REQUEST['save'] - check for value of array element with key of 'save', If it's not defined display a warning
    ! (NOT) check if the last returned value was boolean false (and an undefined array index would return null which
evaluates as false)
    @ If a warning was produced by the last operation don't show it (but this applies to ! now).

You can also use the @ in <?php if(@isset($_REQUEST['submit_count'])

Another one that came up is:
_WARNINGsession_start(): Cannot send session cookie headers already sent
/my_path_to_/cmsAdmin/lib/database_functions.php (line 762)
http://

To which Dave Edis responded:

That session one is one we'd want to know about normally!  But in this case we've patched it for the next release
(2.65?).  Just add a @

  @
session_start(); // hide error: E_WARNING: session_start(): Cannot send session cache limiter - headers already sent

Another save by Dave...

If 
you're getting this type of error in an older version of the autobackup plugin:

E_WARNING: unlink(/your_server_path/cmsAdmin/data/backups/your_backup-v2.64-daily-Mon.sql.php): No such file or
directory
/your_server_path/cmsAdmin/plugins/autoBackup.php (line 71)

According to the Master...

For autobackup you can either update to the new version 1.04 (which has @ if front of unlink) or you can just add @ in
from of the unlink's.  It's likely because two instances of the backup script are running and one erased the file before
the second one got to it.  Note that if you upgrade the new autobackup script does require a cron.php running

CHAPTER 10 - NEW UPGRADES AND NEW CMSB FUNCTIONALITY



WHAT'S NEW?

Since CMS Builder's initial release back in November 2007 there have been a slew of updates to CMS Builder. Recently,
there has been a flurry of activity and a lot of new functionality has been added.

If you’re like me, you’ve got a number of web sites that use CMSB and many of them still use the version of CMSB
that was around when you set up that site. You’ve probably been reluctant to update many of those “older” sites to
the latest version of CMSB because, for the most part, the “If it ain’t broken, why try to fix it” rule applies,
and because of the retraining that could be required on the end user level.

Here’s a recap of the major changes that have been added since V1.29.

For those of you that have not been keeping up with all of the upgrades, some of the cool new features (and changes) are
not always evident if you don’t know they exist. So I thought that I’d recap the major ones and list some other
things to look out for. 

You can view the entire official changelog at: 

        http://www.interactivetools.com/upgrade/changelog.php#cmsbuilder

And then go to:

       http://www.interactivetools.com/upgrade/

 for  your copy of the latest version.


VERSION 3.60 JULY 12, 2023 (ENHANCED SERVER MANAGEMENT & ERROR TRACKING) - Jul 13th, 2023

*** July 12, 2023 - Version 3.60 (Enhanced Server Management & Error Tracking)

SERVER REQUIREMENTS
- This software version REQUIRES: PHP 8.0+ and MySQL 5.5+

NEW FEATURES
- Server Info: "Admin > General > Server" now show some additional details about the current server
- Server Changes: "Admin > General > Server" now shows change history for server components (PHP, etc)
- Developer Log: New "Filepath Report" groups errors by filepath for easier debugging
- Developer Log: Improved detection of source filepath and line num of errors
- Developers: Added shellCommand() function to try various PHP shell execution methods and/or fail gracefully

BUG FIXES
- Error reporting: Fixed issue where @ silenced variables will still returning errors
- Default Data Files: Updated defaults to: ENGINE=InnoDB CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
- mysql_escape now properly escapes backslashes ("\") so they match in searches
- Misc Code and other minor improvements.


VERSION 3.59 - JUNE 2, 2023 (PHP 8 NOW REQUIRED) - Jun 3rd, 2023

SERVER REQUIREMENTS
- This software version REQUIRES: PHP 8.0+ and MySQL 5.5+

MINOR CHANGES
- Server Info: "Admin > General > Server" has been updated to display more info about server
- Server Info: "Admin > General > Server" now provides links for viewing MySQL status and variables
- Backups: CMS Backups are now created as temp files and renamed on completion to prevent partial backups on error
- Translations: Added some additional text to the translation system
- Viewers: date searches now work with special createdDate and updatedDate fields
- Languages: Made some additional text translatable

BUG FIXES & CODE UPDATES
- MySQL 8 Support: Renamed _cron_log table column "function" to "functionName" (reversed name)
- Database: Fixed bug where MariaDB 'null' defaults in backups couldn't be restored in MySQL
- Swift Mailer: Fixed some additional PHP 8.x warnings and errors.
- Fixed minor bug in SVG detection causing false positives in non-SVG images
- Developer Log: Removed Symbol Table as PHP 8 set_error_handler() function no longer supports it
- Libraries: Updated HTML Purifier from 4.14 to 4.15
- Remove /3rdParty/PHP_Compat/ PHP compatibility libraries that aren't required with PHP 8
- Prevented "Erase" button from appearing on Single Record Sections where it's not functional.
- Updated CA Root Certificates in /3rdParty/cacert.pem - used by curl_ functions.
- Additional PHP 8.x support fixes
- Misc Code and other minor improvements.
You can download the latest version here:https://www.interactivetools.com/download/


VERSION 3.58 - MARCH 17, 2023 (MAINTENANCE RELEASE) - Mar 17th, 2023

** March 17, 2023 - Version 3.58 (Maintenance Release)

SERVER REQUIREMENTS
- This software version REQUIRES: PHP 7.3+ and MySQL 5.5+

MINOR CHANGES
- Security: Added .htaccess code to prevent it from being loaded in a frame (to prevent clickjacking)
- Developer Log: Optimized memory usage to prevent memory limit errors when listing many error records
- Performance: Optimized getRecords() to load results faster in many cases (by replacing SQL_CALC_FOUND_ROWS)
- Programmers: getEvalOutput() now returns errors instead of halting execution. See function header for details

BUG FIXES
- Fixed PHP 8 transparent image error that returned imagecolorsforindex(): Argument #2 ($color) is out of range
- Added workaround for Maria 10.5.16 where COLUMN_DEFAULT of NULL is instead of an empty string for text columns
- Separator fields: Fixed PHP 8.2 error: Deprecated: Creation of dynamic property SeparatorField::$order is deprecated
- Additional PHP 8.x support fixes
- Misc Code and other minor improvements.

You can download this new release from https://www.interactivetools.com/download/


VERSION 3.57 - SEPTEMBER 26, 2022 (MAINTENANCE RELEASE) - Sep 26th, 2022

SERVER REQUIREMENTS
- This software version REQUIRES: PHP 7.3+ and MySQL 5.5+

MINOR CHANGES
- Restoring databases on install now uses incremental restore to support large backups

BUG FIXES
- Additional PHP 8.1 support fixes
- Patched issue where emails would sometimes be received as raw HTML with PHP 8
- Fixed issue where cms admin searches for zero would return be ignored
- Misc code, security and other minor improvements.


VERSION 3.56 - MARCH 22, 2022 (PHP 8.1 SUPPORT) - Aug 6th, 2022

SERVER REQUIREMENTS
- This software version REQUIRES: PHP 7.3+ and MySQL 5.5+
- NOTE: Some plugins may need to be updated when upgrading from v2.xx

NEW FEATURES
- PHP 8.1 Support: Updates to support PHP 8.1
- License updates: Removed license key requirements for white-label branding
- WebP Support: Added default plugin for converting media files to webp

MINOR CHANGES
- License: Updated license agreement to remove references to registration and licence keys
- Updated jQuery to 3.6.0

BUG FIXES
- Misc media library bug fixes
- Misc Code and other minor improvements.


Note:
On 8/6/22, user tcbshifter noticed a small bug that hasn't been fixed in mail_functions.php on line 230. The variable
set at the top is called $oldEncoding, not $mbEncoding.
Check this if you're having mail function issues


VERSION 3.55 - NOVEMBER 10, 2021 (PHP 8 SUPPORT - AUTOMATIC CONVERSION OF UPLOADS TO WEBP) - Nov 10th, 2021

    SERVER REQUIREMENTS
    - This software version REQUIRES: PHP 7.3+ and MySQL 5.5+
    - NOTE: Some plugins may need to be updated when upgrading from v2.xx

    NEW FEATURES
    - PHP 8 Support: Updates to support PHP 8
    - WebP Support: Added option to automatically convert uploads to webp format.
    - Media Library: WYSIWYG media files can now be updated via media library (images must be the same size)
    - Media Library: Added paging to media lists
    - Media Library: Added multi-file uploader to the media library

    MINOR CHANGES
    - Plugin System: Added hooks for: list_postListTable_inner

    BUG FIXES
    - Fixed issue where MySQL timezone wasn't being correctly set
    - Fixed issue where converting mysql localhost hostname to 127.0.0.1 prevented connections on some servers
    - Fixed where incorrect warning was displayed about version of MariaDB being too old
    - Fixed issue where media library wouldn't if there was more than one image field on a page
    - Fixed error when saving category records
    - Misc Code and other minor improvements.


VERSION 3.54 - OCTOBER 29, 2020 - (MEDIA LIBRARY BETA) (INLUDING INSTRUCTIONS TO FIX TIME STAMP ISSUE) - Jan 19th, 2021

How to fix time stamp issues:

According to Dave Edis, Senior Programmer at Interactive Tools, this fix will be included in the next release, however
if you want to patch this version, he offered the following suggestion:

Backup your /lib/database_functions.php file.

Then in /lib/database_functions.php, search for mysqli($mysqli) 

After the code:

mysqli($mysqli); // save object on successful connection

add the following code:

// set connected flag
mysql_isConnected(true);

Your timestamps should now report correctly.

SERVER REQUIREMENTS
- This software version REQUIRES: PHP 7.2+ and MySQL 5.5+
- NOTE: Some plugins may need to be updated when upgrading from v2.xx

NEW FEATURES
- Media Library (BETA): Use uploaded images in multiple sections
  You can enable this feature under: Admin > General > Advanced > Use Media Library

MINOR CHANGES
- "removeDate" special field is now ignored when left blank. **SEE UPGRADE NOTES BELOW**
- Automated schema sync now also adds missing indexes for fields that already exist (for better
  compatibility with uploading schema files edited externally)

BUG FIXES
- Fixed issue stopping 'all' search field working when section has a tab field type.
- Disable database encryption when MySQL hostname is set to "localhost"
- Update for iframe sizing in Firefox
- Code updates to add support for PHP 7.4
- Updates for MySQL 8 compatibility
- Upgraded the HTML purifier library to version 4.12.0
- Blank passwords are no longer allowed (previously only possible by erasing password with a mysql query)
- Tabs no longer appear if users do not have permission to access them
- Misc Code and other minor improvements.

REMOVEDATE UPGRADE NOTES
Prior to this version, the default behavior for removeDate was to hide a post if 
the field was blank. We've reversed this so that posts with a blank removeDate are hidden.
Before upgrading from older versions, verify that no sections are relying on the old behavior.

This removeDate explanation is a bit confusing and as soon as IT clarifies, I'll post it here.

What this means, is that previously if you had a removeDate field, and it was blank, then the post wouldn't show.  Now, 
the removeDate only functions if a date is entered, so if there's no date, it is assumed that the record has never
remove checked.


VERSION 3.53 -AUGUST 12, 2019 - Feb 10th, 2020

BUG FIXES
- Allowed editing fields that start with an underscore.
- Fixed issue with WYSIWYG uploads when in demo mode.
- Fixed issue stopping upgrade when completing successfully. 


VERSION 3.52 - JULY 26, 2019 - Feb 10th, 2020

SERVER REQUIREMENTS
- This software version REQUIRES: PHP 7.1+ and MySQL 5.5+
- NOTE: Some plugins may need to be updated when upgrading from v2.xx

NEW FEATURES
- Section Editors: Added "Insert Field Here" (+) link to allow inserting a new field beside an existing field
- Section Editors: New "Tab Group" field for organizing section editor fields
- Section Editors: New '<input type="hidden">' advanced field for inserting hidden fields in sections.
- Server Admin: Admin > General > Disk Space now has an option to show largest files (for linux servers)
- Server Admin: Admin > Background Tasks now includes setup instructions for cPanel, Plesk, Windows, and Linux
- Database: Switched default storage engine from MyISAM to InnoDB
- Field Editor: "Update all existing records" feature available when adding a new field with a default value.
- Background Tasks: New option to limit the number of entries stored in the background task log.
- Category Menus: Now possible to assign Author/Viewer access to category menu sections.
- Admin Menu: Security Settings have been moved to their own menu
- Audit Log: Now logs login events and record add/modify/delete

MINOR CHANGES
- Select upload dialog only shows matching file extensions.
- Libraries: Updated TinyMCE from v4.9.0 to v4.9.4 (Released 2019-03-20)
- Libraries: Upgraded jQuery to v3.4.1
- Section Editors: New menu groups get default table suffix of "_menugroup" for usability

BUG FIXES
- Plugins: Added fix for issue that may cause plugins to get deactivated when they're being uploaded
- Category Menu: drag-order functionality no longer dependant on "name" column.
- Misc Code and other minor improvements.


VERSION 3.51 - APRIL 11, 2019 - (PHP 7 UPGRADE) - Apr 12th, 2019

SERVER REQUIREMENTS
- This software version REQUIRES: PHP 7.1+ and MySQL 5.5+
- NOTE: Some plugins may need to be updated when upgrading from v2.xx

NEW FEATURES
- Server Requirements: PHP 7.1+ is now required (older versions will return an error)

BUG FIXES & MINOR CHANGES
- Upgraded to FontAwesome 5
- HTML 5 Style: Removed trailing slashes from single tags in Code Generator and CMS code
- Admin Menu: Updated admin menu warning to recommend latest supported PHP versions
- Libraries: Updated HTML Purifier from 4.9.3 to 4.10.0 (Released 2018-02-22)
- Fixed HTMLPurifier/PHP 7.2 warning: E_DEPRECATED: idn_to_ascii(): INTL_IDNA_VARIANT_2003 is deprecated
- Updated backup process to use considerably less memory
- MySQL 8 compatibility: Remove deprecated SQL mode, addressing case-sensitivity in information schema fields.
- Fix error when restoring encrypted fields to a blank database.
- Fix issue with Chrome autocomplete activating on admin pages.
- Removed unused libraries: swfObject, mobile_detect
- Misc Code and other minor improvements.

CSS UPDATES/CUSTOM THEMES
As part of the FontAwesome 5 update, some updates have been made to the base CSS theme templates included with CMSB.
If you use a custom CSS theme that is based on these theme templates, it will likely need to be updated.
Please compare to the new versions to match these changes if necessary. The specific changes are:
- The selector ".dragger, .label" (original line 17) has been changed to ".label"
- The block "ul.main-navigation-menu > li > ul > li.current > a:after { ... }" (original lines 64-71) has been fully
reworked.
To see the differences compare /3rdParty/clipone/css/theme_blue.css.old with theme_blue.css

Also note that if your custom theme added any FontAwesome icons using pseudo-elements, they will also need to be updated
as per: https://fontawesome.com/how-to-use/on-the-web/advanced/css-pseudo-elements


VERSION 3.50 - DECEMBER 18, 2018 (FREE VERSION) - Dec 19th, 2018

SERVER REQUIREMENTS
- This software version REQUIRES: PHP 5.6+ and MySQL 5.5+
- Future versions will require: PHP 7.1+
- NOTE: Some plugins may need to be updated when upgrading from v2.xx

NEW FEATURES
- Free Version: Removed "Powered By" link from the footer of unlicensed versions.
There is now no branding on generated website pages. You can purchase a license
if you want to private-label the CMS backend/admin program.

BUG FIXES & MINOR CHANGES
- WYSIWYG: Updated TinyMCE from v4.7.2 to v4.9.0 (Released 2018-11-27)
- WYSIWYG: Fixed issue where tinymce cache files weren't getting saved in data folder
- Improved support for automatic image orientation on upload using ImageMagick
- File Uploading: Uploader now reports mime type on forbidden file errors (for debugging)
- Prevent "Copy of" from being added to separator field labels when using Save and Copy plugin
- Misc Code and other minor improvements.


VERSION 3.15 -SEPTEMBER 17, 2018 - (MAINTENANCE RELEASE) - Oct 12th, 2018

SERVER REQUIREMENTS
- This software version REQUIRES: PHP 5.6+ and MySQL 5.5+
- NOTE: Some plugins may need to be updated when upgrading from v2.xx

NEW FEATURES
- Section Editors: Added new field "Section Description" for HTML to be displayed below section titles.
- Usability: Uploaded jpegs are now automatically rotated as needed while being saved.

BUG FIXES & MINOR CHANGES
- Image Resizing: Updated resizing code to use less memory (resizing might be slightly slower).
- File Uploading: Updated error reporting to show more descriptive errors.
- File Uploading: Fixed bug when uploading file with no extension.
- utf8mb4 upgrading: Improved error reporting, misc improvements.
- utf8mb4 upgrading: Fixed bug when using multiple CMSB databases with same table prefix.
- Fixed javascript error on advanced filter list fields.
- Fixed broken link on Searching and Sorting tabs for multi-record sections.
- Fixed "view" pages in multi-record sections that use upload fields.
- Misc Code and other minor improvements.


VERSION 3.14 AND DATABASE ERROR FIX - AUGUST 22, 2018 (HTML5 MULTI-FILE UPLOADER) - Sep 4th, 2018

Before listing all the improvements in this version, I wanted to share Greg's solution to a database error that was
thrown afer install when trying to access the Section Editor. 

According to Greg: 
"The issue seems to happen when you have multiple databases setup in MySQL that have CMS Builder installed with the same
prefix. This causes the first statement below to select the same column more than once, which invalidated the next MySQL
update statement. I'll push this change to our dev copy of CMS Builder so that the fix is in the next release."

He went on to say:
To fix the issue, in version 3.14 only, you'll need to change line 417 of the file
your_cmsb_install_directory/lib/mysql_functions.php from this:


WHERE table_name = '" .mysql_escape($mysqlTable). "' AND
character_set_name IS NOT NULL AND
collation_name IS NOT NULL"; // check for NULL to skip numeric/date fields that don't have charset/collation


To this:


WHERE table_name = '" .mysql_escape($mysqlTable). "' AND
table_schema = '{$GLOBALS['SETTINGS']['mysql']['database']}' AND
character_set_name IS NOT NULL AND
collation_name IS NOT NULL"; // check for NULL to skip numeric/date fields that don't have charset/collation


*** August 22, 2018 - Version 3.14 (HTML5 Multi-File Uploader)

SERVER REQUIREMENTS
- This software version REQUIRES: PHP 5.6+ and MySQL 5.5+
- NOTE: Some plugins may need to be updated when upgrading from v2.xx

NEW FEATURES
- File Uploader: Implemented new HTML5 uploader for easier multi-file uploading.
- Security: Added new option under Admin > General > Security "Encrypt Database Connections" for using SSL with MySQL
- Security: Added new option in Field Editor > Advanced Options > Data Encryption - Automatically encrypt data stored in
database
- Security: /data/ folder can now be stored outside of the web root. See /data/how_to_move_data_folder.txt for details
- Backup & Restore: Restore operations are now incremental and reload the browser to avoid timeouts with large backups

BUG FIXES & MINOR CHANGES
- Email Delivery: Added tips to "Email Settings" on how to resolve delivery issues with Microsoft email services
(hotmail, etc)
- Database: Fixed issue where not all fields would get converted to 4-byte UTF8 (utf8mb4)
- Field Editor: Added new 'MySQL Column Type' of MEDIUMBLOB (max: 16 megs)
- Backup Menu: Skipped tables are now listed under Backup pulldown
- Programmers: Added new plugin filters: loginExpirySeconds
- Programmers: Added utility function: makeWritable
- Misc Code and other minor improvements


VERSION 3.13 - FEBRUARY 20, 2018 - (MAINTENANCE RELEASE) - Apr 29th, 2018

SERVER REQUIREMENTS
- This software version REQUIRES: PHP 5.6+ and MySQL 5.5+
- NOTE: Some plugins may need to be updated when upgrading from v2.xx

BUG FIXES & MINOR CHANGES
- Usability: Admin > General > Server Info now reports which config files PHP is using (.htaccess, php.ini, etc)
- Compatibility: Removed rename_winsafe() function (not needed for modern PHP versions)
- Compatibility: Fixes PHP 7.2 error: ob_start(): Cannot use output buffering in output buffering display handlers
- Programmers: Added utility function: composerAutoload
- Misc Code and other minor improvements


VERSION 3.12 - DECEMBER 5, 2017 - (NEW MULTI-SELECT PILLBOX FIELD) - Apr 29th, 2018

SERVER REQUIREMENTS
- This software version REQUIRES: PHP 5.6+ and MySQL 5.5+
- NOTE: Some plugins may need to be updated when upgrading from v2.xx

NEW FEATURES
- New Pillbox Field: Replaced multi-select pulldown with select2 pillbox (searchable pulldown with tags)
- Legacy MySQL Scanner: Added new plugin for detecting PHP/mysql code that needs to be updated for PHP 7
- Plugin Developers: View-source on any CMS page to see a list of all plugin hooks available to be called
- WYSIWYG: Added "Code Sample" button for showing formatted source code

BUG FIXES & MINOR CHANGES
- Programmers: Added utility function: curl_post
- Plugin System: Added hooks for: listRow_html
- Fixed issue that caused uploads and uploads paths not to display correctly (bug in endWith() function)
- WYSIWYG: The "Browse" button on the insert link popup now shows an error message if uploads aren't enabled.
- WYSIWYG: Updated TinyMCE from v4.6.0 to v.4.7.2 (Released 2017-11-07)
- Misc Code and other minor improvements


VERSION 3.11 - NOVEMBER 6, 2017 - (USER REQUESTS) - Apr 29th, 2018

SERVER REQUIREMENTS
- This software version requires: PHP 5.6+ and MySQL 5.5+
- NOTE: Some plugins may need to be updated when upgrading from v2.xx

NEW FEATURES
- Unsaved changes warning: when you navigate away from a add/edit form after making changes it warns you
- Developer Email: we've added a field for this so error reports can go to a separate email and not the default site
email
- Emoji Support: We've switched 4-byte UTF8 (utf8mb4) to support storing of extended charset languages and emojis in
database
- Lastpass Usability: Added workaround for issue where LastPass would automatically fill out username and password
fields when modifying accounts
- Image Uploads: CMYK "print" images are now automatically converted to browser-safe RGB images on upload (instead of
just showing an error)
- Email Delivery: Added tips on the bottom of the Email Settings menu on what to do when emails aren't getting delivered
- Offline Mode: Switched to storing and caching resources locally to speed up access for offline and limited internet
access installations
- Server Security: Added new options for setting file permissions and umask (search 'permissions' in /data/settings
file)
- Database Order: MySQL columns are now automatically reordered in database to match order shown in field list
- Clearing Task Log: Added link in header to "Clear Log" in: Admin > Background Tasks > Task Log
- Server Requirements: PHP 5.6 is now required (older versions will return an error)
- Plugin System: Added hooks for: admin_footer_preButtons

MINOR CHANGES
- Programmers: New functions: schema_isMultiValueField, listValues_pack, listValues_unpack, js_escapef
- Compatibility: Updated included "Sample Section Generator" plugin to support PHP 7 and mysqli
- Libraries: Added PHP 5.6 compatibility functions for: random_bytes, random_int
- Libraries: Updated TinyMCE from v4.6.0 to v4.6.4 (Released 2017-06-13)
- Libraries: Updated HTML Purifier from 4.7.0 to 4.9.3 (Released 2017-06-02)
- Libraries: Updated SwiftMailer from 5.4.1 to 5.4.8 (Released 2017-05-01)
- Libraries: Updated Mobile Detect from 2.5.3 to 2.8.25 (Released 2017-03-29)
- Usability: Editor fields with no size specified now default to medium size instead of small

BUG FIXES
- Related Records: Fixed issue where drag sorting wouldn't work on edit pages with multiple related records fields
- Advanced Commands: Fixed issue where Advanced Commands that didn't redirect the page would get trigger twice
- Schema Presets: Updated date fields to default to showing options for 5 years before and the current date
- Code Generator: Fixed issue where direct linking to Category Menu Generator from a non-category section caused an
error
- WYWISYG: Temporarily disabled WYSIWYG content menu as it was interfering with browser based spellcheckers
- Misc Code and other minor improvements


VERSION 3.10 - MAY 8, 2017 (***MAINTENEACE RELEASE***) - Jul 1st, 2017

SERVER REQUIREMENTS
- This software version requires: PHP 5.6+ and MySQL 5.5+
- NOTE: Some plugins may need to be updated when upgrading from v2.xx

NEW FEATURES
- Experimental: New Code Generators and Viewers

MINOR CHANGES
- Programmers: Added utility function: mysql_do, curl_get
- Programmers: Added readme file: /how to upgrade from v3.08 (mysql to mysqli).txt

BUG FIXES
- Plugins: Fixed caching issue that caused some plugins to not install or upgrade properly
- Zend OPcache: We now call opcache_invalidate() when loading saved data files to prevent loading old cached versions
- PHP 7: Fixed error displayed on CMS view menus "Using $this when not in object context"
- Misc Code and other minor improvements


VERSION 3.09 - MAY 8, 2017 (***PHP 7 SUPPORT***) - May 26th, 2017

SERVER REQUIREMENTS
- This software version requires: PHP 5.6+ and MySQL 5.5+
- NOTE: Some plugins may need to be updated when upgrading from v2.xx

NEW FEATURES
- Added support for PHP 7 (switched from mysql to mysqli libraries)
- MySQL Legacy Support: Can be enabled with checkbox under: Admin > General > Advanced
- MySQL Legacy Support: (automatically enabled when upgrading from earlier versions)
- WYSIWYG: Updated TinyMCE from v4.4.3 to v4.6.0 (Released 2017-05-04)
- Backup & Restore: Backup files can now be downloaded directly from the admin menu.
- Section Editors: Added more menu icons

MINOR CHANGES
- Disabled auto-capitalization and auto-complete on the username field on login page.
- MySQL reserved keywords can no longer be used as fieldnames
- Upgraded Font Awesome from 4.6.3 to 4.7.0 (more menu icons)

BUG FIXES
- Fixed TinyMCE issue where images couldn't be selected in latest Chrome browser:
https://github.com/tinymce/tinymce/issues/3611
- Misc Code and other minor improvements


VERSION 3.08 - FEBRUARY 24, 2017 (***NEW SERVER REQUIREMENTS***) - Mar 15th, 2017

NEW SERVER REQUIREMENTS
- This software version now requires: PHP 5.6+ and MySQL 5.5+
- The following additional PHP extensions are now required: mysqli, openssl, curl
- Please check with your hosting provider to ensure you have these installed BEFORE upgrading.
- NOTE: Some plugin may need to be updated when upgrading from v2.xx

MINOR CHANGES
- Images: Resized JPEGs are now saved as progressive JPEGs so they display as they load.
- Admin Menu > Email Settings > Outgoing Mail: Added "Clear Log" option to page header to erase outgoing mail log.

BUG FIXES
- Database: Fixed MySQL 5.7 error when adding date fields: Incorrect datetime value: '0000-00-00 00:00:00'
- Database: Fixed issue where mysql locks were server wide and would slow down servers with many CMS installs
- Field Editor: Fixed error that appeared on some sections: "Notice: Undefined index: separatorHTML"
- WYSIWYG: Fixed issue with linking to a file not working if the file isn't an image.
- Misc Code and other minor improvements


VERSION 3.07 - DECEMBER 7, 2016 (UPGRADED WYSIWYG EDITOR) - Mar 15th, 2017

December 7, 2016 (Upgraded WYSIWYG Editor)

SERVER REQUIREMENTS:
- This software version requires: PHP 5.5+ and MySQL 5.0+
- Future versions will require: PHP 5.6+ and MySQL 5.5+ (and PHP extensions: mysqli, openssl, curl)
- NOTE: Some plugin may need to be updated when upgrading from v2.xx

NEW FEATURES
- WYSIWYG: Updated TinyMCE from v3 to v4.4.3 (Released 2016-09-01)
- WYSIWYG: NOTE: Custom wysiwyg scripts will need to be updated. Upgrade process provides instructions.

MINOR CHANGES
- Image types: Added SVG to the list of default image types allowed and added emulated SVG thumbnails
- CMS Editors: Multi-value fields now show a warning when 1000 or more options are listed
- Field Editor: HTML Separators now support HTML in separator titles
- Field Editor: HTML Separators now show manually entered field labels in field list (for ease of use)
- Languages: Made some additional text translatable and set lang="xx" in CMS <html> tag
- Programmers: dieAsCaller() now logs error messages to Developer Log

BUG FIXES
- Category section: Fixed the issue where selecting a parent category would sometimes cause the current category branch
depth to exceed the max depth
- Fixed a bug where clicking the "Go" button of the "Advanced commands" on a record list page opens a new tab
- Fixed issue where getPage() function failed on HTTP-chunked data when headers had extra whitespace
- Misc Code and other minor improvements


VERSION 3.06 - AUGUST 25, 2016 (CMS MENU ICONS AND THUMBNAIL CROPPING) - Mar 15th, 2017

August 25, 2016 -  (CMS Menu Icons and Thumbnail Cropping)

SERVER REQUIREMENTS:
- This software version requires: PHP 5.5+ and MySQL 5.0+
- Future versions will require: PHP 5.6+ and MySQL 5.5+
- NOTE: Some plugin may need to be updated when upgrading from v2.xx

NEW FEATURES
- CMS Menus: Added icons for "CMS Setup" and "Admin" menus
- Section Editors: Added "Menu Icon" field for adding menu icon
- Field Editor: Added "crop" feature for upload field's thumbnail images

MINOR CHANGES
- CMS Menus: "Backup & Restore" has been moved from "General Settings" to its own page
- CMS Menus: "Text Link" menus that redirects to an external page now display an "external-link" icon
- Section Editors: Moved "Hidden menu" option from "Advanced" tab to "General" tab
- Field Editor: Added
tag support for text field prefix and description
- Code Generator: Changed the RSS feed's isPermalink attribute value to false by default so that it will pass the w3c
validation
- Code Generator: Included hidden menus in the "Select Section" list options
- Programmers: Updated getPage() function to send HTTP/1.1 header, added support for HTTP-chunked data
- Programmers: Added new plugin filters: sendMessage_options, upload_removeFilePath

BUG FIXES
- CMS Menus: Fixed display issue where menus sometimes didn't appear correctly
- CMS Menus: Fixed issue where background color turns black when printing a CMS Menu page in Chrome browser
- CMS Menus: Fixed issue where thumbnails on the section list page sometimes disappear when adding a new thumbnail
setting
- Field Editor: Fixed issue where save & copying a field doesn't automatically update the fieldname when changing the
label
- Upload Field Editor: Fixed issue where recreating new thumbnail, the URL and file paths of that new thumbnail don't
get set in its upload record
- Misc Code and other minor improvements


VERSION 3.05 - MAY 26, 2016 (FREE "POWERED BY" VERSION) - Mar 15th, 2017

*** May 26, 2016 -  (Free "Powered By" Version)

SERVER REQUIREMENTS:
- This software version requires: PHP 5.5+ and MySQL 5.0+
- Future versions will require: PHP 5.6+ and MySQL 5.5+
- NOTE: Some plugin may need to be updated when upgrading from v2.xx

NEW FEATURES
- Free Version: CMS can now be used and installed without buying a license so long as "Powered By" link is displayed on
website pages
- Branding Menu: License owners can now access all customization features under the Admin > Branding menu.
- License Agreement: Added section 4h requiring "Powered By HTML" be displayed unless users have a license

MINOR CHANGES
- CMS Menus: "Admin" menu has been re-organized into "CMS Setup" and "Admin Menu" menus
- Admin > General > Background Tasks: now shows full path to PHP binary for setting up cronjobs
- Admin > General > Background Tasks: Added options to temporarily disable background tasks when needed
- Admin > General > Server Info: Now shows installed PHP security modules (for debugging problems)
- Programmers: Added option to add icons before and after a CMS menu item
- Programmers: Added new plugin hooks: list_preListTable, list_postListTable
- Programmers: Added new plugin filters: list_showListSchema, list_showListRecords, listRow_record, listRow_showView,
listRow_showModify, listRow_showErase

BUG FIXES
- CMS Menus: Fixed bugs where CMS menu didn't display correctly on mobile devices
- Reset Password page: Added adminUI header and footer
- Misc Code and other minor improvements


VERSION 3.04 - APRIL 26, 2016 (MAINTENANCE RELEASE) - Mar 15th, 2017

SERVER REQUIREMENTS:
- This software version requires: PHP 5.5+ and MySQL 5.0+
- Future versions will require: PHP 5.6+ and MySQL 5.5+
- NOTE: Some plugin may need to be updated when upgrading from v2.xx

BUG FIXES
- Fixed issue where CMS menu didn't work correctly on Safari browser
- Fixed issue where Collapsible Separator didn't work properly on IE browser
- Fixed issue where thumbnails didn't get correct file permissions on some servers
- Fixed issue where some errors didn't get logged when there was an out of memory error.
- Misc Code and other minor improvements


VERSION 3.03 - APRIL 20, 2016 (COLLAPSIBLE SEPARATORS & PHP 5.5) - Mar 15th, 2017

SERVER REQUIREMENTS:
- This software version requires: PHP 5.5+ and MySQL 5.0+
- Future versions will require: PHP 5.6+ and MySQL 5.5+
- NOTE: Some plugin may need to be updated when upgrading from v2.xx

NEW FEATURES
- Collapsible Separators: The separator bars can now be defined as "collapsible", and open or closed by default.
- Field Editor: Bootstrap style "Field Addons" (input-group-addons) can now be added around text fields
- PHP 5.5+ is now required, this is the oldest supported PHP version: http://php.net/supported-versions.php
- New Upload System: Rewrote upload system to support different upload storage methods in future

MINOR CHANGES
- CMS List page: Added "Update" button in advanced search
- CMS View page: Added "Edit" button
- Tab titles: CMS Menu page titles are now menu-specific which can help when working with multiple CMS tabs open
- Editors: List field options specified as text can now contain PHP code
- Programmers: Added new function tag() for programmatically generating HTML tags
- Programmers: getCategories() callback options can now be specified as anonymous functions
- Programmers: Added new plugin hooks: admin_getRequestedAction, viewPage_preShowViewFormRows,
viewPage_postShowViewFormRows
- Programmers: Added new plugin filters: post_loadSchema, showWysiwygUploadPreview_html, showField_addRowClass,
viewPageFieldRowHtml
- Programmers: Added new global variable $GLOBALS['BACKUP_DIR'] so plugins can override the backup directory path

BUG FIXES
- Upload List dragging: Fixed an issue where rows could not be dragged to the very top or bottom of the list when row
heights differ
- CMS List page: Fixed issue that causes an error if these is no "Search Fields" set in: Section Editor > Searching
- CMS Menus: Fixed issue where field widths created in v2.xx weren't the correct size after v3.xx upgrade
- Header Menu Links: Fixed issue wherein My Account menu group doesn't stay on top if the menus has been modified
- Section Field Editor: Fixed bugs with using "Save and Copy" button where fieldname prefix get duplicated and not all
field copies get saved
- Editors: Fixed issue wherein uploading a jpg file using iOS devices causes an error
- Editors: Fixed datepicker issue wherein the calendar popup appeared underneath other fields
- General Settings: Re-added "Disable Autocomplete" option
- General Settings: Fixed Upload directory and URL preview
- Misc Code and other minor improvements


VERSION 3.03 - APRIL 20, 2016 (COLLAPSIBLE SEPARATORS & PHP 5.5) - Mar 15th, 2017

SERVER REQUIREMENTS:
- This software version requires: PHP 5.5+ and MySQL 5.0+
- Future versions will require: PHP 5.6+ and MySQL 5.5+
- NOTE: Some plugin may need to be updated when upgrading from v2.xx

NEW FEATURES
- Collapsible Separators: The separator bars can now be defined as "collapsible", and open or closed by default.
- Field Editor: Bootstrap style "Field Addons" (input-group-addons) can now be added around text fields
- PHP 5.5+ is now required, this is the oldest supported PHP version: http://php.net/supported-versions.php
- New Upload System: Rewrote upload system to support different upload storage methods in future

MINOR CHANGES
- CMS List page: Added "Update" button in advanced search
- CMS View page: Added "Edit" button
- Tab titles: CMS Menu page titles are now menu-specific which can help when working with multiple CMS tabs open
- Editors: List field options specified as text can now contain PHP code
- Programmers: Added new function tag() for programmatically generating HTML tags
- Programmers: getCategories() callback options can now be specified as anonymous functions
- Programmers: Added new plugin hooks: admin_getRequestedAction, viewPage_preShowViewFormRows,
viewPage_postShowViewFormRows
- Programmers: Added new plugin filters: post_loadSchema, showWysiwygUploadPreview_html, showField_addRowClass,
viewPageFieldRowHtml
- Programmers: Added new global variable $GLOBALS['BACKUP_DIR'] so plugins can override the backup directory path

BUG FIXES
- Upload List dragging: Fixed an issue where rows could not be dragged to the very top or bottom of the list when row
heights differ
- CMS List page: Fixed issue that causes an error if these is no "Search Fields" set in: Section Editor > Searching
- CMS Menus: Fixed issue where field widths created in v2.xx weren't the correct size after v3.xx upgrade
- Header Menu Links: Fixed issue wherein My Account menu group doesn't stay on top if the menus has been modified
- Section Field Editor: Fixed bugs with using "Save and Copy" button where fieldname prefix get duplicated and not all
field copies get saved
- Editors: Fixed issue wherein uploading a jpg file using iOS devices causes an error
- Editors: Fixed datepicker issue wherein the calendar popup appeared underneath other fields
- General Settings: Re-added "Disable Autocomplete" option
- General Settings: Fixed Upload directory and URL preview
- Misc Code and other minor improvements


VERSION 3.02 - FEBRUARY 3, 2016 (MAINTENANCE RELEASE) - Mar 15th, 2017

SERVER REQUIREMENTS:
- This software version requires: PHP 5.4.0+ and MySQL 5.0+
- Future versions will require: PHP 5.5.0+ and MySQL 5.5+
- NOTE: Some plugin may need to be updated when upgrading from v2.xx

MINOR CHANGES
- Section Editors: Updated "Field Width" option to be a pulldown with 5 sizes: tiny, small, medium, large, and
full-width
- Section Editors: Updated "Field Width" pulldown to change size to show the user what the selected size will look like

BUG FIXES
- Section Editors: Fixed issue where all numeric fieldnames causes errors (All numeric fieldnames are no longer allowed)
- CMS Menus: Fixed issue where menu items would not appear when listed below a menu group the user didn't have access to
- CMS Upload Field: Fixed issue where black box appeared on top of page in Firefox when uploading
- CMS List page: Fixed issue where Preview button did not redirect to list page defined in section editors
- Misc design improvements including css, layout, colors and spacing
- Misc Code and other minor improvements


VERSION 3.01 - JANUARY 20, 2016 (MAINTENANCE RELEASE) - Mar 15th, 2017

SERVER REQUIREMENTS:
- This software version requires: PHP 5.4.0+ and MySQL 5.0+
- Future versions will require: PHP 5.5.0+ and MySQL 5.5+
- NOTE: Some plugin may need to be updated when upgrading from v2.xx

MINOR CHANGES
- Developers: New plugin hooks: getLastNumberInUrl_removeFields
- Added resize cursor on hover to drag sort icon
- Install screen password field no longer displays entered characters (password field instead of text field)

BUG FIXES
- Upgrade code now defaults to "theme_blue.css" if a non-existant theme is specified
- Fixed default settings file to use "theme_blue.css" instead of invalid "theme_classic.css"
- Fixed "how to install.txt" to list to correct PHP version required: v5.4+
- Fixed double htmlencoding on CMS home dashboard page which caused single quotes to appear as &#039;
- Fixed issue that prevented some errors and menu notices from displaying correctly
- Fixed issue that caused menu items to sometimes display twice
- Fixed issue where clicking search button wouldn't show count of matching records
- Misc Code and other minor improvements


VERSION 3.00 - JANUARY 8, 2016 (RESPONSIVE UI) - Mar 15th, 2017

SERVER REQUIREMENTS:
- This software version requires: PHP 5.4.0+ and MySQL 5.0+
- Future versions will require: PHP 5.5.0+ and MySQL 5.5+
- NOTE: Some plugin may need to be updated when upgrading from v2.xx

NEW FEATURES
- Backend: New mobile-friendly responsive design based on Bootstrap

MINOR CHANGES
- CMS Menus: Menus can now be indented from 1-5 levels deep
- Programmers: Error Log renamed to "Developer Log" and now has a stacktrace field
- SwiftMailer from 4.3 to 5.4.1
- HTML Purifier from 4.4.0 to 4.7.0

CODE CHANGES & BUG FIXES
- Programmers: New adminUI() function for generating CMS backend pages.
- Programmers: New functions: htmlencodef, ob_capture, preg_match_get, mysql_isConnected
- Programmers: New plugin hooks: adminUI_args, edit_createdByNumToName
- Deprecated Plugin Hooks: edit_buttonsRight, list_buttonsRight, view_buttonsRight (use the new 'adminUI_args' hook
instead)
- Deprecated Plugin Hooks: header_links (use the new 'menulinks_myAccount' hook instead)
- Deprecated PHP Constants: START_TIME (use $_SERVER['REQUEST_TIME_FLOAT'] instead)
- Programmers: Added hooks for: relatedRecords_buttons
- Removed CMSB constant: START_TIME (replaced with PHP 5.3 variable $_SERVER['REQUEST_TIME_FLOAT'])
- Misc Code and other minor improvements


VERSION 2.65 - APRIL 8, 2015 (MAINTENANCE RELEASE) - May 20th, 2015

*** Dave Edis says, 
"This is a maintenance and security update, it's a recommended upgrade for all users and the last planned release on the
version 2 branch.  

We recommend you make sure you install this version to confirm that your server meets the requirements for version 3 of:
PHP 5.4.0+ and MySQL 5.5+ (A notice will display at the top of "Admin > General" for servers running outdated and
unsupported versions of PHP or MySQL)."

NEW SERVER REQUIREMENTS:
- This software version requires: PHP 5.2.4+ and MySQL 5.0+
- Future versions will require: PHP 5.4.0+ and MySQL 5.5+

NEW FEATURES
- Security: Added notice to top of "Admin > General" for servers running outdated and unsupported versions of PHP or
MySQL
- Security: Autocomplete on login form is now enabled by default and can be disabled under Admin Menu
- Security: Added checkbox to toggle "Disable Autocomplete" browser function that saves usernames and passwords

MINOR CHANGES
- User Accounts: Added error checking to prevent admin users from accidentally disabling their own accounts
- Code: added "IN" operator support to mysql_where() (automatically applied when value is an array)

CODE CHANGES & BUG FIXES
- CMS List Search fields: Custom Name|createdBy.fullname fields now only list users who have records in the section
instead of all users
- Code: Added new function: utf8_force()
- Code: Fixed encoding issue that caused false HTTP 418 security error on some hosting servers.
- Fixed Invalid UTF-8 Error "MySQL Error: Incorrect string value: '\x... caused when logging errors when a variable had
binary data
- Fixed PHP 5.6 issue that prevented loading content from https:// secure websites with getPage() function
- Fixed _error_log schema so "Error Log" menu isn't listed twice on menu and is hidden under Section Editors
- Fixed config file .user.ini replacing # comments with ; comments, old-style # comments not supported by newer PHP
versions
- Fixed logged error "E_WARNING: session_start(): Cannot send session cache limiter - headers already sent"
- Fixed PHP 5.5 warnings: E_DEPRECATED: preg_replace(): The /e modifier is deprecated, use preg_replace_callback instead
- Related Records: Fixed issue that prevented erasing related records from a view menu (no _CSRFToken error)
- sendMessage: Improved catching and logging of errors related to sending messages
- Misc Code and other minor improvements


VERSION 2.64 - FEBRUARY 17, 2015 (UNIVERSAL ERROR LOGGING) - Mar 7th, 2015

SERVER REQUIREMENTS: Since v2.50 the requirements are: PHP 5.2.4+ and MySQL 5.0+

NEW FEATURES

- Universal Error Logging: PHP errors on any CMS viewer page will be logged in backend CMS

MINOR CHANGES

- Flash Uploader: Rewrote how Flash Uploader system works for better compatability with different browsers and servers

CODE CHANGES & BUG FIXES

- BugFix: Fixed issue where AutoBackup plugin caused login error: No _CSRFToken exists

- Admin Menu > PhpInfo: Added Multibye Language settings from mb_get_info() to bottom of output page for debugging

- CMS UI: Fixed issue on view pages where existing date fields with NULL value in MySQL would show current date instead
of blank

- CMS UI: Fixed issue that prevented date picker from displaying in any language other than the default

- CMS UI: Fixed issue where date selector icon link was broken when used on "Advanced Search" on list pages

- CMS UI: Fixed an issue where "Create" button in related records list would fail from record view.

- Code Generator: Updated generated code to prevent issue where uploads with ampersands ("&") didn't always display
correctly.

- Code: Fixed a bug in emailTemplate_loadFromDB()'s error reporting

- Code: Fixed encoding issue that caused false HTTP 418 security error on some hosting servers.

- Code: getPage() now connects to IPs directly to avoid bug where fsockopen can't connect to resolved ipv6 addresses

- Compatability: Fixed filemtime(): stat failed errors in backend admin when current working directory was different
from script dir

- Compatability: We now call opcache_invalidate() on saved /data/ files to prevent loading of old cached versions in PHP
5.5+ with Zend OPcache enabled

- Config: PHP date.timezone set to UTC by default to prevent errors in .htaccess and php.ini

- Email Templates: Added the following fields to list view: Reply-to, CC and BCC

- Flash Uploader: Fixed issue where flash uploader didn't work correctly with some browsers

- PHP Errors: Fixed filemtime(): stat failed errors in backend admin when current working directory was different from
script dir

- PHP Sessions: Fixed issue where backup process or autobackup plugin would cause PHP sessions to be closed sometimes
preventing logins

- PhoneGap Apps: Added workaround for issue where Android Apps don't send filenames when uploading so common image file
extensions get automatically added if needed

- Security: Added blank index.html files to /uploads/ folders to prevent directory browsing for servers that would
otherwise allow that

- Security: Disabled the ability for users to login with plaintext value of encrypted password hash

- Security: Added rule to .htaccess files to disable directory indexes (file lists) for folders without index files.

- Uploads: Fixed issue where uploads with ampersands ("&") didn't always display correctly in the CMS interface.

- Viewers: Fixed issues where certain single record section viewers would show updated content when "Preview" was
clicked.

- WYSIWYG: Fixed issue that had spellcheck results with UTF8 characters get double encoded (TinyMCE Bug #4700)

- Misc Code and other minor improvements


VERSION 2.63 - SEPTEMBER 3, 2014 - (MAINTENANCE RELEASE) - Sep 28th, 2014

SERVER REQUIREMENTS: Since v2.50 the requirements are: PHP 5.2.4+ and MySQL 5.0+

MINOR CHANGES

- Upload Settings: Upload dir and url now shows live path preview for relative paths

- Upload Settings: New admin section "Server Upload Settings" shows PHP settings to assist in identifying server issues

CODE CHANGES & BUG FIXES

- Images: Fixed issue where transparent backgrounds weren't being saved when creating thumbnails of larger images

- Uploads: 100x faster renaming when larger number of duplicate filenames.  Eg: photo.jpg yeilds: photo.jpg,
photo_001.jpg, photo_002.jpg, etc

- Uploads: Added link to check if flash is installed beside flash uploader option under admin menu

- Install: Improved internet connectivity checking for web hosting servers (detects when server firewalls would prevent
access to 3rd party APIs)

- Install: Improved PHP version detection check code so it works on PHP 4.4 (displays message to upgrade to PHP v5.2.4+)

- Codebase: getAbsolutePath() now renamed as absPath() and can calculate filepaths even if they don't exist (unlike
PHP's realpath function)

- Misc Code and other minor improvements


VERSION 2.62 - JULY 15, 2014 - (SECURITY ENHANCEMENTS) - Aug 30th, 2014

SERVER REQUIREMENTS: Since v2.50 the requirements are: PHP 5.2.4+ and MySQL 5.0+

NEW FEATURES

- Code Generator: Added new code generator for Category Menus

- Email Templates: Added new fields for: CC, BCC, and Reply-to

- Security: Added new "Security Settings" section under: Admin, General Settings

- Security: Custom generated "Security Tips" for the current server and connection are now displayed in red under
"Security Settings"

- Security: Numerous security enhancements and new features (see below): 

SECURITY ENHANCEMENTS

- Security: Password encryption is now required (ALL previous passwords are automatically encrypted on cms upgrade, a
failed logins, or first login with a plaintext password)

- Security: To prevent identification, program version, build, and license info are now hidden from HTML footer code

- Security: expose_php is now disabled in /cmsb/php.ini to prevent broadcasting PHP version (custom php.ini not
supported on all servers)

- Security: Enabled secure cookies by default - all cookies set on HTTPS:// connections can't be read from HTTP://
connections

- Security: PHP Sessions now force and require secure cookies when created on HTTPS:// connections

- Security: All PHP errors and warnings are now logged in a common file for easy review.

- Security: PHP errors are now all logged in the same secure log file which is here: data/php_error.log.php

- Security: Disabled browser autocomplete on all CMS forms with HTML5 attribute autocomplete="off" (previously done with
javascript)

- Security: Disabled browser autocomplete on all password input fields with HTML5 attribute autocomplete="off" 

- Security: Disabled browser autocomplete for browsers that ignore autocomplete with hidden field workaround:
http://crbug.com/352347#c11

- Security: Added 1-3 second delay after failed login attempt to delay brute-force dictionary password attacks

- Security: Post-login redirects are now limited to application urls only

- Security: CMS uploads and assets (js libraries, etc) are now stored separately to prevent revealing admin folder name

- Security: CMS now displays a warning and automatically rejects form submissions from outside the CMS to prevent CSRF
attacks

- Security: CMS now displays a warning and automatically rejects links from outside the CMS to prevent CSRF attacks

- Security: CMS now displays a warning for direct/manual links with no referer to prevent CSRF attacks

- Security: CMS menus now only accept POST form submissions to prevent CSRF (Cross-Site Request Forgery) attacks

- Security: CMS menus now check HTTP_REFERER and only accept form submissions from the CMS url to prevent CSRF attacks

- Security: CMS menus now warn when no HTTP_REFERER is sent to prevent CSRF and malicious URL attacks

- Security: CMS menus now check for unique session token on each form submission to prevent CSRF attacks

CODE CHANGES & BUG FIXES

- WYSIWYG: Fixed issue where wysiwyg wouldn't work on some servers that had output compression enabled

- WYSIWYG: Added workaround for servers with broken gzencode() function that always returned 'stream error'

- Bugfix: Added workaround for white-screen segfault/crash bug in PHP versions before 5.2.14 or 5.3.3 -
https://bugs.php.net/bug.php?id=51552

- Bugfix: prevent realUrl() and redirectBrowserToUrl() from returning multiple slashes in some situations

- Bugfix: Fixed issue where cms list search fields showed as selected if they had a value of zero

- Bugfix: Fixed case of useDatePicker (uppercase P) in settings file, corrects issue that caused datepicker option to be
unselectable in settings

- Bugfix: My Account menu wouldn't let you save settings unless you entered password again (should only be requied when
changing password)

- Bugfix: Default Account "Expiry Date" field year range now defaults to 5 years before/ahead of current date, not
2008-2016

- Bugfix: Background Tasks cron.php script will not dispatch any tasks unless software is installed.

- Bugfix: saveSettings() won't allow settings to be saved from command-line scripts until software has been installed
from web

- Compatibility: Rewrote code that could not be uploaded to some GoDaddy FTP servers due to false-positive from
server-side virus scanner.

- Compatibility: Fixed a problem with getPrevAndNextRecords() which caused it not to function correctly on some MySQL
servers.

- Compatibility: Added improved detection for "Suhosin PHP patch" to Admin > General to help debugging of issues caused
by Suhosin

- Compatibility: Improved outbound network connectivity test that is run on install to detect common network
misconfigurations and provide tips.

- Plugins: Added new filters: showUploadPreview_html

- Misc Code and other minor improvements


VERSION 2.61 - FEBRUARY 19, 2014 - (SECURITY UPDATE) - Aug 30th, 2014

SECURITY UPDATE

- Update MySQL library to address security vulnerability introduced in v2.60


VERSION 2.60 - FEBRUARY 13, 2014 - (FIELD EDITOR ENHANCEMENTS) - May 2nd, 2014

NEW FEATURES

- Field Editor: MySQL indexes can be added with a checkbox setting to speed up searches and sorting

- Field Editor: All MySQL Indexes for a table show are now shown under: Field Editor > Advanced (tab)

- Field Editor: Select from five different column types (or enter a custom type) to optimize data storage in MySQL

- Field Editor: MySQL column type and index status is now shown on field list page (for advanced users)

- CMS & Viewer Search: Added support for multi-value OR searches by adding [] after fieldname (see docs & section editor
search tab)



MINOR CHANGES

- Updated TinyMCE spellchecker plugin to use Google's new JSON API

- Editors: "Created By" users pulldown field is now sorted by username

- Languages: Updated Hebrew language translation file



CODE CHANGES & BUG FIXES

- Code: Upgraded to jQuery 1.11.0 loading from jQuery CDN servers for speed (with local fallback)

- Code: Upgraded to jQuery UI 1.10.4 loading from jQuery CDN servers for speed (with local fallback)

- Code: Upgraded to TinyMCE 3.5.10 (Released: 2013-10-24) - fixes some IE11 issues

- Admin Settings: Fixed error with default values "Upload Folder Url must start with a slash! (eg: /uploads/)"

- Admin Settings: Program Url is now automatically set if it is blank

- Admin Backup: These log tables are now skipped during backup: _outgoing_mail, _cron_log (can be overridden with plugin
filter backupDatabase_skippedTables)

- Category Menus: Added CSS classes to rows: category_row, category_num_#, category_parent_#, category_depth_#,
category_lineage#

- CMS Menus: Image preview thumbnails are now limited in height so extremely tall images don't make page too long

- CMS Menus: Dates on list pages now display in whatever format is specified under: Admin > General > Regional Settings

- CMS Preview: Fixed issue where preview button would appear if "Website Prefix URL" was set but preview url was not.

- Code Generator: Removed HTML comment tags that were confusing some users in default code generated for uploads

- Compatibility: Updated workaround to jquery.ajax for hosts that incorrectly send "0" when no output should be returned

- Compatibility: Fixed PHP 5.5 warning: Deprecated: preg_replace(): The /e modifier is deprecated

- Editor Fields: tip for uploading multiple files no longer appears for upload fields which only allow one upload

- Email Templates: Wysiwyg now displays and saves full links including hostname

- Install: Admin accounts created during install now have their passwords encrypted properly (fixes a problem with
Website Membership login)

- Images: Using new faster image resizing code by default

- Outgoing Mail: Wysiwyg now displays and saves full links including hostname

- Plugins: "System Plugins" are no longer loaded if CMS version is lower than required version

- Plugins: Added new filters: upload_uploadDir, upload_uploadUrl, listRow_trClass

- Plugins: Added new actions: upload_dirAndUrl

- Section Editors: Fixed error when saving single record sections: "Notice: Undefined index:  _listPage ..."

- Security: Disabled autocomplete on login username and password fields so sensitive data won't be remembered in browser

- Usability: Admin script now attempts to automatically detect when whitespace added during uploading might cause
errors.

- Viewers: whereRecordNumberInUrl() now ignores Google AdWords auto-tagging values (gclid) when determining record
numbers

- WYSIWYG: Updated Google Spellcheck to use new JSON API so it works again (XML API was discontinued)

- Misc Code and other minor improvements


VERSION 2.53 - APRIL 4, 2013 - (DEVELOPMENT SERVER ENHANCEMENTS) - May 2nd, 2014

SERVER REQUIREMENTS: Since v2.50 the requirements are: PHP 5.2.4+ and MySQL 5.0+

NEW FEATURES

- Development Servers: Added install option to create separate settings files for development domains for easier
migrations

- Development Servers: Added install option to support "prefix urls" such as /~username or /development/client-name for
easier migrations

- Development Servers: Added new "Prefix Url" field under: Admin > General Settings > Directories & URLs

- Programmers: New /plugins/cron-example.php script shows how to run tasks at intervals such as once a day or once an
minute.

CODE CHANGES & BUG FIXES

- Section Editors: Viewer Urls must now all start with a /

- Languages: New "Developer Mode" under Admin > General now works when "Default" is selected as a language.

- Custom Upload Folders: Fixed "directory doesn't exist" error on CMS edit pages

- Misc Code and other minor improvements



Here are some notes from Dave Edis that might prove helpful:

We've just released CMSB v2.53 beta 1  (beta list members will get an email with a download link shortly). 

The major new features are easier server moves and a sample cron plugin.  Here's some details: 

Development Servers

This update is mostly about development servers (also called staging servers) and making it easier to move files between
development and live servers.  If you're not already using a development server you should set one up, you'll save a lot
of time not having to upload files after every change to see the result, and you will avoid breaking your clients live
sites while developing features. We use the free package from http://www.wampserver.com/ on a Windows PC.  It's a combo
Apache, MySQL, PHP package that installs in one click.  Mac users can google for "MAMP" and if you're running Linux, you
probably don't need any tips setting up a web server. ;) 

Domain-based Settings Files

You've probably already found that sometimes you'll have to maintain files on a few servers while developing a site. 
You might develop on your internal server: http://yourBigClient.yourWebCo.com/, then have to upload to the clients beta
site:  http://beta.yourBigClient.com/, then finally upload to their live site when you're done:
http://www.yourBigClient.com/.  Normally you can just copy files from one server to another and everything will just
work.  But if these sites have different MySQL details (as they often do), and you copy a settings file from one site to
another you'll take down the whole site with an "Invalid MySQL Password" error.

The fix?  We've added support for domain-based settings files so you can create a custom settings file that specifies
the domain name like this: /data/settings.yourbigclient.yourwebco.com.php and it will automatically use that settings
file for that domain only.  This ensures you never overwrite live-server settings with dev-server settings (since they
are stored in a separate file) and you can copy files from server to server without fear.  We actually added this
feature over 3 years ago in v2.02 but many people still don't use it (even some who work at interactivetools!) so for
this version we added a checkbox to the install screen for development servers.  It looks like this: 

Use Custom Settings File [X] For this domain name only (localhost) use this setting file: /data/settings.localhost.php.
Using a separate settings file for development servers ensures you never accidentally overwrite your live server
settings when uploading CMS /data/ files. Always use custom settings files for development servers only, not your live
servers.

So if you're running on a non-live server (development, staging, beta, etc) make sure to check that box and you'll be
much safer.

Website Prefix URL

Next up, the other thing we see a lot of is development servers that host websites under existing sub-folders.  This
could look like any of the following: 

    http://yourWebCo.com/clients/2013/yourBigClient/ - developing client sites in a subfolder under your own website
    http://localhost/clients/2013/yourBigClient/ - developing client sites on your own PC or an internal development
server
    http://yourBigClient.com/beta2013/ - when your client has setup a beta folder for the new site
    http://1.1.1.1/~bigclie/ - When DNS hasn't moved yet and you have to use a /~username/ preview url

If you've ever had to work with a site like this, you might have found yourself hardcoding /~bigclie/ or a similar
folder prefix into the HTML, only to have to go back later and remove it when you moved the files to the live server
that didn't need that prefix.

Our fix in 2.53 for this is a new field available on both the install page and under: Admin > General.  It looks like
this: 

Website Prefix URL [________] eg: /~username or /development/client-name
If your development server uses a different URL prefix than your live server you can specify it here. This prefix can be
changed in the Admin Menu and will be automatically added to Viewer URLs and can be displayed with <?php echo PREFIX_URL
?>. This will allow you to easily move files between a development and live server, even if they have different URL
prefixes.

So if you've ever been stuck search and replacing URL prefixes before, now you can just use the above feature.  

Example Cron Plugin (for programmers)

For the programmers, we've included a sample plugin in /plugins/cron-example.php that shows how to run a background task
at a set interval such as once a day or once an minute.  This is really useful if you need to build a custom site that
sends out email updates once a night, expires auctions, imports or backs up data, sends out reminders or marketing
emails once a month, etc.

We've built cron support into CMS Builder now, so it's way easier and more powerful then the standard cron/scheduled
task features you get with hosting accounts.  CMSB automatically shows you when all the scheduled tasks last run, when
they're scheduled to run next, keeps a log of every task, and even records errors (even if your script/plugin dies or
crashes) and emails you to tell you when there is a problem.  And all you need to do to set it up is add a single line
of code to your plugin telling it what function you want to run and how often.  Like this:
addCronJob('emailSubscribers', "Send daily email updates", "how often to run");  

Try setting up cron and enabling the example plugin.  You can find the Background Tasks menu here with instructions:
admin.php?menu=admin&action=general#background-tasks


VERSION 3.53- AUGUST 12, 2019 (MAINTENANCE RELEASE) - Aug 12th, 2019

BUG FIXES
- Allowed editing fields that start with an underscore.
- Fixed issue with WYSIWYG uploads when in demo mode.
- Fixed issue stopping upgrade when completing successfully. 


VERSION 2.52 - MARCH 18, 2013 - (SECURITY) - Aug 5th, 2013





NEW FEATURES

- Sending Mail: Added support for SMTP authentication, unsecured, SSL secured, and TLS secured mail servers.

- Security: Added blacklist of top 10,000 common passwords.  Found in /lib/login_password_blacklist.txt

MINOR CHANGES

- Programmers: Added "Export showMessage() PHP" option to Email Templates menu under Advanced Commands

- Programmers: sendMessage() now logs message headers

- Programmers: Added new function nullIfFalse()

- Viewers: getLastNumberInUrl() now automatically ignores facebook added url data, eg: fb_source, fb_action_ids, etc

- Usability: Whitespace is removed from beginning and end of username and password on login (to prevent copy and paste
errors)

- Usability: Passwords that start or end with whitespace are no longer allowed.

- Security: Added plugin filter login_newPasswordErrors to allow custom validation rules for new passwords

- Plugins: Section Editors > Advanced (tab) > Required Plugins, now only requires plugin filenames match (in case users
rename plugin folders)

- Editors: Checkbox field descriptions can now contain PHP code

CODE CHANGES & BUG FIXES

- Sending Mail: Added Swift Mailer v4.30 - Mailing library

- PHP Sessions: Added workaround to prevent PHP from creating 0-byte files in session folder

- CMS Preview: Fixed error "You don't have permissions to access this menu."

- Fixed loading problem when there were more than two chained selects on a section edit page.

- Fixed date field year range bug with modifying blank dates

- View Menus: Fixed bug where list values showed instead of labels when loading from another table

- Previously saved search keywords and sort orders are cleared on CMS logout

- Misc Code and other minor improvements


VERSION 2.51 - FEBRUARY 7, 2013 - (SECURITY & MAINTENANCE UPDATES) - Aug 5th, 2013

NEW FEATURES

- Login Sessions: Login Timeout can be set to any duration under: Admin > General > Advanced

- Login Sessions: Switched Logins to use cookies instead of PHP sessions to prevent early expiry

- Security: Added option to restrict CMS access by IP address to: Admin > General > Advanced

- Security: Updated TinyMCE WYSIWYG Spellchecker to v2.0.6.1 (Released 2012-11-20 - Security Update)

MINOR CHANGES

- WYSIWYG: Updated TinyMCE to v3.5.8 (Released 2012-11-20)

- Background Tasks: Added log search field "Completed: Any/Yes/No" for finding failed tasks

- Background Tasks: Failed tasks now send alert email to admin (max one email per hour)

- Email Templates: Reformatted list view to show all message details

- Email Messages: Automatically set 'Reply-To' to 'From' address (required by some email clients)

- Programmers: Added htmlPurify() function and HTML Purify library for filtering HTML

- Programmers: Added new function emailTemplate_addToDB() for adding email templates

- Programmers: Added "Export templates as PHP" option to Email Templates menu under Advanced Commands

CODE CHANGES & BUG FIXES

- Fixed file permissions error which reported "rename return value not boolean true or false"

- Misc Code and other minor improvements


VERSION 2.50 - DECEMBER 5, 2012 - (LANGUAGE & TRANSLATION FEATURES) - Aug 5th, 2013

****SERVER REQUIREMENTS HAVE CHANGED****

- The requirements for version 2.50 and later are: PHP 5.2.4+ and MySQL 5.0+

- For older servers use version 2.17 which requires: PHP 4.3.2+ and MySQL 4.1.7+

NEW FEATURES

- Languages: Updated translation system and added more translatable text

- Languages: Plugin system now support translation files stored in /languages/ under each plugins folder

- Languages: CMS Admin menus now support translation files stored in /lib/languages/adminMenu/

- Languages: Updated "/lib/how_to_add_languages.txt" file includes instructions for programmers on adding new language
strings

- Languages: New "Developer Mode" under Admin > General automatically adds language strings to files as programmers
code.

- Languages: New debug language "d|e|b|u|g" to help translators identify text that hasn't been added to translation
system yet.

- Email Templates: Added new admin editor menu for CMS Password Reset email, and "email templates" that may be used by
plugins.

- Background Tasks: Added new generic system running scheduled tasks/cronjobs with cron.php script for plugins

- favicon.ico: Add a favicon.ico file to the cms admin folder and it will be displayed by the browser when using the cms

MINOR CHANGES

- Wysiwyg: <style> tags are now supported within body content (required to style HTML emails).

- CMS Editor lists: Alternate account fields can be displayed with either: createdBy.username or
otherAccountTable.username

CODE CHANGES & BUG FIXES

- Increased version to 2.50 to help identify versions with increased requirements (v2.5+ requires PHP/MySQL 5)

- Performance: Increased minimum PHP required to 5.2.4 (over 5 years old)

- Performance: Increased minimum MySQL required to 5.0.0 (over 8 years old)

- CMS List Search: Fixed issue where custom search fields sometimes wouldn't work (removed extra return chats from
listPageSearchFields)

- Fixed Windows PHP "File exists" error that sometimes occurred when when renaming files

- Fixed issue where .swf files were being rejected as invalid when PHP returned a .swc file type

- Programmers: Added new functions: array_keys_prefix, contains, getValidationErrors, rename_winsafe,
file_put_contents_atomic

- Login Code: Failed logins no longer erase PHP $_SESSION, now only the login data is removed.

- Editors: Updated dates are now shown on add and edit pages even if updatedByUserNum field isn't defined

- Settings: Custom settings files such as: /data/settings.example.com.php no longer require www. prefix

- Code: prettyDate() function now accepts "0000-00-00 00:00:00" and returns "never"

- Code: emailTemplate_loadFromDB & emailTemplate_replacePlaceholders() no longer double encodes HTML placeholders

- Code: login functions now automatically encrypt unencrypted passwords on failed front-end logins

- Misc Code and other minor improvements


VERSION 2.17 - SEPTEMBER 1, 2012 - (MAINTENANCE RELEASE) - Aug 5th, 2013

NEW FEATURES

- Section Editors: You can now set the default records to show "Per Page" under the Advanced Tab

MINOR CHANGES

- Added utility function getCurrentUserFromCMS() to allow command-line scripts to easily check which user is logged in

CODE CHANGES & BUG FIXES

- Fixed issue were message encoding caused some email readers to display HTML sourcecode instead of proper message

- Fixed error when using viewers from command-line or cronjobs "Notice: Undefined index: SCRIPT_NAME"

- Fixed error when using viewers from command-line or cronjobs "Notice: Undefined index: HTTP_HOST"

- Misc Code and other minor improvements


VERSION 2.16 - AUGUST 28, 2012 (SPEED, MAIL, AND USABILITY IMPROVEMENTS) - Aug 29th, 2012

NEW FEATURES
- Speed: Improved program speed by up to 60%
- Mail: Logging of outgoing mail now supported under: Admin > General Settings > Email Settings
- Sorting: Category menus can now be re-ordered with click and drag sorting
MINOR CHANGES
- Plugin filters: Added filters for: userSectionAccess and userHasFieldAccess for overriding access levels
- Plugin Actions: Added actions for: list_postAdvancedSearch for outputting code directly after the advanced search
fields
- Compatibility: Fixed PHP 5.4.0+ warning "Function get_magic_quotes_gpc() is deprecated"
- Compatibility: Added upload error for images saved with CMYK palette since not all browses can display them
- Compatibility: Set PHP arg_separator.output value to & so PHP http_build_query() function always works as expected
- Compatibility: Added workaround for Chrome browser bug when uploading "User Agent must be Flash, not..."
- Performance: Program data files (under /data/ folder) now use a new faster loading data format
- Performance: Plugins loading code is now faster and more efficient
- Programmers: isValidEmail() function now returns an array of matched emails instead of just true
- Programmers: Added functions: mysql_get_query, mysql_select_query, emailTemplate_loadFromDB,
emailTemplate_replacePlaceholders, sendBackground
- Field Editor: Date fields with blank start or end year range values now default to 5 years before and after current
year
CODE CHANGES & BUG FIXES
- Code: Added alias function mysql_escapeCSV()
- Code: sendMessage() encodes message HTML as quoted-printable so they're easier to read when debugging (for
programmers)
- Code: Updated sendMessage so iphone mail app could view messages with combined text/html parts correctly
- Fixed preview error "Preview isn't supported when website membership uses separate login or alternate table name."
- Fixed bug where Related Records field could cause fields below it on edit page to error or not to display correctly
- Changed internal template tags used by code generator to avoid conflicts when PHP asp_tags setting is enabled
- Fixed problems sending to emails which contained underscores
- Misc Code and other minor improvements


VERSION 2.15 - MAY 7, 2012 (NEW CODE GENERATOR) - Jun 12th, 2012

NEW FEATURES
- Code Generator: Completely re-written code generator
- Code Generator: New code generator menus for: Combo Page, and RSS feeds
- Code Generator: New addGenerator() function for adding custom generators through plugins
- WYSIWYG: Updated TinyMCE to v3.5b3 (Released 2012-29-03) (Fixes Firefox 11 issues)
MINOR CHANGES
- Uploads: Upload file names are now saved in lower case for better cross platform compatibility
- Uploads: When an upload filename already exits and ends in a number the number is now incremented to find a unique
filename
- Editors: When admins change the "Created By" user, now only users with access to the section are shown in the pulldown
- Editors: Datepicker icon is now supported for edit search fields that use dates
- Compatibility: Added whitespace to uploadForm_functions.php to avoid a Network Solutions FTP uploading issue
- Compatibility: Spellchecking is now properly disabled on appropriate fields for Firefox 11 (jQuery Bug #6548)
- Docs: Improved install and upgrade docs and added a page on moving servers (online docs)
- Docs: Improved the configuration help messages shown on the install menu.
- Fields: Related Records now support drag-sorting with the custom flag in settings.dat.php under [advanced]:
allowRelatedRecordsDragSorting = 1
- Fields: Related Records now support adding a "Create" button through the field editor
- Admin: Removed version number from dashboard/home page
- Code Generator: Image tags generated with double-quotes for consistency
- Code Generator: Added table name to comment above call to getRecords()
CODE CHANGES & BUG FIXES
- Code: Upgraded to jQuery UI 1.8.18
- Code: pluginAction_addHandlerAndLink() and pluginAction_addHandler() now support specifying access levels with a
string
- Code: Plugin filter list_buttonsRight() now passed 3rd argument $isRelatedTable and allows adding buttons to related
tables
- Code: user_createLoginSession() no longer requires password as a second argument
- Code: Added alias function jsEncode()
- Code: Added plugin filter login_isValidLogin to allow custom authentication
- Code: Added plugin filter edit_show_upload_link to allow overriding of the "Add or Upload File(s)" link
- Fixed loading problem when there were more than two chained selects on a section edit page.
- Fixed tiny_mce compression issue when a server uses ob_gzhandler as their output handler
- Fixed loaded records' _tableName key to be without prefix
- Fixed getRecords' firstPageUrl to respect extra path or query info
- Fixed default search functionality for fields ending in _year, _month, and _day


VERSION 2.14 - DECEMBER 27, 2011 (MAINTENANCE RELEASE) - Jan 28th, 2012

NEW FEATURES
- Datepicker: You can enable a datepicker icon and popup calendar under: Admin > General Settings > Advanced Settings
MINOR CHANGES
- WYSIWYG: "Paste as Text" button now defaults to on (and can be clicked to turn off)
- WYSIWYG: Added buttons for superscript, subscript, charmap, and "visual aid" (to hide/show hidden table borders)
- WYSIWYG: Old tinymce cache files are automatically removed on upgrade to prevent errors
- Usability: Command line scripts now always return text (not HTML) for MySQL errors
- Programmers: getSelectOptionsFromTable() now returns values in schema sort order
- Programmers: Moved all mysql_* utility functions into /lib/mysql_functions.lib
- Languages: Added more text to language translation files.
BUG FIXES
- Compatibility: Added workaround to jquery.ajax for hosts that incorrectly send "0" when no output should be returned
- Fixed error with some PHP versions "Notice: Only variable references should be returned by reference"
- Fixed Advanced menu commands on edit page, commands now direct to the correct page.
- Code: getRecordsCustom() now returns the last pageNum with content if pageNum is higher than totalPages.
- My Account: Fixed issue where users couldn't drag-sort images if they didn't have access to the accounts table
- Misc Code and other minor improvements


SMALL BUG IN 2.13 - Nov 11th, 2011

Steve from MustBeOnline.net found this one, and Damon Edis from Interactive Tools offered a temporary fix until the next
version is released.

In a single record editor only, when an admin tries to access the Code Generator using the Advanced Options drop down
menu at the bottom of the record, this bug sends you into the General Settings area instead. 

This doesn't seem to affect multi-record editors and there are no issues with going straight to the Code Generator from
the Admin Section Editors area.

Here's the fix:

in the /cmsAdmin/lib/menus/default/edit.php file

On line 91 and 92 replace this:

 
      if ($CURRENT_USER['isAdmin']) { $advancedCommands['Admin: Edit Section']   =
'?menu=database&amp;action=editTable&amp;tableName=' . urlencode($tableName); }
      if ($CURRENT_USER['isAdmin']) { $advancedCommands['Admin: Code Generator'] =
'?menu=admin&amp;action=generator&amp;tableName='    . urlencode($tableName); }


with this:



if ($CURRENT_USER['isAdmin']) { $advancedCommands['Admin: Edit Section'] = '?menu=database&action=editTable&tableName='
. urlencode($tableName); }
if ($CURRENT_USER['isAdmin']) { $advancedCommands['Admin:: Code Generator'] = '?menu=admin&action=generator&tableName='
. urlencode($tableName); }


Basically it is replacing "&amp;" with just "&".

Thank you Steve


VERSION 2.13 - OCTOBER 27, 2011 (MAINTENANCE RELEASE) - Nov 11th, 2011

NEW FEATURES
- WYSIWYG: Updated TinyMCE to v3.4.6 (Released 2011-09-29)
- WYSIWYG: Latest TinyMCE includes support for iOS 5 devices (iPad, iPhone, etc)
MINOR CHANGES
- Editors: Date field year menu range now expands if needed for the record data.
- Plugins: Added new filter: edit_advancedCommands
- Code: Plugins can now override accounts tablename by calling accountsTable()
- Code: Plugins can now override session cookie name by setting $GLOBALS['SESSION_NAME_SUFFIX']
- Code: isValidEmail() now supports emails with display names and validation of multiple emails
- Programmers: Added new mysql helper function mysql_fetch()
- Programmers: Loaded records now contain a _tableName key
- Programmers: Added row numbering to Plugin Hook List
BUG FIXES
- sendMessage: $php_errormsg now cleared before mail() to prevent "undefined index: disabled" errors
- users can no longer accidentally delete their own account with the "Erase Selected" feature
- Fixed Plugin Hook List bug where actions and filters would be confused.
- Updated getListRows() to support relative upload paths
- Misc Code and other minor improvements


VERSION 2.12 - AUGUST, 26, 2011 - Oct 15th, 2011

NEW FEATURES
- Text Link Sections: Added new option to open links in "inline iframe" to integrate content from external sources
- Text Link Sections: Added new option to display message when opening or displaying links

MINOR CHANGES & BUG FIXES
- Admin General: Added "Program Url" field so plugins can easily find URL of admin script
- Code Generator: Single Record Sections now automatically ignore search queries (caused errors when using includes)
- Plugins: Added new filters: listHeader_checkAll and listPage_footer
- Admin General: Supressed error message that sometimes appeared on windows servers "not recognized as an internal or
external command"
- Compatibility: Fixed document root check to deal with restrictive open_basedirs
- Accounts: Changing your password no longer logs you out when password encryption is on
- Uploads: Updated upload display code that caused error when using parent path references: failed to get realpath of:
../
- Plugins: "Developer's Plugin Hook List" list no longer excludes hooks called in double-quoted strings
- Editors: "Max Length" option no longer counts  tag length towards character limit for textboxes
- Editors: Fixed bug in related records that showed incorrect output when viewing user is not an administrator.
- Programmers: Added new viewer function getRecordsCustom() for getting records with custom SQL queries
- Programmers: Added new function: getAbsolutePath(), fixSlashes()
- Programmers: Updated getPluginPathAndUrl() to automatically return the last plugin called from the call stack
- Programmers: Updated pluginAction_addHandler() to support multiple access levels and allow calls from viewer pages
- Programmers: Updated prettyDate() to respects timezones offsets set in Admin menu
- Programmers: Updated sedepndMessage() to support 'disabled' option selectively not sending messages
- Programmers: Updated realUrl() to url-encode spaces
- Programmers: Updated pluginAction_addHandler() to be case-insensitive when comparing function names (so we can use
__FUNCTION__)
- Uploads: Uploads will stay with a table after a table is renamed
- Misc Code and other minor improvements


VERSION 2.11 - JULY 7, 2011 (MAINTENANCE RELEASE) - Aug 24th, 2011

- Updated upload display code that caused error: failed to get realpath of: ./
- Updated .htaccess to remove debug code that caused error 500 message on some servers
- Added index.php redirect to /cmsAdmin/ for web servers that don't recognize index.html
- When selecting backup files, more recent files will sort to the top of the list
- Misc Code and other minor improvements


VERSION 2.10 - JUNE 30, 2011 (FASTER SERVER MOVES) - Aug 24th, 2011

NEW FEATURES
- Server Moves: You can now re-install the software by erasing /data/isInstalled.php
- Server Moves: You can now restore from a backup file on the install screen
MINOR CHANGES
- Viewers: Added _isSiblingSelected, _isParentSelected, and _isChildSelected to getCategories() viewer function output
- Plugins: Added new filter: login_content for login page content
- Plugins: Added new filter: backupDatabase_skippedTables (to prevent certain tables from being backed up)
- Plugins: Added new filter: edit_fieldSchema
- Plugins: Added plugin helper functions: plugin_header, plugin_footer, plugin_createSchemas
- Plugins: Added plugin helper functions: pluginAction_getLink, pluginAction_addHandler, pluginAction_addHandlerAndLink
- Programmers: Added function 'prettyDate' to output human readable style relative dates such as: 29 minutes ago
- Programmers: mysql_count() now accepts WHERE queries as arrays
- Programmers: Added helper PHP functions: startsWith, endsWith
- Programmers: Added helper Javascript function: sprintf
BUG FIXES
- Passwords: Fixed issue where users could login with their encrypted password string (actual text password now
required)
- Field Editor: Fixed issue where recreating thumbnails would keep repeating errors if height or width was blank
- Editor Fields: Fixed issue where the default value of a date field would not be set if the user did not have edit
access to it.
- Upload Urls: Upload fields with custom paths/urls no longer add the current hostname to generated urls in viewers
- Misc Code and other minor improvements


VERSION 2.09 - MAY 31, 2011 (RELATIVE UPLOAD PATHS) - Aug 24th, 2011

NEW FEATURES
- Uploads: Upload paths are now stored as relative to make server moves easier
- Uploads: Existing uploads path will automatically be made relative where possible.
- Section Editor: The "Add New Editor..." button now lets you copy an existing section (without the data)
MINOR CHANGES
- Code Changes: Removed unused function from /lib/common.php - array_change_value_case()
- Plugin Developers: All plugin hooks can now be listed with "show plugin hooks" link on plugins page
- Programmers: Added plugin hooks for: ui_header, ui_footer, list_orderBy
- Programmers: Added custom functions: isAbsoluteUrl, realUrl, array_value, array_where, dieWith404
- Developers: Added help file on setting up development settings files: /data/how_to_use_settings_files.txt
- WYSIWYG: Added support for /lib/wysiwyg_custom.css file that won't get overwritten during upgrades
- WYSIWYG: Once clicked, "Paste as Text" now stays active until clicked again (instead of deselecting after paste)
BUG FIXES
- Fixed bug from 2.07 where renaming a section displayed an error.
- Fixed demo mode bug where demo couldn't be created if tablenames exceeded 64 characters
- whereRecordNumberInUrl() and getLastNumberInUrl() now ignore page numbers such as viewer.php?title-123&page=2
- Admin Option "Store encrypted passwords in database (can not be disabled)" can no longer be disabled.
- Misc Code and other minor improvements


VERSION 2.08 - APRIL 20, 2011 (SECURITY ENHANCEMENTS) - May 5th, 2011

NOTE:

THERE WAS A NOTE ABOUT A CONFLICT WHEN USING ENCRYPTED PASSWORDS WITH THE MEMBERSHIP PLUGIN. 

IT READS: 

Website Membership does not currently work with Password Encryption. If you are using Website Membership, do not enable
Password Encryption just yet; we're working on a new version of Website Membership which will be available in a couple
of days. 

CHECK THIS POST FOR THE LATEST ON THIS ISSUE:
_

 http://www.interactivetools.com/forum/gforum.cgi?post=87642#87642


NEW FEATURES
- Admin General: Added "Encrypt Password" option to encrypt database user passwords
- Admin General: Added "Require HTTPS" option to only allow logins through secure connections
- User Accounts: Added new field "Last Login Date" that shows last date user accessed system
- User Accounts: Password reminders have been replaced with a password reset form
- Editors: Fields can now be set to "Editor only" or "Admin only"

MINOR CHANGES
- Uploads: Added a "Modify All Uploads" link to the Modify Upload popup
- Viewer Functions: getCategories() now supports 'ignoreHidden' option
- WYSIWYG: Updated TinyMCE to v3.4.1 (Released 2011-03-24)
- WYSIWYG: Updated TinyMCE Compressor to v2.04 (Released 2011-03-23)
- License: updated to allow sourcecode modifications, register install path, and allow staging installs (sections 3.4
(removed), 4.4, 7)
- Programmers: Added database functions: mysql_datetime, mysql_getValuesAsCSV, mysql_get, mysql_select, mysql_delete,
mysql_update, mysql_insert
- Programmers: Added custom functions: array_groupBy, isHTTPS, coalesce
- Programmers: Added new plugin hooks: plugin_activate, plugin_deactivate, list_postselect, menulinks_array
- Section Editor: New sections' upload and wysiwyg fields' allowedExtensions are now consistent and include PDF
- Languages: The Datepicker is now automatically translated

BUG FIXES
- Related Records: More link now automatically ignores previous searches when linked to related sections
- Admin Menu: Fixed bug where view only menus wouldn't show up if selected menu had "Disable View" selected
- Editor List: Accounts search form, fixed MySQL Error: Unknown column 'accessList' in 'where clause'
- Preview: Fixed preview error: Notice: Undefined index: hasEditorAccess
- Misc Code and other minor improvements


VERSION 2.07 - FEBRUARY 3, 2011 (PREVIEW BUTTON) - Feb 19th, 2011

New Features:

    * Preview button: This useful feature lets you see changes to your detail page before saving or publishing.
    * New access levels: The new ‘Author and Viewer’ access level allows you to view all records, while only make
modifications to your own.
    * Editor List: Simplify searching by either resetting or ignoring previous search options.
    * Editors: A new date picker popup has been added for use with date fields.
    * Viewers: An automatic search suffix has been created to match empty fields.

Other Changes:

    * The backup database now allows you to backup and restore individual tables.
    * Missing upload directories are automatically created, if possible.
    * Additional text has been added to the language translation files.
    * An ‘Editor’ link has been added to the section edit menu.
    * Set menu text links to open in the same tab.
    * Several new filters have been added to the plugin system.
    * Plugins that require newer versions of CMSB are now automatically deactivated.
    * The WYSIWYG has been upgraded to tinyMCE version 3.3.9.2.


VERSION 2.06 - SEPTEMBER 9, 2010 (WYSIWYG BLOCKQUOTE BUTTON) - Sep 15th, 2010

MAINTENANCE RELEASE

Minor Changes

    * Languages: Added more text to language translation files
    * Code: thisPageUrl() now lets you pass an array to insert/override query string values
    * Wysiwyg: Added blockquote button (between link and horizontal rule buttons)
    * Viewers: getPrevAndNextRecords() now supports 'where' option
    * Categories: Added advanced getCategories() option: ulAttributesCallback
    * Categories: Added advanced getCategories() option: liAttributesCallback
    * Plugin System: Added new filter "viewerOnly_allowed_actions" for adding to the "Advanced Commands" menu
    * Plugin System: Added utility functions: getPluginPathAndUrl() and createSchemaFromFile()

Bug Fixes

    * View Menus: Textboxes with autoformatting disabled now display line breaks correctly
    * View Menus: Fixed error "Couldn't find field class file"
    * View Menus: Users with only "viewer" menu access no longer see menu links that don't have "view" enabled
    * View Menus: Advanced Command "Erase Selected" is no longer displayed for users with only "viewer" access
    * Accounts Menu: Users with author access can no longer see the accounts menu list page
    * Viewers: Fixed MySQL error caused when using field_min or field_max searches with non-numeric values
    * Editors: Fixed error "Strict Standards: var: Deprecated" disabled by PHP 5.0.0 to 5.1.3
    * Editors: Fixed bug where blank dates on list page sometimes showed up as 1969-12-31
    * Admin > General: Fixed JSON errors when changing timezones on servers with PHP versions older than 5.2.0
    * Misc Code and other minor improvements


VERSION 2.05 - JULY 6, 2010 (VIEW MENUS) - Aug 6th, 2010

NEW FEATURES
- Added new "View" menu option to record lists (enabled under Admin > Section Editors)
- Added new "View" menu option to Related Records lists (enabled in Field Editor)
- Field Editor: List fields may now have a Default Value
- Category Sections: Added new setting: Section Editor > Advanced > Max level of depth for categories

MINOR CHANGES
- Plugin System: Added new filter edit_buttonsRight to add or modify buttons on edit page
- Plugin System: Added new filter view_buttonsRight to add or modify buttons on view page
- Plugin System: Added new filters: home_title, home_content for post-login homepage
- Plugin System: Added new filters: listHeader_thAttributes, viewer_output_rows
- Plugins: Added Norwegian characters to "Remove Viewer Link Accents" plugin
- Viewers: Added _hasSiblings to getCategories() viewer function output
- Code: Upgraded tinyMCE to version 3.3.7 (Released 2010-06-10)
- Code: Added internal function saveUploadFromFilepath() for importing local files
- Code: Improved DOCUMENT_ROOT auto-detection code
- Code: Wysiwyg now automatically generates new cache file when tiny_mce.js changes

BUG FIXES
- Compatibility: Added workaround for hosts with safe-mode set incorrectly (PHP Bug #23663)
- Wysiwyg Spellchecker: Fixed error "Please specify: spellchecker_rpc_url"
- Editors: Pressing Save after a login session has timed out now redirects to the login page
- Misc Code and other minor improvements


VERSION 2.04 - APRIL 27, 2010 - (VIEWER PSEUDO-FIELDS) - Aug 6th, 2010

This one’s been a while in the making. Check out the list of features and changes and you’ll see why.

NEW FEATURES
- Editor Search: You can now add custom search fields based on record owner with: Owner|createdBy.fullname
- Viewers: Added new option 'loadPseudoFields' for adding fields below (defaults to on).
- Viewers: Added pseudo-field *:unixtime for date fields with unix time of date (for passing to date()). Example:
$record['startdate:unixtime']
- Viewers: Added pseudo-field *:text for checkboxes that displays checked or unchecked text. Example:
$record['hidden:text']
- Viewers: Added pseudo-field *:label for single-value list fields that displays the label of the selected option.
Example: $record['category:label']
- Viewers: Added pseudo-field *:values for multi-value list fields with an array of values for the selected options.
Example: $record['categories:values']
- Viewers: Added pseudo-field *:labels for multi-value list fields with an array of labels for the selected options.
Example: $record['categories:labels']
- Wysiwyg: You can now insert any of the thumbnail sizes, not just the first one.

MINOR CHANGES
- Code Generator: Viewers now automatically locate CMS libraries, even if you've moved servers or filepaths have changed
- Code Generator: New "Expert mode" under: Admin > General for hiding instructions and extra html in output
- Viewers: Added _isFirstChild and _isLastChild to getCategories() viewer function output (named after similar CSS2
selectors)
- Viewers: _match searches on multi-value list fields now match single values as well (if field was converted from
single to multi-value)
- Admin > General: Time zones are now sorted by GMT offset and no longer include legacy timezone names from:
- Admin > Regional: Language settings can be hidden with hideLanguageSettings in settings file (for regional
distributors)
- Plugin System: All record save plugin hooks are now passed $oldRecord when updating an existing record
- Plugin System: 'section_init' is now passed the current $action
- Plugin System: 'record_postsave' is now passed record_num
- Plugin System: Added new hook for: upload_saved, upload_adopted
- Plugin System: Added new filter uploadModify_infoFieldHTML to modify how the upload info fields look
- Code: Added helper functions for external upload forms: removeUpload(), getUploadInfoArrays()
- Code: Added /3rdParty/ to the PHP include path so Zend library will work (for possible future use)
- Code: Upgraded tinyMCE to version 3.3.2 (Released 2010-03-25) and removed safari plugin (now built-in)
- Code: Upgraded to jQuery 1.4.1
- Code: Included JSON Javascript library


VERSION 2.03 - MARCH 1ST, 2010 - RELATED RECORDS - Aug 6th, 2010

NEW FEATURES
- New field "Related Records" lists records from related tables on edit page
- Custom JS: You can now define your own upgrade-safe Javascript by creating a file named: cmsAdmin/custom.js
MINOR CHANGES
- Plugin System: Added filters for: upload_saveAsFilename - Plugin System: Added filters for: listRow_trStyle,
listRow_tdAttributes, listRow_displayValue, listRow_actionLinks
- Plugin System: Plugins are now sorted alphabetically under: Admin > Plugins
- Field Editor: Added "Field Prefix" option that is displayed before or above most field types.
- Field Editor: Added "Description" option this is displayed after or below most field types.
- Docs: Added readme documents to program root folder: custom.css.readme and custom.js.readme
- Viewers: whereRecordNumberInUrl() now ignore campaign tracking utm_* values when determining record number
- Categories: Added _hasParent and _hasChild to getCategories() viewer function output
- Languages: Added french language file for WYSIWYG
- Install: Added php.ini to correct common server misconfigurations


VERSION 2.02 - RELEASED ON JANUARY 25,2010 - MULTI FILE UPLOADS - Aug 6th, 2010

The major new feature is a new flash based multi-file upload component. This makes it WAY easier to upload lots of files
at the same time. No more clicking "Browse..." for each file. There are a few other fun new features in this release as
well (see below).
- Uploads: You can now upload multiple files at once with uploadify flash upload component

This addition uses a jQuery component from uploadify.com. You can click “upload” and then select a dozen or more
files (either by holding shift or ctrl or by selecting a box with your mouse) and clicking upload. If some of your files
exceeded the maximum allowable file size, only those that are too big will throw an error message, the rest will be
uploaded to the server and will appear in the uploaded list.  

- Viewers: Sort ordering can now be defined in viewer URLs with orderBy=fieldname or fieldname DESC

This makes it very easy to sort a viewer list page multiple ways (e.g.: by price, product name, etc). We've written
custom code to do this many times, now you can just create a link to ?orderBy=price DESC to let the user sort viewer
lists the way they want.

- Viewers: min/max date searches now support date without time formatted as: YYYY-MM-DD

If you wanted to do date min/max range searches you previously had to enter a full MySQL format date like this:
?date_min=2010-01-01 00:00:00. Now you can just enter the date part and it figures out the rest (00:00:00 for _min
searches and 24:00:00 for _max searches).

- Settings: Custom settings files can be created for dev domains, such as: /data/settings.localhost.php
If you've ever had to keep a local staging/development environment in sync with a live server you've probably noticed
what a hassle it is to manage the /data/settings.dat.php file. You need different mysql details, upload paths, etc for
each environment.
Now CMSB will automatically check for a settings file with the current domain name in it and use that if it's available.
Just copy settings.dat.php and replace "dat" with your hostname or IP.

Note: When using this feature, Dave suggests that you always setup a new file for your dev environment and let
production/live use the default one. That way if a domain name is added or changed it won't break things on the live
site.

- Viewers: Added the following options to getRecords() for advanced MySQL users: 'groupBy' and 'having'

For advanced MySQL users (and to make forum support easier for us). If you know what "GROUP BY" and "HAVING" are, you
can now pass them to getRecords with the 'groupBy' and 'having' options.

- Viewers: Added utility function mysql_escapef(), example: mysql_escapef("num = ? AND name = ?", $num, $name);

This is another one for more advanced MySQL users. When writing custom MySQL you have to be careful to escape user input
to avoid "SQL Injection Attacks" (Google for details). Basically this means passing any input to
mysql_real_escape_string().

This can make simple queries like this:
'where' => "city = '$city' AND year = '$year' ",

Look a lot more complicated like this:

'where' => "city = '" .mysql_real_escape_string($city). "' AND year = '" .mysql_real_escape_string($year). "' ",

Dave Edis from Interactive Tools explained that mysql_escapef lets you write your query with a ? where each value goes,
then mysql_escapef  automatically escapes and inserts the values for you.

So now the query would be written this way:
'where' => mysql_escapef("city = ? AND year = ?", $city, $year),

- Viewers: getPrevAndNextRecords() now supports 'orderBy' option

The getPrevAndNextRecords() function has been optimized and an orderBy option has been added to it. If you'd like to
display links to the previous and next record on a detail page you can do that like this:

 list($prevRecord, $nextRecord, $firstRecord, $lastRecord) = getPrevAndNextRecords(array( 
   'Table name' => 'news', 
   'recordNum' => $record['num'], 
   'orderBy'   => 'createdDate', 
 ));

And you can display links to the previous and next record as simply as:
<?php echo $prevRecord['_link'?> or <?php echo $nextRecord['_link'?>

- Custom CSS:

 You can now define your own upgrade-safe CSS by creating
a file named: 
cmsAdmin/custom.css

According to Dave, “It's only loaded if it exists and it will be loaded _after_ all the rest of the CSS, making it
easy for you to override any CSS styling you want to change.”


VERSION 2.01 RELEASED ON DECEMBER 14, 2009 - DATE FIELDS REVISED - Aug 6th, 2010

The biggest changes are in the way Date fields are created:

Default date field values can now be: blank, current date, specified date, or values such as "+2 weeks" or "next
Thursday"

Short month names used in date pulldowns can now be updated in the language files in /lib/languages/

In addition, an advanced option 'addSelectExpr' has been added for generating pseudo-fields or calculated values.

A FEW THINGS TO KEEP IN MIND.
As of this writing, here are a few things that I found regarding the new Date Field modifications.
1) The display format of a "Date" type field shown in the ListPage Fields does not seem to follow the format set up in
General Settings. The format of a" Date" field within the record does, but the “Listpage Field” display doesn't.
2) Alpha characters seem to be allowed in a "Date" type field's "year range" field but give very strange results.
3) Blank publish_date and Remove_date fields can result in very unhappy consequences. 
_____
addSelectExpr
According to Dave:
What it does is insert the addSelectExpr value into a SELECT query here:
SELECT *, addSelectExpr FROM Table name
So if you had:
'addSelectExpr' => ' NOW() as _currentDate ',

It would add a field _currentDate to your $records like $records['_currentDate']. That's because NOW() is a mysql
function:

 http://dev.mysql.com/doc/refman/5.1/en/date-and-time-functions.html#function_now

Usually you'd use that when you had lots and lots of records and you wanted to sort them on a calculated value. Some
examples of this that I'm working on now are a custom search engine that returns records based on a "match score" from
0-100 that is calculated, and a distance search that returns results based on distance from the searching users location
and displayed that distance as well.

Some of the MySQL for this can be very complicated.

 (see: http://www.zcentric.com/blog/2007/03/calculate_distance_in_mysql_wi.html )

But as we do it more and more ourselves we'll work on ways to make it simpler and build it into the software or plugins.

If you have say 10,000 or more records you need to do these calculations and the sorting in the database because it's
not possible to do it in the application without loading all the records and that takes too much memory. 


VERSION 2.0 RELEASED ON DECEMBER 4, 2009 - A WHOLE NEW LOOK - Aug 6th, 2010

CMSB 2.0 has arrived, with a totally new look and some neat new features under the hood. If you haven’t upgraded your
client’s version in a while, this might be a really good time. Log on to

        http://www.interactivetools.com/upgrade/

 for  your copy of the latest version

The biggest obvious change is the look. Much sleeker and hipper than the old look. And the color scheme can be change by
the Admin form Blue to Green or red Just go to: 

Admin > General > Color / Theme

Along with the new look there’s some added functionality. For example:

Users with "Editor" access can now change owners of a record (also called "Created By" user) 
Text field descriptions can now contain PHP code 
An Advanced Command: "Admin: Code Generator" to jump to Code Generator 
The Password Reminder has added support for non-English characters in the subject line 
Plugins can now be organized in subdirectories under the /plugins/ directory 

One of my favorites is that Header separators now show the header message instead of "--- separator ---" in the field
list
 
The database down error page now sends a 503 "Service Temporarily Unavailable" so search engines will retry later 

One that you might want to make note of...

'page' is now a reserved fieldname, so check and rename any existing fields named  'page' to something else.
_


VERSIONS 1.35 AND 1.36 - Aug 6th, 2010

These releases consisted  mostly of bug fixes.


VERSION 1.34 RELEASED ON SEPTEMBER 8, 2009 BACKUP AND RESTORE) - Nov 11th, 2011

Backup & Restore - You can now backup & restore your MySQL database under: Admin > General Settings > Database Settings.
Backing up creates an .sql file in /data/backups/ and Restoring allows you to select a file from that directory to
restore. Backup your MySQL with another tool before testing this feature (or test it on a dev environment).
Note that to backup an entire site you'd want to first create a MySQL backup with the software, then download all the
files (including CMS Builder (with the backup file) and your uploads and viewer files).
New Menu Option - We added an option "Always Show Expanded Menu" under: Admin > General Settings > Advanced Settings. If
you don't have very many menu options and you want them to always show then you may prefer this.
Filepath changes - They've moved some things around. The only manual change you will have to make is to copy any custom
plugins from /lib/plugins/ to /plugins/ (and the program will remind you to do this if you forget). They've added a step
4 to the "how to upgrade.txt" that lists some files you can remove if desired:

4) Remove these files & folders if they exist: 
 /[cmsAdmin]/css/ui.css              (removed in v1.34 - moved to /[cmsAdmin]/style.css) 
/[cmsAdmin]/css/ui_ie6.css          (removed in v1.34 - moved to /[cmsAdmin]/style_ie6.css) 
/[cmsAdmin]/css/wysiwyg.css         (removed in v1.34 - moved to /[cmsAdmin]/lib/wysiwyg.css) 
/[cmsAdmin]/images/                 (removed in v1.34 - moved to /[cmsAdmin]/lib/images/) 
/[cmsAdmin]/tinymce3/               (removed in v1.34 - moved to /[cmsAdmin]/3rdParty/tiny_mce/) 
/[cmsAdmin]/lib/plugins/            (removed in v1.34 - moved to /[cmsAdmin]/plugins/ *MOVE PLUGINS BEFORE REMOVING*) 
/[cmsAdmin]/lib/menus/accounts/     (removed in v1.24 - no longer needed, merged with /lib/menus/default/) 
/[cmsAdmin]/tinyMCE/                (removed in v1.06 - moved to /[cmsAdmin]/tinymce3/)

Here's what's changed:
- WYSIWYG: Added Spellchecker button to WYSIWYG editor (experimental) See the note below for WAMP users
- Viewers: Added pseudo-field *:text for checkboxes that displays checked or unchecked text. Example:
 <?php echo $listingsRecord['your_checkbox_field:text'?>
- Special Fields: Creating 'showExpandedMenu' field in user accounts allows you to specify menu setting on a per user
basis
- Section Editors: Added new option "Required Plugins" to Advanced tab of "Section Editors"
- Added extra error checking on upgrade for user added wysiwyg_custom.php files with old WYSIWYG paths
- Backup filenames now start with current hostname and end with .php to prevent downloading and provide additional
security
- Backup files now ignore 'table prefix' when backing up and restore using the table prefix of the current CMS
- Section Editors Table List: Fixed left padding so menu names that wrap are indented as expected
- Admin Menu: The maximum number of connections MySQL allows is now displayed under: General Settings > Server Info
- Default Data Support: Files named data/schema/sectionName.defaultSqlData.php are imported when new sections are
created.
- Plugin System: Added actions for: admin_prelogin
- Section Editors: Fixed IE8 issue where Advanced Search/Simple Search links weren't working properly.
- Misc Changes
And more details on some of those items:
>WYSIWYG: Added Spellchecker button to WYSIWYG editor (experimental)
This will add a spellcheck button to the WYSIWYG. If you've setup a /lib/wysiwyg_custom.php file you'll need to update
it to include the new code from /lib/wysiwyg.php. Note: Spell checking requires some extra modules. Let me know if you
get any errors.
>Viewers: Added pseudo-field *:text for checkboxes that displays checked or unchecked text. Example:
$record['hidden:text']
This is useful if you want to display the checked/unchecked text for a checkbox instead of 0 or 1. Use
$record['yourField'] for if and comparison tests and $record['yourField:text'] when you want to display the text value.
>Backup filenames now start with current hostname and end with .sql.php to prevent downloading and provide additional
security
>Backup files now ignore 'table prefix' when backing up and restore using the table prefix of the current CMS
These changes are to make it easier to tell which backup files are from where and to move between CMSB installs that use
different table prefixes. Old .sql files can still be restored.
>Admin Menu: The maximum number of connections MySQL allows is now displayed under: General Settings > Server Info
This is the total number of simultaneous connections your MySQL server will accept. This will give you an idea of the
max capacity of your website.
>Default Data Support: Files named data/schema/sectionName.defaultSqlData.php are imported when new sections are
created.

This is so we can bundle data with schema and template files. To create one of these files just name it after your
section and copy the sql for that section from a backup file to a .defaultSqlData.php file. When the section gets
created for the first time (after install or uploading a new schema file) the sql will be imported as well.
_____
For anyone running WAMP on their local machine, to make the Spellchecker work, you just have to enable the PHP
extension: php_openssl

To do this:
1. click on the WAMP icon in your task bar
2. select PHP then PHP extensions
3. you should see a large list of extensions. Click php_openssl to enable it.
WAMP will reboot and the Spellchecker works! 


VERSION 1.33 RELEASED ON AUGUST 24, 2009 - Aug 6th, 2010

NEW FEATURES
- Menu Groups allow grouping, hiding and showing of related menu items***
- Menu Text Links allow you to add external links to the menu bar
- Menus can now be indented if desired: Admin > Section Editors > Section Name > Indent
MINOR CHANGES
- Admin: Removed admin 'home' page as it's no longer needed with new menu layout
- Admin: Regional Settings have now been moved to Admin > General
- Editors: Created and Updated dates now display in format from: Admin > General > Date Field Format
- Editors: Added "Disable Modify" option under section editors to prevent users from modifying records
- Accounts: Added "change" link to edit page so "Created By" user can now be changed by admins
- Plugin System: Added actions for: admin_preadd, admin_preedit, record_save_posterrorchecking, section_init,
admin_postlogin
- Plugin System: Added filters for: record_saved_message
Create a text link - Click Admin > Section Editors > Add New Editor. Then select: Advanced Menus > Text Link and enter
"Google" (or anything) for "Menu Name" and click Save. Then click modify on the new section and change the link url to
http://www.google.com/ (or whatever you link).
Create a menu group - Click Admin > Section Editors > Add New Editor. Then select: Advanced Menus > Menu Group and enter
"My Group" (or anything) for "Menu Name" and click Save.

You can drag your new group up and down the menu list. Any menu items under it will be hidden unless the menu group is
open. Try clicking on the menu groups and mousing off to see how they work.
Indenting a menu option - Just click modify from the Section Editor and then check "Indent on menubar" beside the menu
name. The checkbox shows for menu group sections as well but I'll remove it in beta2 as it doesn't apply.
Updating the menu colors & CSS - I've tried to make the CSS easier to custom if you want to change the menu colors for
menu groups, menu items, and indented menu items. See cmsAdmin/css/ui.css for more details.
- Added code to list admin menu at top - search for $showAdminAtTop in lib/menus/header_functions.php
- Editors: Added "Disable Modify" option under section editors to prevent users from modifying records
- Accounts: Added "change" link to edit page so "Created By" user can now be changed by admins
- Plugins: Added some more plugin hooks
- Plugins: Plugins now show filename if no plugin name is defined 
**** Don’t forget that access to menu groups is not automatically granted to all users. If you've granted access "Per
Section" to a user, the menu groups won't automatically get displayed for them. You’ll have to go back into their
account and grant them access to the menu groups.


VERSION 1.32 RELEASED ON AUGUST 4, 2009 - Aug 6th, 2010

NEW FEATURES
- Users can now modify their own account details (username, password, email, etc) with new "My Account" menu
- Field Editor: New advanced option for Accounts section 'My Account - Show this field in "My Account" section'
MINOR CHANGES
- Admin > General: "Free Disk Space" is now displayed on the bottom of the page
- Plugin System: Added hooks for: admin_preerase, admin_posterase
- Viewers: New option 'includeDisabledAccounts' shows records otherwise hidden with: Section Editors > Advanced >
Disabled Accounts
- Editors: Administrators can now access all menus by url, even if they haven't been given access under "User Accounts"
The main new feature in this release is a "My Account" menu (link from the top by Logoff). This menu lets CMS users edit
their username, password, email, and any other fields you choose (such as upload fields, WYSIWYG fields, etc).

This is useful to allow users to modify their own email or password, but can also be used to allow the user to easily
manage a profile or homepage that can be displayed on the website.

By default, if a section has a field called 'createdByUserNum' (which they all do) viewers will already automatically
make all the user account variables available as $record['createdBy.fullname'], $record['createdBy.email'], etc. So you
can now easily add fields to the accounts section, let the users edit them through the "My Account" menu, and display
them on the site.


VERSION 1.31 RELEASED ON JULY 6, 2009 - Aug 6th, 2010

NEW FEATURES
- Added advanced editor search and filter options (See: Admin > Section Editors > Searching)
MINOR CHANGES
- PHP sessions are now used instead of cookies to store admin search preferences and login credentials
- Viewers: New option 'ignoreHidden' allows you to display hidden records
- Viewers: New option 'ignorePublishDate' allows you to display records before their publish date
- Viewers: New option 'ignoreRemoveDate' allows you to display records after their remove date
- Viewers: Function getNumberFromEndOfUrl() now lets you specify a default num if no number found in url
- Category Viewer: Added new option 'defaultCategory' (enter 'first', category number, or blank '' for none)
- Category Viewer: Added new option 'rootCategoryNum' to show only part of the category tree (defaults to showing all)
- Category Viewer: Added new option 'ulAttributes' for adding attributes to generated ul tags
- Category Viewer: Added new category format 'breadcrumb' for displaying select menu path in breadcrumb format
- Category Viewer: Added new pseudofield _isBreadcrumb to make it easier to display breadcrumb menu for selected
category


VERSION 1.30 RELEASED JULY 3, 2009 (ADVANCED EDITOR SEARCH FILTERS) - Aug 6th, 2010

NEW FEATURES
- Added advanced editor search and filter options (See: Admin > Section Editors > Searching)

MINOR CHANGES
- PHP sessions are now used instead of cookies to store admin search preferences and login credentials
- Viewers: New option 'ignoreHidden' allows you to display hidden records
- Viewers: New option 'ignorePublishDate' allows you to display records before their publish date
- Viewers: New option 'ignoreRemoveDate' allows you to display records after their remove date
- Viewers: Function getNumberFromEndOfUrl() now lets you specify a default num if no number found in url
- Category Viewer: Added new option 'defaultCategory' (enter 'first', category number, or blank '' for none)
- Category Viewer: Added new option 'rootCategoryNum' to show only part of the category tree (defaults to showing all)
- Category Viewer: Added new option 'ulAttributes' for adding attributes to generated ul tags
- Category Viewer: Added new category format 'breadcrumb' for displaying select menu path in breadcrumb format
- Category Viewer: Added new pseudofield _isBreadcrumb to make it easier to display breadcrumb menu for selected
category

BUG FIXES
- Password reminder emails are not sent with UTF8 charset so international characters display properly
- Viewers: getListValues() and getListLabels() didn't work if the list field wasn't a multi-value field
- Viewers: getPrevAndNextRecords() fixed error message: "Undefined variable: errors"
- Viewers: getCategories() fixed error message: "Undefined offset: 0" when no category selected
- Editor List: After drag sorting any previously selected sorting columns are reset so new order shows as expected.
- User Accounts: Hidden menu sections are now visible in section access list


VERSION 1.29 RELEASED ON JUNE 16, 2009 - Aug 6th, 2010

NEW FEATURES
- Demo Mode: program can now be run in demo mode where data is copied and resets every hour. 
MINOR CHANGES
- WYSIWYG: Upgraded tinyMCE to version 3.2.4.1 (Released 2009-05-25)
- WYSIWYG: Word content is now automatically detected and cleaned up without need for the paste dialog popup window.
- Editors: List Fields now show previously entered database values even when they are no longer in list options
- Editors: Added "Date Field Format" to Admin > Regional Settings to allow displaying date fields as "Day Month Year"
- Viewers: Added 'pageNum' option to viewers to allow you to specify exact page number to display
- Multi Table Search: Added 10 fields that can be passed from each table searched by searchMultipleTables() as field1,
field2, etc.

CHAPTER 11 - THE HIDDEN MANUAL



SUPPRESSING ERROR MESSAGES - Feb 12th, 2022

You can try adding an @ before the $ in variable names

Also, according to Daniel Technical Lead at Interactive Tools:

It's possible to adjust the errors which display by using the error_reporting() function. To turn off only the
deprecated warnings, you can use this:

error_reporting(E_ALL ^ E_DEPRECATED);

Or to suppress all errors/notices/warnings, you can use this:

error_reporting(0);

Note that you will need to include this in your code after the CMSB viewer library has been loaded.


HIDDEN SWITCHES THAT CHANGE THE WAY RECORDS ARE DISPLAYED - Feb 12th, 2022

These are from the 3.55 lib/viewer-functions.php

  list($records, $details) = getRecords(array(
    'tableName'           => 'listings', // REQUIRED, error if not specified, tableName is prefixed with $TABLE_PREFIX
    'where'               => '',         // optional, defaults to blank
    'orderBy'             => '',         // optional, defaults to $_REQUEST['orderBy'], or table sort order
    'limit'               => '',         // optional, defaults to blank
    'offset'              => '',         // optional, defaults to blank (if set but no limit then limit is set to high
number as per mysql docs)
    'perPage'             => '',         // optional, number of records to show per page
    'pageNum'             => '',         // optional, page number to display defaults to $_REQUEST['page'] or 1
    'allowSearch'         => '',         // optional, defaults to yes, adds search info from query string
    'requireSearchMatch'  => '',         // optional, don't show any results unless search keyword submitted and matched
    'requireSearchSuffix' => '',         // optional, search fields must end in a suffix such as _match or _keyword,
original field=value match search is ignored
    'loadUploads'         => '',         // optional, defaults to yes, loads upload array into upload field
    'loadCreatedBy'       => '',         // optional, defaults to yes, adds createdBy. fields for created user
    'loadListDetails'     => '',         // optional, defaults to yes, adds $details with prev/next page, etc info
    'loadPseudoFields'    => false,      // optional, defaults to yes, adds additional fields for :text, :label,
:values, etc
    'orWhere'             => '',         // optional, adding " OR ... " to end of where clause
    'useSeoUrls'          => false,      // optional, use SEO urls, defaults to no

    'leftJoin'      => array(        // Note: leftJoins require you to use fully qualified fieldnames in WHERE and ORDER
BY, such as tablename.fieldname
      'grocery_aisle' => 'aisleNum', // foreign table => local field (that matches num in foreign table)
      'brands'        => 'brandNum',
      'otherTable'    => 'ON mainTable.foreignKey = foreignTable.num',
    ),

    'ignoreHidden'            => false,  // don't hide records with hidden flag set
    'ignorePublishDate'       => false,  // don't hide records with publishDate > now
    'ignoreRemoveDate'        => false,  // don't hide records with removeDate < now
    'includeDisabledAccounts' => true,   // include records that were created by disabled accounts.  See: Admin >
Section Editors > Advanced > Disabled Accounts

    'addSelectExpr'           => 'NOW() as _currentDate',   // add expression to SELECT, useful for generating
pseudo-fields or calculated values
    'groupBy'                 => '',                        // optional, defaults to blank
    'having'                  => '',                        // optional, defaults to blank

    'useCache'      => true,       // use cache - requires cache plugin
    'debugSql'      => false,      // optional, display SQL query, defaults to no
  ));

CHAPTER 12 - THE END USER PERSPECTIVE



CREATING END USER MANUALS

This section of the CMSB cookbook focuses on the needs of the end user, the folks who will use our designs and the CMSB
interface on a daily basis. 

The most important advice that I can offer to a designer who is writing a document for an end user is to never assume
prior knowledge on the part of the end user. Assume that they have extremely little computer knowledge and that because
of this, they will take you instructions very literally and will be reticent to make any changes for fear of
“breaking” something. 

After you’ve written the manual, and after you’ve tried to follow your own instructions, have a layperson (or a
small focus group of lay people) try to follow the instructions and give you feedback about where they had problems.
Then adjust the document accordingly before releasing it to the public. 

If at all possible, try not to use the “it” word. It is the one word in technical manuals that leads to more
confusion than almost any other.

I’ve included a copy of an end user manual that was written (using the WYSIWYG editor)  for an association that had a
number of members assigned to update specific pages on their web site. Your particular client’s needs will be
different but this material should provide you with a jumping off point to provide your clients with effective end user
instruction documents.

I've also included all the files necessary for an end user manual that uses a multi-record editor and text boxes. Among
the advantages are automatic formatting of chapters, main topics and sub topics and the ability to list all new ad
revised entries at the top of the page for a period o 30 days. 


A SAMPLE END USER MANUAL USING WYSIWYG - Dec 29th, 2018

Although the CMS Builder interface is fairly straight forward, most end users will benefit from the inclusion of a
comprehensive, easy to understand set of instructions.

This recipe contains the latest version of one that I offered  to one of my clients. Hope you find it useful.

For my help page, I create a single record editor called "Help", with the default configuration of one text field for a
title and one WYSIWYG field for the help content.

This is displayed by a viewer called "help.php" that's styled to match the client's web site. 

I've also been using the createPDF plugin to allow them to print a PDF of the latest updates to the manual.

You can restrict access to this page by surrounding your load viewer library call at the top of your viewer as in the
following example::


if (!defined('START_SESSION')) { define('START_SESSION', true); }
  // load viewer library
 ... your load viewer library code...
if (!@$_SESSION['username']) { header("Location: http:/yourdomain.com/cmsAdmin/admin.php?redirectUrl=" .
$_SERVER['REQUEST_URI']); exit; }


This one's a little long. You can download a zipped HTML document from: 

http://www.thecmsbcookbook.com/downloads/help_wysiwyg.zip

Open the HTML document and modify the information to meet your needs.
NOTE: I've used "yourdomain.com" anywhere a domain is mentioned

When you're done revising, copy the code that's between the body tags.

Then open your "help" editor, click on the HTML icon on the WYSIWYG editor and paste the code into the HTML window.

Your viewer should display the sample help manual. Make any formatting adjustments necessary.


A SAMPLE END USER MANUAL USING A MULTI-RECORD EDITOR - May 24th, 2012

The zip file at <a target="_blank"
href="http://www.thecmsbcookbook.com/downloads/help_multi_record.zip">http://www.thecmsbcookbook.com/downloads/help_multi_record.zip</a>
contains all the files necessary to create and populate an end user manual using a multi-record editor.

There is a ini.php file that gets uploaded to your data/schemaPresets folder (you'll use this to create your help
editor).
A help.php viewer that you can style to meet your needs.
A data backup file with all the content for the help records. (NOTE: you can only restore a backup to an identical
version of CMSB so there are separate backups for V2.14 and 2.15)

There are also 3 image files for a large and small magnifying glass and a back to the top image. These should be in an
images directory or you should change the path.

You'll have to go through the data and change things like your_site.com and add or take out information that doesn't
apply to your sites to suit your needs, but this should get you started.





MODIFYING THE ALERT RECORD SAVED PLUGIN - Jun 17th, 2022

There's are a set of very handy free plugins that user Djulia created. 

"Alert Record Saved" which sends an email to the admin each time a record is saved, modified or erased.
http://www.thecmsbcookbook/downloads/emailonapproved.zip  

"Alert Admin Login" which sends an email to the admin each time a user logs in.
http://www.thecmsbcookbook/downloads/alertrecordsaved.zip

I needed to modify the "Alert Record Saved" plugin for an art exhibition submission application so that it was only
triggered when a new record was created in particular section (not when the record was updated or erased)  and the email
was sent to the user that created the record instead of the admin.

Here's how.
:
First I surrounded the active code with an if statement that executed the code only if the tablename was equal to my
target table and the new record flag was equal to 1

    
 if($tableName == 'my_trigger_field' && ($isNewRecord == 1) ){

$emailTemplate = "alert-record-saved.mel.php";
    $emailHeaders  = emailTemplate_load(array(
        'template'            => $emailTemplate,
        'template'            => saved_sendAlertMessage_pluginDir() . "/$emailTemplate",
        'subject'            => '[CMSB :: Alert New Record Is Saved]',
        'from'                => $SETTINGS['adminEmail'],
        'to'                => $SETTINGS['adminEmail'],
        'placeholders'        => array(
            'username'        => $CURRENT_USER['username'],
            'email'            => $CURRENT_USER['email'],
            'tableName'        => $tableName,
            'isNewRecord'    => $isNewRecord,
            'recordNum'        => intval(@$_REQUEST['num']),
            'title'            => htmlspecialchars(@$_REQUEST['title']),
            'oldTitle'        => htmlspecialchars($oldRecord['title']),
        ),
        'disabled'            => false,
        'logging'            => false,
    ));
    $mailErrors   = sendMessage($emailHeaders);
    if ($mailErrors) { die("Mail Error: $mailErrors"); }
}

}

And then, since I did not want to send an alert when a record was erased, I  commented out the entire "send email on
erase" code block like this:
    



To make the email a bit more user friendly, I changed
    
 'subject'            => '[CMSB :: Alert New Record Is Saved]',

to
    
'subject'            => 'Your submission has been received',

and to direct the email alert to the current user, I changed 
    
'to'                => $SETTINGS['adminEmail'],

to
    
'to'                => $CURRENT_USER['email'],


In the placeholder array (the values passed to the email template) I added a few placeholders that were more relevant to
my application. (as an example, 'projectTitle' is a new placeholder I want to be available in the email template and
@$_REQUEST['project_title'] is the field in my table where I want the value to come from.)

    
'projectTitle'    => htmlspecialchars(@$_REQUEST['project_title']),
'ship'    => htmlspecialchars(@$_REQUEST['i_am_planning_to_ship_my_artwork']),
'firstName'        => $CURRENT_USER['first_name'],
'lastName'        => $CURRENT_USER['last_name'],

_____________________________________________________

In the email template I changed the body to include a masthead image, included the user's first and last name and a link
to a simple viewer so that they could view their submission:
    
<body>
<img src="http://www.mysite.com/images/masthead.jpg" width="489" height="88" alt="Masthead" />
<br /> <br />
Hi <?php echo htmlspecialchars($PLACEHOLDERS['firstName']) ?>,
<br /> <br />

Your submission to <?php echo htmlspecialchars($PLACEHOLDERS['projectTitle']) ?> was received successfully.
<br /> <br />

<a href="http://www.mysite.com/exhibition_submission_viewer.php?num=<?php echo intval($PLACEHOLDERS['recordNum'])
?>">CLICK/TAP HERE TO VIEW / PRINT YOUR SUBMISSION INFORMATION</a>
<br /> <br />
<?php if($PLACEHOLDERS['ship'] == 1):?>You've indicated that you'll be shipping your work to this exhibition.Please
insure that it will arrive by the shipping deadline.<?php endif?>
<br /> <br />

If you have any questions, please <a href="http://www.nawafl.com/contact.php">CONTACT US</a>
<br /> <br />
Thanks,
The Exhibitions Committee
<br /> <br />
</body>



MYSQL TO MYSQLI CHEAT SHEET - Feb 9th, 2023

MySql to MySqli Cheat Sheet

According to Daniel Louwe, Technical Lead at Interactive Tools, "CMSB also now includes a Legacy MySQL Scanner plugin -
you can activate this plugin and then use it to run a scan both on your plugins and website files to locate other places
that need to be updated. Updating to the latest version of a plugin should fix any legacy MySQL issues there, however,
anything found in your website files may require some manual fixing."

Here's some more information from Interactive Tools

For each mysql_ function, find it in the Original MySQL API here:
http://php.net/manual/en/book.mysql.php

And then follow the link to the replacement function and copy the code replacing
$mysql-> with mysqli()->

I'll add fixes as I come across them.
Please email me with your conversion notes.

Here are some example search and replaces:

Search for:  ........  Replace with:
--------------------------------------------------------------------------------
mysql_query(   ........              mysqli()->query(
mysql_error(    ........             mysqli()->error
mysql_ping(        ........         mysqli()->ping(
mysql_select_db(    ........       mysqli()->select_db(
mysql_server_info(     ........;      mysqli()->server_info
mysql_set_charset    ........       mysqli()->set_charset(
mysql_insert_id      ........        mysqli()->insert_id;
mysql_close(           ........;      mysqli()->close();
mysql_affected_rows   ........      mysqli()->affected_rows
mysql_fetch_assoc($result) ........  $result->fetch_assoc()
mysql_fetch_row($result)  ........   $result->fetch_row()

For any other mysql function find the original and replacement code in the PHP
manual online here: http://php.net/manual/en/book.mysql.php

Reference:
PHP Original MySQL API: http://php.net/manual/en/book.mysql.php
PHP MySQL Improved Extension: http://php.net/manual/en/book.mysqli.php
________________________________________
Some changes that I've made:

Change this: 

$query = "SELECT DATE_FORMAT(date, '%M %Y') as dateAndYear, YEAR(date) as year, MONTH(date) as month FROM cmsb_blog
WHERE `hidden` = 0 GROUP BY dateAndYear ORDER BY date";

$result =  mysql_query($query) or die("MySQL Error: ". htmlspecialchars(mysql_error()) . "\n");
while ($record = mysql_fetch_assoc($result)):

To this:

$query = "SELECT DATE_FORMAT(date, '%M %Y') as dateAndYear, YEAR(date) as year, MONTH(date) as month FROM cmsb_blog
WHERE `hidden` = 0 GROUP BY dateAndYear ORDER BY date";

$result = mysqli()->query($query) or die("MySQL Error: ". htmlspecialchars(mysqli()->error) . "\n");
while ($record = $result->fetch_assoc()):


And this:

mysql_query(mysql_escapef("INSERT INTO {$TABLE_PREFIX}login_log SET 


Becomes this:

 mysqli()->query(mysql_escapef("INSERT INTO {$TABLE_PREFIX}login_log SET


And this:

mysql_query("INSERT INTO `{$TABLE_PREFIX}salon_listings` SET

becomes this:

mysqli()->query("INSERT INTO `{$TABLE_PREFIX}salon_listings` SET


And this:

mysql_query("UPDATE `{$TABLE_PREFIX}salon_listings` SET

becomes this:

mysqli()->query("UPDATE `{$TABLE_PREFIX}salon_listings` SET

And this:
 

mysql_query("UPDATE `{$TABLE_PREFIX}accounts` SET  
expiresDate = expiresDate + INTERVAL 1 YEAR    
   WHERE num = '".mysql_escape( $CURRENT_USER['num'] )."'")  
       or die("MySQL Error:\n". htmlspecialchars(mysql_error()) . "\n");  
        $userNum = mysql_insert_id(); 

Can also become this:

 $colsToValues = array();
      $colsToValues['updatedDate=']     = 'NOW()';
      $colsToValues['expiresDate=']     = 'expiresDate + INTERVAL 1 YEAR';
      mysql_update(accountsTable(), $CURRENT_USER['num'], null, $colsToValues);
 

According to Daniel Loewe at Interactive Tools, 

You should be able to replace mysqli_insert_id() with mysqli()->insert_id; 

A suitable replacement for mysql_real_escape_string() is mysql_escape().


VERSION 3.52 - JULY 26, 2019 - Jul 28th, 2019


SERVER REQUIREMENTS
- This software version REQUIRES: PHP 7.1+ and MySQL 5.5+
- NOTE: Some plugins may need to be updated when upgrading from v2.xx

NEW FEATURES
- Section Editors: Added "Insert Field Here" (+) link to allow inserting a new field beside an existing field
- Section Editors: New "Tab Group" field for organizing section editor fields
- Section Editors: New '<input type="hidden">' advanced field for inserting hidden fields in sections.
- Server Admin: Admin > General > Disk Space now has an option to show largest files (for linux servers)
- Server Admin: Admin > Background Tasks now includes setup instructions for cPanel, Plesk, Windows, and Linux
- Database: Switched default storage engine from MyISAM to InnoDB
- Field Editor: "Update all existing records" feature available when adding a new field with a default value.
- Background Tasks: New option to limit the number of entries stored in the background task log.
- Category Menus: Now possible to assign Author/Viewer access to category menu sections.
- Admin Menu: Security Settings have been moved to their own menu
- Audit Log: Now logs login events and record add/modify/delete

MINOR CHANGES
- Select upload dialog only shows matching file extensions.
- Libraries: Updated TinyMCE from v4.9.0 to v4.9.4 (Released 2019-03-20)
- Libraries: Upgraded jQuery to v3.4.1
- Section Editors: New menu groups get default table suffix of "_menugroup" for usability

BUG FIXES
- Plugins: Added fix for issue that may cause plugins to get deactivated when they're being uploaded
- Category Menu: drag-order functionality no longer dependant on "name" column.
- Misc Code and other minor improvements.
The materials on this web site have been created for use with CMS Builder content management software. CMS Builder software is published and licensed for use by InteractiveTools.com. Please contact Interactive Tools for information on the downloading of the software or the purchasing of licenses.


Terms of Service



4.33 seconds