diff --git a/README.md b/README.md
index 3e19543..52d53e0 100644
--- a/README.md
+++ b/README.md
@@ -3,20 +3,27 @@ Admin Pack
A PHP toolkit designed specifically for programmers to quickly create a nice-looking, custom-built, secure administrative web interface. Choose from a MIT or LGPL license. Proven to cut development time by at least 50% over traditional frameworks and template engines. But it isn't a CMS nor a framework or a template engine. Admin Pack is very different. Give it a go to power your next PHP-based administrative backend.
+Be sure to check out [Admin Pack with Extras](https://github.com/cubiclesoft/admin-pack-with-extras). It is Admin Pack plus some extra options with Javascript components (e.g. a convenient visual date picker widget) in a slightly beefier package.
+
Features
--------
* Quick-n-dirty custom administrative interface builder.
-* The default template and print stylesheet looks nice.
+* The default templates look nice enough. Gets the job done.
* Integrated CSRF/XSRF defenses.
-* Very lightweight (~140KB).
+* Very lightweight (~180KB).
* Has a liberal open source license. MIT or LGPL, your choice.
* Designed for relatively painless integration into your project.
* Sits on GitHub for all of that pull request and issue tracker goodness to easily submit changes and ideas respectively.
+Under the Hood
+--------------
+
+Admin Pack uses [FlexForms](https://github.com/cubiclesoft/php-flexforms), which makes it easy to extend Admin Pack with custom functionality (see `support/view_print_layout.php`). For example, [FlexForms Modules](https://github.com/cubiclesoft/php-flexform-modules) contains several official extensions (e.g. charts, HTML editor, character/word counter).
+
More Information
----------------
-Documentation, demos, examples, and official downloads of this project sit on the Barebones CMS website:
+Documentation, demos, examples, and official downloads of this project sit on the Barebones CMS website (Admin Pack does not depend on Barebones CMS):
http://barebonescms.com/documentation/admin_pack/
diff --git a/admin.php b/admin.php
new file mode 100644
index 0000000..2b63ef7
--- /dev/null
+++ b/admin.php
@@ -0,0 +1,530 @@
+ array(
+ // Replace these menu options with your own. The examples contain useful boilerplate code.
+ "Manage" => BB_GetRequestURLBase() . "?action=manageexample&sec_t=" . BB_CreateSecurityToken("manageexample"),
+ "Add Entry" => BB_GetRequestURLBase() . "?action=addeditexample&sec_t=" . BB_CreateSecurityToken("addeditexample"),
+ "Bulk Edit" => BB_GetRequestURLBase() . "?action=bulkeditexample&sec_t=" . BB_CreateSecurityToken("bulkeditexample"),
+ "View/Print" => BB_GetRequestURLBase() . "?action=viewprintexample&id=1&sec_t=" . BB_CreateSecurityToken("viewprintexample")
+ )
+ );
+
+ // An example function used later on to demonstrate loading user information from a database.
+ function LoadUserDetails($info)
+ {
+ $defaults = array(
+ "first" => "John", "last" => "Smith", "email" => "", "city" => "", "state" => "", "zip" => "",
+ "status" => "Approved", "notes" => "What a great guy.\n\nUser approved on " . date("m/d/Y") . ".", "othernotes" => ""
+ );
+
+ return BB_ProcessInfoDefaults($info, $defaults);
+ }
+
+ if (isset($_REQUEST["action"]) && $_REQUEST["action"] == "deleteexample")
+ {
+ $id = (isset($_REQUEST["id"]) ? (int)$_REQUEST["id"] : 0);
+// $db->Query("DELETE FROM userdetails WHERE id = ?", array($id));
+
+ BB_RedirectPage("success", "Successfully deleted the details entry. (Just imagine that it worked. After all, this is only an example.)", array("action=manageexample&sec_t=" . BB_CreateSecurityToken("manageexample")));
+ }
+ else if (isset($_REQUEST["action"]) && $_REQUEST["action"] == "manageexample")
+ {
+ // Demonstrates a common pattern to show all entries in a MySQL/MariaDB database and provide options.
+
+// $rows = array();
+// $result = $db->Query("SELECT * FROM userdetails");
+// while ($row = $result->NextRow())
+// {
+// $info = LoadUserDetails(unserialize($row->info));
+//
+// $rows[] = array(htmlspecialchars($info["first"]), htmlspecialchars($info["last"]), "id . "&sec_t=" . BB_CreateSecurityToken("viewprintexample") . "\">View | id . "&sec_t=" . BB_CreateSecurityToken("addeditexample") . "\">Edit | id . "&sec_t=" . BB_CreateSecurityToken("deleteexample") . "\" onclick=\"return confirm('Are you sure you want to delete these details?');\">Delete");
+// }
+
+ // A fake entry to show what this pattern looks like.
+ $info = LoadUserDetails(array());
+ $rows[] = array(htmlspecialchars($info["first"]), htmlspecialchars($info["last"]), "View | Edit | Delete");
+
+ // Custom HTML for a mini-menu.
+ $desc = "
";
+ $desc .= "Add New Entry";
+
+ $contentopts = array(
+ "desc" => "Manage user details.",
+ "htmldesc" => $desc,
+ "fields" => array(
+ array(
+ "type" => "table",
+ "cols" => array("First", "Last", "Options"),
+ "rows" => $rows
+ )
+ )
+ );
+
+ BB_GeneratePage("Manage Entries Example", $menuopts, $contentopts);
+ }
+ else if (isset($_REQUEST["action"]) && $_REQUEST["action"] == "addeditexample")
+ {
+ // Demonstrates a common pattern to easily add new entries AND edit existing entries in a MySQL/MariaDB database with writing code only one time.
+ // Less code results in fewer logic errors. Some code is commented out so that the example actually functions.
+
+ $id = (isset($_REQUEST["id"]) ? (int)$_REQUEST["id"] : 0);
+// $row = $db->GetRow("SELECT * FROM userdetails WHERE id = ?", array($id));
+// if ($row) $info = LoadUserDetails(unserialize($row->info));
+// else
+// {
+ $info = LoadUserDetails(array());
+// $id = 0;
+// }
+
+ if (isset($_REQUEST["first"]))
+ {
+ if ($_REQUEST["first"] == "") BB_SetPageMessage("error", "Please fill in 'Field 1'.");
+
+ if (BB_GetPageMessageType() != "error")
+ {
+ // [Save data here.]
+ $originfo = $info;
+ $info["first"] = $_REQUEST["first"];
+ $info["last"] = $_REQUEST["last"];
+// $info["email"] = $_REQUEST["email"];
+
+// if ($id) $db->Query("UPDATE userdetails SET email = ?, info = ? WHERE id = ?", array($info["email"], serialize($info), $id));
+// else $db->Query("INSERT INTO userdetails SET email = ?, info = ?", array($info["email"], serialize($info)));
+
+ BB_RedirectPage("success", ($_REQUEST["id"] > 0 ? "Successfully saved the details." : "Successfully created the details."), array("action=manageexample&sec_t=" . BB_CreateSecurityToken("manageexample")));
+ }
+ }
+
+ // [Do processing here to generate any dynamic content options.]
+ $somevar = "default value 2";
+ $items = array("Furries", "Fuzzies", "Fluffies", "Puppies", "Kitties", "Penguins", "Ponies", "Tribbles", "Unicorns");
+ $rows = array();
+ foreach ($items as $num => $item)
+ {
+ $rows[] = array(($num + 1), htmlspecialchars($item), "Edit");
+ }
+
+ // Load and use most FlexForms Modules if available.
+ if (file_exists("support/flex_forms_calendar_table.php")) require_once "support/flex_forms_calendar_table.php";
+ if (file_exists("support/flex_forms_chart.php")) require_once "support/flex_forms_chart.php";
+ if (file_exists("support/flex_forms_tablefilter.php")) require_once "support/flex_forms_tablefilter.php";
+ if (file_exists("support/flex_forms_htmledit.php")) require_once "support/flex_forms_htmledit.php";
+ if (file_exists("support/flex_forms_textcounter.php")) require_once "support/flex_forms_textcounter.php";
+
+ $tomorrow = mktime(0, 0, 0, date("n"), date("j") + 1);
+
+ $contentopts = array(
+ "desc" => ($id ? "Edit the user details." : "Add user details."),
+ "hidden" => array(
+ "id" => $id
+ ),
+ "fields" => array(
+ // The accordions only show correctly when FlexForms Extras is used.
+ array(
+ "title" => "Some Options",
+ "type" => "accordion"
+ ),
+ // Field 1 demonstrates current "best practice".
+ array(
+ "title" => "Field 1",
+ "type" => "text",
+ "name" => "first",
+ "default" => $info["first"],
+ "desc" => "Basic text field."
+ ),
+ array(
+ "title" => "Field 2",
+ "type" => "text",
+ "name" => "last",
+ "value" => BB_GetValue("last", $info["last"]),
+ // ^^^ Using 'value' directly is an older solution. Using 'default' usually accomplishes the same thing with less code (see 'Field 1' above).
+ "desc" => "Another text field."
+ ),
+ array(
+ "title" => "Some More Options",
+ "type" => "accordion"
+ ),
+ "startrow",
+ array(
+ "title" => "Field 2a",
+ "type" => "text",
+ "width" => "200px",
+ "name" => "field2a",
+ "default" => "",
+ ),
+ array(
+ "title" => "Field 2b",
+ "type" => "text",
+ "width" => "50px",
+ "name" => "field2b",
+ "default" => "",
+ ),
+ "endrow",
+ array(
+ "title" => "Date",
+ "type" => "date",
+ "name" => "date",
+ "default" => date("Y-m-d"),
+ "desc" => "Description for Date. Requires FlexForms Extras to show this field."
+ ),
+ array(
+ "title" => "Table",
+ "split" => false,
+ "type" => "table",
+ "cols" => array("ID", "Type", "Options"),
+ "rows" => $rows,
+ "order" => "Order",
+ "stickyheader" => true,
+ "desc" => "Description for Table. When used with FlexForms Extras, drag-and-drop and sticky header support are enabled."
+ ),
+ "nosplit",
+ "startrow",
+ array(
+ "title" => "Field 2c",
+ "type" => "text",
+ "width" => "200px",
+ "name" => "field2c",
+ "default" => "",
+ ),
+ array(
+ "title" => "Field 2d",
+ "type" => "text",
+ "width" => "50px",
+ "name" => "field2d",
+ "default" => "",
+ ),
+ "endrow",
+ array(
+ "title" => "Cookies?",
+ "type" => "checkbox",
+ "name" => "field2e",
+ "value" => "Yes",
+ "display" => "I like cookies",
+ "default" => true
+ ),
+ array(
+ "title" => "File",
+ "type" => "file",
+ "name" => "file",
+ "desc" => "Description for File."
+ ),
+ "endaccordion",
+ "split",
+ "startrow",
+ array(
+ "title" => "Field 3",
+ "type" => "text",
+ "width" => "200px",
+ "name" => "field3",
+ "default" => "default value",
+ "desc" => "Description for Field 3."
+ ),
+ array(
+ "title" => "Field 4",
+ "type" => "text",
+ "width" => "200px",
+ "name" => "field4",
+ "default" => $somevar
+ ),
+ "startrow",
+ array(
+ "title" => "Field 5",
+ "type" => "text",
+ "width" => "220px",
+ "name" => "field5",
+ "default" => "default value",
+ "desc" => "Description for Field 5."
+ ),
+ array(
+ "title" => "Field 6",
+ "type" => "text",
+ "width" => "220px",
+ "name" => "field6",
+ "default" => $somevar,
+ "desc" => "Description for Field 6."
+ ),
+ "endrow",
+ array(
+ "title" => "Informational",
+ "type" => "static",
+ "value" => "Did you know that FlexForms, which powers Admin Pack, actually has its origins in Barebones CMS? BB_PropertyForm() started it all."
+ ),
+ array(
+ "title" => "Admin Notes",
+ "type" => "textarea",
+ "name" => "notes",
+ "default" => $info["notes"]
+ ),
+ array(
+ "title" => "Custom HTML Output",
+ "type" => "custom",
+ "value" => "
Why, hello there!
Have you had your Wheaties today?
", + "html" => true, + "desc" => "Description for HTML editor. This feature requires FlexForms Modules to be included." + ), + array( + "title" => "Module: Text Counter", + "type" => "text", + "name" => "textcount", + "default" => "", + "counter" => 150, + "desc" => "Description for Text Counter. This feature requires FlexForms Modules to be included." + ), + ), + "submit" => ($id ? "Save" : "Create") + ); + + if (file_exists("support/flex_forms_passwordmanager.php")) + { + require_once "support/flex_forms_passwordmanager.php"; + + $contentopts["fields"][] = array( + "title" => "Module: Stop Password Manager", + "type" => "password", + "name" => "signature", + "default" => "", + "passwordmanager" => false, + "desc" => "Description for Stop Password Manager. This feature requires FlexForms Modules to be included." + ); + } + + BB_GeneratePage(($id ? "Edit Entry Example" : "Add Entry Example"), $menuopts, $contentopts); + } + else if (isset($_REQUEST["action"]) && $_REQUEST["action"] == "bulkeditexample_getinfo") + { + $id = (int)$_REQUEST["id"]; + +?> + + "mainitem_" . $x, + "display" => "Item #" . $x, + "onclick" => "LoadItem('" . $x . "');" + ); + } + + ob_start(); +?> + + $items, + "topbarhtml" => "An optional top bar", + "initialhtml" => "Select an option from the left.", + "bottombarhtml" => "An optional bottom bar", + "javascript" => $js + ); + + BB_GenerateBulkEditPage("Example Bulk Edit Page", $contentopts); + } + else if (isset($_REQUEST["action"]) && $_REQUEST["action"] == "viewprintexample") + { + // Demonstrates a common pattern to display a summary view that is also print-ready. + // The view/print layout adds additional features to FlexForms. + + $id = (isset($_REQUEST["id"]) ? (int)$_REQUEST["id"] : 0); +// $row = $db->GetRow("SELECT * FROM userdetails WHERE id = ?", array($id)); +// if ($row) +// { +// $info = LoadUserDetails(unserialize($row->info)); + + // Using fake user details here so that the example works. + $info = LoadUserDetails(array()); + + require_once "support/view_print_layout.php"; + + // Any extra/custom HTML goes here. + $desc = ""; + + $contentopts = array( + "desc" => "", + "htmldesc" => $desc, + "fields" => array( + array( + "type" => "viewmedia", + "value" => "", + "desc" => "A floating image. Well, it would show if the file existed." + ), + array( + "type" => "viewtable", + "data" => array( + "First" => $info["first"], + "Last" => $info["last"], + "E-mail" => $info["email"], + "Status" => $info["status"] + ), + "desc" => "Shows first name, last name, and status but not e-mail since 'compact' data mode is enabled (the default). Values are also HTML encoded by default for safety." + ), + "endmedia", + array( + "use" => ($info["notes"] != ""), + "title" => "Admin Notes", + "type" => "viewstatic", + "value" => $info["notes"] + ), + array( + "use" => ($info["othernotes"] != ""), + "title" => "Other Notes", + "type" => "viewstatic", + "value" => $info["othernotes"], + "desc" => "This field won't show at all since 'use' is false. Value for viewstatic is HTML encoded by default." + ) + ) + ); + + BB_GeneratePage("View Entry Example for #" . $id, $menuopts, $contentopts); +// } + } + else + { + $contentopts = array( + "desc" => "Pick an option from the menu." + ); + + BB_GeneratePage("Home", $menuopts, $contentopts); + } +?> \ No newline at end of file diff --git a/index.html b/index.html new file mode 100644 index 0000000..e69de29 diff --git a/index.php b/index.php deleted file mode 100644 index 3e02258..0000000 --- a/index.php +++ /dev/null @@ -1,204 +0,0 @@ - array( - "Some Page" => BB_GetRequestURLBase() . "?action=somepage&sec_t=" . BB_CreateSecurityToken("somepage"), - "Some Page 2" => BB_GetRequestURLBase() . "?action=somepage2&sec_t=" . BB_CreateSecurityToken("somepage2") - ) - ); - - if (isset($_REQUEST["action"]) && $_REQUEST["action"] == "somepage") - { - if (isset($_REQUEST["field1"])) - { - if ($_REQUEST["field1"] == "") BB_SetPageMessage("error", "Please fill in 'Field 1'."); - - if (BB_GetPageMessageType() != "error") - { - // [Save data here.] - - BB_RedirectPage("success", "Successfully saved the data."); - } - } - - // [Do processing here to generate content options dynamically.] - $somevar = "default value 2"; - - $contentopts = array( - "desc" => "This is some page.", - "nonce" => "action", - "hidden" => array( - "action" => "somepage" - ), - "fields" => array( - array( - "title" => "Field 1", - "type" => "text", - "name" => "field1", - "value" => BB_GetValue("field1", ""), - "desc" => "Description for Field 1." - ), - array( - "title" => "Field 2", - "type" => "text", - "name" => "field2", - "default" => $somevar, - "desc" => "Description for Field 2." - ), - array( - "title" => "Date", - "type" => "date", - "name" => "date", - "default" => date("Y-m-d"), - "desc" => "Description for Date." - ), - array( - "title" => "File", - "type" => "file", - "name" => "file", - "desc" => "Description for File." - ), - "split", - "startrow", - array( - "title" => "Field 3", - "type" => "text", - "width" => "200px", - "name" => "field3", - "default" => "default value", - "desc" => "Description for Field 3." - ), - array( - "title" => "Field 4", - "type" => "text", - "width" => "200px", - "name" => "field4", - "default" => $somevar - ), - "startrow", - array( - "title" => "Field 5", - "type" => "text", - "width" => "220px", - "name" => "field5", - "default" => "default value", - "desc" => "Description for Field 5." - ), - array( - "title" => "Field 6", - "type" => "text", - "width" => "220px", - "name" => "field6", - "default" => $somevar, - "desc" => "Description for Field 6." - ), - "endrow", - "split", - array( - "title" => "Field 7", - "type" => "select", - "multiple" => true, - "mode" => "dropdown", - "height" => "250px", - "name" => "field7", - "options" => array("name" => "Name", "email" => "E-mail Address", "phone" => "Phone Number"), - "default" => array(), - "desc" => "Description for Field 7." - ), - ), - "submit" => "Save", - "focus" => true - ); - - BB_GeneratePage("Some Page", $menuopts, $contentopts); - } - else if (isset($_REQUEST["action"]) && $_REQUEST["action"] == "somepage2_getinfo") - { - $id = (int)$_REQUEST["id"]; - -?> - - "mainitem_" . $x, - "display" => "Item #" . $x, - "onclick" => "LoadItem('" . $x . "');" - ); - } - - ob_start(); -?> - - $items, - "topbarhtml" => "An optional top bar", - "initialhtml" => "Select an option from the left.", - "bottombarhtml" => "An optional bottom bar", - "javascript" => $js - ); - - BB_GenerateBulkEditPage("Some Bulk Edit Page", $contentopts); - } - else - { - $contentopts = array( - "desc" => "Pick an option from the menu." - ); - - BB_GeneratePage("Home", $menuopts, $contentopts); - } - - // [Put any cleanup/finalization logic here.] -?> \ No newline at end of file diff --git a/support/admin.css b/support/admin.css index 9e34bf3..ef0c141 100644 --- a/support/admin.css +++ b/support/admin.css @@ -369,7 +369,7 @@ div.maincontent div.propmain div.formfields div.formitem div.formitemtitle { margin-bottom: 2px; } -div.maincontent div.propmain div.formfields div.formitem div.static { +div.maincontent div.propmain div.formfields div.formitem div.static, div.maincontent div.propmain div.formfields div.formitem div.staticwrap { margin-left: 7px; width: 95%; font-size: 0.95em; diff --git a/support/admin.js b/support/admin.js index 259b4af..0a500cb 100644 --- a/support/admin.js +++ b/support/admin.js @@ -9,20 +9,4 @@ $(function() { }); $('.leftnav').clone().appendTo('#navdropdown'); - - $('input.nopasswordmanager[type=password]').each(function() { - $(this).attr('data-background-color', $(this).css('background-color')); - $(this).css('background-color', $(this).css('color')); - $(this).attr('type', 'text'); - - $(this).focus(function() { - $(this).attr('type', 'password'); - $(this).css('background-color', $(this).attr('data-background-color')); - }); - - $(this).blur(function() { - $(this).css('background-color', $(this).css('color')); - $(this).attr('type', 'text'); - }); - }); }); \ No newline at end of file diff --git a/support/admin_view.css b/support/admin_view.css new file mode 100644 index 0000000..80bfe2a --- /dev/null +++ b/support/admin_view.css @@ -0,0 +1,374 @@ +body { + margin: 0; + padding: 0; + border: 0; + font-family: Verdana, Arial, Helvetica, sans-serif; + max-width: 800px; + margin: 0 auto; +} + +div.pagewrap { + margin: 0 5px 5px 5px; +} + +div.pagewrap div.headerwrap { + text-align: center; + font-weight: bold; + font-size: 1.7em; + border-left: 1px solid #BBBBBB; + border-right: 1px solid #BBBBBB; + border-bottom: 1px solid #BBBBBB; + background-color: #F5F5F5; + padding: 0.1em; + border-bottom-right-radius: 3px; + border-bottom-left-radius: 3px; + -moz-border-radius-bottomright: 3px; + -moz-border-radius-bottomleft: 3px; + -webkitborder-bottom-right-radius: 3px; + -webkitborder-bottom-left-radius: 3px; +} + +div.pagewrap div.contentwrap { + margin-top: 10px; +} + +div.pagewrap div.menuwrap { + margin-top: 7px; + border-top: 1px solid #CCCCCC; + padding: 25px 0 10px; +} + +/* Basic styles for elements. */ +img { + border: 0px none; + margin: 0px; +} + +form { + border: 0px none; + margin: 0px; +} + +a { + color: #035488; + text-decoration: none; +} + +a:hover { + color: #444444; + text-decoration: underline; +} + +/* Optional message styles. */ +div.message { + margin: 10px 7px; + font-weight: bold; +} + +div.message .success { + background-color: #E8FCDC; + border: 1px solid #008800; + color: #008800; + padding: 7px 15px; + border-radius: 3px; + -moz-border-radius: 3px; + -webkit-border-radius: 3px; +} + +div.message .error { + background-color: #FCDCDC; + border: 1px solid #880000; + color: #880000; + padding: 7px 15px; + border-radius: 3px; + -moz-border-radius: 3px; + -webkit-border-radius: 3px; +} + +div.message .info { + background-color: #DCDCDC; + border: 1px solid #333333; + color: #333333; + padding: 7px 15px; + border-radius: 3px; + -moz-border-radius: 3px; + -webkit-border-radius: 3px; +} + +div.maincontent { +} + +div.maincontent div.proptitle { + font-weight: bold; + font-size: 1.3em; + color: #1F1F1F; +} + +div.maincontent div.propdesc { + margin-top: 5px; + padding: 10px 7px; + border-top: 1px solid #CCCCCC; + color: #333333; + font-size: 1.0em; +} + +div.maincontent div.propinfo { +} + +div.maincontent div.propmain { + margin: 0px 7px; +} + +div.maincontent div.propmain div.formfields { + font-size: 0.9em; +} + +div.maincontent div.propmain div.formfields div.formaccordionwrap.ui-accordion { + margin-bottom: 7px; +} + +div.maincontent div.propmain div.formfields div.formaccordionwrap.ui-accordion h3.ui-accordion-header { + margin-top: 7px; +} + +div.maincontent div.propmain div.formfields div.formaccordionwrap.ui-accordion h3.ui-state-active { + background: url("jquery_ui_themes/smoothness/images/ui-bg_glass_75_e6e6e6_1x400.png") repeat-x scroll 50% 50% #E6E6E6; +} + +div.maincontent div.propmain div.formfields div.formaccordionwrap.ui-accordion div.formaccordionitems.ui-accordion-content { + padding: 0 14px; + background: none; + background-color: #FEFEFE; +} + +div.maincontent div.propmain div.formfields div.formaccordionwrap.ui-accordion div.formaccordionitems.ui-widget-content a { + color: #035488; +} + +div.maincontent div.propmain div.formfields div.formaccordionwrap.ui-accordion div.formaccordionitems.ui-accordion-content div.formitem { + margin-top: 6px; + border-top: 1px dotted #CCCCCC; + padding-top: 6px; +} + +div.maincontent div.propmain div.formfields div.formaccordionwrap.ui-accordion div.formaccordionitems.ui-accordion-content div.formitem.firstitem { + border-top: 0; +} + +div.maincontent div.propmain div.formfields div.formitem { + margin: 0.9em 0 0.5em; +} + +div.maincontent div.propmain div.formfields div.formitem div.formitemtitle { + font-weight: bold; + margin-bottom: 2px; +} + +div.maincontent div.propmain div.formfields div.formitem div.staticwrap, div.maincontent div.propmain div.formfields div.formitem div.static { + margin-left: 7px; + width: 95%; + font-size: 0.95em; + color: #333333; +} + +div.maincontent div.propmain div.formfields div.formitem input.text { + padding: 0.5em; + width: 95%; + border: 1px solid #BBBBBB; +} + +div.maincontent div.propmain div.formfields div.formitem input.text:hover { + border: 1px solid #888888; +} + +div.maincontent div.propmain div.formfields div.formitem input.text:focus { + border: 1px solid #888888; +} + +div.maincontent div.propmain div.formfields div.formitem input.date { + padding: 0.5em; + max-width: 20em; + width: 95%; + border: 1px solid #BBBBBB; +} + +div.maincontent div.propmain div.formfields div.formitem input.date:hover { + border: 1px solid #888888; +} + +div.maincontent div.propmain div.formfields div.formitem input.date:focus { + border: 1px solid #888888; +} + +div.maincontent div.propmain div.formfields div.formitem div.textareawrap { + width: 95%; +} + +div.maincontent div.propmain div.formfields div.formitem textarea.text { + padding: 0.3em; + width: 100%; + border: 1px solid #BBBBBB; +} + +div.maincontent div.propmain div.formfields div.formitem textarea.text:hover { + border: 1px solid #888888; +} + +div.maincontent div.propmain div.formfields div.formitem textarea.text:focus { + border: 1px solid #888888; +} + +div.maincontent div.propmain div.formfields div.formitem input.checkbox { + border: 1px solid #BBBBBB; +} + +div.maincontent div.propmain div.formfields div.formitem select { + padding: 0.3em; + width: 95%; + max-width: 750px; + border: 1px solid #BBBBBB; +} + +div.maincontent div.propmain div.formfields div.formitem select:hover { + border: 1px solid #888888; +} + +div.maincontent div.propmain div.formfields div.formitem select:focus { + border: 1px solid #888888; +} + +div.maincontent div.propmain div.formfields div.formitem table.viewtable td { + vertical-align: top; +} + +div.maincontent div.propmain div.formfields div.formitem table.viewtable td.datakey { + padding-right: 2.0em; + font-weight: bold; + white-space: nowrap; +} + +div.maincontent div.propmain div.formfields div.formitem div.formitemdesc { + margin-left: 7px; + font-size: 0.9em; + color: #333333; +} + +div.maincontent div.propmain div.formsubmit { + margin: 15px auto; + text-align: center; +} + +div.maincontent div.propmain div.formsubmit input.submit { + color: #1F1F1F; + font-size: 0.9em; + font-weight: bold; + padding: 0.2em 0.5em; +} + +div.maincontent div.propmain hr { + margin-top: 0.7em; + border: none 0; + border-top: 1px dashed #AAAAAA; + height: 1px; +} + +div.maincontent div.propmain div.nontablewrap { + border-left: 1px solid #CCCCCC; + border-right: 1px solid #CCCCCC; + border-bottom: 1px solid #CCCCCC; + color: #333333; +} + +div.maincontent div.propmain div.nontable_row { + background-color: #F5F5F5; + border-top: 1px dashed #CCCCCC; + padding: 0.5em; +} + +div.maincontent div.propmain div.nontable_row.firstrow { + border-top: 1px solid #CCCCCC; +} + +div.maincontent div.propmain div.nontable_row.altrow { + background-color: #FEFEFE; +} + +div.maincontent div.propmain div.nontable_th { + margin-top: 0.4em; + font-weight: bold; +} + +div.maincontent div.propmain div.nontable_th.firstcol { + margin-top: 0; +} + +div.maincontent div.propmain div.nontable_td { + margin-left: 7px; + color: #444444; +} + +div.maincontent div.propmain div.mediawrap { + float: right; + margin-left: 2.0em; + max-width: 45%; + margin-bottom: 0.5em; +} + +@media (max-width: 600px) { + div.maincontent div.propmain div.mediawrap { + float: none; + margin-left: 0; + max-width: none; + } +} + +div.maincontent div.propmain div.mediawrap div.mediaitemtitle { + font-weight: bold; + margin-bottom: 0.3em; +} + +div.maincontent div.propmain div.mediawrap .mediaitem { + max-width: 100%; +} + +div.maincontent div.propmain div.mediawrap div.mediaitemdesc { + font-size: 0.9em; + color: #333333; +} + +/* Optional menu navigation styles. */ +div.menuwrap { +} + +div.menuwrap div.menu { + font-size: 0.7em; + border: 1px solid #BBBBBB; + background-color: #F5F5F5; + margin-top: 7px; + padding: 5px; +} + +div.menuwrap div.menu div.title { + font-weight: bold; + padding-bottom: 6px; +} + +div.menuwrap div.menu a { + display: block; + border-top: 1px solid #CCCCCC; + padding: 12px 15px; +} + +div.menuwrap div.menu a:hover { + background-color: #EAEAEA; + text-decoration: none; +} + +/* Floating navigation menu styles. */ +div.maincontent div.proptitle div#navbutton { + display: none; +} + +div.maincontent div.proptitle div#navdropdown { + display: none; +} diff --git a/support/admin_view_print.css b/support/admin_view_print.css new file mode 100644 index 0000000..bcd9b8b --- /dev/null +++ b/support/admin_view_print.css @@ -0,0 +1,3 @@ +div.pagewrap div.menuwrap { + display: none; +} diff --git a/support/flex_forms.php b/support/flex_forms.php new file mode 100644 index 0000000..ec5a904 --- /dev/null +++ b/support/flex_forms.php @@ -0,0 +1,1228 @@ +state = array( + "formnum" => 0, + "formtables" => true, + "formwidths" => true, + "autofocused" => false, + "jqueryuiused" => false, + "jqueryuitheme" => "smoothness", + "supporturl" => "support", + "customfieldtypes" => array(), + "ajax" => false, + "action" => self::GetRequestURLBase(), + "js" => array(), + "jsoutput" => array(), + "css" => array(), + "cssoutput" => array() + ); + + $this->secretkey = false; + $this->extrainfo = ""; + $this->autononce = false; + } + + public function GetState() + { + return $this->state; + } + + public function SetState($newstate) + { + $this->state = array_merge($this->state, $newstate); + } + + public function SetAjax($enable) + { + if ($enable) + { + $this->state["ajax"] = true; + $this->state["action"] = self::GetFullRequestURLBase(); + } + else + { + $this->state["ajax"] = false; + $this->state["action"] = self::GetRequestURLBase(); + } + } + + public function AddJS($name, $info) + { + $this->state["js"][$name] = $info; + } + + public function SetJSOutput($name) + { + $this->state["jsoutput"][$name] = true; + } + + public function AddCSS($name, $info) + { + $this->state["css"][$name] = $info; + } + + public function SetCSSOutput($name) + { + $this->state["cssoutput"][$name] = true; + } + + public function SetSecretKey($secretkey) + { + $this->secretkey = (string)$secretkey; + } + + public function SetTokenExtraInfo($extrainfo) + { + $this->extrainfo = (string)$extrainfo; + } + + public function CreateSecurityToken($action, $extra = "") + { + if ($this->secretkey === false) + { + echo self::FFTranslate("Secret key not set for form."); + exit(); + } + + $str = $action . ":" . $this->extrainfo; + if (is_string($extra) && $extra !== "") + { + $extra = explode(",", $extra); + foreach ($extra as $key) + { + $key = trim($key); + if ($key !== "" && isset($_REQUEST[$key])) $str .= ":" . (string)$_REQUEST[$key]; + } + } + else if (is_array($extra)) + { + foreach ($extra as $val) $str .= ":" . $val; + } + + return hash_hmac("sha1", $str, $this->secretkey); + } + + public static function IsSecExtraOpt($opt) + { + return (isset($_REQUEST["sec_extra"]) && strpos("," . $_REQUEST["sec_extra"] . ",", "," . $opt . ",") !== false); + } + + public function CheckSecurityToken($action) + { + if (isset($_REQUEST[$action]) && (!isset($_REQUEST["sec_t"]) || $_REQUEST["sec_t"] != $this->CreateSecurityToken($_REQUEST[$action], (isset($_REQUEST["sec_extra"]) ? $_REQUEST["sec_extra"] : "")))) + { + echo self::FFTranslate("Invalid security token. Cross-site scripting (XSRF attack) attempt averted."); + exit(); + } + else if (isset($_REQUEST[$action])) + { + $this->autononce = array("action" => $action, "value" => $_REQUEST[$action]); + } + } + + public function OutputFormCSS($delaycss = false) + { + if (!isset($this->state["cssoutput"]["formcss"])) + { + if ($delaycss) $this->state["css"]["formcss"] = array("mode" => "link", "dependency" => false, "src" => $this->state["supporturl"] . "/flex_forms.css"); + else + { +?> + " type="text/css" media="all" /> +state["cssoutput"]["formcss"] = true; + } + } + } + + public function OutputMessage($type, $message) + { + $type = strtolower((string)$type); + if ($type === "warn") $type = "warning"; + + $this->OutputFormCSS(); + +?> + +CreateSecurityToken("forms__message", array($type, $message)); + } + + public function OutputSignedMessage($prefix = "") + { + if (isset($_REQUEST[$prefix . "msgtype"]) && isset($_REQUEST[$prefix . "msg"]) && isset($_REQUEST[$prefix . "msg_t"]) && $_REQUEST[$prefix . "msg_t"] === $this->CreateSecurityToken("forms__message", array($_REQUEST[$prefix . "msgtype"], $_REQUEST[$prefix . "msg"]))) + { + $this->OutputMessage($_REQUEST[$prefix . "msgtype"], htmlspecialchars($_REQUEST[$prefix . "msg"])); + } + } + + public function OutputJQuery($delayjs = false) + { + if (!isset($this->state["jsoutput"]["jquery"])) + { + if ($delayjs) $this->state["js"]["jquery"] = array("mode" => "src", "dependency" => false, "src" => $this->state["supporturl"] . "/jquery-3.1.1.min.js", "detect" => "jQuery"); + else + { +?> + +state["jsoutput"]["jquery"] = true; + } + } + } + + public function OutputJQueryUI($delayjs = false) + { + $this->OutputJQuery($delayjs); + + if (!isset($this->state["jsoutput"]["jqueryui"])) + { + if ($delayjs) + { + $this->state["css"]["jqueryui"] = array("mode" => "link", "dependency" => false, "src" => $this->state["supporturl"] . "/jquery_ui_themes/" . $this->state["jqueryuitheme"] . "/jquery-ui-1.12.1.css"); + $this->state["js"]["jqueryui"] = array("mode" => "src", "dependency" => "jquery", "src" => $this->state["supporturl"] . "/jquery-ui-1.12.1.min.js", "detect" => "jQuery.ui"); + } + else + { +?> + state["jqueryuitheme"] . "/jquery-ui-1.12.1.css"); ?>" type="text/css" media="all" /> + +state["cssoutput"]["jqueryui"] = true; + $this->state["jsoutput"]["jqueryui"] = true; + } + } + } + + public static function GetValue($key, $default) + { + return (isset($_REQUEST[$key]) ? $_REQUEST[$key] : $default); + } + + public static function GetSelectValues($data) + { + $result = array(); + foreach ($data as $val) $result[$val] = true; + + return $result; + } + + public static function ProcessInfoDefaults($info, $defaults) + { + foreach ($defaults as $key => $val) + { + if (!isset($info[$key])) $info[$key] = $val; + } + + return $info; + } + + public static function SetNestedPathValue(&$data, $pathparts, $val) + { + $curr = &$data; + foreach ($pathparts as $key) + { + if (!isset($curr[$key])) $curr[$key] = array(); + + $curr = &$curr[$key]; + } + + $curr = $val; + } + + public static function GetIDDiff($origids, $newids) + { + $result = array("remove" => array(), "add" => array()); + foreach ($origids as $id => $val) + { + if (!isset($newids[$id])) $result["remove"][$id] = $val; + } + + foreach ($newids as $id => $val) + { + if (!isset($origids[$id])) $result["add"][$id] = $val; + } + + return $result; + } + + public function GetHashedFieldName($name) + { + if ($this->secretkey === false) + { + echo self::FFTranslate("Secret key not set."); + exit(); + } + + return "f_" . hash_hmac("md5", $name, $this->secretkey); + } + + public function GetHashedFieldValues($nameswithdefaults) + { + if ($this->secretkey === false) + { + echo self::FFTranslate("Secret key not set."); + exit(); + } + + $result = array(); + foreach ($nameswithdefaults as $name => $default) + { + $name2 = "f_" . hash_hmac("md5", $name, $this->secretkey); + + $result[$name] = (isset($_REQUEST[$name2]) ? $_REQUEST[$name2] : $default); + } + + return $result; + } + + public function Generate($options, $errors = array(), $lastform = true) + { + $this->InitFormVars($options); + + $this->OutputFormCSS(); + +?> +$val) echo " " . $key . "=\"" . htmlspecialchars($val) . "\""; ?>> | + +
---|
$val) echo " " . $key . "=\"" . htmlspecialchars($val) . "\""; } ?>> | + +