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 PDFSection 2 (Recipes 101 - 200) - Download PDFSection 3 (Recipes 201 - 300) - Download PDFSection 4 (Recipes 301 - 400) - Download PDFSection 5 (Recipes 401 - 500) - Download PDFSection 6 (Recipes 501 - 559) - Download PDF
LAST UPDATE -
Aug 8th, 2023
Click 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>" 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 criteria, you wont get a “lookupReferringRecords: 'recordList' option missing” error.
IMAGES When implementing this function for images, Dan 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(" ", $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_2) VALUES
(‘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-in, but you could copy the code, rename 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($string, 0, $length+1), $matches) ? $matches[1] : substr($string, 0, $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($string, 0, $setlength); else return substr($string, 0, $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($html, 90); $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); $text= str_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 == 1 ):?> 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 answers, I included functions called maxWords and wordCount to show a "read more" link only if there were more than 25 words in the answer. These functions are defined in the head section of my viewer, with:
<?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'] ?> <?php echo $record['createdBy.last_name'] ?>
or
<?php echo $yourtableRecord['createdBy.first_name'] ?> <?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']; ?> <?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'] - 1 : 0; $firstIndex = $photoPage * $photosPerPage; if ($firstIndex > sizeof($your_tableRecord['images'])-1 || $firstIndex < 0) { $firstIndex = 0; $photoPage = 0; } $lastIndex = min($firstIndex + $photosPerPage, sizeof($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'] ?>"><< 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 >></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'] - 1 : 0; $firstIndex = $photoPage * $photosPerPage; $lastIndex = min($firstIndex + $photosPerPage, sizeof($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'])-1 || $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="body-text-9"><?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'] ?>"><< prev</a> <?php else: ?> << 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 >></a> <?php else: ?> next >> <?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 dash) between [ and ] for the code to work ?> <?PHP $record['recipe'] = preg_replace("[]", "", $record['recipe'] ); // insert <b-r/> (remove the dash) between [ and ] for the code to work ?> <?PHP $record['recipe'] = preg_replace("[]", "", $record['recipe'] ); // insert <b-r /> (remove the dash) between [ 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 dash) between " and ", and remove the dash between z and x 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($recipe, 120) ; $recipe = highlight_string($recipe, true);
// 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 x for the code to work ?> <?php $recipe = wordwrap($recipe, 120) ; ?> <?php $recipe = highlight_string($recipe, true); ?> <?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 >>" /></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 $count= 0 ?> <?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 == 0 &&!$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, $colsToValues, true); // 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 == 0 && @$_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 > 0 && @$_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($tableName, null, $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 >>" /></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 == 0 ): ?>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 == 1 ): ?> <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 == 0 ): ?>name="submit"<?php else:?>name="submit2"<?php endif ?> value="Submit >>" /></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, $colsToValues, true); } ?> <?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"> </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']/2 )?>" 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 >=1 ) { $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 days. This 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== 0 || @$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 >= 1 ):?> <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> </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 == 0 && (@$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 $ischeck= 0; ?> <?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("/[-_]/", " ", $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'] ?> </span> <span class="body-text"> <?PHP if ($record['end_date']): ?>Through <?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("/[-]/", " ", $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]/”, " ", $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”, " ", $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("/[-]/", " ", $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]/”, " ", $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]/”, " ", $your_tableRecord['your_field'] ); ?>
Would become this:
<?PHP $your_tableRecord['your_field'] = preg_replace("[rx]”, " ", $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 allowed, so I use this little trick to replace any inadvertent double quotes with 2 single quotes, which looks (almost) the 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 Sharing Meeting”
The navigation menu required the Artists for the text to line up appropriately, but the header required:
A R T I S T S S H A R I N G M E E T I N 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 O N T H L Y // ...: A R T I S T S // ...: S H A R I N G
// function expandText($text) { $text = preg_replace("/ /i", '', $text); // replace 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("/ /", ' ', $text); // replace " " with " " $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'] * 5, 2) ?>
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'] == 1 && $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"> </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> </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> </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"> <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'] ?>"> << <?PHP echo $prevRecord['title'] ?></a> <?PHP endif; ?> <?PHP if (@$nextRecord): ?> <a href="<?PHP echo $nextRecord['_link'] ?>"><?PHP echo $nextRecord['title'] ?> >></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> </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'] ?>"><< previous page</a> <?php else: ?> << 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 >></a> <?php else: ?> next >> <?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 >></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;">< < 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> </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"> << <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'] ?>; <?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>1 ? "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 > 1 && $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 page, just 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> </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> </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("/['\"]/","´", htmlencode($record['title']) ) ; $record['size_of_work'] = preg_replace("/['\"]/","´", 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("/['\"]/","´", $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("/['\"]/","´", 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 " 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="your_css_class"><?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 " 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'] - 1 : 0; $firstIndex = $photoPage * $photosPerPage; $lastIndex = min($firstIndex + $photosPerPage, sizeof($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> <?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'])-1 || $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="text_font"><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="medium-bold"><?php echo ($title); ?></span><br /><?php $size_of_work = htmlspecialchars($record['size_of_work']); ?><span class="medium"><?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 " for other tags like class="your_class"
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:::<br><?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 " 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="#"><h2><?php echo $record['title'] ?><br /><?php echo $record['sub_title'] ?></h2></a><br /><?php endif ?>
<?php if (!$record['title']): ?><a href="<?php echo $record['_link'] ?>"><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-height: 85%; 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"> </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
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"> </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
|
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&v=2&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'] ?> ¤cy_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’d like to encode: <input name="__enc_request" size="60" style="width:100%"> Enter the Second Return-URL you’d like to encode: <input name="__enc2_request" size="60" style="width:100%"> Enter your PayPal ‘business’ 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><head></code> and <code></head></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 >>" /></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 == 1 ) :?> <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 &item_number=5-pack &item_name=Five Pack &business=payments@your_site.com &no_note=1 &cpp_header_image=http://your_site.com/images/pp_header.png&currency_code=USD &lc=US&add=1&cmd=_cart &no_shipping=0&return=http://www.your_site.com/linklokipnret.php &notify_url=http://www.your_site.com/linklokipn.php &bn value=PP-ShopCartBF &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($row, 50); ?>
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'] == 6 ) : ?> - Author <?php endif; ?> <?php if(@$accessRecord['accessLevel'] == 7 ) : ?> - Author & Viewer<?php endif; ?> <?php if(@$accessRecord['accessLevel'] == 9 ) : ?> - 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 © - © ® - ®
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(5, 10, 25, 50, 100, 250, 1000)); ?>
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 >></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 == 1 ):?> <?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.
|
|
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
|
|
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'] == 1 && 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 selectedIf( 50, @$_REQUEST['maxDistance']) ?> >within 50 <?php if ( $kmOrMiles == 'miles'):?> miles <?php elseif ( $kmOrMiles == 'km'):?> km <?php endif ?> </option> <option value="25" <?php selectedIf( 25, @$_REQUEST['maxDistance']) ?> >within 25 <?php if ( $kmOrMiles == 'miles'):?> miles <?php elseif ( $kmOrMiles == 'km'):?> km <?php endif ?> </option> <option value="10" <?php selectedIf( 10, @$_REQUEST['maxDistance']) ?> >within 10 <?php if ( $kmOrMiles == 'miles'):?> miles <?php elseif ( $kmOrMiles == 'km'):?> km <?php endif ?> </option> <option value="5" <?php selectedIf( 5, @$_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> </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'] ==1 || !$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 Louwe, a 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 >>', '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 >>', '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 see, you 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' link) after 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 automatically. You 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', null, 4);
// 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', null, 4);
// 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 calls, add:
?> <?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
|
|
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 = '" . 0 . "'" ) 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']): ?> <?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']): ?> <?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 . 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($recipe, true); $recipe = str_replace(' ', ' ', $recipe); $recipe = wordwrap($recipe, 140, '', 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($recipe, true); $recipe = str_replace(' ', ' ', $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 + 1 ); $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&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"> </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 "Quick Start Guide" 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 added, 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
?>
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 records, so 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 "revalidate or update" 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 "revalidate or update" 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_update( accountsTable(), $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 > </td> <td align="center"> <input class="button" type="submit" name="submit" value="Change Password >>" /></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', null, 4); // 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 "submit". 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> </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 &item_number=Your_Item_Number &item_name=Your Renewal &business=payments@your_site.com &cpp_header_image=http://www.your_site.com/images/CMSBPP.png &currency_code=USD&lc=US&add=1 &cmd=_cart&no_shipping=0 &return=http://www.your_site.com/linklokipnret.php &notify_url=http://www.your_site.com/linklokipn.php &no_note=1&bn value=PP-ShopCartBF&"> <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'] ?>; </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($url, 5, '', true); $recaptchaResponse = json_decode($json, true); 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"> </td> <td> </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 >>" /> </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> </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> </td> </tr> <tr> <td valign="top"> </td> <td> </td> </tr> </table> <input type="submit" name="add" value="Click to Submit >>" /> </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($url, 5, '', true); $recaptchaResponse = json_decode($json, true); 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 >>" /></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'] == 0 && $record['confirmed'] == 1 && $record['remove'] == 0): ?> <?php echo $record['email'] ?>; <?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) !=1 ) { $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"> </td> <td> </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 >>" /><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'] == 0 && $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> <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) !=1 ) { $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) !=1 ) { 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']; ?> <?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']; ?> <?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"> </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', null, 4);
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($displayValue, 0, 4); $firstHyphen = substr($displayValue, 4, 1);
$month = substr($displayValue, 5, 2); $secondHyphen = substr($displayValue, 7, 1);
$day = substr($displayValue, 8, 2);
//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'] = 0 || $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_message, 70); ?> <?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_message, 70); ?> <?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'] == 1 ):?><?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'] == 1 ):?><?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==1 || $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'] == 1 ):?><?php $expires = date("M jS, Y", strtotime($record['test_date'])) ;?><?php elseif ($common_informationRecord['send_test_emails_only'] == 0 || $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_message, 70); ?> <?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_message, 70); ?> <?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'] == 0 || $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'] == 1 ):?><?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'] == 1 ):?><?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==2 || $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'] == 1 ):?><?php $expires = date("M jS, Y", strtotime($record['test_date'])) ;?><?php elseif ($common_informationRecord['send_test_emails_only'] == 0 || $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_message, 70); ?> <?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_message, 70); ?> <?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'] == 0 || $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'] == 1 ):?><?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'] == 1 ):?><?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 $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==5 || $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'] == 1 ):?><?php $expires = date("M jS, Y", strtotime($record['test_date']));?><?php elseif ($common_informationRecord['send_test_emails_only'] == 0 || $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_message, 70); ?> <?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_message, 70); ?> <?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_message, 70); ?> <?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'] == 0 || $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'] == 1 ):?><?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'] == 1 ):?><?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 records, you’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
|
|
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']?>" /> <?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']): ' '; // default to to display even with empty string
With this line:
$attrs['label'] = $attrs['label'] !== "" ? $attrs['label'] : ' '; // default to 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"/> <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($im, 255, 255, 255); $black = imagecolorallocate($im, 0, 0, 0); $gray = imagecolorallocate($im, 150, 150, 150);
imagerectangle($im, 0, 0, 25, 39, $gray); imagerectangle($im, 25, 0, 50, 39, $gray); imagerectangle($im, 50, 0, 75, 39, $gray); imagerectangle($im, 75, 0, 99, 39, $gray);
imageline($im, 0, 0, 25, 39, $gray); imageline($im, 25, 0, 50, 39, $gray); imageline($im, 50, 0, 75, 39, $gray); imageline($im, 75, 0, 99, 39, $gray);
imageline($im, 0, 39, 25, 0, $gray); imageline($im, 25, 39, 50, 0, $gray); imageline($im, 50, 39, 75, 0, $gray); imageline($im, 75, 39, 99, 0, $gray);
$c1 = rand(65, 90); $c2 = rand(65, 90); $c3 = rand(65, 90); $c4 = rand(65, 90); $c5 = rand(65, 90);
$textOut = chr($c1) . ' ' . chr($c2) . ' ' . chr($c3) . ' ' . chr($c4) . ' ' . chr($c5); $textCaptcha = chr($c1) . chr($c2) . chr($c3) . chr($c4) . chr($c5);
$a = imagestring($im, 5, 11, 13, $textOut, $black);
$fileName = substr(md5($textCaptcha), 0, 12);
$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 "verify" 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 "verify" is not writeable. Script will quit now.</body></html>'; exit; }
$handle = opendir( $captchaDir );
while ( $captchaFile = readdir($handle) ) { if ( ( substr($captchaFile, 0, 1) != '.' ) && ( substr($captchaFile, 0, 1) != '_' ) && ( !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) == 1 ) { 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 "verify" not found. Script will quit now.</body></html>'; exit; }
$handle = opendir( $captchaDir );
$fileAccessCode = substr( md5( $accessCode ), 0, 12 );
while ( $captchaFile = readdir( $handle ) ) { if ( substr( $captchaFile, 0, 1 ) != '.' ) { 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 "verify" not found. Script will quit now.</body></html>'; exit; }
$handle = opendir( $captchaDir );
$fileAccessCode = substr( md5( $accessCode ), 0, 12 );
while ( $captchaFile = readdir( $handle ) ) { if ( ( substr( $captchaFile, 0, 1 ) != '.' ) && ( substr( $captchaFile, 0, 1 ) != '_' ) && ( !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($FTGreason, 1, 0, kStringRangeFrom, kNo, kNo, kNo, '', kMandatory)) { $FTGErrorMessage['reason'] = 'Tell us why you\'re contacting us so we can route your message correctly.'; $validationFailed = true; }
if (!CheckString($FTGfirst_name, 1, 36, kStringRangeBetween, kNo, kNo, kNo, '', kMandatory)) { $FTGErrorMessage['first_name'] = 'Please enter your First Name'; $validationFailed = true; }
if (!CheckString($FTGlast_name, 1, 36, kStringRangeBetween, kNo, kNo, kNo, '', kMandatory)) { $FTGErrorMessage['last_name'] = 'Please enter your Last Name'; $validationFailed = true; }
if (!CheckEmail($FTGemail, kMandatory)) { $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'] == 1 ) && $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'] == 1 ) && $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'] == 1 && $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_split( base64_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($bodEmail, 0, -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($url, 5, '', true); $recaptchaResponse = json_decode($json, true); 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($bodEmail, 0, -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($url, 5, '', true); $recaptchaResponse = json_decode($json, true); 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($questionEmail, 0, -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> </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;"> </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;"> </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> </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( $message, 70); ?>
|
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($variable1, 70); ?> <?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 & ) 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'] ?>; </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'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 TO SOUTH FLORIDA ART 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'] == 0 || $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 >>" /> </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: _WARNING: session_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 ' - 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&action=editTable&tableName=' . urlencode($tableName); } if ($CURRENT_USER['isAdmin']) { $advancedCommands['Admin: Code Generator'] = '?menu=admin&action=generator&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 "&" 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
|