Initial commit
authorSteve Sutton <steve@gaslightmedia.com>
Tue, 17 Feb 2015 14:45:23 +0000 (09:45 -0500)
committerSteve Sutton <steve@gaslightmedia.com>
Tue, 17 Feb 2015 14:45:23 +0000 (09:45 -0500)
940 files changed:
.htaccess [new file with mode: 0644]
Toolkit/BaseControllerAbstract.php [new file with mode: 0644]
Toolkit/Blocks/Admin/EditPage.php [new file with mode: 0644]
Toolkit/Blocks/Admin/ListPages.php [new file with mode: 0644]
Toolkit/Blocks/Admin/PageTree.php [new file with mode: 0644]
Toolkit/Blocks/Block.php [new file with mode: 0644]
Toolkit/Blocks/Database/application.sql [new file with mode: 0644]
Toolkit/Blocks/Database/removeApplication.sql [new file with mode: 0644]
Toolkit/Blocks/Database/tables/blocks.sql [new file with mode: 0644]
Toolkit/Blocks/Database/upgrade.sql [new file with mode: 0644]
Toolkit/Blocks/Display.php [new file with mode: 0644]
Toolkit/Blocks/IndexController.php [new file with mode: 0644]
Toolkit/Blocks/application.ini [new file with mode: 0644]
Toolkit/Blocks/assets/btn_add.gif [new file with mode: 0755]
Toolkit/Blocks/assets/btn_add_sm.gif [new file with mode: 0755]
Toolkit/Blocks/assets/btn_back.gif [new file with mode: 0755]
Toolkit/Blocks/assets/btn_delete.gif [new file with mode: 0755]
Toolkit/Blocks/assets/btn_edit.gif [new file with mode: 0755]
Toolkit/Blocks/assets/btn_link.gif [new file with mode: 0755]
Toolkit/Blocks/assets/btn_update.gif [new file with mode: 0755]
Toolkit/Blocks/assets/btn_upload.gif [new file with mode: 0755]
Toolkit/Blocks/assets/left_circle.gif [new file with mode: 0755]
Toolkit/Blocks/assets/right_circle.gif [new file with mode: 0755]
Toolkit/Blocks/css/style.css [new file with mode: 0644]
Toolkit/Blocks/js/column.js [new file with mode: 0644]
Toolkit/Blocks/js/editPage.js [new file with mode: 0644]
Toolkit/Blocks/js/jquery.columnview.js [new file with mode: 0644]
Toolkit/Blocks/js/listPages.js [new file with mode: 0644]
Toolkit/Blocks/positionBlock.php [new file with mode: 0644]
Toolkit/Blocks/templates/editPage.html [new file with mode: 0644]
Toolkit/Blocks/templates/listPages.html [new file with mode: 0644]
Toolkit/BreadCrumbsFactory.php [new file with mode: 0644]
Toolkit/CKImages/Connector.php [new file with mode: 0644]
Toolkit/CKImages/Database/Import.php [new file with mode: 0755]
Toolkit/CKImages/Database/application.sql [new file with mode: 0644]
Toolkit/CKImages/Database/data/applicationData.sql [new file with mode: 0644]
Toolkit/CKImages/Database/importHtImages.php [new file with mode: 0755]
Toolkit/CKImages/Database/phpImageEditorUpdates.sql [new file with mode: 0644]
Toolkit/CKImages/Database/removeApplication.sql [new file with mode: 0644]
Toolkit/CKImages/Database/tables/ckeditor_folders.sql [new file with mode: 0644]
Toolkit/CKImages/Database/tables/ckeditor_images.sql [new file with mode: 0644]
Toolkit/CKImages/Exception.php [new file with mode: 0644]
Toolkit/CKImages/Factory.php [new file with mode: 0644]
Toolkit/CKImages/Folders.php [new file with mode: 0644]
Toolkit/CKImages/ImageUpdater.php [new file with mode: 0644]
Toolkit/CKImages/assets/.keepme [new file with mode: 0644]
Toolkit/CKImages/assets/night-fate-stock2.jpg [new file with mode: 0755]
Toolkit/CKImages/browser.php [new file with mode: 0644]
Toolkit/CKImages/controller.php [new file with mode: 0644]
Toolkit/CKImages/imageFetch.php [new file with mode: 0644]
Toolkit/CKImages/libjs/image_selector.js [new file with mode: 0644]
Toolkit/CKImages/styles.css [new file with mode: 0755]
Toolkit/CKImages/templates/thumbnails.html [new file with mode: 0755]
Toolkit/Common.php [new file with mode: 0644]
Toolkit/Contacts/Admin/EditContact.php [new file with mode: 0644]
Toolkit/Contacts/ContactUs.php [new file with mode: 0755]
Toolkit/Contacts/Database/application.sql [new file with mode: 0644]
Toolkit/Contacts/Database/removeApplication.sql [new file with mode: 0644]
Toolkit/Contacts/Database/tables/contact.sql [new file with mode: 0644]
Toolkit/Contacts/Database/tables/contact_inq.sql [new file with mode: 0644]
Toolkit/Contacts/Database/tables/inq_group.sql [new file with mode: 0644]
Toolkit/Contacts/Database/tables/news_response.sql [new file with mode: 0644]
Toolkit/Contacts/Database/tables/query_db.sql [new file with mode: 0644]
Toolkit/Contacts/ENewsSignup.php [new file with mode: 0755]
Toolkit/Contacts/SaveTripPlanner.php [new file with mode: 0755]
Toolkit/Contacts/SendTripPlanner.php [new file with mode: 0755]
Toolkit/Contacts/StreamSend.php [new file with mode: 0755]
Toolkit/Contacts/VisitorGuide.php [new file with mode: 0755]
Toolkit/Contacts/application.ini [new file with mode: 0644]
Toolkit/Contacts/assets/.keepme [new file with mode: 0644]
Toolkit/Contacts/config.ini [new file with mode: 0644]
Toolkit/Contacts/database-table-modifiers.sql [new file with mode: 0644]
Toolkit/Contacts/database-tables.sql [new file with mode: 0644]
Toolkit/Contacts/templates/brochurePage.html [new file with mode: 0755]
Toolkit/Contacts/templates/contactForm.html [new file with mode: 0644]
Toolkit/Contacts/templates/currentTables/Element.tpl [new file with mode: 0755]
Toolkit/Contacts/templates/currentTables/Form.tpl [new file with mode: 0755]
Toolkit/Contacts/templates/currentTables/Group.tpl [new file with mode: 0755]
Toolkit/Contacts/templates/currentTables/GroupElement.tpl [new file with mode: 0755]
Toolkit/Contacts/templates/currentTables/Header.tpl [new file with mode: 0755]
Toolkit/Contacts/templates/currentTables/RequiredNote.tpl [new file with mode: 0755]
Toolkit/Contacts/templates/direct-list.html [new file with mode: 0755]
Toolkit/Contacts/templates/emailOwner.tpl [new file with mode: 0755]
Toolkit/Contacts/templates/friendEmail.html [new file with mode: 0644]
Toolkit/Contacts/templates/pdfDownloadEmail.html [new file with mode: 0755]
Toolkit/DataGridBuilder.php [new file with mode: 0644]
Toolkit/Database.php [new file with mode: 0644]
Toolkit/Events/AddCommonEventForm.php [new file with mode: 0644]
Toolkit/Events/AddEventForm.php [new file with mode: 0755]
Toolkit/Events/Ajax.php [new file with mode: 0644]
Toolkit/Events/Auxiliary.php [new file with mode: 0644]
Toolkit/Events/Calendar.php [new file with mode: 0755]
Toolkit/Events/CategoryBlocks.php [new file with mode: 0644]
Toolkit/Events/Database/addJustHideAddress.sql [new file with mode: 0644]
Toolkit/Events/Database/addJustHideAddressNoSchema.sql [new file with mode: 0644]
Toolkit/Events/Database/application.sql [new file with mode: 0644]
Toolkit/Events/Database/applicationWithMembers.sql [new file with mode: 0644]
Toolkit/Events/Database/removeApplication.sql [new file with mode: 0644]
Toolkit/Events/Database/tables/event.sql [new file with mode: 0644]
Toolkit/Events/Database/tables/event_recur.sql [new file with mode: 0644]
Toolkit/Events/Database/tables/event_with_members.sql [new file with mode: 0644]
Toolkit/Events/Database/tables/topic.sql [new file with mode: 0644]
Toolkit/Events/Database/updateEventsTable.sql [new file with mode: 0644]
Toolkit/Events/Database/updateEventsTableNoSchema.sql [new file with mode: 0644]
Toolkit/Events/Display.php [new file with mode: 0755]
Toolkit/Events/EventCalendar.php [new file with mode: 0755]
Toolkit/Events/EventHomePage.php [new file with mode: 0644]
Toolkit/Events/Forms/Admin/EditEvent.php [new file with mode: 0755]
Toolkit/Events/Forms/Admin/EditTopics.php [new file with mode: 0644]
Toolkit/Events/HomeEvents.php [new file with mode: 0644]
Toolkit/Events/ListEvents.php [new file with mode: 0644]
Toolkit/Events/QueryBuilder.php [new file with mode: 0644]
Toolkit/Events/SmallCal.php [new file with mode: 0644]
Toolkit/Events/UserSearchForm.php [new file with mode: 0644]
Toolkit/Events/assets/.keepme [new file with mode: 0644]
Toolkit/Events/assets/arrowDown.png [new file with mode: 0755]
Toolkit/Events/assets/arrowUp.png [new file with mode: 0755]
Toolkit/Events/assets/go.png [new file with mode: 0755]
Toolkit/Events/config.ini [new file with mode: 0644]
Toolkit/Events/css/edit-topic.css [new file with mode: 0644]
Toolkit/Events/css/event.css [new file with mode: 0755]
Toolkit/Events/getEvents.php [new file with mode: 0644]
Toolkit/Events/libjs/AddCommonEventForm.php [new file with mode: 0644]
Toolkit/Events/libjs/addEvent.js [new file with mode: 0644]
Toolkit/Events/libjs/calendar.js [new file with mode: 0644]
Toolkit/Events/libjs/edit-event.js [new file with mode: 0644]
Toolkit/Events/libjs/edit-topic.js [new file with mode: 0644]
Toolkit/Events/libjs/editEvent.js [new file with mode: 0644]
Toolkit/Events/libjs/eventLoader.js [new file with mode: 0644]
Toolkit/Events/libjs/eventRotate.js [new file with mode: 0644]
Toolkit/Events/libjs/events.js [new file with mode: 0644]
Toolkit/Events/libjs/geoCoder.js [new file with mode: 0644]
Toolkit/Events/templates/currentTables/Element.tpl [new file with mode: 0755]
Toolkit/Events/templates/currentTables/Form.tpl [new file with mode: 0755]
Toolkit/Events/templates/currentTables/Group.tpl [new file with mode: 0755]
Toolkit/Events/templates/currentTables/GroupElement.tpl [new file with mode: 0755]
Toolkit/Events/templates/currentTables/Header.tpl [new file with mode: 0755]
Toolkit/Events/templates/currentTables/RequiredNote.tpl [new file with mode: 0755]
Toolkit/Events/templates/editTopics.html [new file with mode: 0644]
Toolkit/Events/templates/emailOwner.tpl [new file with mode: 0755]
Toolkit/Events/templates/eventDetail.html [new file with mode: 0755]
Toolkit/Events/templates/eventFeed.html [new file with mode: 0644]
Toolkit/Events/templates/eventHomePage.html [new file with mode: 0644]
Toolkit/Events/templates/eventPage.html [new file with mode: 0644]
Toolkit/Events/templates/eventSearchForm.html [new file with mode: 0755]
Toolkit/Events/templates/events.html [new file with mode: 0755]
Toolkit/Events/templates/form.html [new file with mode: 0644]
Toolkit/FileServer/AdapterAbstract.php [new file with mode: 0644]
Toolkit/FileServer/Exception.php [new file with mode: 0644]
Toolkit/FileServer/FileAdapter.php [new file with mode: 0644]
Toolkit/FileServer/ImageAdapter.php [new file with mode: 0644]
Toolkit/FileServer/Mock/ImageAdapter.php [new file with mode: 0644]
Toolkit/FlexyDataGridBuilder.php [new file with mode: 0644]
Toolkit/Form.php [new file with mode: 0644]
Toolkit/FormBuilder.php [new file with mode: 0644]
Toolkit/Forms/Rules/Date.php [new file with mode: 0644]
Toolkit/Forms/Rules/Image.php [new file with mode: 0644]
Toolkit/Forms/Rules/ShortUrl.php [new file with mode: 0644]
Toolkit/Forms/Rules/Url.php [new file with mode: 0644]
Toolkit/Forms/templates/tables/Element.tpl [new file with mode: 0644]
Toolkit/Forms/templates/tables/Form.tpl [new file with mode: 0644]
Toolkit/Forms/templates/tables/Group.tpl [new file with mode: 0644]
Toolkit/Forms/templates/tables/GroupElement.tpl [new file with mode: 0644]
Toolkit/Forms/templates/tables/Header.tpl [new file with mode: 0644]
Toolkit/Forms/templates/tables/RequiredNote.tpl [new file with mode: 0644]
Toolkit/IController.php [new file with mode: 0644]
Toolkit/IMapper.php [new file with mode: 0644]
Toolkit/INavigation.php [new file with mode: 0644]
Toolkit/ITable.php [new file with mode: 0644]
Toolkit/Image/Converter.php [new file with mode: 0644]
Toolkit/Image/Server.php [new file with mode: 0755]
Toolkit/LeadManager/Affiliates/ConstantContact.php [new file with mode: 0644]
Toolkit/LeadManager/Affiliates/StreamSend.php [new file with mode: 0755]
Toolkit/LeadManager/ConstantContact.php [new file with mode: 0644]
Toolkit/LeadManager/Contact.php [new file with mode: 0644]
Toolkit/LeadManager/Customer.php [new file with mode: 0644]
Toolkit/LeadManager/Lead.php [new file with mode: 0644]
Toolkit/LeadManager/Observer.php [new file with mode: 0644]
Toolkit/LeadManager/StreamSend.php [new file with mode: 0644]
Toolkit/LeadManager/Subject.php [new file with mode: 0644]
Toolkit/Logger.php [new file with mode: 0644]
Toolkit/Maps/geoCoder.js [new file with mode: 0644]
Toolkit/Maps/marker.php [new file with mode: 0755]
Toolkit/Maps/marker2.php [new file with mode: 0644]
Toolkit/Members.php [new file with mode: 0644]
Toolkit/Members/AddPhoto.php [new file with mode: 0644]
Toolkit/Members/AddYourBusiness.php [new file with mode: 0755]
Toolkit/Members/Admin/AddPhoto.php [new file with mode: 0644]
Toolkit/Members/Admin/AdvancedSearch.php [new file with mode: 0644]
Toolkit/Members/Admin/AdvancedSearchController.php [new file with mode: 0644]
Toolkit/Members/Admin/AdvancedSearchDataGrid.php [new file with mode: 0644]
Toolkit/Members/Admin/AmenitiesController.php [new file with mode: 0644]
Toolkit/Members/Admin/AuthorizeNewMemberForm.php [new file with mode: 0755]
Toolkit/Members/Admin/AuthorizeUpdates.php [new file with mode: 0755]
Toolkit/Members/Admin/Auxiliary.php [new file with mode: 0644]
Toolkit/Members/Admin/BasicSearch.php [new file with mode: 0644]
Toolkit/Members/Admin/BillingController.php [new file with mode: 0644]
Toolkit/Members/Admin/CategoriesController.php [new file with mode: 0644]
Toolkit/Members/Admin/CitiesController.php [new file with mode: 0644]
Toolkit/Members/Admin/CountiesController.php [new file with mode: 0644]
Toolkit/Members/Admin/EditAmenity.php [new file with mode: 0644]
Toolkit/Members/Admin/EditCategory.php [new file with mode: 0644]
Toolkit/Members/Admin/EditCity.php [new file with mode: 0644]
Toolkit/Members/Admin/EditHtmlEmail.php [new file with mode: 0644]
Toolkit/Members/Admin/EditPackages.php [new file with mode: 0644]
Toolkit/Members/Admin/EditPhoto.php [new file with mode: 0644]
Toolkit/Members/Admin/EditRegion.php [new file with mode: 0644]
Toolkit/Members/Admin/ExportController.php [new file with mode: 0644]
Toolkit/Members/Admin/ExportFileForm.php [new file with mode: 0644]
Toolkit/Members/Admin/ExportMembers.php [new file with mode: 0644]
Toolkit/Members/Admin/IndexController.php [new file with mode: 0644]
Toolkit/Members/Admin/InvoicingController.php [new file with mode: 0644]
Toolkit/Members/Admin/ListAmenities.php [new file with mode: 0644]
Toolkit/Members/Admin/ListCategories.php [new file with mode: 0644]
Toolkit/Members/Admin/ListCities.php [new file with mode: 0644]
Toolkit/Members/Admin/ListHtmlEmails.php [new file with mode: 0644]
Toolkit/Members/Admin/ListMembers.php [new file with mode: 0644]
Toolkit/Members/Admin/ListNewRequests.php [new file with mode: 0755]
Toolkit/Members/Admin/ListPendingMembers.php [new file with mode: 0755]
Toolkit/Members/Admin/ListRegions.php [new file with mode: 0644]
Toolkit/Members/Admin/MailOut.php [new file with mode: 0644]
Toolkit/Members/Admin/MailOutForm.php [new file with mode: 0644]
Toolkit/Members/Admin/MemberReportSearch.php [new file with mode: 0644]
Toolkit/Members/Admin/MembersController.php [new file with mode: 0644]
Toolkit/Members/Admin/Navigation.php [new file with mode: 0644]
Toolkit/Members/Admin/NewsletterController.php [new file with mode: 0644]
Toolkit/Members/Admin/Newsletters.php [new file with mode: 0644]
Toolkit/Members/Admin/PaymentController.php [new file with mode: 0644]
Toolkit/Members/Admin/PreviewHtmlEmail.php [new file with mode: 0644]
Toolkit/Members/Admin/RegionsController.php [new file with mode: 0644]
Toolkit/Members/Admin/ReportsController.php [new file with mode: 0644]
Toolkit/Members/Admin/Search.php [new file with mode: 0644]
Toolkit/Members/Admin/SettingsController.php [new file with mode: 0644]
Toolkit/Members/Auth.php [new file with mode: 0644]
Toolkit/Members/AuthContainer.php [new file with mode: 0644]
Toolkit/Members/Auxiliary.php [new file with mode: 0644]
Toolkit/Members/Billing/AdminNavigation.php [new file with mode: 0644]
Toolkit/Members/Billing/Auxiliary.php [new file with mode: 0644]
Toolkit/Members/Billing/Billing.php [new file with mode: 0644]
Toolkit/Members/Billing/Database/application.sql [new file with mode: 0644]
Toolkit/Members/Billing/Database/tables/billing.sql [new file with mode: 0644]
Toolkit/Members/Billing/Database/tables/member.sql [new file with mode: 0644]
Toolkit/Members/Billing/Database/tables/member_account.sql [new file with mode: 0644]
Toolkit/Members/Billing/Database/tables/payment_types.sql [new file with mode: 0644]
Toolkit/Members/Billing/EditBillingForm.php [new file with mode: 0644]
Toolkit/Members/Billing/EditMemberAccount.php [new file with mode: 0644]
Toolkit/Members/Billing/EditMemberPayment.php [new file with mode: 0644]
Toolkit/Members/Billing/EditPaymentType.php [new file with mode: 0644]
Toolkit/Members/Billing/EmailInvoices.php [new file with mode: 0644]
Toolkit/Members/Billing/Factory.php [new file with mode: 0644]
Toolkit/Members/Billing/IInvoice.php [new file with mode: 0644]
Toolkit/Members/Billing/InvoiceAbstract.php [new file with mode: 0644]
Toolkit/Members/Billing/InvoiceDate.php [new file with mode: 0644]
Toolkit/Members/Billing/InvoiceFilterForm.php [new file with mode: 0644]
Toolkit/Members/Billing/InvoicePdf.php [new file with mode: 0644]
Toolkit/Members/Billing/Invoices.php [new file with mode: 0644]
Toolkit/Members/Billing/ListPaymentTypes.php [new file with mode: 0644]
Toolkit/Members/Billing/MailingLabelPdf.php [new file with mode: 0644]
Toolkit/Members/Billing/MemberAccount.php [new file with mode: 0644]
Toolkit/Members/Billing/MemberLists.php [new file with mode: 0644]
Toolkit/Members/Billing/PaymentForm.php [new file with mode: 0644]
Toolkit/Members/Billing/PaymentTypes.php [new file with mode: 0644]
Toolkit/Members/Billing/PrintInvoices.php [new file with mode: 0644]
Toolkit/Members/Billing/QifExport.php [new file with mode: 0644]
Toolkit/Members/Billing/RecordNavigation.php [new file with mode: 0644]
Toolkit/Members/Billing/Report.php [new file with mode: 0644]
Toolkit/Members/Billing/ReportSearch.php [new file with mode: 0644]
Toolkit/Members/Billing/Statement.php [new file with mode: 0644]
Toolkit/Members/Billing/billing.css [new file with mode: 0644]
Toolkit/Members/Billing/checkInvestmentType.php [new file with mode: 0644]
Toolkit/Members/Billing/config.ini [new file with mode: 0644]
Toolkit/Members/Billing/deleteBilling.php [new file with mode: 0644]
Toolkit/Members/Billing/js/edit-billing.js [new file with mode: 0644]
Toolkit/Members/Billing/pdfInvoice.php [new file with mode: 0644]
Toolkit/Members/Billing/recreateInvoices.php [new file with mode: 0644]
Toolkit/Members/Billing/templates/editBilling.html [new file with mode: 0644]
Toolkit/Members/Billing/templates/editPaymentTypes.html [new file with mode: 0644]
Toolkit/Members/Billing/templates/invoiceFilterForm.html [new file with mode: 0644]
Toolkit/Members/Billing/templates/memberList.html [new file with mode: 0644]
Toolkit/Members/Billing/templates/memberStatements.html [new file with mode: 0644]
Toolkit/Members/Billing/templates/paymentForm.html [new file with mode: 0644]
Toolkit/Members/Billing/templates/reportSearch.html [new file with mode: 0644]
Toolkit/Members/Billing/templates/sendEmailInvoice.html [new file with mode: 0644]
Toolkit/Members/Billing/testMember.php [new file with mode: 0644]
Toolkit/Members/BreadCrumbs.php [new file with mode: 0644]
Toolkit/Members/CategoriesIterator.php [new file with mode: 0644]
Toolkit/Members/Category.php [new file with mode: 0644]
Toolkit/Members/CategoryTree.php [new file with mode: 0644]
Toolkit/Members/City.php [new file with mode: 0644]
Toolkit/Members/Contact.php [new file with mode: 0644]
Toolkit/Members/ContactsDataGrid.php [new file with mode: 0644]
Toolkit/Members/Coupons/EditCouponForm.php [new file with mode: 0644]
Toolkit/Members/Coupons/Mailer.php [new file with mode: 0644]
Toolkit/Members/Database/application.sql [new file with mode: 0644]
Toolkit/Members/Database/procedures/explode_members_name_POST_8.3.sql [new file with mode: 0644]
Toolkit/Members/Database/procedures/explode_members_name_PRE_8.3.sql [new file with mode: 0644]
Toolkit/Members/Database/procedures/last_record_update_timestamp.sql [new file with mode: 0644]
Toolkit/Members/Database/removeApplication.sql [new file with mode: 0644]
Toolkit/Members/Database/tables/amenity.sql [new file with mode: 0644]
Toolkit/Members/Database/tables/banners2membercategories.sql [new file with mode: 0644]
Toolkit/Members/Database/tables/category.sql [new file with mode: 0644]
Toolkit/Members/Database/tables/category_amenities.sql [new file with mode: 0644]
Toolkit/Members/Database/tables/ccard_type.sql [new file with mode: 0644]
Toolkit/Members/Database/tables/city.sql [new file with mode: 0644]
Toolkit/Members/Database/tables/county.sql [new file with mode: 0644]
Toolkit/Members/Database/tables/exploded_members_name.sql [new file with mode: 0644]
Toolkit/Members/Database/tables/exposure.sql [new file with mode: 0644]
Toolkit/Members/Database/tables/member.sql [new file with mode: 0644]
Toolkit/Members/Database/tables/member_accommodations.sql [new file with mode: 0644]
Toolkit/Members/Database/tables/member_amenity.sql [new file with mode: 0644]
Toolkit/Members/Database/tables/member_categories2toolbox_pages.sql [new file with mode: 0644]
Toolkit/Members/Database/tables/member_categories2toolbox_pages_draft.sql [new file with mode: 0644]
Toolkit/Members/Database/tables/member_category.sql [new file with mode: 0644]
Toolkit/Members/Database/tables/member_ccard_type.sql [new file with mode: 0644]
Toolkit/Members/Database/tables/member_contacts.sql [new file with mode: 0644]
Toolkit/Members/Database/tables/member_files.sql [new file with mode: 0644]
Toolkit/Members/Database/tables/member_golf.sql [new file with mode: 0644]
Toolkit/Members/Database/tables/member_last_updates.sql [new file with mode: 0644]
Toolkit/Members/Database/tables/member_leads.sql [new file with mode: 0644]
Toolkit/Members/Database/tables/member_newsletters.sql [new file with mode: 0644]
Toolkit/Members/Database/tables/member_packages.sql [new file with mode: 0644]
Toolkit/Members/Database/tables/member_photos.sql [new file with mode: 0644]
Toolkit/Members/Database/tables/member_regions2toolbox_pages.sql [new file with mode: 0644]
Toolkit/Members/Database/tables/member_regions2toolbox_pages_draft.sql [new file with mode: 0644]
Toolkit/Members/Database/tables/member_restaurants.sql [new file with mode: 0644]
Toolkit/Members/Database/tables/member_session.sql [new file with mode: 0644]
Toolkit/Members/Database/tables/member_updates.sql [new file with mode: 0644]
Toolkit/Members/Database/tables/region.sql [new file with mode: 0644]
Toolkit/Members/Database/tables/state.sql [new file with mode: 0644]
Toolkit/Members/Database/tables/streamsend.sql [new file with mode: 0644]
Toolkit/Members/Database/utilities/unnest.sql [new file with mode: 0644]
Toolkit/Members/Database/utilities/update_exploded_members_name_table_POST_8.3.sql [new file with mode: 0644]
Toolkit/Members/Database/utilities/update_exploded_members_name_table_PRE_8.3.sql [new file with mode: 0644]
Toolkit/Members/Display.php [new file with mode: 0644]
Toolkit/Members/EditContactForm.php [new file with mode: 0644]
Toolkit/Members/EditMemberAmenities.php [new file with mode: 0644]
Toolkit/Members/EditMemberContacts.php [new file with mode: 0644]
Toolkit/Members/EditMemberFile.php [new file with mode: 0644]
Toolkit/Members/EditMemberInfo.php [new file with mode: 0644]
Toolkit/Members/EditMemberOnlyAmenities.php [new file with mode: 0644]
Toolkit/Members/EditMemberOnlyContacts.php [new file with mode: 0644]
Toolkit/Members/EditMemberOnlyFile.php [new file with mode: 0644]
Toolkit/Members/EditMemberOnlyInfo.php [new file with mode: 0644]
Toolkit/Members/EditPackages.php [new file with mode: 0644]
Toolkit/Members/EditPhoto.php [new file with mode: 0644]
Toolkit/Members/Events/CommonEditEvent.php [new file with mode: 0644]
Toolkit/Members/Events/EditEvent.php [new file with mode: 0755]
Toolkit/Members/Events/templates/emailOwner.tpl [new file with mode: 0755]
Toolkit/Members/Exception.php [new file with mode: 0644]
Toolkit/Members/Exposure.php [new file with mode: 0755]
Toolkit/Members/ExposureDetailReports.php [new file with mode: 0755]
Toolkit/Members/ExposureReports.php [new file with mode: 0755]
Toolkit/Members/FileDownload.php [new file with mode: 0644]
Toolkit/Members/FileImporter/index.php [new file with mode: 0644]
Toolkit/Members/Import/ActionDisplay.php [new file with mode: 0644]
Toolkit/Members/Import/ActionUpload.php [new file with mode: 0644]
Toolkit/Members/Import/AnalyzePage.php [new file with mode: 0644]
Toolkit/Members/Import/ConversionPage.php [new file with mode: 0644]
Toolkit/Members/Import/DBPage.php [new file with mode: 0644]
Toolkit/Members/Import/FieldsPage.php [new file with mode: 0644]
Toolkit/Members/Import/FilePage.php [new file with mode: 0644]
Toolkit/Members/Import/ImportPage.php [new file with mode: 0644]
Toolkit/Members/Import/OpPage.php [new file with mode: 0644]
Toolkit/Members/Import/Page.php [new file with mode: 0644]
Toolkit/Members/Import/ServerPage.php [new file with mode: 0644]
Toolkit/Members/Import/index.php [new file with mode: 0644]
Toolkit/Members/Import/templates/template.html [new file with mode: 0644]
Toolkit/Members/Leads/ExportFileForm.php [new file with mode: 0644]
Toolkit/Members/Leads/ExportLeads.php [new file with mode: 0644]
Toolkit/Members/Leads/libjs/visitorLeads.js [new file with mode: 0644]
Toolkit/Members/Map.php [new file with mode: 0644]
Toolkit/Members/Member.php [new file with mode: 0644]
Toolkit/Members/MemberImport/Category.php [new file with mode: 0644]
Toolkit/Members/MemberImport/City.php [new file with mode: 0644]
Toolkit/Members/MemberImport/FileForm.php [new file with mode: 0644]
Toolkit/Members/MemberImport/Import.php [new file with mode: 0644]
Toolkit/Members/MemberImport/ImportForm.php [new file with mode: 0644]
Toolkit/Members/MemberImport/Member.php [new file with mode: 0644]
Toolkit/Members/MemberImport/State.php [new file with mode: 0644]
Toolkit/Members/MemberImport/index.php [new file with mode: 0644]
Toolkit/Members/MembersOnly/BreadCrumbs.php [new file with mode: 0644]
Toolkit/Members/MembersOnly/CommonEventsController.php [new file with mode: 0644]
Toolkit/Members/MembersOnly/Controller.php [new file with mode: 0644]
Toolkit/Members/MembersOnly/CouponsController.php [new file with mode: 0644]
Toolkit/Members/MembersOnly/EditProfileController.php [new file with mode: 0644]
Toolkit/Members/MembersOnly/EventsController.php [new file with mode: 0644]
Toolkit/Members/MembersOnly/IndexController.php [new file with mode: 0644]
Toolkit/Members/MembersOnly/LeadsController.php [new file with mode: 0644]
Toolkit/Members/MembersOnly/Navigation/Factory.php [new file with mode: 0644]
Toolkit/Members/MembersOnly/Navigation/Side.php [new file with mode: 0644]
Toolkit/Members/MembersOnly/ReportsController.php [new file with mode: 0644]
Toolkit/Members/MembersOnly/index.php [new file with mode: 0644]
Toolkit/Members/PackageList.php [new file with mode: 0644]
Toolkit/Members/Packages.php [new file with mode: 0644]
Toolkit/Members/Packages/Package.php [new file with mode: 0644]
Toolkit/Members/Photos.php [new file with mode: 0644]
Toolkit/Members/Photos/Photo.php [new file with mode: 0644]
Toolkit/Members/ProfilePage.php [new file with mode: 0644]
Toolkit/Members/ProfileWriter.php [new file with mode: 0644]
Toolkit/Members/RecordNavigation.php [new file with mode: 0644]
Toolkit/Members/Rules/DuplicateMember.php [new file with mode: 0644]
Toolkit/Members/Rules/MemberFile.php [new file with mode: 0644]
Toolkit/Members/Rules/MemberLogin.php [new file with mode: 0644]
Toolkit/Members/SearchList.php [new file with mode: 0644]
Toolkit/Members/SearchQueryGenerator.php [new file with mode: 0644]
Toolkit/Members/StreamSend.php [new file with mode: 0644]
Toolkit/Members/TripPlanner/AuthContainer.php [new file with mode: 0644]
Toolkit/Members/TripPlanner/Forgot.php [new file with mode: 0644]
Toolkit/Members/TripPlanner/Sessions.php [new file with mode: 0644]
Toolkit/Members/TripPlanner/TripAuth.php [new file with mode: 0644]
Toolkit/Members/TripPlanner/googleMapData.php [new file with mode: 0644]
Toolkit/Members/TripPlanner/helpme.html [new file with mode: 0644]
Toolkit/Members/TripPlanner/login.php [new file with mode: 0644]
Toolkit/Members/TripPlanner/memberList.php [new file with mode: 0755]
Toolkit/Members/TripPlanner/tripPlannerMap.php [new file with mode: 0644]
Toolkit/Members/TripPlanner/wish-list.php [new file with mode: 0755]
Toolkit/Members/TripPlannerList.php [new file with mode: 0644]
Toolkit/Members/UserSearchForm.php [new file with mode: 0644]
Toolkit/Members/addressHelp.html [new file with mode: 0644]
Toolkit/Members/assets/.keepme [new file with mode: 0644]
Toolkit/Members/assets/btn_close.gif [new file with mode: 0755]
Toolkit/Members/assets/btn_planneractions.gif [new file with mode: 0755]
Toolkit/Members/assets/btn_submit.gif [new file with mode: 0755]
Toolkit/Members/assets/saveplanner.gif [new file with mode: 0755]
Toolkit/Members/config.ini [new file with mode: 0644]
Toolkit/Members/css/member-admin.css [new file with mode: 0755]
Toolkit/Members/css/member.css [new file with mode: 0755]
Toolkit/Members/export-images-is0.php [new file with mode: 0755]
Toolkit/Members/libjs/advancedsearch.js [new file with mode: 0644]
Toolkit/Members/libjs/authorizeChanges.js [new file with mode: 0644]
Toolkit/Members/libjs/business-detail.js [new file with mode: 0755]
Toolkit/Members/libjs/business-search.js [new file with mode: 0644]
Toolkit/Members/libjs/edit-amenity.js [new file with mode: 0644]
Toolkit/Members/libjs/edit-category.js [new file with mode: 0644]
Toolkit/Members/libjs/edit-city.js [new file with mode: 0644]
Toolkit/Members/libjs/edit-html-email.js [new file with mode: 0644]
Toolkit/Members/libjs/edit-member-amenities.js [new file with mode: 0644]
Toolkit/Members/libjs/edit-member-contacts.js [new file with mode: 0644]
Toolkit/Members/libjs/edit-member-files.js [new file with mode: 0644]
Toolkit/Members/libjs/edit-member-packages.js [new file with mode: 0755]
Toolkit/Members/libjs/edit-member-photos.js [new file with mode: 0755]
Toolkit/Members/libjs/edit-member.js [new file with mode: 0755]
Toolkit/Members/libjs/edit-region.js [new file with mode: 0644]
Toolkit/Members/libjs/google-map.js [new file with mode: 0644]
Toolkit/Members/libjs/list-categories.js [new file with mode: 0644]
Toolkit/Members/libjs/member-list.js [new file with mode: 0644]
Toolkit/Members/libjs/member-pending-list.js [new file with mode: 0644]
Toolkit/Members/libjs/new-member-sign-up.js [new file with mode: 0755]
Toolkit/Members/libjs/travel-list.js [new file with mode: 0644]
Toolkit/Members/libjs/trip-planner-map.js [new file with mode: 0644]
Toolkit/Members/memberClickThru.php [new file with mode: 0644]
Toolkit/Members/memberDBGoogleAreaMap.php [new file with mode: 0644]
Toolkit/Members/memberDBGoogleMap.php [new file with mode: 0644]
Toolkit/Members/memberFileDownload.php [new file with mode: 0644]
Toolkit/Members/memberdb.css [new file with mode: 0644]
Toolkit/Members/sortPhotos.php [new file with mode: 0644]
Toolkit/Members/templates/addPackage.tpl [new file with mode: 0644]
Toolkit/Members/templates/addPhoto.tpl [new file with mode: 0644]
Toolkit/Members/templates/addYourBusinessAdminEmail.tpl [new file with mode: 0755]
Toolkit/Members/templates/addYourBusinessMemberEmail.tpl [new file with mode: 0644]
Toolkit/Members/templates/admin.tpl [new file with mode: 0644]
Toolkit/Members/templates/authorizeChanges.tpl [new file with mode: 0644]
Toolkit/Members/templates/currentTables/Element.tpl [new file with mode: 0644]
Toolkit/Members/templates/currentTables/Form.tpl [new file with mode: 0644]
Toolkit/Members/templates/currentTables/Group.tpl [new file with mode: 0644]
Toolkit/Members/templates/currentTables/GroupElement.tpl [new file with mode: 0644]
Toolkit/Members/templates/currentTables/Header.tpl [new file with mode: 0644]
Toolkit/Members/templates/currentTables/RequiredNote.tpl [new file with mode: 0644]
Toolkit/Members/templates/editAmenities.tpl [new file with mode: 0644]
Toolkit/Members/templates/editAmenity.tpl [new file with mode: 0644]
Toolkit/Members/templates/editBilling.tpl [new file with mode: 0644]
Toolkit/Members/templates/editCategory.tpl [new file with mode: 0644]
Toolkit/Members/templates/editCity.tpl [new file with mode: 0644]
Toolkit/Members/templates/editContact.html [new file with mode: 0644]
Toolkit/Members/templates/editContacts.tpl [new file with mode: 0644]
Toolkit/Members/templates/editCouponEmail.tpl [new file with mode: 0755]
Toolkit/Members/templates/editFile.tpl [new file with mode: 0644]
Toolkit/Members/templates/editHtmlEmail.tpl [new file with mode: 0644]
Toolkit/Members/templates/editMember.tpl [new file with mode: 0644]
Toolkit/Members/templates/editPackage.tpl [new file with mode: 0644]
Toolkit/Members/templates/editPackages.tpl [new file with mode: 0644]
Toolkit/Members/templates/editPhoto.tpl [new file with mode: 0644]
Toolkit/Members/templates/editPhotoGallery.tpl [new file with mode: 0644]
Toolkit/Members/templates/editRegion.tpl [new file with mode: 0644]
Toolkit/Members/templates/emailOwner.tpl [new file with mode: 0644]
Toolkit/Members/templates/exposureDetail.tpl [new file with mode: 0755]
Toolkit/Members/templates/exposureList.tpl [new file with mode: 0755]
Toolkit/Members/templates/htmlEmailBody.tpl [new file with mode: 0644]
Toolkit/Members/templates/listAmenities.tpl [new file with mode: 0644]
Toolkit/Members/templates/listCategories.tpl [new file with mode: 0644]
Toolkit/Members/templates/listCities.tpl [new file with mode: 0644]
Toolkit/Members/templates/listContacts.html [new file with mode: 0644]
Toolkit/Members/templates/listHtmlEmails.tpl [new file with mode: 0644]
Toolkit/Members/templates/listMembers.tpl [new file with mode: 0644]
Toolkit/Members/templates/listNewMembers.tpl [new file with mode: 0644]
Toolkit/Members/templates/listPendingMembers.tpl [new file with mode: 0644]
Toolkit/Members/templates/listRegions.tpl [new file with mode: 0644]
Toolkit/Members/templates/memberContactsList.tpl [new file with mode: 0644]
Toolkit/Members/templates/memberDetail.tpl [new file with mode: 0644]
Toolkit/Members/templates/memberNewsletter.tpl [new file with mode: 0755]
Toolkit/Members/templates/memberSearchForm.tpl [new file with mode: 0644]
Toolkit/Members/templates/membersList.tpl [new file with mode: 0644]
Toolkit/Members/templates/newMemberApproval.tpl [new file with mode: 0755]
Toolkit/Members/templates/newMemberDenial.tpl [new file with mode: 0755]
Toolkit/Members/templates/packageList.tpl [new file with mode: 0644]
Toolkit/Members/templates/previewHtmlEmail.tpl [new file with mode: 0644]
Toolkit/Members/templates/previewNewsletterWrapper.html [new file with mode: 0644]
Toolkit/Members/templates/settings.html [new file with mode: 0644]
Toolkit/Members/templates/tripPlannerList.tpl [new file with mode: 0644]
Toolkit/Members/templates/tripPlannerNoList.tpl [new file with mode: 0644]
Toolkit/Membersonly.php [new file with mode: 0644]
Toolkit/NavigationAbstract.php [new file with mode: 0644]
Toolkit/NavigationFactoryAbstract.php [new file with mode: 0644]
Toolkit/PHPImageEditor/classes/phpimageeditor.php [new file with mode: 0644]
Toolkit/PHPImageEditor/config.php [new file with mode: 0644]
Toolkit/PHPImageEditor/css/jquery.jcrop.css [new file with mode: 0644]
Toolkit/PHPImageEditor/css/style.css [new file with mode: 0644]
Toolkit/PHPImageEditor/css/ui.resizable.css [new file with mode: 0644]
Toolkit/PHPImageEditor/css/ui.slider.css [new file with mode: 0644]
Toolkit/PHPImageEditor/images/crop_view.png [new file with mode: 0644]
Toolkit/PHPImageEditor/images/edit.gif [new file with mode: 0644]
Toolkit/PHPImageEditor/images/empty.gif [new file with mode: 0644]
Toolkit/PHPImageEditor/images/jcrop.gif [new file with mode: 0644]
Toolkit/PHPImageEditor/images/slider_pointer.gif [new file with mode: 0644]
Toolkit/PHPImageEditor/images/slider_track.gif [new file with mode: 0644]
Toolkit/PHPImageEditor/images/tab_body.png [new file with mode: 0644]
Toolkit/PHPImageEditor/images/tab_not_selected.png [new file with mode: 0644]
Toolkit/PHPImageEditor/images/tab_selected.png [new file with mode: 0644]
Toolkit/PHPImageEditor/includes/constants.php [new file with mode: 0644]
Toolkit/PHPImageEditor/includes/functions.php [new file with mode: 0644]
Toolkit/PHPImageEditor/index.php [new file with mode: 0644]
Toolkit/PHPImageEditor/javascript/effects.core.js [new file with mode: 0644]
Toolkit/PHPImageEditor/javascript/joomla_editimagelink.js [new file with mode: 0644]
Toolkit/PHPImageEditor/javascript/jquery-1.3.2.min.js [new file with mode: 0644]
Toolkit/PHPImageEditor/javascript/jquery.jcrop.js [new file with mode: 0644]
Toolkit/PHPImageEditor/javascript/jquery.numeric.js [new file with mode: 0644]
Toolkit/PHPImageEditor/javascript/phpimageeditor.js [new file with mode: 0644]
Toolkit/PHPImageEditor/javascript/ui.core.js [new file with mode: 0644]
Toolkit/PHPImageEditor/javascript/ui.resizable.js [new file with mode: 0644]
Toolkit/PHPImageEditor/javascript/ui.slider.js [new file with mode: 0644]
Toolkit/PHPImageEditor/language/en-GB/en-GB.com_admin.ini [new file with mode: 0644]
Toolkit/Page.php [new file with mode: 0755]
Toolkit/Photos/Admin/FileUploader.php [new file with mode: 0644]
Toolkit/Photos/Admin/PageTree.php [new file with mode: 0644]
Toolkit/Photos/Auth.php [new file with mode: 0644]
Toolkit/Photos/AuthContainer.php [new file with mode: 0644]
Toolkit/Photos/Controllers/IndexController.php [new file with mode: 0644]
Toolkit/Photos/Database/addSlideShow.sql [new file with mode: 0644]
Toolkit/Photos/Database/application.sql [new file with mode: 0644]
Toolkit/Photos/Database/removeApplication.sql [new file with mode: 0644]
Toolkit/Photos/Database/resetSequences.sql [new file with mode: 0644]
Toolkit/Photos/Database/tables/photo.sql [new file with mode: 0755]
Toolkit/Photos/Database/tables/photo2category.sql [new file with mode: 0644]
Toolkit/Photos/Database/tables/photo_category.sql [new file with mode: 0755]
Toolkit/Photos/Database/tables/photo_category_bus.sql [new file with mode: 0755]
Toolkit/Photos/Database/tables/photo_default.sql [new file with mode: 0755]
Toolkit/Photos/Database/updatePhotos.sql [new file with mode: 0644]
Toolkit/Photos/Database/upgradeApp.sql [new file with mode: 0644]
Toolkit/Photos/Display.php [new file with mode: 0644]
Toolkit/Photos/Exception.php [new file with mode: 0644]
Toolkit/Photos/Factory.php [new file with mode: 0644]
Toolkit/Photos/Gallery.php [new file with mode: 0644]
Toolkit/Photos/GalleryCategoryFilter.php [new file with mode: 0644]
Toolkit/Photos/Models/Category.php [new file with mode: 0644]
Toolkit/Photos/Models/MediaMapper.php [new file with mode: 0644]
Toolkit/Photos/Models/Page2Category.php [new file with mode: 0644]
Toolkit/Photos/Models/Photo.php [new file with mode: 0644]
Toolkit/Photos/Models/Photo2Category.php [new file with mode: 0644]
Toolkit/Photos/Models/User.php [new file with mode: 0644]
Toolkit/Photos/Photo.php [new file with mode: 0644]
Toolkit/Photos/SlideShowCategoryFilter.php [new file with mode: 0644]
Toolkit/Photos/application.ini [new file with mode: 0644]
Toolkit/Photos/assets/.keepme [new file with mode: 0644]
Toolkit/Photos/css/gallery.css [new file with mode: 0755]
Toolkit/Photos/css/style.css [new file with mode: 0644]
Toolkit/Photos/css/thickbox.css [new file with mode: 0755]
Toolkit/Photos/js/jquery.columnview.js [new file with mode: 0755]
Toolkit/Photos/js/photoGallery.js [new file with mode: 0644]
Toolkit/Photos/login.php [new file with mode: 0644]
Toolkit/Photos/photoProxy.php [new file with mode: 0644]
Toolkit/Photos/photoSearch.php [new file with mode: 0644]
Toolkit/Photos/setupNewPhotos.php [new file with mode: 0644]
Toolkit/Photos/templates/Admin/editCategory.html [new file with mode: 0644]
Toolkit/Photos/templates/Admin/editPhoto.html [new file with mode: 0644]
Toolkit/Photos/templates/Admin/editUser.html [new file with mode: 0644]
Toolkit/Photos/templates/Admin/emailUser.tpl [new file with mode: 0644]
Toolkit/Photos/templates/Admin/index.html [new file with mode: 0644]
Toolkit/Photos/templates/Admin/listCategories.html [new file with mode: 0644]
Toolkit/Photos/templates/Admin/listPhotos.html [new file with mode: 0644]
Toolkit/Photos/templates/Admin/listUsers.html [new file with mode: 0644]
Toolkit/Photos/templates/Admin/nav.html [new file with mode: 0644]
Toolkit/Photos/templates/Admin/searchForm.html [new file with mode: 0644]
Toolkit/Photos/templates/Admin/userNav.html [new file with mode: 0644]
Toolkit/Photos/templates/photoCats.html [new file with mode: 0755]
Toolkit/Photos/templates/photoGalleryWrapper.html [new file with mode: 0644]
Toolkit/Photos/templates/photos.html [new file with mode: 0755]
Toolkit/Registry.php [new file with mode: 0644]
Toolkit/Router.php [new file with mode: 0644]
Toolkit/ShortURL.php [new file with mode: 0755]
Toolkit/SiteMap.php [new file with mode: 0755]
Toolkit/SiteMapXml.php [new file with mode: 0644]
Toolkit/SortForm.php [new file with mode: 0644]
Toolkit/Table.php [new file with mode: 0644]
Toolkit/Template/BreadCrumbs.php [new file with mode: 0644]
Toolkit/Template/DraftBreadCrumbs.php [new file with mode: 0644]
Toolkit/Template/DraftController.php [new file with mode: 0644]
Toolkit/Template/Exception.php [new file with mode: 0644]
Toolkit/Template/Image/Factory.php [new file with mode: 0644]
Toolkit/Template/Image/ImageAbstract.php [new file with mode: 0644]
Toolkit/Template/Image/Left.php [new file with mode: 0644]
Toolkit/Template/Image/Null.php [new file with mode: 0644]
Toolkit/Template/Image/Right.php [new file with mode: 0644]
Toolkit/Template/IndexController.php [new file with mode: 0644]
Toolkit/Template/KeywordReplacement.php [new file with mode: 0644]
Toolkit/Template/Navigation/AllInOneSideNav.php [new file with mode: 0755]
Toolkit/Template/Navigation/Factory.php [new file with mode: 0644]
Toolkit/Template/Navigation/MainNavigationAbstract.php [new file with mode: 0644]
Toolkit/Template/Navigation/MainNavigationAllInOne.php [new file with mode: 0644]
Toolkit/Template/Navigation/MainNavigationDynamic.php [new file with mode: 0644]
Toolkit/Template/Navigation/MainNavigationStatic.php [new file with mode: 0644]
Toolkit/Template/Navigation/Renderer/DirectTreeLastLi.php [new file with mode: 0644]
Toolkit/Template/Navigation/Renderer/DirectTreeLiId.php [new file with mode: 0644]
Toolkit/Template/Navigation/Renderer/DirectTreeSideNavTitle.php [new file with mode: 0644]
Toolkit/Template/Navigation/Renderer/DirectTreeUlId.php [new file with mode: 0644]
Toolkit/Template/Navigation/SideNavigation.php [new file with mode: 0644]
Toolkit/Template/Page.php [new file with mode: 0644]
Toolkit/Template/Page/Bad.php [new file with mode: 0644]
Toolkit/Template/Page/BodyFactory.php [new file with mode: 0644]
Toolkit/Template/Page/FileLink/Abstract.php [new file with mode: 0644]
Toolkit/Template/Page/FileLink/Factory.php [new file with mode: 0644]
Toolkit/Template/Page/FileLink/File.php [new file with mode: 0644]
Toolkit/Template/Page/FileLink/Image.php [new file with mode: 0644]
Toolkit/Template/Page/GLMSearch.php [new file with mode: 0644]
Toolkit/Template/Page/GoogleSearch.php [new file with mode: 0644]
Toolkit/Template/Page/IBody.php [new file with mode: 0644]
Toolkit/Template/Page/Member.php [new file with mode: 0644]
Toolkit/Template/Page/SiteMap.php [new file with mode: 0644]
Toolkit/Template/Page/Toolbox.php [new file with mode: 0644]
Toolkit/Template/PageControllerAbstract.php [new file with mode: 0644]
Toolkit/Toolbox/BreadCrumbsAbstract.php [new file with mode: 0644]
Toolkit/Toolbox/Database/addIncludeCoupons.sql [new file with mode: 0644]
Toolkit/Toolbox/Database/addIncludeMembers.sql [new file with mode: 0644]
Toolkit/Toolbox/Database/application.sql [new file with mode: 0644]
Toolkit/Toolbox/Database/data/applicationData.sql [new file with mode: 0644]
Toolkit/Toolbox/Database/procedures/check_pages_pos_consistency.sql [new file with mode: 0644]
Toolkit/Toolbox/Database/procedures/check_paragraph_pos_consistency.sql [new file with mode: 0644]
Toolkit/Toolbox/Database/procedures/delete_subtree.sql [new file with mode: 0644]
Toolkit/Toolbox/Database/procedures/files_pos.sql [new file with mode: 0644]
Toolkit/Toolbox/Database/procedures/files_update.sql [new file with mode: 0644]
Toolkit/Toolbox/Database/procedures/pages_pos.sql [new file with mode: 0644]
Toolkit/Toolbox/Database/procedures/pages_update.sql [new file with mode: 0644]
Toolkit/Toolbox/Database/procedures/paragraphs_draft_pos.sql [new file with mode: 0644]
Toolkit/Toolbox/Database/procedures/paragraphs_pos.sql [new file with mode: 0644]
Toolkit/Toolbox/Database/procedures/paragraphs_update.sql [new file with mode: 0644]
Toolkit/Toolbox/Database/removeApplication.sql [new file with mode: 0644]
Toolkit/Toolbox/Database/tables/files.sql [new file with mode: 0644]
Toolkit/Toolbox/Database/tables/files_draft.sql [new file with mode: 0644]
Toolkit/Toolbox/Database/tables/files_history.sql [new file with mode: 0644]
Toolkit/Toolbox/Database/tables/pages.sql [new file with mode: 0644]
Toolkit/Toolbox/Database/tables/pages_draft.sql [new file with mode: 0644]
Toolkit/Toolbox/Database/tables/pages_history.sql [new file with mode: 0644]
Toolkit/Toolbox/Database/tables/pages_mobile.sql [new file with mode: 0644]
Toolkit/Toolbox/Database/tables/pages_preview.sql [new file with mode: 0644]
Toolkit/Toolbox/Database/tables/paragraphs.sql [new file with mode: 0644]
Toolkit/Toolbox/Database/tables/paragraphs_draft.sql [new file with mode: 0644]
Toolkit/Toolbox/Database/tables/paragraphs_history.sql [new file with mode: 0644]
Toolkit/Toolbox/Database/tables/paragraphs_preview.sql [new file with mode: 0644]
Toolkit/Toolbox/DraftPagesTree.php [new file with mode: 0644]
Toolkit/Toolbox/EditDraftController.php [new file with mode: 0644]
Toolkit/Toolbox/EditPageController.php [new file with mode: 0644]
Toolkit/Toolbox/Exception.php [new file with mode: 0644]
Toolkit/Toolbox/FileExtension.php [new file with mode: 0644]
Toolkit/Toolbox/FormControllerAbstract.php [new file with mode: 0644]
Toolkit/Toolbox/Forms/EditPage.php [new file with mode: 0644]
Toolkit/Toolbox/Forms/EditParagraph.php [new file with mode: 0644]
Toolkit/Toolbox/Forms/SearchForm.php [new file with mode: 0644]
Toolkit/Toolbox/GatewayAbstract.php [new file with mode: 0644]
Toolkit/Toolbox/GatewayFactoryAbstract.php [new file with mode: 0644]
Toolkit/Toolbox/IndexController.php [new file with mode: 0644]
Toolkit/Toolbox/ListDraftsController.php [new file with mode: 0644]
Toolkit/Toolbox/Navigation.php [new file with mode: 0644]
Toolkit/Toolbox/Page.php [new file with mode: 0644]
Toolkit/Toolbox/PageBreadCrumbs.php [new file with mode: 0644]
Toolkit/Toolbox/PageDraftBreadCrumbs.php [new file with mode: 0644]
Toolkit/Toolbox/PageGatewayAbstract.php [new file with mode: 0644]
Toolkit/Toolbox/PageGatewayDraft.php [new file with mode: 0644]
Toolkit/Toolbox/PageGatewayDraftFactory.php [new file with mode: 0644]
Toolkit/Toolbox/PageGatewayPublish.php [new file with mode: 0644]
Toolkit/Toolbox/PageGatewayPublishFactory.php [new file with mode: 0644]
Toolkit/Toolbox/PagesTree.php [new file with mode: 0644]
Toolkit/Toolbox/ParagraphBreadCrumbs.php [new file with mode: 0644]
Toolkit/Toolbox/ParagraphDraftBreadCrumbs.php [new file with mode: 0644]
Toolkit/Toolbox/ParagraphGatewayAbstract.php [new file with mode: 0644]
Toolkit/Toolbox/ParagraphGatewayDraft.php [new file with mode: 0644]
Toolkit/Toolbox/ParagraphGatewayDraftFactory.php [new file with mode: 0644]
Toolkit/Toolbox/ParagraphGatewayPublish.php [new file with mode: 0644]
Toolkit/Toolbox/ParagraphGatewayPublishFactory.php [new file with mode: 0644]
Toolkit/Toolbox/ParagraphsController.php [new file with mode: 0644]
Toolkit/Toolbox/ParagraphsDraftController.php [new file with mode: 0644]
Toolkit/Toolbox/ParagraphsDraftTree.php [new file with mode: 0644]
Toolkit/Toolbox/ParagraphsTree.php [new file with mode: 0644]
Toolkit/Toolbox/SearchController.php [new file with mode: 0644]
Toolkit/Toolbox/SearchTree.php [new file with mode: 0644]
Toolkit/Toolbox/TreeAbstract.php [new file with mode: 0644]
Toolkit/Toolbox/assets/.keepme [new file with mode: 0644]
Toolkit/Toolbox/assets/arrow.png [new file with mode: 0755]
Toolkit/Toolbox/assets/arrowClosed.png [new file with mode: 0755]
Toolkit/Toolbox/assets/arrowOpen.png [new file with mode: 0755]
Toolkit/Toolbox/assets/collapse.png [new file with mode: 0755]
Toolkit/Toolbox/assets/expand.png [new file with mode: 0755]
Toolkit/Toolbox/assets/grnball.gif [new file with mode: 0755]
Toolkit/Toolbox/assets/mobilemgreen.jpg [new file with mode: 0644]
Toolkit/Toolbox/assets/mobilemred.jpg [new file with mode: 0644]
Toolkit/Toolbox/assets/redball.gif [new file with mode: 0755]
Toolkit/Toolbox/assets/template1.gif [new file with mode: 0755]
Toolkit/Toolbox/assets/template2.gif [new file with mode: 0755]
Toolkit/Toolbox/assets/template3.gif [new file with mode: 0755]
Toolkit/Toolbox/assets/template4.gif [new file with mode: 0755]
Toolkit/Toolbox/assets/template5.gif [new file with mode: 0755]
Toolkit/Toolbox/assets/template6.gif [new file with mode: 0644]
Toolkit/Toolbox/config.ini [new file with mode: 0644]
Toolkit/Toolbox/fixPageNoContent.php [new file with mode: 0644]
Toolkit/Toolbox/fixPagePos.php [new file with mode: 0644]
Toolkit/Toolbox/libjs/edit-page.js [new file with mode: 0644]
Toolkit/Toolbox/libjs/edit-paragraph.js [new file with mode: 0644]
Toolkit/Toolbox/libjs/pagesTree.js [new file with mode: 0644]
Toolkit/Toolbox/libjs/paragraph-tree.js [new file with mode: 0644]
Toolkit/Toolbox/moveBySelect.php [new file with mode: 0644]
Toolkit/Toolbox/moveBySelectPara.php [new file with mode: 0644]
Toolkit/Toolbox/pageChangeState.php [new file with mode: 0644]
Toolkit/Toolbox/pageMobileChangeState.php [new file with mode: 0644]
Toolkit/Toolbox/pageMove.php [new file with mode: 0644]
Toolkit/Toolbox/paragraphChangeState.php [new file with mode: 0644]
Toolkit/Toolbox/paragraphFileUpload.php [new file with mode: 0644]
Toolkit/Toolbox/paragraphMove.php [new file with mode: 0644]
Toolkit/Toolbox/styles.css [new file with mode: 0644]
Toolkit/Tree.php [new file with mode: 0644]
Toolkit/Videos/AdminEditVideoForm.php [new file with mode: 0644]
Toolkit/Videos/Controller.php [new file with mode: 0644]
Toolkit/Videos/Database/application.sql [new file with mode: 0644]
Toolkit/Videos/Database/removeApplication.sql [new file with mode: 0644]
Toolkit/Videos/Database/table/videos.sql [new file with mode: 0644]
Toolkit/Videos/IDecorator.php [new file with mode: 0644]
Toolkit/Videos/Navigation.php [new file with mode: 0644]
Toolkit/Videos/Video.php [new file with mode: 0644]
Toolkit/Videos/VideoMapper.php [new file with mode: 0644]
Toolkit/Videos/VideosDataGrid.php [new file with mode: 0644]
Toolkit/Videos/WebDecorator.php [new file with mode: 0644]
Toolkit/Videos/WebPageDecorator.php [new file with mode: 0644]
Toolkit/Videos/config.ini [new file with mode: 0644]
Toolkit/Videos/libjs/videos.js [new file with mode: 0644]
Toolkit/Videos/moveVideo.php [new file with mode: 0644]
Toolkit/Videos/styles.css [new file with mode: 0644]
Toolkit/Videos/templates/webDecorator.html [new file with mode: 0644]
Toolkit/Videos/templates/webPageDecorator.html [new file with mode: 0644]
Toolkit/Videos/toggleActive.php [new file with mode: 0644]
Toolkit/Videos/toggleFeatured.php [new file with mode: 0644]
Toolkit/qfcaptcha.php [new file with mode: 0755]
admin/.htaccess [new file with mode: 0644]
admin/CommonEvents/.htaccess [new file with mode: 0644]
admin/CommonEvents/index.php [new file with mode: 0644]
admin/Contact/contact_inquiry.phtml [new file with mode: 0755]
admin/Contact/contact_setup.inc [new file with mode: 0755]
admin/Contact/del_query.phtml [new file with mode: 0755]
admin/Contact/download.phtml [new file with mode: 0755]
admin/Contact/edit_autoresponse.phtml [new file with mode: 0755]
admin/Contact/edit_contact.php [new file with mode: 0644]
admin/Contact/edit_group.php [new file with mode: 0644]
admin/Contact/edit_inquiry.phtml [new file with mode: 0755]
admin/Contact/emails.php [new file with mode: 0644]
admin/Contact/form.js [new file with mode: 0755]
admin/Contact/help/contact.phtml [new file with mode: 0755]
admin/Contact/htmlarea.css [new file with mode: 0644]
admin/Contact/index.phtml [new file with mode: 0755]
admin/Contact/list_contact.phtml [new file with mode: 0755]
admin/Contact/list_groups.php [new file with mode: 0644]
admin/Contact/list_query.phtml [new file with mode: 0755]
admin/Contact/mailout.phtml [new file with mode: 0644]
admin/Contact/main.css [new file with mode: 0755]
admin/Contact/msg.js [new file with mode: 0755]
admin/Contact/newsletter_template.html [new file with mode: 0755]
admin/Contact/notes/ChangeLog [new file with mode: 0755]
admin/Contact/notes/Contact [new file with mode: 0755]
admin/Contact/notes/contact.sql [new file with mode: 0755]
admin/Contact/preview.phtml [new file with mode: 0755]
admin/Contact/query_contact.phtml [new file with mode: 0755]
admin/Contact/query_db.phtml [new file with mode: 0755]
admin/Contact/query_save.phtml [new file with mode: 0755]
admin/Contact/update_autoresponse.phtml [new file with mode: 0755]
admin/Contact/update_contact.phtml [new file with mode: 0755]
admin/Contact/update_inquiry.phtml [new file with mode: 0755]
admin/Contact/verify.js [new file with mode: 0755]
admin/Contact/view_newsletter.phtml [new file with mode: 0755]
admin/Contact/wm.js [new file with mode: 0755]
admin/Events/down.gif [new file with mode: 0755]
admin/Events/editEvent.php [new file with mode: 0644]
admin/Events/editTopics.php [new file with mode: 0644]
admin/Events/event.css [new file with mode: 0644]
admin/Events/event.js [new file with mode: 0644]
admin/Events/event_setup.inc [new file with mode: 0755]
admin/Events/events.sql [new file with mode: 0644]
admin/Events/importEvents.php [new file with mode: 0755]
admin/Events/index.phtml [new file with mode: 0755]
admin/Events/left.gif [new file with mode: 0755]
admin/Events/list_events.phtml [new file with mode: 0755]
admin/Events/setupEventsRecur.php [new file with mode: 0644]
admin/Events/topic_action.php [new file with mode: 0644]
admin/Events/up.gif [new file with mode: 0755]
admin/blocks.php [new file with mode: 0644]
admin/coupons.php [new file with mode: 0755]
admin/index.phtml [new file with mode: 0755]
admin/main.css [new file with mode: 0755]
admin/members.php [new file with mode: 0755]
admin/nav.phtml [new file with mode: 0644]
admin/photos.php [new file with mode: 0644]
admin/splash.phtml [new file with mode: 0755]
admin/toolbox.php [new file with mode: 0644]
admin/videos.php [new file with mode: 0644]
assets/accreditationsBg.png [new file with mode: 0644]
assets/bg.jpg [new file with mode: 0644]
assets/bgInside.jpg [new file with mode: 0644]
assets/bgRepeat.png [new file with mode: 0644]
assets/blue.png [new file with mode: 0755]
assets/divider.png [new file with mode: 0644]
assets/footerBg.png [new file with mode: 0644]
assets/logo.png [new file with mode: 0644]
assets/navbgHover.png [new file with mode: 0644]
assets/newsletter/header.jpg [new file with mode: 0644]
assets/searchResultOn.gif [new file with mode: 0755]
assets/subnavBg.jpg [new file with mode: 0644]
assets/toolboxTMP.gif [new file with mode: 0644]
classes/class_db.inc [new file with mode: 0755]
classes/class_template.inc [new file with mode: 0644]
classes/class_toolbox.inc [new file with mode: 0755]
config/application.ini [new file with mode: 0644]
config/eventCalendar.ini [new file with mode: 0644]
config/server.ini [new file with mode: 0644]
config/site.ini [new file with mode: 0644]
css/contactform.css [new file with mode: 0644]
css/email.css [new file with mode: 0644]
css/gsearch.css [new file with mode: 0755]
css/ie7.css [new file with mode: 0644]
css/siteMap.css [new file with mode: 0644]
images/add.png [new file with mode: 0755]
images/amenities/1182867688waterfront.gif [new file with mode: 0644]
images/amenities/1182867790sauna.gif [new file with mode: 0644]
images/amenities/barrierfree.gif [new file with mode: 0644]
images/amenities/beachaccess.gif [new file with mode: 0644]
images/amenities/breakfast.gif [new file with mode: 0644]
images/amenities/cable.gif [new file with mode: 0644]
images/amenities/conference.gif [new file with mode: 0644]
images/amenities/cont_break.gif [new file with mode: 0644]
images/amenities/eff.gif [new file with mode: 0644]
images/amenities/excersize.gif [new file with mode: 0644]
images/amenities/free_internet.gif [new file with mode: 0644]
images/amenities/guest_laundry.gif [new file with mode: 0644]
images/amenities/internet.gif [new file with mode: 0644]
images/amenities/kidfriendly.gif [new file with mode: 0644]
images/amenities/meeting.gif [new file with mode: 0644]
images/amenities/pets.gif [new file with mode: 0644]
images/amenities/pool-out.gif [new file with mode: 0644]
images/amenities/pool.gif [new file with mode: 0644]
images/amenities/restaurant.gif [new file with mode: 0644]
images/amenities/restnear.gif [new file with mode: 0644]
images/amenities/sauna.gif [new file with mode: 0644]
images/amenities/smokefree.gif [new file with mode: 0644]
images/amenities/snowmobile.gif [new file with mode: 0644]
images/amenities/waterfront.gif [new file with mode: 0644]
images/amenities/waterview.gif [new file with mode: 0644]
images/amenities/wireless.gif [new file with mode: 0644]
images/application_edit.png [new file with mode: 0644]
images/cross.png [new file with mode: 0644]
images/delete.png [new file with mode: 0644]
images/error.gif [new file with mode: 0644]
images/file-ext/cad.gif [new file with mode: 0644]
images/file-ext/doc.gif [new file with mode: 0644]
images/file-ext/download.gif [new file with mode: 0644]
images/file-ext/gif.gif [new file with mode: 0644]
images/file-ext/html.gif [new file with mode: 0644]
images/file-ext/jpg.gif [new file with mode: 0644]
images/file-ext/movie.gif [new file with mode: 0755]
images/file-ext/mp3.gif [new file with mode: 0644]
images/file-ext/pdf.png [new file with mode: 0644]
images/file-ext/ppt.gif [new file with mode: 0644]
images/file-ext/rar.gif [new file with mode: 0644]
images/file-ext/rm.gif [new file with mode: 0644]
images/file-ext/txt.png [new file with mode: 0644]
images/file-ext/xls.gif [new file with mode: 0644]
images/file-ext/zip.png [new file with mode: 0644]
images/find.png [new file with mode: 0644]
images/flag_green.png [new file with mode: 0644]
images/folder.png [new file with mode: 0755]
images/folder_add.png [new file with mode: 0755]
images/folder_delete.png [new file with mode: 0755]
images/grnball.gif [new file with mode: 0755]
images/help.gif [new file with mode: 0755]
images/html.gif [new file with mode: 0755]
images/img.gif [new file with mode: 0755]
images/left.gif [new file with mode: 0644]
images/loadingAnimation.gif [new file with mode: 0644]
images/logo.gif [new file with mode: 0755]
images/magnifier.png [new file with mode: 0755]
images/members/add.png [new file with mode: 0755]
images/members/application_edit.png [new file with mode: 0755]
images/members/cancel.png [new file with mode: 0755]
images/members/cardamex.gif [new file with mode: 0644]
images/members/carddinner.gif [new file with mode: 0644]
images/members/carddiscover.gif [new file with mode: 0644]
images/members/cardmaster.gif [new file with mode: 0644]
images/members/cardvisa.gif [new file with mode: 0644]
images/members/cross.png [new file with mode: 0755]
images/members/delete.png [new file with mode: 0755]
images/members/find.png [new file with mode: 0755]
images/members/img.gif [new file with mode: 0644]
images/members/lock.png [new file with mode: 0755]
images/members/note_edit.png [new file with mode: 0755]
images/members/page_edit.png [new file with mode: 0755]
images/members/tick.png [new file with mode: 0755]
images/members/user_add.png [new file with mode: 0755]
images/members/user_edit.png [new file with mode: 0755]
images/note_edit.png [new file with mode: 0644]
images/page_edit.png [new file with mode: 0644]
images/redball.gif [new file with mode: 0755]
images/right.gif [new file with mode: 0644]
images/shadowb.gif [new file with mode: 0644]
images/shadowr.gif [new file with mode: 0644]
images/size.sh [new file with mode: 0755]
images/tick.png [new file with mode: 0644]
images/top2.jpg [new file with mode: 0755]
images/user_add.png [new file with mode: 0644]
images/user_edit.png [new file with mode: 0644]
index.php [new file with mode: 0644]
libjs/couponLimitText.js [new file with mode: 0644]
libjs/get_adobe_reader.png [new file with mode: 0755]
setup.phtml [new file with mode: 0644]
setup_functions.phtml [new file with mode: 0644]
static/.keepme [new file with mode: 0755]
static/8.phtml [new file with mode: 0644]
templates/siteMap.html [new file with mode: 0644]
templates/template.html [new file with mode: 0644]

diff --git a/.htaccess b/.htaccess
new file mode 100644 (file)
index 0000000..686c716
--- /dev/null
+++ b/.htaccess
@@ -0,0 +1,144 @@
+<Files ~ "^\.ht">
+Order allow,deny
+Deny from all
+</Files>
+<Files ~ "^setup.phtml">
+Order allow,deny
+Deny from all
+</Files>
+<Files ~ "\.ini$">
+Order allow,deny
+Deny from all
+</Files>
+ErrorDocument 404 /404.html
+RewriteEngine On
+AddDefaultCharset utf-8
+
+RewriteBase /www.gaylordgolfmecca.com/
+
+# Redirect non-www to www
+RewriteCond %{ENV:GLM_HOST_ID} PRODUCTION
+RewriteCond %{HTTP_HOST} !^www\. [NC]
+RewriteCond %{HTTP_HOST} !^demo\. [NC]
+RewriteRule ^(.*)$ http://www.%{HTTP_HOST}/$1 [R=301,L]
+
+# CacheBusting
+RewriteCond %{REQUEST_URI} v/[0-9\.]+/
+RewriteRule ^v/[0-9\.]+/(.*) $1 [L]
+
+############
+## Events ##
+############
+RewriteRule ^events/([0-9]*)/([0-9]*)/$ index.php?catid=$1&eventid=$2 [QSA,L]
+RewriteRule ^events/([0-9]*)/$ index.php?catid=$1 [QSA,L]
+
+RewriteRule ^VisitorGuide/(.*)/$ pdf-download.php?uidpdf=$1&pdf_file=VisitorGuide [L]
+RewriteRule ^.*-([0-9]*)/([0-9]*)/$ index.php?catid=$1&photo_catid=$2 [L]
+# rewrites for the toolbox pages
+RewriteRule ^.*-([0-9]*)/index.php index\.php?%{QUERY_STRING}
+RewriteRule ^.*-([0-9]*)/$ index\.php?catid=$1 [QSA,L]
+
+# trailing slash
+RewriteCond %{REQUEST_FILENAME} !-f
+RewriteCond %{REQUEST_URI} ^.*-([0-9]*)$
+RewriteRule ^(.*)$ %{REQUEST_URI}/ [L,R=301]
+
+RewriteRule bannerClick.php Toolkit/Banners/banner_click_through.php [QSA,L]
+
+##############
+## MemberDB ##
+##############
+# Members only area
+RewriteRule ^members-only-area/$ Toolkit/Members/MembersOnly/index.php [QSA,L]
+RewriteRule ^member-area-google-map/$ Toolkit/Members/memberDBGoogleAreaMap.php [QSA,L]
+RewriteRule ^member-city-google-map/$ Toolkit/Members/memberDBGoogleAreaMapCity.php [QSA,L]
+# Ajax call for google map
+RewriteRule ^member-db-google-map/$ Toolkit/Members/memberDBGoogleMap.php [QSA,L]
+# Ajax call for google maps w/ SEO urls - extract catid from url to pass
+# along w/ the query string.
+RewriteRule ^.*-([0-9]*)/member-db-google-map/$ Toolkit/Members/memberDBGoogleMap.php?catid=$1 [QSA,L]
+# short urls w/ google maps
+RewriteRule ^([A-Za-z0-9_-]*)/member-db-google-map/$ Toolkit/Members/memberDBGoogleMap.php?glmPage=$1 [L]
+# Downloading member files from profile pages
+RewriteRule ^member-file/([0-9]*)/([0-9]*)/$ Toolkit/Members/memberFileDownload.php?mid=$1&fid=$2 [L]
+# Ajax call for saving photo repositioning
+RewriteRule ^member-save-photos/$ Toolkit/Members/sortPhotos.php [QSA,L]
+RewriteRule ^memberdb-site-map index.php?catid=0&memberDbSiteMap=1 [L]
+
+RewriteRule ^map/([0-9A-F]{6})/([0-9]*).png Toolkit/Maps/marker.php?color=$1&text=$2 [L]
+RewriteRule ^areamap/([0-9A-F]{6}).png Toolkit/Maps/marker2.php?color=$1 [L]
+
+# Trip planner
+RewriteRule ^trip-planner-map.js$ Toolkit/Members/libjs/trip-planner-map.js [L]
+RewriteRule ^trip-planner-members/$ Toolkit/Members/TripPlanner/googleMapData.php [L]
+RewriteRule ^trip-planner-wish-list/([0-9]*)/([0-9]*)/$ Toolkit/Members/TripPlanner/wish-list.php?catid=$1&member_id=$2&detail=1 [L]
+# This is the rewrite rule for the pending member emails that get sent
+# out when a user updates their member record from the members only area.
+RewriteRule ^pending-member/([0-9]*)/$ admin/members.php?page=memberUpdates&module=authorizeUpdates&id=$1 [L]
+# Profile pages
+#RewriteRule ^member-profile/([0-9]*)/([0-9]*)/$ index.php?catid=$1&member_id=$2 [L]
+RewriteRule ^memberProfiles/.*-([0-9]*).html index\.php?catid=9999&member_id=$1 [L]
+
+# trip planner list (member sessions)
+RewriteRule ^trip-planner-list/([0-9]*)/([0-9]*)/ Toolkit/Members/TripPlanner/memberList.php?member_id=$1&catid=$2 [L]
+
+#############
+## Banners ##
+#############
+# Ajax call for incrementing rotating banner impressions
+RewriteRule ^rotating-banner/([0-9]*)/([0-9]*)/$ Toolkit/Banners/incrementRotatingBanner.php?banner=$1&catid=$2 [L]
+
+############
+## Blocks ##
+############
+RewriteRule ^block-pos/ Toolkit/Blocks/positionBlock.php [QSA,L]
+
+#############
+## Toolbox ##
+#############
+# Ajax call for changing state (active/inactive)
+RewriteRule ^toolbox-change-state/([0-9]*)/ Toolkit/Toolbox/pageChangeState.php?kpass=&catid=$1 [L]
+RewriteRule ^toolbox-change-state-mobile/([0-9]*)/ Toolkit/Toolbox/pageMobileChangeState.php?kpass=&catid=$1 [L]
+RewriteRule ^toolbox-paragraph-change-state/([0-9]*)/ Toolkit/Toolbox/paragraphChangeState.php?kpass=&paragraphId=$1 [L]
+# Ajax call for changing position (move up / move down)
+RewriteRule ^toolbox-page-move/cat_([0-9]*)/([a-z]*)/$ Toolkit/Toolbox/pageMove.php?id=$1&direction=$2&kpass= [L]
+RewriteRule ^toolbox-page-move-sel/pos([0-9]*)/([0-9]*)/([0-9]*)/$ Toolkit/Toolbox/moveBySelect.php?id=$1&oldpos=$2&newpos=$3 [L]
+RewriteRule ^toolbox-paragraph-move-sel/pos([0-9]*)/([0-9]*)/([0-9]*)/$ Toolkit/Toolbox/moveBySelectPara.php?id=$1&oldpos=$2&newpos=$3 [L]
+RewriteRule ^toolbox-paragraph-move/cat_([0-9]*)/([a-z]*)/$ Toolkit/Toolbox/paragraphMove.php?id=$1&direction=$2&kpass= [L]
+# Ajax call for uploading a file
+RewriteRule ^toolbox-paragraph-upload/$ Toolkit/Toolbox/paragraphFileUpload.php?kpass= [L]
+
+################
+## short urls ##
+################
+#RewriteCond %{REQUEST_URI} !(^.*-([0-9]*)/$)
+#RewriteRule ^([A-Za-z0-9_-]*)/$ index.php?glmPage=$1 [L]
+
+RewriteRule site-map index.php?catid=1&sitemap=1
+
+###############
+## Templates ##
+###############
+# View flexy templates as php files
+AddType application/x-httpd-php .tpl
+
+###############
+##   Videos  ##
+###############
+# call for changing position on videos
+RewriteRule ^video-move/([0-9]*)/([0-9]*)/$ Toolkit/Videos/moveVideo.php?kpass=&id=$1&newpos=$2
+# Toggle active on / off
+RewriteRule ^video-active-toggle/([0-9]*)/ Toolkit/Videos/toggleActive.php?kpass=&id=$1
+# Toggle featured on /off
+RewriteRule ^video-featured-toggle/([0-9]*)/ Toolkit/Videos/toggleFeatured.php?kpass=&id=$1
+
+############
+## Photos ##
+############
+RewriteRule photoSearch Toolkit/Photos/photoSearch.php [QSA,L]
+RewriteRule ^download-photo-web/([0-9]*)/$ Toolkit/Photos/photoProxy.php?type=web&photo_id=$1 [L]
+RewriteRule ^download-photo-print/([0-9]*)/$ Toolkit/Photos/photoProxy.php?type=print&photo_id=$1 [L]
+###############
+## REDIRECTS ##
+###############
+
diff --git a/Toolkit/BaseControllerAbstract.php b/Toolkit/BaseControllerAbstract.php
new file mode 100644 (file)
index 0000000..176c7f3
--- /dev/null
@@ -0,0 +1,50 @@
+<?php
+/**
+ * BaseControllerAbstract.php
+ *
+ * PHP version 5
+ *
+ * @category  Toolkit
+ * @package   Toolkit_BaseControllerAbstract
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @release   Release: $id$
+ * @link      <>
+ */
+
+/**
+ * Toolkit_BaseControllerAbstract
+ *
+ * Creates object of toolkit registry
+ *
+ * @category  Toolkit
+ * @package   Toolkit_BaseControllerAbstract
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @release   Release: $id$
+ * @link      <>
+ */
+abstract class Toolkit_BaseControllerAbstract
+{
+    /**
+     * Description of $registry
+     * @var    Toolkit_Registry
+     * @access protected
+     */
+    protected $registry;
+
+    /**
+     * Class constructor
+     *
+     * Creates object of Toolkit Registry
+     *
+     * @param Toolkit_Registry $registry Toolkit Registry
+     */
+    public function __construct(Toolkit_Registry $registry)
+    {
+        $this->registry = $registry;
+    }
+}
+?>
diff --git a/Toolkit/Blocks/Admin/EditPage.php b/Toolkit/Blocks/Admin/EditPage.php
new file mode 100644 (file)
index 0000000..e8f7271
--- /dev/null
@@ -0,0 +1,254 @@
+<?php
+
+/**
+ * EditPage.php
+ *
+ * PHP version 5.2
+ *
+ * @category  Toolkit
+ * @package   Blocks
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @version   SVN: (0.1)
+ * @link      <>
+ */
+
+/**
+ * Toolkit_Blocks_Admin_EditPage
+ *
+ * Handles the Edit page for the Block page
+ *
+ * @category  Toolkit
+ * @package   Blocks
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @release   Release: (0.1)
+ * @link      <>
+ */
+class Toolkit_Blocks_Admin_EditPage
+{
+    private $_adminPath;
+    private $_dbh;
+    private $_flexyOptions;
+    private $_template;
+    private $_pageId;
+    private $_routerPath;
+    private $_canCreateOnAnyPage;
+
+    /**
+     * Creates a edit page for blocks
+     *
+     * @param Toolkit_Registry $registry Registry Object
+     */
+    public function __construct(
+        Toolkit_Registry $registry,
+        $pageId = null
+    ) {
+        $config = $registry->appConfig;
+        $this->_dbh          = $registry->dbh;
+        $this->_flexyOptions = $GLOBALS['flexyOptions'];
+        $this->_adminPath    = $config->application->path;
+        $this->_routerPath
+            = MEDIA_BASE_URL . $config->application->setPath;
+        $this->_flexyOptions['templateDir']
+            = BASE . $config->flexy->options->templateDir;
+        $this->_flexyOptions['compileDir']
+            = BASE . $config->flexy->options->compileDir;
+        $this->_template
+            = $config->flexy->templates->editPage;
+        $this->_pageId = $pageId;
+        $this->_canCreateOnAnyPage = $config->canCreateOnAnyPage;
+    }
+
+    private function _getPageName()
+    {
+        try {
+            $sql = "
+            SELECT navigation_name
+              FROM pages
+             WHERE id = :id";
+            $stmt = $this->_dbh->prepare($sql);
+            $stmt->bindParam(':id', $this->_pageId, PDO::PARAM_INT);
+            $stmt->execute();
+            return $stmt->fetchColumn();
+        } catch (PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+    }
+
+    /**
+     * returns the featured pages table data
+     *
+     * @return array features
+     */
+    public function _getBlocks()
+    {
+        $blocks = array();
+        try {
+            $sql = "
+              SELECT b.*, p.navigation_name
+                FROM blocks b LEFT OUTER JOIN pages p
+                     ON (b.page_to = p.id)
+               WHERE page_on = :page
+            ORDER BY pos";
+            $stmt = $this->_dbh->prepare($sql);
+            $stmt->bindParam(':page', $this->_pageId, PDO::PARAM_INT);
+            $stmt->execute();
+            while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                if ($row['image']) {
+                    $row['imageUrl'] = HEADLINE_THUMB . $row['image'];
+                }
+                $row['deleteUrl']
+                    = 'blocks.php?ac=Edit&Command=Delete&blockId=' .
+                    $row['id'];
+                $blocks[] = $row;
+            }
+        } catch (PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+        return $blocks;
+    }
+
+    /**
+     * Returns the string of html used to generate the edit page
+     *
+     * @return string
+     */
+    public function toHtml()
+    {
+        $GLOBALS['bottomScripts'][]
+            = CKEDITOR_JS . '';
+        $GLOBALS['bottomScripts'][]
+            = $this->_routerPath . '/js/editPage.js';
+        if ($_GET['blockId'] && $_REQUEST['Command'] == 'Delete') {
+            $blockId = filter_input(
+                INPUT_GET,
+                'blockId',
+                FILTER_SANITIZE_NUMBER_INT
+            );
+            if ($blockId) {
+                $block = Toolkit_Blocks_Block::fetchById(
+                    $this->_dbh,
+                    $blockId
+                );
+                if ($block) {
+                    $pageOn = $block->getPageOn();
+                    // remove this cache file for the block
+                    $cache = new Cache_Lite($GLOBALS['cacheOptions']);
+                    $cache->remove($pageOn, 'Block');
+                    $block->delete($this->_dbh);
+                }
+                header('Location: blocks.php?ac=Edit&page_on=' . $pageOn);
+            }
+        } else if ($_POST) {
+            if ($_FILES['image']['size'] > 0) {
+                $is = new Toolkit_FileServer_ImageAdapter();
+                $imageUploaded = $is->upload('image');
+            }
+            $pageOn = filter_input(
+                INPUT_POST,
+                'page_on',
+                FILTER_SANITIZE_NUMBER_INT
+            );
+            $pageTo = filter_input(
+                INPUT_POST,
+                'page_to',
+                FILTER_SANITIZE_NUMBER_INT
+            );
+            $blockId = filter_input(
+                INPUT_POST,
+                'blockId',
+                FILTER_SANITIZE_NUMBER_INT
+            );
+            $title = filter_input(
+                INPUT_POST,
+                'title',
+                FILTER_SANITIZE_STRING
+            );
+            $url = filter_input(
+                INPUT_POST,
+                'url',
+                FILTER_SANITIZE_STRING
+            );
+            $external = filter_input(
+                INPUT_POST,
+                'external',
+                FILTER_VALIDATE_BOOLEAN
+            );
+            $description = filter_var(
+                $_REQUEST['description'],
+                FILTER_UNSAFE_RAW
+            );
+            if (get_magic_quotes_gpc()) {
+                $description = stripslashes($description);
+            }
+            $pos = filter_input(
+                INPUT_POST,
+                'pos',
+                FILTER_SANITIZE_NUMBER_INT
+            );
+            $oldImage = filter_input(
+                INPUT_POST,
+                'oldImage',
+                FILTER_SANITIZE_STRING
+            );
+
+            $values = array(
+                'title'       => $title,
+                'description' => $description,
+                'image'       => (($imageUploaded['name'])
+                    ? $imageUploaded['name']
+                    :$oldImage),
+                'pageOn'      => $pageOn,
+                'pageTo'      => $pageTo,
+                'url'         => Toolkit_Common::filterURI($url),
+                'external'    => $external
+            );
+            if ($blockId) {
+                $values['id'] = $blockId;
+                $oldBlock = Toolkit_Blocks_Block::fetchById
+                    ($this->_dbh,
+                    $blockId
+                );
+                if ($oldBlock) {
+                    $values['pos'] = $oldBlock->getPos();
+                    $pageOn = $oldBlock->getPageOn();
+                    // remove this cache file for the oldblock
+                    $cache = new Cache_Lite($GLOBALS['cacheOptions']);
+                    $cache->remove($pageOn, 'Block');
+                }
+            }
+            $block = Toolkit_Blocks_Block::createByValues(
+                $values
+            );
+            if ($block) {
+                $pageOn = $block->getPageOn();
+                // remove this cache file for the block
+                $cache = new Cache_Lite($GLOBALS['cacheOptions']);
+                $cache->remove($pageOn, 'Block');
+                $block->save($this->_dbh);
+            }
+            header('Location: blocks.php?ac=Edit&page_on=' . $pageOn);
+            return false;
+        } else {
+            $tpl = new HTML_Template_Flexy($this->_flexyOptions);
+            $tpl->compile($this->_template);
+
+            $page = new stdClass();
+            if (is_numeric($this->_pageId)) {
+                $page->pageName = $this->_getPageName();
+            }
+            $page->baseUrl = MEDIA_BASE_URL;
+            $page->pageOn = $this->_pageId;
+            $page->blocks
+                = ($this->_pageId)
+                ? $this->_getBlocks()
+                : false;
+            $page->canCreateOnAnyPage = $this->_canCreateOnAnyPage;
+            return $tpl->bufferedOutputObject($page);
+        }
+    }
+}
+
diff --git a/Toolkit/Blocks/Admin/ListPages.php b/Toolkit/Blocks/Admin/ListPages.php
new file mode 100644 (file)
index 0000000..e6e9989
--- /dev/null
@@ -0,0 +1,140 @@
+<?php
+
+/**
+ * ListFeatures.php
+ *
+ * PHP version 5.2
+ *
+ * @category  Toolkit
+ * @package   Blocks
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @version   SVN: (0.1)
+ * @link      <>
+ */
+
+/**
+ * Toolkit_Tags_Admin_ListFeatures
+ *
+ * Handles the listing of the Pages that have the blocks on them.
+ *
+ * @category  Toolkit
+ * @package   Blocks
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @release   Release: (0.1)
+ * @link      <>
+ */
+class Toolkit_Blocks_Admin_ListPages
+{
+    private $_adminPath;
+    private $_dbh;
+    private $_flexyOptions;
+    private $_template;
+    private $_routerPath;
+
+    /**
+     * Creates a list of the featured pages
+     *
+     * @param Toolkit_Registry $registry Registry Object
+     */
+    public function __construct(Toolkit_Registry $registry)
+    {
+        $config = $registry->appConfig;
+        $this->_dbh          = $registry->dbh;
+        $this->_flexyOptions = $GLOBALS['flexyOptions'];
+        $this->_adminPath    = $config->application->path;
+        $this->_routerPath
+            = MEDIA_BASE_URL . $config->application->setPath;
+        $this->_flexyOptions['templateDir']
+            = BASE . $config->flexy->options->templateDir;
+        $this->_flexyOptions['compileDir']
+            = BASE . $config->flexy->options->compileDir;
+        $this->_template
+            = $config->flexy->templates->listPages;
+    }
+
+    /**
+     * returns the featured pages table data
+     *
+     * @return array features
+     */
+    private function _getPages()
+    {
+        $breadCrumbsFactory = new Toolkit_BreadCrumbsFactory(
+            new Toolkit_Toolbox_PageGatewayPublishFactory(
+                $this->_dbh
+            )
+        );
+        $breadCrumbHelper = $breadCrumbsFactory->createBreadCrumbsHelper();
+        $pages = array();
+        try {
+            $sql = "
+            SELECT count(b.id) as count,b.page_on,p.navigation_name
+              FROM blocks.blocks b
+                   LEFT OUTER JOIN pages p ON (b.page_on = p.id)
+            GROUP BY navigation_name,page_on";
+            $stmt = $this->_dbh->query($sql);
+            while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                $row['editUrl']
+                    = $this->_adminPath . '?ac=Edit&page_on=' .
+                    $row['page_on'];
+                $row['breadCrumbs']
+                    = $breadCrumbHelper->toHtml($row['page_on']);
+                $row['pageUrl'] = Toolkit_Template_Page::getSeoUrl(
+                    new Toolkit_Toolbox_PageGatewayPublish($this->_dbh),
+                    $row['page_on']
+                );
+                $row['deleteUrl']
+                    = 'blocks.php?Command=Delete&pageId=' .
+                    $row['page_on'];
+                $pages[$row['page_on']] = $row;
+            }
+        } catch (PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+        return $pages;
+    }
+
+    /**
+     * Returns the string of html used to generate the list of Pages.
+     *
+     * @return string
+     */
+    public function toHtml()
+    {
+        if ($_REQUEST['Command'] == 'Delete') {
+            $pageId = filter_input(
+                INPUT_GET,
+                'pageId',
+                FILTER_SANITIZE_NUMBER_INT
+            );
+            if ($pageId) {
+                try {
+                    $sql = "
+                    DELETE
+                      FROM blocks
+                     WHERE page_on = :page_on";
+                    $stmt = $this->_dbh->prepare($sql);
+                    $stmt->bindParam(':page_on', $pageId, PDO::PARAM_INT);
+                    $stmt->execute();
+                } catch(PDOException $e) {
+                    Toolkit_Common::handleError($e);
+                }
+            }
+        }
+        $GLOBALS['bottomScripts'][]
+            = $this->_routerPath . '/js/listPages.js';
+        $tpl = new HTML_Template_Flexy($this->_flexyOptions);
+        $tpl->compile($this->_template);
+
+        $page = new stdClass();
+        $page->baseUrl = MEDIA_BASE_URL;
+        $page->addUrl  = $this->_adminPath . '?ac=Edit';
+        $page->pages   = $this->_getPages();
+
+        return $tpl->bufferedOutputObject($page);
+    }
+}
diff --git a/Toolkit/Blocks/Admin/PageTree.php b/Toolkit/Blocks/Admin/PageTree.php
new file mode 100644 (file)
index 0000000..a5e2b32
--- /dev/null
@@ -0,0 +1,148 @@
+<?php
+
+/**
+ * PageTree.php
+ *
+ * PHP version 5.2
+ *
+ * @category  Toolkit
+ * @package   Blocks
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @version   SVN: (0.1)
+ * @link      <>
+ */
+
+/**
+ * Toolkit_Package_PageTree
+ *
+ * Display the toolbox page as ul lil list for jQuery-Column viewer
+ *
+ * @category  Toolkit
+ * @package   Blocks
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @release   Release: (0.1)
+ * @link      <>
+ */
+class Toolkit_Blocks_Admin_PageTree
+{
+    private $_dbh;
+    private $_rootNodeStart = "<ul class=\"menu\" id=\"demo1\">\n";
+    private $_leafStartExpanded = "\n\t<li class=\"expanded\" %s>\n";
+    private $_leafStartLeaf = "\n\t<li class=\"leaf\" %s>\n";
+    private $_subTreeStart = "\n<ul class=\"menu\">\n";
+    private $_treeEnd = "\n</ul>\n";
+    private $_leafEnd = "\n\t</li>\n";
+    private $_tree;
+
+    public function __construct(PDO $dbh)
+    {
+        $this->_dbh = $dbh;
+    }
+
+    /**
+     * creates and executes the sql query for getting the pages
+     *
+     * @return array | null
+     */
+    private function _findAll()
+    {
+        try {
+            if (defined('MEMBERS_CATEGORY') && MEMBERS_CATEGORY) {
+                $sql = "
+                SELECT id,parent,navigation_name
+                  FROM pages
+                 WHERE id NOT IN (".MEMBERS_CATEGORY.")
+                   AND parent NOT IN (".MEMBERS_CATEGORY.")
+                 ORDER by parent, pos";
+            } else {
+                $sql = "
+                SELECT id,parent,navigation_name
+                  FROM pages
+                 ORDER by parent, pos";
+            }
+
+
+            return $this->_dbh
+                ->query($sql)
+                ->fetchAll(PDO::FETCH_ASSOC);
+        } catch (PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+    }
+
+    /**
+     * Get all pages for the tree
+     *
+     * @return array
+     */
+    private function _fetchPages()
+    {
+        $pages = $this->_findAll();
+        if (is_array($pages)) {
+            $threads = array();
+            foreach ($pages as $page) {
+                $page['children'] = array();
+                $threads[] = $page;
+            }
+
+            $children = array();
+            while (list($key, $value) = each ($threads)) {
+                $children[$value['parent']][$value['id']] = $value;
+            }
+
+            $this->_tree = $children;
+        } else {
+            $this->_tree = array();
+        }
+    }
+
+    /**
+     * Create html of the pages tree for jqueyr.columnview
+     *
+     * @return string
+     */
+    public function toHtml()
+    {
+        $this->_fetchPages();
+        if (is_array($this->_tree)) {
+            $html = $this->createTree($this->_tree, reset($this->_tree));
+        }
+        return $html;
+    }
+
+    /**
+     * Creates the tree structure for the pages jquery column view
+     *
+     * @param array $tree  Array for tree
+     * @param type  $leaf  Array for leaf
+     * @param type  $level tree level
+     *
+     * @return string
+     */
+    protected function createTree(array $tree, $leaf, $level = 0)
+    {
+        $html = !$level ? $this->_rootNodeStart : $this->_subTreeStart;
+        if (is_array($leaf) && !empty($leaf)) {
+            while (list($parent, $branch) = each($leaf)) {
+                if ($tree[$parent]) {
+                    $html .= sprintf($this->_leafStartExpanded, null);
+                    $html .= "<a href=\"#\" rel=\"{$branch['id']}\">{$branch['navigation_name']} </a> ";
+                    $html .= $this->createTree($tree, $tree[$parent], $level + 1);
+                } else {
+                    $html .= sprintf($this->_leafStartLeaf, null);
+                    $html .= "<a href=\"#\" rel=\"{$branch['id']}\">{$branch['navigation_name']} </a> ";
+                    $html .= $this->_leafEnd;
+                }
+            }
+        }
+        $html .= $this->_treeEnd;
+        if ($level) {
+            $html .= $this->_leafEnd;
+        }
+        return $html;
+    }
+}
diff --git a/Toolkit/Blocks/Block.php b/Toolkit/Blocks/Block.php
new file mode 100644 (file)
index 0000000..4ff43ff
--- /dev/null
@@ -0,0 +1,529 @@
+<?php
+
+/**
+ * Block.php
+ *
+ * PHP version 5.2
+ *
+ * @category  Toolkit
+ * @package   Blocks
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @version   SVN: (0.1)
+ * @link      <>
+ */
+
+/**
+ * Toolkit_Package_Block
+ *
+ * Class constructs an object for the Block table of the GLM BLock
+ * Application. Handles the insert and update by way of the save method.
+ *
+ * @category  Toolkit
+ * @package   Blocks
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @release   Release: (0.1)
+ * @link      <>
+ */
+class Toolkit_Blocks_Block
+{
+    protected $tableName = "blocks";
+    protected $primaryKey = "id";
+    private $_id;
+    private $_title;
+    private $_description;
+    private $_image;
+    private $_pageOn;
+    private $_pageTo;
+    private $_url;
+    private $_external;
+    private $_pos;
+
+    /**
+     * Creates objects of type Block
+     *
+     * @param array $values values to use for the Block
+     */
+    public function __construct($values)
+    {
+        extract($values);
+        $this->setTitle($title)
+            ->setImage($image)
+            ->setDescription($description)
+            ->setPageOn($pageOn)
+            ->setPageTo($pageTo)
+            ->setPos($pos)
+            ->setUrl($url)
+            ->setExternal($external);
+        if ($id) {
+            $this->setId($id);
+        }
+    }
+
+    /**
+     * Static method to create Block objects from an array of values
+     *
+     * @param array $values values to create block with
+     *
+     * @return Toolkit_Blocks_Block
+     */
+    public static function createByValues($values)
+    {
+        return new Toolkit_Blocks_Block($values);
+    }
+
+    /**
+     * Fetch a block by id if not found return false
+     *
+     * @param PDO  $dbh Database Connection
+     * @param type $id  id of block to fetch
+     *
+     * @return false | Toolkit_Blocks_Block
+     */
+    public static function fetchById(PDO $dbh, $id)
+    {
+        try {
+            $sql = "
+            SELECT *
+              FROM blocks
+             WHERE id = :id";
+            $stmt = $dbh->prepare($sql);
+            $stmt->bindParam(':id', $id, PDO::PARAM_INT);
+            $stmt->execute();
+            $values = $stmt->fetch(PDO::FETCH_ASSOC);
+            if ($values) {
+                $values['pageOn'] = $values['page_on'];
+                $values['pageTo'] = $values['page_to'];
+                return Toolkit_Blocks_Block::createByValues($values);
+            } else {
+                return false;
+            }
+        } catch (PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+    }
+
+    /**
+     * Fetch a block by id if not found return false
+     *
+     * @param PDO  $dbh Database Connection
+     * @param type $pageOn  id of block to fetch
+     *
+     * @return false | Toolkit_Blocks_Block
+     */
+    public static function fetchByPageOn(PDO $dbh, $pageOn)
+    {
+        $blocks = array();
+        try {
+            $sql = "
+              SELECT *
+                FROM blocks
+               WHERE page_on = :id
+            ORDER BY pos";
+            $stmt = $dbh->prepare($sql);
+            $stmt->bindParam(':id', $pageOn, PDO::PARAM_INT);
+            $stmt->execute();
+            while ($values = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                $values['pageOn'] = $values['page_on'];
+                $values['pageTo'] = $values['page_to'];
+                $blocks[] = Toolkit_Blocks_Block::createByValues($values);
+            }
+        } catch (PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+        return $blocks;
+    }
+
+    /**
+     * Returns the Blocks id
+     *
+     * @return id
+     */
+    public function getId()
+    {
+        return $this->_id;
+    }
+
+    /**
+     * Sets the Blocks id
+     *
+     * @param type $id Id to set (must be numeric)
+     *
+     * @return Toolkit_Blocks_Block
+     * @throws Exception
+     */
+    public function setId($id)
+    {
+        if (!is_numeric($id)) {
+            throw new Exception('id must be an integer');
+        }
+        if (!$this->_id) {
+            $this->_id = $id;
+        }
+        return $this;
+    }
+
+    /**
+     * Returns Blocks title
+     *
+     * @return title
+     */
+    public function getTitle()
+    {
+        return $this->_title;
+    }
+
+    /**
+     * Sets the Blocks title
+     *
+     * @param string $title Title to set for Block
+     *
+     * @return Toolkit_Blocks_Block
+     */
+    public function setTitle($title)
+    {
+        $this->_title = $title;
+        return $this;
+    }
+
+    /**
+     * Returns Blocks url
+     *
+     * @return url
+     */
+    public function getUrl()
+    {
+        return $this->_url;
+    }
+
+    /**
+     * Sets the Blocks url
+     *
+     * @param string $url Url to set for Block
+     *
+     * @return Toolkit_Blocks_Block
+     */
+    public function setUrl($url)
+    {
+        $this->_url = $url;
+        return $this;
+    }
+
+    /**
+     * Returns if External
+     *
+     * @return external
+     */
+    public function getExternal()
+    {
+        return $this->_external;
+    }
+
+    /**
+     * Sets if Url is external
+     *
+     * @param string $external Boolean if external url
+     *
+     * @return Toolkit_Blocks_Block
+     */
+    public function setExternal($external)
+    {
+        $this->_external = $external;
+        return $this;
+    }
+
+    /**
+     * Returns the Blocks description
+     *
+     * @return description
+     */
+    public function getDescription()
+    {
+        return $this->_description;
+    }
+
+    /**
+     * Sets the Blocks desciption
+     *
+     * @param type $description The description for Block
+     *
+     * @return Toolkit_Blocks_Block
+     */
+    public function setDescription($description)
+    {
+        $this->_description = $description;
+        return $this;
+    }
+
+    /**
+     * Returns the Blocks image
+     *
+     * @return image
+     */
+    public function getImage()
+    {
+        return $this->_image;
+    }
+
+    /**
+     * Sets the Blocks image
+     *
+     * @param string $image
+     *
+     * @return Toolkit_Blocks_Block
+     */
+    public function setImage($image)
+    {
+        $this->_image = $image;
+        return $this;
+    }
+
+    /**
+     * Returns the Blocks Page id to display the block on
+     *
+     * @return page_on
+     */
+    public function getPageOn()
+    {
+        return $this->_pageOn;
+    }
+
+    /**
+     * Sets the Blocks page on
+     *
+     * @param type $pageOn The page to display the Block on
+     *
+     * @return Toolkit_Blocks_Block
+     */
+    public function setPageOn($pageOn)
+    {
+        $this->_pageOn = $pageOn;
+        return $this;
+    }
+
+    /**
+     * Returns the Page To display Block on
+     *
+     * @return page_to
+     */
+    public function getPageTo()
+    {
+        return $this->_pageTo;
+    }
+
+    /**
+     * Sets the Page to display the Block on
+     *
+     * @param type $pageTo The page id to display the Block on
+     *
+     * @return Toolkit_Blocks_Block
+     */
+    public function setPageTo($pageTo)
+    {
+        $this->_pageTo = $pageTo;
+        return $this;
+    }
+
+    /**
+     * Return the Blocks position on the page
+     *
+     * @return pos
+     */
+    public function getPos()
+    {
+        return $this->_pos;
+    }
+
+    /**
+     * Sets the Position of the Block
+     *
+     * @param type $pos Position of the Block on the Page
+     *
+     * @return Toolkit_Blocks_Block
+     */
+    public function setPos($pos)
+    {
+        $this->_pos = $pos;
+        return $this;
+    }
+
+    /**
+     * Checks the id of the object if it is set then calls update othervise
+     * calls insert function
+     *
+     * @param PDO     $dbh            Database connection
+     * @param boolean $saveNullFields To save nul fields or not
+     *
+     * @return viod
+     */
+    public function save(PDO $dbh)
+    {
+        if ($this->_id) {
+            $this->update($dbh);
+        } else {
+            $this->insert($dbh);
+        }
+    }
+
+    /**
+     * Inserts the object into the $this->tableName and returns the object
+     * being created
+     *
+     * @param PDO $dbh Database Connection
+     *
+     * @return Toolkit_Blocks_Block
+     */
+    public function insert(PDO $dbh)
+    {
+        try {
+            $methods = get_class_methods(get_class($this));
+            $values = array();
+            if ($methods) {
+                $pattern = '/get(.*)/';
+                foreach ($methods as $mName) {
+                    if (preg_match($pattern, $mName, $matches)) {
+                        $func = create_function(
+                            '$c',
+                            'return "_" . strtolower($c[1]);'
+                        );
+                        $fieldName = preg_replace_callback(
+                            '/([A-Z])/',
+                            $func,
+                            $matches[1]
+                        );
+                        $fieldName = preg_replace('/^_/', '', $fieldName);
+                        $values[$fieldName] = $this->$matches[0]();
+                    }
+                }
+            }
+            $defaultVars = get_class_vars(get_class($this));
+            $primaryKey  = $defaultVars['primaryKey'];
+            $tableName   = $defaultVars['tableName'];
+            unset($values[$primaryKey]);
+            // get the position for this insert
+            $values['pos'] = $this->_returnNextPos($dbh, $values['page_on']);
+            $sql = Toolkit_Common::createSQLInsert(
+                $tableName,
+                array_keys($values)
+            );
+            $sql .= " RETURNING $primaryKey";
+            $stmt = Toolkit_Common::prepareQuery(
+                $dbh,
+                $tableName,
+                $sql,
+                $values
+            );
+            $stmt->execute();
+            $setter = 'set'.ucfirst($primaryKey);
+            $this->$setter($stmt->fetchColumn());
+            return $this;
+        } catch(PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+    }
+
+    /**
+     * returs the next positition for the given page_on
+     *
+     * @param PDO $dbh    Database Connection
+     * @param int $pageOn page to display block on
+     *
+     * @return int
+     */
+    private function _returnNextPos(PDO $dbh, $pageOn)
+    {
+        try {
+            $sql = "
+            SELECT count(id)
+              FROM blocks
+             WHERE page_on = :page_on";
+            $stmt = $dbh->prepare($sql);
+            $stmt->bindParam(':page_on', $pageOn, PDO::PARAM_INT);
+            $stmt->execute();
+            $nextPos = $stmt->fetchColumn();
+            return ($nextPos) ? ++$nextPos : 1;
+        } catch (PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+    }
+
+    /**
+     * Updates the object
+     *
+     * @param PDO $dbh Database Connection
+     *
+     * @return Toolkit_Blocks_Block
+     */
+    public function update(PDO $dbh)
+    {
+        try {
+            $methods = get_class_methods(get_class($this));
+            $values = array();
+            if ($methods) {
+                $pattern = '/get(.*)/';
+                foreach ($methods as $mName) {
+                    if (preg_match($pattern, $mName, $matches)) {
+                        $func = create_function(
+                            '$c',
+                            'return "_" . strtolower($c[1]);'
+                        );
+                        $fieldName = preg_replace_callback(
+                            '/([A-Z])/',
+                            $func,
+                            $matches[1]
+                        );
+                        $fieldName = preg_replace('/^_/', '', $fieldName);
+                        $fieldValue = $this->$matches[0]();
+                        $values[$fieldName] = $fieldValue;
+                    }
+                }
+            }
+            $defaultVars = get_class_vars(get_class($this));
+            $primaryKey  = $defaultVars['primaryKey'];
+            $tableName   = $defaultVars['tableName'];
+            $sql = Toolkit_Common::createSQLUpdate(
+                $tableName,
+                array_keys($values),
+                array("$primaryKey = :$primaryKey")
+            );
+            $stmt = Toolkit_Common::prepareQuery(
+                $dbh,
+                $tableName,
+                $sql,
+                $values
+            );
+            $stmt->execute();
+            return $this;
+        } catch(PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+    }
+
+    /**
+     * Removes a object from the database by the id
+     * if id is not set then it throws an error
+     *
+     * @param PDO $dbh
+     */
+    public function delete(PDO $dbh)
+    {
+        if (!$this->getId()) {
+            throw new Exception('id is not set');
+        }
+        try {
+            $sql = "
+            DELETE
+              FROM {$this->tableName}
+             WHERE id = :id";
+            $stmt = $dbh->prepare($sql);
+            $stmt->bindParam(':id', $this->getId(), PDO::PARAM_INT);
+            $stmt->execute();
+        } catch(PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+    }
+}
+
diff --git a/Toolkit/Blocks/Database/application.sql b/Toolkit/Blocks/Database/application.sql
new file mode 100644 (file)
index 0000000..349392a
--- /dev/null
@@ -0,0 +1,12 @@
+--
+-- setup schema
+--
+
+CREATE SCHEMA blocks;
+GRANT ALL ON SCHEMA blocks TO nobody;
+
+--
+-- Tables
+--
+
+\i ./tables/blocks.sql
\ No newline at end of file
diff --git a/Toolkit/Blocks/Database/removeApplication.sql b/Toolkit/Blocks/Database/removeApplication.sql
new file mode 100644 (file)
index 0000000..c08046a
--- /dev/null
@@ -0,0 +1,6 @@
+--
+-- Drops schema
+-- WARNING: CANNOT BE UNDONE
+--
+
+DROP SCHEMA IF EXISTS blocks CASCADE;
\ No newline at end of file
diff --git a/Toolkit/Blocks/Database/tables/blocks.sql b/Toolkit/Blocks/Database/tables/blocks.sql
new file mode 100644 (file)
index 0000000..8139346
--- /dev/null
@@ -0,0 +1,19 @@
+DROP TABLE IF EXISTS blocks.blocks;
+
+CREATE TABLE blocks.blocks (
+    id SERIAL,
+    title TEXT,
+    description TEXT,
+    image TEXT,
+       url TEXT,
+    external BOOLEAN,
+    page_on INTEGER NOT NULL
+        REFERENCES toolbox.pages(id)
+        ON DELETE CASCADE,
+    page_to INTEGER,
+    pos INTEGER NOT NULL DEFAULT 1,
+    PRIMARY KEY (id)
+);
+
+GRANT ALL ON blocks.blocks TO nobody;
+GRANT ALL ON blocks.blocks_id_seq TO nobody;
\ No newline at end of file
diff --git a/Toolkit/Blocks/Database/upgrade.sql b/Toolkit/Blocks/Database/upgrade.sql
new file mode 100644 (file)
index 0000000..ca1c8f3
--- /dev/null
@@ -0,0 +1,8 @@
+--
+-- Upgrade block to have url and external option
+--
+
+ALTER TABLE blocks.blocks drop CONSTRAINT blocks_page_to_fkey;
+ALTER TABLE blocks.blocks ALTER page_to DROP NOT NULL;
+ALTER TABLE blocks.blocks ADD url TEXT;
+ALTER TABLE blocks.blocks ADD external BOOLEAN;
\ No newline at end of file
diff --git a/Toolkit/Blocks/Display.php b/Toolkit/Blocks/Display.php
new file mode 100644 (file)
index 0000000..028f7c2
--- /dev/null
@@ -0,0 +1,67 @@
+<?php
+
+/**
+ * Display.php
+ *
+ * PHP version 5.2
+ *
+ * @category  Toolkit
+ * @package   Blocks
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @version   SVN: (0.1)
+ * @link      <>
+ */
+
+/**
+ * Toolkit_Package_Display
+ *
+ * Creates an array
+ *
+ * @category  Toolkit
+ * @package   Blocks
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @release   Release: (0.1)
+ * @link      <>
+ */
+class Toolkit_BLocks_Display
+{
+    private $_dbh;
+
+    public function __construct(PDO $dbh)
+    {
+        $this->_dbh = $dbh;
+    }
+
+    public function getPageBlocksAsArray($pageId)
+    {
+        $pageBlocks = array();
+        $blocks = Toolkit_Blocks_Block::fetchByPageOn($this->_dbh, $pageId);
+        if (!empty($blocks)) {
+            foreach ($blocks as $block) {
+                if ($block->getUrl()) {
+                    $url = $block->getUrl();
+                } else {
+                    $url = Toolkit_Template_Page::getSeoUrl(
+                        new Toolkit_Toolbox_PageGatewayPublish($this->_dbh),
+                        $block->getPageTo()
+                    );
+                }
+                $pageBlocks[] = array(
+                    'external' => $block->getExternal(),
+                    'href'     => $url,
+                    'img'      => ($block->getImage())
+                        ? HEADLINE_THUMB . $block->getImage()
+                        : '',
+                    'header'   => $block->getTitle(),
+                    'descr'    => $block->getDescription()
+                );
+            }
+        }
+        return $pageBlocks;
+    }
+}
+
diff --git a/Toolkit/Blocks/IndexController.php b/Toolkit/Blocks/IndexController.php
new file mode 100644 (file)
index 0000000..39e48b0
--- /dev/null
@@ -0,0 +1,105 @@
+<?php
+
+/**
+ * IndexController.php
+ *
+ * PHP version 5.2
+ *
+ * @category  Toolkit
+ * @package   Blocks
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @version   SVN: (0.1)
+ * @link      <>
+ */
+
+/**
+ * Toolkit_Tags_IndexController
+ *
+ * Controller for admin side of the Featured pages
+ *
+ * @category  Toolkit
+ * @package   Blocks
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @release   Release: (0.1)
+ * @link      <>
+ */
+class Toolkit_Blocks_IndexController
+    extends Toolkit_BaseControllerAbstract
+    implements Toolkit_IController
+{
+
+    public function __construct(Toolkit_Registry $registry)
+    {
+        parent::__construct($registry);
+        $GLOBALS['styleSheets'][] = MEDIA_BASE_URL .
+            $registry->appConfig->application->setPath . '/css/style.css';
+    }
+    /**
+     * default action used
+     *
+     * @return string html
+     */
+    public function indexAction()
+    {
+        $GLOBALS['topScripts'][] = MEDIA_APP_BASE_URL
+            . 'libjs/jquery/jquery-1.7.2.min.js';
+        $GLOBALS['bottomScripts'][] = MEDIA_BASE_URL .
+                $this->registry->appConfig->application->setPath .
+                '/js/jquery.columnview.js';
+        // look at canCreateOnAnyPage
+        $canCreateOnAnyPage = $this->registry->appConfig->canCreateOnAnyPage;
+        if ($canCreateOnAnyPage) {
+
+            $list = new Toolkit_Blocks_Admin_ListPages($this->registry);
+            $html = $list->toHtml();
+        } else {
+            $edit = new Toolkit_Blocks_Admin_EditPage(
+                $this->registry,
+                HOME_ID
+            );
+            $html = $edit->toHtml();
+        }
+        return $html;
+    }
+
+    /**
+     * Add/Edit Page Action
+     *
+     * @return string html
+     */
+    public function EditAction()
+    {
+        $GLOBALS['topScripts'][] = MEDIA_APP_BASE_URL
+            . 'libjs/jquery/jquery-1.7.2.min.js';
+        $GLOBALS['bottomScripts'][] = MEDIA_BASE_URL .
+            $this->registry->appConfig->application->setPath .
+            '/js/jquery.columnview.js';
+        $pageId = filter_input(
+            INPUT_GET,
+            'page_on',
+            FILTER_SANITIZE_NUMBER_INT
+        );
+        $edit = new Toolkit_Blocks_Admin_EditPage(
+            $this->registry,
+            $pageId
+        );
+        return $edit->toHtml();
+    }
+
+    /**
+     * Used to create the page selector Column View
+     * this method will call exit to stop it from creating any more html
+     * from top and fotter methods
+     */
+    public function AddAction()
+    {
+        $pageTree = new Toolkit_Blocks_Admin_PageTree($this->registry->dbh);
+        echo $pageTree->toHtml();
+        exit;
+    }
+
+}
diff --git a/Toolkit/Blocks/application.ini b/Toolkit/Blocks/application.ini
new file mode 100644 (file)
index 0000000..3be385e
--- /dev/null
@@ -0,0 +1,42 @@
+; Production server configuration data
+[production]
+application.name    = "GLM Blocks"
+application.path    = MEDIA_BASE_URL "admin/blocks.php"
+application.setPath = "Toolkit/Blocks"
+
+; router config
+router.path        = BASE "Toolkit/Blocks"
+router.application = "Blocks"
+
+; PEAR HTML_Template_Flexy Options and settings
+flexy.options.templateDir    = "Toolkit/Blocks/templates"
+flexy.options.compileDir     = "Toolkit/Blocks/templates/compiled"
+flexy.templates.listPages    = "listPages.html"
+flexy.templates.editPage     = "editPage.html"
+
+; jQuery settings
+jqueryui.version = "1.8.13"
+; jQuery themes {"base", "cupertino", "smoothness", "start"}
+jqueryui.theme = "base"
+
+canCreateOnAnyPage = Off
+
+; development server configuration data inherits from production and
+; overrides values as necessary
+[development : production]
+
+; chuck's server configuration data inherits from development
+; and overrides values as necessary
+[chuck : development]
+
+; john's server configuration data inherits from development
+; and overrides values as necessary
+[john : development]
+
+; steve's server configuration data inherits from development
+; and overrides values as necessary
+[steve : development]
+
+; Vagrant server configuration inherits from development and
+; overrides values as needed
+[vagrant : development]
diff --git a/Toolkit/Blocks/assets/btn_add.gif b/Toolkit/Blocks/assets/btn_add.gif
new file mode 100755 (executable)
index 0000000..7ca35fb
Binary files /dev/null and b/Toolkit/Blocks/assets/btn_add.gif differ
diff --git a/Toolkit/Blocks/assets/btn_add_sm.gif b/Toolkit/Blocks/assets/btn_add_sm.gif
new file mode 100755 (executable)
index 0000000..50994dc
Binary files /dev/null and b/Toolkit/Blocks/assets/btn_add_sm.gif differ
diff --git a/Toolkit/Blocks/assets/btn_back.gif b/Toolkit/Blocks/assets/btn_back.gif
new file mode 100755 (executable)
index 0000000..31ae1f4
Binary files /dev/null and b/Toolkit/Blocks/assets/btn_back.gif differ
diff --git a/Toolkit/Blocks/assets/btn_delete.gif b/Toolkit/Blocks/assets/btn_delete.gif
new file mode 100755 (executable)
index 0000000..b320511
Binary files /dev/null and b/Toolkit/Blocks/assets/btn_delete.gif differ
diff --git a/Toolkit/Blocks/assets/btn_edit.gif b/Toolkit/Blocks/assets/btn_edit.gif
new file mode 100755 (executable)
index 0000000..0630f07
Binary files /dev/null and b/Toolkit/Blocks/assets/btn_edit.gif differ
diff --git a/Toolkit/Blocks/assets/btn_link.gif b/Toolkit/Blocks/assets/btn_link.gif
new file mode 100755 (executable)
index 0000000..155c6c8
Binary files /dev/null and b/Toolkit/Blocks/assets/btn_link.gif differ
diff --git a/Toolkit/Blocks/assets/btn_update.gif b/Toolkit/Blocks/assets/btn_update.gif
new file mode 100755 (executable)
index 0000000..91838e0
Binary files /dev/null and b/Toolkit/Blocks/assets/btn_update.gif differ
diff --git a/Toolkit/Blocks/assets/btn_upload.gif b/Toolkit/Blocks/assets/btn_upload.gif
new file mode 100755 (executable)
index 0000000..121f0b3
Binary files /dev/null and b/Toolkit/Blocks/assets/btn_upload.gif differ
diff --git a/Toolkit/Blocks/assets/left_circle.gif b/Toolkit/Blocks/assets/left_circle.gif
new file mode 100755 (executable)
index 0000000..075391b
Binary files /dev/null and b/Toolkit/Blocks/assets/left_circle.gif differ
diff --git a/Toolkit/Blocks/assets/right_circle.gif b/Toolkit/Blocks/assets/right_circle.gif
new file mode 100755 (executable)
index 0000000..08cfbad
Binary files /dev/null and b/Toolkit/Blocks/assets/right_circle.gif differ
diff --git a/Toolkit/Blocks/css/style.css b/Toolkit/Blocks/css/style.css
new file mode 100644 (file)
index 0000000..72e5f36
--- /dev/null
@@ -0,0 +1,485 @@
+html {
+    font-size: 62.5%;
+    }
+#blocksEditWrapper, #blocksListWrapper {
+    font-size: 12px;
+    font-size: 1.2rem;
+    }
+h2 {
+    font-size: 18px;
+    font-size: 1.8rem;
+    color: #777;
+    }
+.containerobj .widget {
+    background-color: white;
+    }
+.containerobj .active .widget {
+    color: white;
+    background-color: #3671cf;
+    }
+.containerobj .inpath .widget {
+    background-color: #d0d0d0;
+    }
+#demo1 {
+    height: 200px;
+    }
+#demo1 a {
+    padding: 0 3px;
+    }
+.ui-dialog-buttonpane {
+    padding: 0 10px 0 20px !important;
+    }
+#dialog-modal {
+    height: auto !important;
+    }
+.imageBlock {
+    width: 210px;
+    float: left;
+    padding: 0 5px 0 5px;
+}
+.textBlock {
+    float: left;
+    width: 536px;
+    padding: 5px 5px 0 5px;
+}
+.textBlockTextArea {
+    width: 436px;
+    max-width: 436px;
+    min-width: 436px;
+    min-height: 160px;
+    margin: 3px 0 0 0;
+    padding: 3px;
+    font-family: Verdana, Geneva, sans-serif;
+    font-size: 12px;
+    font-size: 1.2rem;
+    border: 1px solid #999;
+}
+#sortable {
+    list-style-type: none;
+    margin: 0;
+    padding: 0;
+    width: 800px;
+    float: left;
+    overflow: hidden;
+}
+#sortable li {
+    margin: 3px;
+    position: relative;
+    padding: 0 0 3px 25px;
+    overflow: hidden;
+}
+#sortable li span.ui-icon {
+    position: absolute;
+    margin-left: -25px;
+}
+#newBlockForm {
+    float: left;
+    clear: both;
+    list-style-type: none;
+    margin: 0;
+    padding: 0;
+    width: 800px;
+    float: left;
+    overflow: hidden;
+}
+#newBlockForm li {
+    margin: 3px;
+    position: relative;
+    padding: 0 0 3px 25px;
+    overflow: hidden;
+}
+#notsortable {
+    list-style-type: none;
+    margin: 10px 0 10px 0;
+    padding: 0;
+    width: 800px;
+    overflow: hidden;
+}
+#notsortable li {
+    overflow: hidden;
+}
+#notsortable li span {
+    margin-left: 20px;
+}
+#blocksListWrapper {
+    width: 800px;
+    border: 1px solid #777;
+    padding: 10px;
+    background: #E1E1E1;
+    }
+#blocksListWrapper h2 {
+    width: 600px;
+    float: left;
+    overflow: hidden;
+    margin: 5px 0 5px 3px;
+    }
+#editPane {
+    float: left;
+    width: 99px;
+    }
+#deletePane {
+    float: right;
+    width: 100px;
+    }
+#mainPane {
+    float: left;
+    width: 500px;
+    overflow: hidden;
+    }
+#infoPane {
+    float: left;
+    display: block;
+    width: 500px;
+    margin: 9px 0 0 8px;
+    border-bottom: 1px solid #B1B1B1;
+    overflow: hidden;
+    font-size: 30px;
+    font-size: 3.0rem;
+    line-height: 28px;
+    color: #777;
+    text-decoration: none;
+    }
+#breadcrumbs {
+    float:left;
+    color: #777;
+    margin: 3px 0 10px 9px;
+    }
+#numberPane {
+    float: right;
+    width: 90px;
+    margin: 20px 0 0 0;
+    }
+a#editBtn, a#deleteBtn {
+    display: block;
+    height: 28px;
+    width: 82px;
+    line-height: 28px;
+    margin: 20px 0 20px 8px;
+    padding: 0 0 0 30px;
+    color: #555;
+    font-family: Verdana, Geneva, sans-serif;
+    text-decoration: none;
+    }
+a#editBtn {
+    background: url(../assets/btn_edit.gif) no-repeat;
+    }
+a#editBtn:hover {
+    color: #09F;
+    background-position: 0px -28px;
+    }
+a#deleteBtn {
+    background: url(../assets/btn_delete.gif) no-repeat;
+    }
+a#deleteBtn:hover {
+    color: #09F;
+    background-position: 0px -28px;
+    }
+#titletext {
+    float: left;
+    display: block;
+    font-size: 30px;
+    font-size: 3.0rem;
+    line-height: 28px;
+    color: #777;
+    text-decoration: none;
+    overflow: hidden;
+    width: 500px;
+    margin: 9px 0 0 8px;
+    border-bottom: 1px solid #B1B1B1;
+    overflow: hidden;
+    font-size: 26px;
+    font-size: 2.6rem;
+    }
+#titletext:hover {
+    color: #09F;
+    }
+#linktext {
+    float: right;
+    padding: 8px 5px 0 0;
+    }
+#numLeft {
+    float: right;
+    display: inline-block;
+    width: 15px;
+    height: 30px;
+    background: url(../assets/left_circle.gif) no-repeat;
+    }
+#numRight {
+    float: right;
+    display: inline-block;
+    width: 15px;
+    height: 30px;
+    background: url(../assets/right_circle.gif) no-repeat;
+    }
+#numMain {
+    float: right;
+    display: inline-block;
+    overflow: hidden;
+    color: #777;
+    font-size: 20px;
+    font-size: 2.0rem;
+    font-weight: bold;
+    line-height: 28px;
+    border-top: 1px solid #B1B1B1;
+    border-bottom: 1px solid #B1B1B1;
+    margin: 0 -3px;
+    }
+a#addPageUrl {
+    display: block;
+    width: 222px;
+    height: 28px;
+    line-height: 28px;
+    padding: 0 0 0 36px;
+    margin: 0 0 10px 0;
+    background: url(../assets/btn_add.gif) no-repeat;
+    color: #555;
+    font-family: Verdana, Geneva, sans-serif;
+    text-decoration: none;
+    }
+a#addPageUrl:hover {
+    color: #09F;
+    background-position: 0px -28px;
+    }
+#blocksEditWrapper {
+    border: 1px solid #777;
+    padding: 10px;
+    margin: 10px 0 0 0;
+    background: #E1E1E1;
+    width: 800px;
+    overflow: hidden;
+    }
+#blocksEditWrapper h2 {
+    width: 600px;
+    float: left;
+    overflow: hidden;
+    margin: 5px 0 5px 3px;
+    }
+#backBtn {
+    display:block;
+    height: 28px;
+    width: 139px;
+    line-height: 28px;
+    margin: 0 3px 0 0;
+    padding: 0 0 0 30px;
+    background: url(../assets/btn_back.gif) no-repeat;
+    color: #555;
+    font-family: Verdana, Geneva, sans-serif;
+    text-decoration: none;
+    }
+#backBtn:hover {
+    color: #09F;
+    background-position: 0px -28px;
+    }
+.title {
+    float: left;
+    display: inline;
+    height: 26px;
+    padding: 0 5px 0 0;
+    margin: 0;
+    }
+.titleLabel {
+    display: inline;
+    font-size: 12px;
+    font-size: 1.2rem;
+    line-height: 26px;
+    margin: 0;
+}
+.urlField {
+    clear: left;
+}
+.editPageTo {
+    display: inline;
+    font-size: 12px;
+    font-size: 1.2rem;
+    line-height: 26px;
+    margin: 0 5px 0 0;
+    }
+#page_to_name {
+    display: inline;
+    font-size: 12px;
+    line-height: 26px;
+    margin: 0 5px 0 0;
+    }
+#addPageTo {
+    display: inline;
+    width: 82px;
+    height: 28px;
+    background: url(../assets/btn_link.gif) no-repeat;
+    border: none;
+    text-align: left;
+    padding: 0 0 0 24px;
+}
+#addPageTo:hover {
+    color: #09F;
+    background-position: 0px -28px;
+    cursor: pointer;
+    }
+#addBtn {
+    display: block;
+    position:absolute;
+    bottom: 5px;
+    right: 5px;
+    width: 82px;
+    height: 28px;
+    background: url(../assets/btn_add_sm.gif) no-repeat;
+    border: none;
+}
+#addBtn:hover {
+    color: #09F;
+    background-position: 0px -28px;
+    cursor: pointer;
+    }
+#updateBtn {
+    display: block;
+    position:absolute;
+    bottom: 5px;
+    right: 5px;
+    width: 82px;
+    height: 28px;
+    background: url(../assets/btn_update.gif) no-repeat;
+    border: none;
+    padding: 0 0px 0 16px;
+    }
+#updateBtn:hover {
+    color: #09F;
+    background-position: 0px -28px;
+    cursor: pointer;
+    }
+#deleteBtn2 {
+    display: block;
+    position:absolute;
+    bottom: 36px;
+    right: 5px;
+    width: 55px;
+    height: 28px;
+    background: url(../assets/btn_delete.gif) no-repeat;
+    border: none;
+    padding: 0 0 0 27px;
+    font-size: 14px;
+    font-size: 1.4rem;
+    line-height: 28px;
+    text-decoration: none;
+    margin: 0px;
+    }
+#deleteBtn2:hover {
+    color: #F00;
+    background-position: 0px -28px;
+    cursor: pointer;
+    }
+.ui-state-highlight {
+    color: #09F;
+    border-color: #09F;
+    background: #09F;
+    }
+.ui-widget-content {
+    margin: 0;
+    padding: 0;
+    }
+.upload {
+    position: relative;
+    width: 210px;
+    height:2 8px;
+    background: url(../assets/btn_upload.gif) no-repeat;
+    line-height: 28px;
+    padding: 0;
+    margin: 5px 0 3px 0;
+    font-size: 12px;
+    font-size: 1.2rem;
+    color: #555;
+    font-family: Verdana, Geneva, sans-serif;
+    text-decoration: none;
+    }
+.upload:hover {
+    color: #09F;
+    background-position: 0px -28px;
+}
+.upload, .image {
+    width: 210px;
+    height: 28px;
+    }
+.image {
+    opacity: 0.01;
+    filter: alpha(opacity=0.01);
+    position: absolute;
+    right: 0px;
+    }
+#upload_text {
+    width: 210px;
+    height: 28px;
+    display: block;
+    cursor: pointer;
+    }
+.upload:hover #upload_text {
+    color: #09F;
+    }
+.file-wrapper {
+    cursor: pointer;
+    display: inline-block;
+    overflow: hidden;
+    position: relative;
+    margin: 5px 0 0 0;
+}
+.file-wrapper input {
+    cursor: pointer;
+    font-size: 100px;
+    height: 100%;
+    filter: alpha(opacity=1);
+    -moz-opacity: 0.01;
+    opacity: 0.01;
+    position: absolute;
+    right: 0;
+    top: 0;
+    width: 210px;
+    height: 28px;
+}
+.file-wrapper .button {
+    width: 180px;
+    height: 28px;
+    background: url(../assets/btn_upload.gif) no-repeat;
+    color: #fff;
+    cursor: pointer;
+    font-size: 12px;
+    font-size: 1.2rem;
+    color: #555;
+    font-family: Verdana, Geneva, sans-serif;
+    text-decoration: none;
+    display: inline-block;
+    line-height: 28px;
+    padding: 0 0 0 30px;
+}
+
+.file-wrapper:hover .button {
+    color: #09F;
+    background-position: 0px -28px;
+    }
+#sideImage {
+    display: block;
+    width: 210px;
+    height: auto;
+}
+.internal_link, .external_link {
+    display: block;
+    float: left;
+    clear: left;
+    overflow: hidden;
+    background: #F5F5F5;
+    border: 1px solid #CCC;
+    width: 290px;
+    margin-top: 5px;
+    padding: 5px;
+}
+.internal_link h3, .external_link h3 {
+    display: block;
+    width: 100%;
+    overflow: hidden;
+    font-size: 14px;
+    font-weight: bold;
+    margin: 0;
+    padding: 0 0 5px 0;
+    color: #777;
+}
+.cke_chrome {
+    float: left;
+    clear: left;
+    margin-top: 5px !important;
+}
diff --git a/Toolkit/Blocks/js/column.js b/Toolkit/Blocks/js/column.js
new file mode 100644 (file)
index 0000000..f1d6d09
--- /dev/null
@@ -0,0 +1,10 @@
+$(document).ready(function(){
+    $("#dialog-modal").load('blocks.php?ac=Add', function(){
+        $('#demo1').columnview({
+            preview:false,
+            onchange: function(element) {
+                $("#page_on").text($(element).text());
+            }
+        });
+    });
+});
diff --git a/Toolkit/Blocks/js/editPage.js b/Toolkit/Blocks/js/editPage.js
new file mode 100644 (file)
index 0000000..2779974
--- /dev/null
@@ -0,0 +1,149 @@
+var SITE = SITE || {};
+
+SITE.fileInputs = function() {
+  var $this = $(this),
+      $val = $this.val(),
+      valArray = $val.split('\\'),
+      newVal = valArray[valArray.length-1],
+      $button = $this.siblings('.button'),
+      $fakeFile = $this.siblings('.file-holder');
+  if(newVal !== '') {
+    $button.text('Image Chosen');
+    if($fakeFile.length === 0) {
+      $button.after('<span class="file-holder">' + newVal + '</span>');
+    } else {
+      $fakeFile.text(newVal);
+    }
+  }
+};
+
+$(function(){
+    $('.file-wrapper input[type=file]').bind('change focus click', SITE.fileInputs);
+
+    $('.deleteBlock > a').click(function(){
+        return confirm('This Delete cannot be undone!\n Are You Sure?');
+    });
+    $(document).on('mousedown', '#sortable', function(e) {
+        e.stopPropagation();
+    });
+    if (CKEDITOR.env.isCompatible) {
+        $("textarea.textBlockTextArea").each(function() {
+            CKEDITOR.replace(
+                $(this).attr('id'),
+                {
+                    toolbar : 'LimitedToolset',
+                    width : 300,
+                    height : 200
+                }
+            );
+        });
+    }
+    $("#sortable").sortable({
+        update: function (event, ui) {
+
+            var url = '../block-pos/?' + $(this).sortable('serialize');
+            $.get(url, function(data){
+                $('.movable').effect('hightlight', {}, 700);
+            });
+        },
+        placeholder: "ui-state-highlight"
+    });
+    //$("#sortable").disableSelection();
+
+    $("#addPageTo").click(function(){
+        $("#dialog-modal").dialog({
+            height: 240,
+            width: 800,
+            modal: true,
+            buttons: {
+                "Select Page": function() {
+                    if ($("#page_to").val() != '') {
+                        $(this).dialog("close");
+                    } else {
+                        alert("Select a Page");
+                    }
+                },
+                Cancel: function() {
+                    $(this).dialog("close");
+                },
+                "Clear Page": function() {
+                    $("#page_to_name").text('No Page');
+                    $("#page_to").val('');
+                    $(this).dialog("close");
+                }
+            }
+        });
+        $("#pages").load('blocks.php?ac=Add', function(){
+            $('#demo1').columnview({
+                preview:false,
+                onchange: function(element) {
+                    $("#page_to_name").text($(element).text());
+                    $("#page_to").val($(element).attr('rel'));
+                }
+            });
+        });
+        return false;
+    });
+    $(".editPageTo").click(function(){
+        var blockId = $(this).attr('rel');
+        $("#dialog-modal").dialog({
+            height: 240,
+            width: 800,
+            modal: true,
+            buttons: {
+                "Select Page": function() {
+                    if ($('[name="page_to"][rel="' + blockId + '"]').val() != '') {
+                        $(this).dialog("close");
+                    } else {
+                        alert("Select a Page");
+                    }
+                },
+                Cancel: function() {
+                    $(this).dialog("close");
+                },
+                "Clear Page": function() {
+                    $('[class="editPageTo"][rel="' + blockId + '"]').text('No Page');
+                    $('[name="page_to"][rel="' + blockId + '"]').val('');
+                    $(this).dialog("close");
+                }
+            }
+        });
+        $("#pages").load('blocks.php?ac=Add', function(){
+            $('#demo1').columnview({
+                preview:false,
+                onchange: function(element) {
+                    $('[class="editPageTo"][rel="' + blockId + '"]').text($(element).text());
+                    $('[name="page_to"][rel="' + blockId + '"]').val($(element).attr('rel'));
+                }
+            });
+        });
+        return false;
+    });
+    $(".blockForm").submit(function(){
+        var msg = '';
+        var errors = 0;
+        var title = $('input[name="title"]', this).val();
+        if (title == '') {
+            errors++;
+            msg += "Headline Required\n";
+        }
+        var page_to = $('input[name="page_to"]', this).val();
+        var blockUrl = $('input[name="url"]', this).val();
+        if (page_to == '' && blockUrl == '') {
+            errors++;
+            msg += "Please select a Page to link to\nor Enter a URL!\n";
+        }
+        if (errors > 0) {
+            alert("Error: " + msg);
+            return false;
+        } else {
+            return true;
+        }
+    });
+});
+
+
+
+$(document).ready(function() {
+  $('.file-wrapper input[type=file]').bind('change focus click', SITE.fileInputs);
+});
diff --git a/Toolkit/Blocks/js/jquery.columnview.js b/Toolkit/Blocks/js/jquery.columnview.js
new file mode 100644 (file)
index 0000000..4131b1d
--- /dev/null
@@ -0,0 +1,267 @@
+/**
+ * jquery.columnview-1.2.js
+ *
+ * Created by Chris Yates on 2009-02-26.
+ * http://christianyates.com
+ * Copyright 2009 Christian Yates and ASU Mars Space Flight Facility. All rights reserved.
+ *
+ * Supported under jQuery 1.2.x or later
+ * Keyboard navigation supported under 1.3.x or later
+ *
+ * Dual licensed under MIT and GPL.
+ */
+
+(function($){
+  $.fn.columnview = function(options){
+
+    var settings = $.extend({}, $.fn.columnview.defaults, options);
+
+    // Add stylesheet, but only once
+    if(!$('.containerobj').get(0)){
+      $('head').prepend('\
+      <style type="text/css" media="screen">\
+        .containerobj {\
+          border: 1px solid #ccc;\
+          height:5em;\
+          overflow-x:auto;\
+          overflow-y:hidden;\
+          white-space:nowrap;\
+          position:relative;\
+        }\
+        .containerobj div {\
+          height:100%;\
+          overflow-y:scroll;\
+          overflow-x:hidden;\
+          position:absolute;\
+        }\
+        .containerobj a {\
+          display:block;\
+          white-space:nowrap;\
+          clear:both;\
+          padding-right:15px;\
+          overflow:hidden;\
+          text-decoration:none;\
+        }\
+        .containerobj a:focus {\
+          outline:none;\
+        }\
+        .containerobj a canvas {\
+        }\
+        .containerobj .feature {\
+          min-width:200px;\
+          overflow-y:auto;\
+        }\
+        .containerobj .feature a {\
+          white-space:normal;\
+        }\
+        .containerobj .hasChildMenu {\
+        }\
+        .containerobj .active {\
+          background-color:#3671cf;\
+          color:#fff;\
+        }\
+        .containerobj .inpath {\
+          background-color:#d0d0d0;\
+          color:#000;\
+        }\
+        .containerobj .hasChildMenu .widget {\
+          color:black;\
+          position:absolute;\
+          right:0;\
+          text-decoration:none;\
+          font-size:0.7em;\
+        }\
+      </style>');
+    }
+
+    // Hide original list
+    $(this).hide();
+    // Reset the original list's id
+    var origid = $(this).attr('id');
+    if (origid) {
+      $(this).attr('id', origid + "-processed");
+    }
+
+    // Create new top container from top-level LI tags
+    var top = $(this).children('li');
+    var container = $('<div/>').addClass('containerobj').attr('id', origid).insertAfter(this);
+    var topdiv = $('<div class="top"></div>').appendTo(container);
+    // Set column width
+    if (settings.fixedwidth || $.browser.msie) { // MSIE doesn't support auto-width
+      var width = typeof settings.fixedwidth == "string" ? settings.fixedwidth : '200px';
+      $('.top').width(width);
+    }
+    $.each(top,function(i,item){
+      var topitem = $(':eq(0)',item).clone(true).wrapInner("<span/>").data('sub',$(item).children('ul')).appendTo(topdiv);
+      if (settings.fixedwidth || $.browser.msie)
+      $(topitem).css({'text-overflow':'ellipsis', '-o-text-overflow':'ellipsis','-ms-text-overflow':'ellipsis'});
+      if($(topitem).data('sub').length) {
+        $(topitem).addClass('hasChildMenu');
+        addWidget(container, topitem);
+      }
+    });
+
+    // Firefox doesn't repeat keydown events when the key is held, so we use
+    // keypress with FF/Gecko/Mozilla to enable continuous keyboard scrolling.
+    var key_event = $.browser.mozilla ? 'keypress' : 'keydown';
+
+    // Event handling functions
+    $(container).bind("click " + key_event, function(event){
+      if ($(event.target).is("a,span")) {
+        if ($(event.target).is("span")){
+          var self = $(event.target).parent();
+        }
+        else {
+          var self = event.target;
+        }
+        if (!settings.multi) {
+          delete event.shiftKey;
+          delete event.metaKey;
+        }
+        self.focus();
+        var container = $(self).parents('.containerobj');
+        // Handle clicks
+        if (event.type == "click"){
+          var level = $('div',container).index($(self).parents('div'));
+          var isleafnode = false;
+          // Remove blocks to the right in the tree, and 'deactivate' other
+          // links within the same level, if metakey is not being used
+          $('div:gt('+level+')',container).remove();
+          if (!event.metaKey && !event.shiftKey) {
+            $('div:eq('+level+') a',container).removeClass('active').removeClass('inpath');
+            $('.active',container).addClass('inpath');
+            $('div:lt('+level+') a',container).removeClass('active');
+          }
+          // Select intermediate items when shift clicking
+          // Sorry, only works with jQuery 1.4 due to changes in the .index() function
+          if (event.shiftKey) {
+            var first = $('a.active:first', $(self).parent()).index();
+            var cur = $(self).index();
+            var range = [first,cur].sort(function(a,b){return a - b;});
+            $('div:eq('+level+') a', container).slice(range[0], range[1]).addClass('active');
+          }
+          $(self).addClass('active');
+          if ($(self).data('sub').children('li').length && !event.metaKey) {
+            // Menu has children, so add another submenu
+            var w = false;
+            if (settings.fixedwidth || $.browser.msie)
+            w = typeof settings.fixedwidth == "string" ? settings.fixedwidth : '200px';
+            submenu(container,self,w);
+          }
+          else if (!event.metaKey && !event.shiftKey) {
+            // No children, show title instead (if it exists, or a link)
+            isleafnode = true;
+            var previewcontainer = $('<div/>').addClass('feature').appendTo(container);
+            // Fire preview handler function
+            if ($.isFunction(settings.preview)) {
+              // We're passing the element back to the callback
+              var preview = settings.preview($(self));
+            }
+            // If preview is specifically disabled, do nothing with the previewbox
+            else if (!settings.preview) {
+            }
+            // If no preview function is specificied, use a default behavior
+            else {
+              var title = $('<a/>').attr({href:$(self).attr('href')}).text($(self).attr('title') ? $(self).attr('title') : $(self).text());
+              $(previewcontainer).html(title);
+            }
+            // Set the width
+            var remainingspace = 0;
+            $.each($(container).children('div').slice(0,-1),function(i,item){
+              remainingspace += $(item).width();
+            });
+            var fillwidth = $(container).width() - remainingspace;
+            $(previewcontainer).css({'top':0,'left':remainingspace}).width(fillwidth).show();
+          }
+          // Fire onchange handler function, but only if multi-select is off.
+          // FIXME Need to deal multiple selections.
+          if ($.isFunction(settings.onchange) && !settings.multi) {
+            // We're passing the element back to the callback
+            var onchange = settings.onchange($(self), isleafnode);
+          }
+        }
+        // Handle Keyboard navigation
+        if(event.type == key_event){
+          switch(event.keyCode){
+            case(37): //left
+              $(self).parent().prev().children('.inpath').focus().trigger("click");
+              break;
+            case(38): //up
+              $(self).prev().focus().trigger("click");
+              break;
+            case(39): //right
+              if($(self).hasClass('hasChildMenu')){
+                $(self).parent().next().children('a:first').focus().trigger("click");
+              }
+              break;
+            case(40): //down
+              $(self).next().focus().trigger("click");
+              break;
+            case(13): //enter
+              $(self).trigger("dblclick");
+              break;
+          }
+        }
+        event.preventDefault();
+      }
+    });
+
+  };
+
+  $.fn.columnview.defaults = {
+    multi: false,     // Allow multiple selections
+    preview: true,    // Handler for preview pane
+    fixedwidth: false,// Use fixed width columns
+    onchange: false   // Handler for selection change
+  };
+
+  // Generate deeper level menus
+  function submenu(container,item,width){
+    var leftPos = 0;
+    $.each($(container).children('div'),function(i,mydiv){
+      leftPos += $(mydiv).width();
+    });
+    var submenu = $('<div/>').css({'top':0,'left':leftPos}).appendTo(container);
+    // Set column width
+    if (width)
+    $(submenu).width(width);
+    var subitems = $(item).data('sub').children('li');
+    $.each(subitems,function(i,subitem){
+      var subsubitem = $(':eq(0)',subitem).clone(true).wrapInner("<span/>").data('sub',$(subitem).children('ul')).appendTo(submenu);
+      if (width)
+      $(subsubitem).css({'text-overflow':'ellipsis', '-o-text-overflow':'ellipsis','-ms-text-overflow':'ellipsis'});
+      if($(subsubitem).data('sub').length) {
+        $(subsubitem).addClass('hasChildMenu');
+        addWidget(container, subsubitem);
+      }
+    });
+  }
+
+  // Uses canvas, if available, to draw a triangle to denote that item is a parent
+  function addWidget(container, item, color){
+    var triheight = $(item).height();
+    var canvas = $("<canvas></canvas>").attr({height:triheight,width:10}).addClass('widget').appendTo(item);    if(!color){ color = $(canvas).css('color'); }
+    canvas = $(canvas).get(0);
+    if(canvas.getContext){
+      var context = canvas.getContext('2d');
+      context.fillStyle = color;
+      context.beginPath();
+      context.moveTo(3,(triheight/2 - 3));
+      context.lineTo(10,(triheight/2));
+      context.lineTo(3,(triheight/2 + 3));
+      context.fill();
+    } else {
+      /**
+       * Canvas not supported - put something in there anyway that can be
+       * suppressed later if desired. We're using a decimal character here
+       * representing a "black right-pointing pointer" in Windows since IE
+       * is the likely case that doesn't support canvas.
+       */
+      $("<span>&#9658;</span>").addClass('widget').css({'height':triheight,'width':10}).prependTo(item);
+    }
+    $(container).find('.widget').bind('click', function(event){
+      event.preventDefault();
+    });
+
+  }
+})(jQuery);
diff --git a/Toolkit/Blocks/js/listPages.js b/Toolkit/Blocks/js/listPages.js
new file mode 100644 (file)
index 0000000..c3d4afb
--- /dev/null
@@ -0,0 +1,35 @@
+$(document).ready(function(){
+    $('.deleteBlock > a').click(function(){
+        return confirm('This Delete cannot be undone!\n Are You Sure?');
+    });
+    $("#addPageUrl").click(function(){
+        $("#dialog-modal").dialog({
+            height: 240,
+            width: 800,
+            modal: true,
+            buttons: {
+                "Select Page": function() {
+                    if ($("#page_on").text() != '') {
+                        location.href = "blocks.php?ac=Edit&page_on=" +
+                            $("#page_on").text();
+                        $(this).dialog("close");
+                    } else {
+                        alert("Select a Page");
+                    }
+                },
+                Cancel: function() {
+                    $(this).dialog("close");
+                }
+            }
+        });
+        $("#pages").load('blocks.php?ac=Add', function(){
+            $('#demo1').columnview({
+                preview:false,
+                onchange: function(element) {
+                    $("#page_on").text($(element).attr('rel'));
+                }
+            });
+        });
+        return false;
+    });
+});
diff --git a/Toolkit/Blocks/positionBlock.php b/Toolkit/Blocks/positionBlock.php
new file mode 100644 (file)
index 0000000..9b02d34
--- /dev/null
@@ -0,0 +1,21 @@
+<?php
+require_once '../../setup.phtml';
+$blocks = $_REQUEST['block'];
+$newPos = 1;
+
+if (is_array($blocks) && !empty($blocks)) {
+    $pageOn = null;
+    $dbh = Toolkit_Database::getInstance();
+    foreach ($blocks as $blockId) {
+        if (is_numeric($blockId)) {
+            $block = Toolkit_Blocks_Block::fetchById($dbh, $blockId);
+            $block->setPos($newPos)->save($dbh);
+            $pageOn = $block->getPageOn();
+        }
+        ++$newPos;
+    }
+    // remove this cache file for the block
+    $cache = new Cache_Lite($GLOBALS['cacheOptions']);
+    $cache->remove($pageOn, 'Block');
+}
+return true;
\ No newline at end of file
diff --git a/Toolkit/Blocks/templates/editPage.html b/Toolkit/Blocks/templates/editPage.html
new file mode 100644 (file)
index 0000000..808e70b
--- /dev/null
@@ -0,0 +1,160 @@
+{if:canCreateOnAnyPage}
+<a id="backBtn" href="../admin/blocks.php">Back to All Blocks</a>
+{end:}
+<div id="blocksEditWrapper">
+    <h2>Add New {pageName} Block</h2>
+    <ul id="newBlockForm">
+        <li class="ui-widget-content">
+            <form
+                id="newForm"
+                class="blockForm"
+                method="post"
+                action="blocks.php?ac=Edit"
+                enctype="multipart/form-data"
+                flexy:ignore="yes">
+                <input type="hidden" name="page_on" value="{pageOn}">
+                <input type="hidden" id="page_to" name="page_to">
+                <div class="imageBlock">
+                    <span class="file-wrapper">
+                        <input
+                            type="file"
+                            name="image"
+                            id="image"
+                            class="image">
+                        <span class="button">Choose Image</span>
+                    </span>
+                </div>
+                <div class="textBlock">
+                    <input
+                        class="title"
+                        type="text"
+                        name="title"
+                        placeholder="Headline">
+                    <div class="internal_link">
+                    <h3>Internal Link</h3>
+                    <label
+                        class="titleLabel">Links to:</label>
+                    <div id="page_to_name"></div>
+                    <button id="addPageTo">Choose</button>
+                    </div>
+                    <div class="external_link">
+                    <h3>External Link</h3>
+                    <label
+                        class="title">External URL:
+                        <input type="checkbox" name="external">
+                    </label>
+                    <input
+                        class="title"
+                        type="text"
+                        name="url"
+                        placeholder="URL">
+                    </div>
+                    <textarea
+                        name="description"
+                        id="descr-new"
+                        class="textBlockTextArea"></textarea>
+                    <input id="addBtn" type="submit" value="Add">
+                </div>
+            </form>
+        </li>
+    </ul>
+    <h2>Edit Current {pageName} Blocks</h2>
+    <div id="dialog-modal" title="Page Selector" style="display:none;">
+        <div id="pages"></div>
+    </div>
+    <ul id="sortable">
+        <li
+            flexy:foreach="blocks,block"
+            id="block_{block[id]}"
+            class="ui-widget-content">
+            <span class="ui-icon ui-icon-arrow-4-diag"></span>
+            <form
+                id="form-{block[id]}"
+                class="blockForm"
+                method="post"
+                action="blocks.php?ac=Edit"
+                enctype="multipart/form-data"
+                flexy:ignore="yes">
+                <input
+                    type="hidden"
+                    rel="{block[id]}"
+                    name="page_on"
+                    value="{block[page_on]}">
+                <input
+                    type="hidden"
+                    rel="{block[id]}"
+                    name="page_to"
+                    value="{block[page_to]}">
+                <input
+                    type="hidden"
+                    name="blockId"
+                    value="{block[id]}">
+                <input
+                    type="hidden" name="oldImage" value="{block[image]}">
+                <div class="imageBlock">
+                    <div class="upload">
+                        <input type="file" class="image" name="image">
+                        <a id="upload_text" class="button">
+                            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Change Image
+                        </a>
+                    </div>
+                    {if:block[imageUrl]}
+                    <img id="sideImage" src="{block[imageUrl]:h}">
+                    {end:}
+                </div>
+                <div class="textBlock">
+                    <input
+                        type="text"
+                        class="title"
+                        name="title"
+                        value="{block[title]}">
+                    <div class="internal_link">
+                    <h3>Internal Link</h3>
+                    <label class="titleLabel">Links to:</label>
+                    {if:block[navigation_name]}
+                    <a
+                        class="editPageTo"
+                        rel="{block[id]}"
+                        href="#">{block[navigation_name]}</a>
+                    {else:}
+                    <a
+                        class="editPageTo"
+                        rel="{block[id]}"
+                        href="#">No Page</a>
+                    {end:}
+                    </div>
+                    <div class="external_link">
+                    <h3>External Link</h3>
+                    <label
+                        class="title">External URL:
+                        {if:block[external]}
+                        <input type="checkbox" name="external" checked>
+                        {else:}
+                        <input type="checkbox" name="external">
+                        {end:}
+                    </label>
+                    <input
+                        class="title"
+                        type="text"
+                        name="url"
+                        placeholder="URL"
+                        value="{block[url]}">
+                    </div>
+                    <textarea
+                        name="description"
+                        id="descr-{block[id]}"
+                        class="textBlockTextArea"
+                        style="min-height:{block[imageHeight]}px;">{block[description]}</textarea>
+                    <input id="updateBtn" type="submit" value="Update" />
+                    <div class="deleteBlock">
+                        <a
+                            id="deleteBtn2"
+                            class=".deleteBlock"
+                            href="{block[deleteUrl]:h}"
+                            rel="{block[id]}">Delete</a>
+                    </div>
+                </div>
+            </form>
+        </li>
+    </ul>
+</div>
diff --git a/Toolkit/Blocks/templates/listPages.html b/Toolkit/Blocks/templates/listPages.html
new file mode 100644 (file)
index 0000000..f362084
--- /dev/null
@@ -0,0 +1,28 @@
+<a id="addPageUrl" href="{addUrl:h}">Add GLM Blocks to a Page</a>
+<div id="dialog-modal" title="Page Selector" style="display:none;">
+    <input type="hidden" name="page_on" id="page_on">
+    <div id="pages"></div>
+</div>
+<div id="blocksListWrapper">
+    <h2 class="blockhead">Current Pages with GLM Blocks</h2>
+    <ul flexy:if="pages" id="notsortable">
+        <li flexy:foreach="pages,page" class="ui-widget-content">
+            <div id="editPane">
+                <a id="editBtn" href="{page[editUrl]:h}">Edit</a>
+            </div><!-- /#editPane -->
+            <div id="mainPane">
+                <a id="titletext" target="_blank" href="{page[pageUrl]:h}">{page[navigation_name]}</a>
+                {page[breadCrumbs]:h} 
+            </div><!-- /#mainPane -->
+            <div id="deletePane" class="deleteBlock">
+                <a id="deleteBtn"  href="{page[deleteUrl]:h}">Delete</a>
+            </div><!-- /#deletePane -->
+            <div id="numberPane">
+                <div id="numRight"></div>
+                <div id="numMain">{page[count]}</div>
+                <div id="numLeft"></div>
+                <div id="linktext">Links:</div>
+            </div><!-- /#numberPane -->
+        </li><!-- /.ui-widget-content -->
+    </ul><!-- /#notsortable -->
+</div><!-- /#blocksListWrapper -->
diff --git a/Toolkit/BreadCrumbsFactory.php b/Toolkit/BreadCrumbsFactory.php
new file mode 100644 (file)
index 0000000..6ca0be2
--- /dev/null
@@ -0,0 +1,85 @@
+<?php
+/**
+ * BreadCrumbsFactory.php
+ *
+ * PHP Version 5.2
+ *
+ * @category  Toolkit
+ * @package   Members_MembersOnly
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @version   SVN: $id$
+ * @link      <>
+ */
+
+/**
+ * Toolkit_BreadCrumbsFactory
+ *
+ * Description of Toolkit_BreadCrumbsFactory
+ *
+ * @category  Toolkit
+ * @package   Members_MembersOnly
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @release   Release: $id$
+ * @link      <>
+ */
+class Toolkit_BreadCrumbsFactory
+{
+    /**
+     * Description of $_gatewayFactory
+     * @var Toolkit_Toolbox_GatewayFactoryAbstract
+     * @access private
+     */
+    private $_gatewayFactory;
+
+    /**
+     * Class constructor
+     *
+     * @param Toolkit_Toolbox_GatewayFactoryAbstract $factory Gateway Factory
+     *
+     * @return void
+     * @access public
+     */
+    public function __construct(
+        Toolkit_Toolbox_GatewayFactoryAbstract $factory
+    ) {
+        $this->_gatewayFactory = $factory;
+    }
+
+    /**
+     * Description for createBreadCrumbsHelper
+     *
+     * @return \Toolkit_Template_BreadCrumbs
+     * @access public
+     */
+    public function createBreadCrumbsHelper()
+    {
+        $gateway = $this->_gatewayFactory->createGateway();
+        if (isset($_GET['member_id'])
+            && ctype_digit((string)$_GET['member_id'])
+        ) {
+            $breadCrumbs = new Toolkit_Members_BreadCrumbs(
+                $gateway,
+                Toolkit_Database::getInstance()
+            );
+        } elseif ($gateway instanceof Toolkit_Toolbox_PageGatewayDraft) {
+            //  Need to have a publish gateway so we can get parent pages
+            $publishGateway = new Toolkit_Toolbox_PageGatewayPublish(
+                Toolkit_Database::getInstance()
+            );
+
+            $breadCrumbs = new Toolkit_Template_DraftBreadCrumbs(
+                $publishGateway,
+                $gateway
+            );
+        } else {
+            $breadCrumbs = new Toolkit_Template_BreadCrumbs($gateway);
+        }
+
+        return $breadCrumbs;
+    }
+}
+?>
diff --git a/Toolkit/CKImages/Connector.php b/Toolkit/CKImages/Connector.php
new file mode 100644 (file)
index 0000000..f97d172
--- /dev/null
@@ -0,0 +1,400 @@
+<?php
+//     vim:set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker syntax=php:
+
+/**
+ * Connects the CKImage browser to the server for image manipulation
+ *
+ * PHP version 5
+ *
+ * @category  CKImages
+ * @package   Toolkit_CKImages
+ * @author    Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @version   CVS: $Id: Connector.php,v 1.15 2010/07/20 01:19:50 jamie Exp $
+ * @link      http://demo.gaslightmedia.com
+ */
+
+
+/**
+ * Connects the CKImage browser to the server for image manipulation
+ *
+ * @category  CKImages
+ * @package   Toolkit_CKImages
+ * @author    Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @version   Release: @package_version@
+ * @link      http://demo.gaslightmedia.com
+ */
+class Toolkit_CKImages_Connector
+{
+    //  {{{ setDbh()
+
+    /**
+     * Sets the database handler for the object to use
+     *
+     * @param PDO $pdo PHP Data Object
+     *
+     * @return void
+     * @access public
+     */
+    public function setDbh(PDO $pdo)
+    {
+        $this->dbh = $pdo;
+    }
+
+    //  }}}
+
+    //  {{{ createFolder()
+
+    /**
+     * Creates a new folder
+     *
+     * @return void
+     * @access public
+     */
+    public function createFolder()
+    {
+        //  get the folder id
+               $folder = filter_var(
+                       $_GET['name'],
+                       FILTER_SANITIZE_STRING,
+                       FILTER_FLAG_STRIP_LOW | FILTER_FLAG_STRIP_HIGH
+               );
+               if ($parent = filter_var($_GET['folder'], FILTER_VALIDATE_INT)) {
+                       //      do nothing, $parent set and validated
+                       //      from the $_GET['folder'] variable
+               } else {
+                       $parent = 1;
+               }
+
+               //      if the parentFolder variable is set and valid, then override the
+               //      existing parent folder value
+               if ($newParent = filter_var($_GET['parentFolder'], FILTER_VALIDATE_INT)) {
+                       $parent = $newParent;
+               }
+
+        //  unset unneeded param values
+        unset($_GET['command'], $_GET['name'], $_GET['parentFolder']);
+
+        try {
+            $sql = "
+                INSERT INTO ckeditor_folders(name, parent)
+                VALUES (:name, :parent)";
+            $stmt = $this->dbh->prepare($sql);
+            $stmt->bindParam(':name', $folder, PDO::PARAM_INT);
+            $stmt->bindParam(':parent', $parent, PDO::PARAM_INT);
+            $stmt->execute();
+            header('Location:' . MEDIA_BASE_URL . 'Toolkit/CKImages/browser.php?' . http_build_query($_GET));
+        } catch (PDOException $e) {
+            return Toolkit_Common::handleError($e);
+        }
+    }
+
+    //  }}}
+
+    //  {{{ deleteFile()
+
+    /**
+     * Deletes a file from the image server for client
+     *
+     * @param Toolkit_FileServer_ImageAdapter $ia Image Server Object
+     *
+     * @return void
+     * @access public
+     */
+    public function deleteFile(Toolkit_FileServer_ImageAdapter $ia)
+    {
+               $getImg = filter_input(INPUT_GET, 'img');
+        //  Get offset where target img starts at
+        $start = strrpos($getImg, '/') + 1;
+        //  get the image name
+        $img = substr($getImg, $start);
+        //  unset unneeded param values
+        unset($_GET['command'], $_GET['img']);
+
+        $ia->delete($img);
+
+        try {
+            $sql = "
+                DELETE FROM ckeditor_images
+                 WHERE name_on_disk = :nod";
+
+            $stmt = $this->dbh->prepare($sql);
+            $stmt->bindParam(':nod', $img, PDO::PARAM_STR);
+            $stmt->execute();
+            header('Location:' . MEDIA_BASE_URL . 'Toolkit/CKImages/browser.php?' . http_build_query($_GET));
+        } catch (PDOException $e) {
+            return Toolkit_Common::handleError($e);
+        }
+    }
+
+    //  }}}
+    //  {{{ deleteFolder()
+
+    /**
+     * Deletes a folder and any sub folders
+     *
+     * Moves all containing images to home folder so they are not
+     * inadvertently deleted
+     *
+     * @return void
+     * @access public
+     */
+    public function deleteFolder()
+    {
+        //  get the folder id
+        $folder = $_GET['folder'];
+        if ($folder == 1) {
+            unset($_COOKIE['glm_image_browser_open'], $_COOKIE['glm_image_browser_selected']);
+            header('Location:' . MEDIA_BASE_URL . 'Toolkit/CKImages/browser.php?' . http_build_query($_GET));
+                       exit();
+        }
+        //  unset unneeded param values
+        unset($_GET['command'], $_GET['tgt']);
+        $_GET['folder'] = 1;
+
+        try {
+            $tree = Toolkit_Common::getHierarchicalTreeStructure(
+                $this->dbh,
+                'ckeditor_folders',
+                'id',
+                'parent',
+                'id',
+                $folder,
+                0,
+                false
+            );
+
+            $sql = "
+                UPDATE ckeditor_images
+                   SET folder = 1
+                 WHERE folder = :folder";
+
+            $imgStmt = $this->dbh->prepare($sql);
+
+            $sql = "
+                DELETE FROM ckeditor_folders
+                 WHERE id = :folder";
+            $fldrStmt = $this->dbh->prepare($sql);
+            while (list($tgt) = each($tree)) {
+                $imgStmt->bindParam(':folder', $tgt, PDO::PARAM_INT);
+                $imgStmt->execute();
+
+                $fldrStmt->bindParam(':folder', $tgt, PDO::PARAM_INT);
+                $fldrStmt->execute();
+            }
+            //  These cookies are defined in the libjs/image_selector.js file
+            //  when the tree is created.
+            unset($_COOKIE['glm_image_browser_open'], $_COOKIE['glm_image_browser_selected']);
+            header('Location:' . MEDIA_BASE_URL . 'Toolkit/CKImages/browser.php?' . http_build_query($_GET));
+        } catch (PDOException $e) {
+            return Toolkit_Common::handleError($e);
+        }
+    }
+
+    //  }}}
+
+    //  {{{ browseImages()
+
+    /**
+     * Creates the browser window
+     *
+     * @param HTML_Template_Flexy  $tEngine Flexy Rendering Engine Object
+     *
+     * @return string HTML page to browse images
+     * @access public
+     */
+    public function browseImages(HTMl_Template_Flexy $tEngine)
+       {
+        $page = new stdClass();
+               $page->baseUrl       = MEDIA_BASE_URL;
+               $page->glmAppBaseUrl = MEDIA_APP_BASE_URL;
+        $page->originalPath  = CKIMAGE;
+        $page->imageManager  = IMAGE_MANAGER;
+
+               if ($folderId = filter_input(INPUT_GET, 'folder', FILTER_VALIDATE_INT)) {
+                       $folder = "&folder=$folderId";
+               } else {
+                       $folder = '';
+               }
+
+               $ckEditorFuncNum
+                       = filter_input(INPUT_GET, 'CKEditorFuncNum', FILTER_VALIDATE_INT);
+               $ckEditor = filter_input(INPUT_GET, 'CKEditor');
+               $langCode = filter_input(INPUT_GET, 'langCode');
+
+        $form = new HTML_QuickForm(
+            'quick_upload',
+            'post',
+            MEDIA_BASE_URL . "Toolkit/CKImages/controller.php?CKEditor=$ckEditor&CKEditorFuncNum=$ckEditorFuncNum&langCode=$langCode&command=UploadFile$folder"
+        );
+        $form->addElement('file', 'upload');
+        $page->quickUploadForm = $form->toHtml();
+
+        $folders = new Toolkit_CKImages_Folders($this->dbh);
+        $page->folders = $folders->getFolders(0);
+
+        try {
+                       $ckEditorFolderId = $folderId ? $folderId : 1;
+            $sql = "
+                SELECT *
+                  FROM ckeditor_images
+                 WHERE folder = :folder
+                 ORDER BY id ASC";
+
+            $stmt = $this->dbh->prepare($sql);
+            $stmt->bindParam(':folder', $ckEditorFolderId, PDO::PARAM_INT);
+            $stmt->execute();
+
+            $images = $stmt->fetchAll(PDO::FETCH_ASSOC);
+            // loop thru images and reset their sizes
+            $isa = new Toolkit_FileServer_ImageAdapter();
+            if (is_array($images) && !empty($images)) {
+                foreach ($images as &$img) {
+                    if (   $img['original_width'] > 700 
+                        || $img['original_height'] > 500
+                    ) {
+                        // get real size for the CKImage Style
+                        $imgData = $isa->getImageSize(CKIMAGE . $img['name_on_disk']);
+                        $img['original_width']  = $imgData[0];
+                        $img['original_height'] = $imgData[1];
+                    }
+                }
+            }
+            $page->images = $images;
+                       $page->CKEditorFuncNum = $ckEditorFuncNum;
+
+            $tEngine->compile('thumbnails.html');
+            $tEngine->outputObject($page);
+        } catch (PDOException $e) {
+            return Toolkit_Common::handleError($e);
+        }
+    }
+
+    //  }}}
+
+    //  {{{ handleUpload()
+
+
+    /**
+     * Handles inserting the upload into the database
+     *
+     * @param string $imageName  image to insert
+        * @param array  $dimensions dimensions of the image
+     *
+     * @return object    Return description (if any) ...
+     * @access protected
+     */
+    protected function handleUpload($imageName, array $dimensions)
+    {
+        if (   !$dimensions['oWidth'] 
+            || !$dimensions['oHeight']
+        ) {
+            return false;
+        }
+        try {
+                       if ($folder = filter_input(INPUT_GET, 'folder', FILTER_VALIDATE_INT)) {
+                               //      do nothing, $folder is already set and validated
+                       } else {
+                               $folder = 1;
+                       }
+            $sql = "
+                INSERT INTO ckeditor_images (file_name, name_on_disk, folder,
+                original_width, original_height)
+                VALUES (:fname, :nod, :folder, :width, :height)";
+
+            $stmt = $this->dbh->prepare($sql);
+            $stmt->bindParam(
+                ':fname',
+                $_FILES['upload']['name'],
+                PDO::PARAM_STR
+            );
+            $stmt->bindParam(':nod', $imageName, PDO::PARAM_STR);
+            $stmt->bindValue(':folder', $folder, PDO::PARAM_INT);
+            $stmt->bindValue(':width', $dimensions['oWidth'], PDO::PARAM_INT);
+            $stmt->bindValue(':height', $dimensions['oHeight'], PDO::PARAM_INT);
+
+
+            return $stmt->execute();
+        } catch (PDOException $e) {
+            return Toolkit_Common::handleError($e);
+        }
+    }
+
+    //  }}}
+
+    //  {{{ quickUpload()
+
+
+    /**
+     * send uploaded image to image server
+     *
+     * @param Toolkit_FileServer_ImageAdapter $ia Image Server
+     *
+     * @return void
+     * @access public
+     */
+    public function quickUpload(Toolkit_FileServer_ImageAdapter $ia)
+    {
+               try {
+                       ob_start();
+                       $image = $ia->upload('upload');
+                       ob_end_clean();
+               } catch (RangeException $e) {
+                       Toolkit_Logger::logException('Runtime Error', $e);
+                       throw new Toolkit_CKImages_Exception('Error uploading image');
+               }
+
+               $dimensions = $ia->getImageSize(CKIMAGE . $image['name']);
+               list($i['oWidth'], $i['oHeight'],) = $dimensions;
+               $this->handleUpload($image['name'], $i);
+
+        unset($_GET['command']);
+        header('Location:' . MEDIA_BASE_URL . 'Toolkit/CKImages/browser.php?' . http_build_query($_GET));
+    }
+
+    //  }}}
+
+    //  {{{ uploadFile()
+
+
+    /**
+     * send uploaded image to image server and select that file to be used
+     *
+     * @param Toolkit_FileServer_ImageAdapter $ia Image server object
+     *
+     * @return void
+     * @access public
+     */
+    public function uploadFile(Toolkit_FileServer_ImageAdapter $ia)
+    {
+               if ($ckEditorFuncNum = filter_input(INPUT_GET, 'CKEditorFuncNum', FILTER_VALIDATE_INT)) {
+                       //      Do nothing, $ckEditorFuncNum set and validated
+               } else {
+                       $ckEditorFuncNum = 1;
+               }
+        echo '<script type="text/javascript">';
+               try {
+                       ob_start();
+                       $image = $ia->upload('upload');
+                       ob_end_clean();
+               } catch (RangeException $e) {
+                       Toolkit_Logger::logException('Runtime Error', $e);
+            echo "window.parent.CKEDITOR.tools.callFunction($ckEditorFuncNum, '', 'Invalid Image Format');";
+               }
+
+               $dimensions = $ia->getImageSize(CKIMAGE . $image['name']);
+               list($i['oWidth'], $i['oHeight'],) = $dimensions;
+               $this->handleUpload($image['name'], $i);
+
+               //  path to image we are going to use
+               $ip = CKIMAGE . $image['name'];
+               echo "window.parent.CKEDITOR.tools.callFunction($ckEditorFuncNum, '$ip', '');";
+        echo '</script>';
+    }
+
+    //  }}}
+}
+?>
diff --git a/Toolkit/CKImages/Database/Import.php b/Toolkit/CKImages/Database/Import.php
new file mode 100755 (executable)
index 0000000..756e7cb
--- /dev/null
@@ -0,0 +1,145 @@
+<?php
+class Toolkit_CKImages_database_Import
+{
+    // {{{ Class Properties
+    protected $dbh;
+    protected $pathToImages;
+    protected $is;
+    protected $urlPathToImages;
+
+    // }}}
+    // {{{ __construct()
+    function __construct(PDO $pdo)
+    {
+        $this->dbh             = $pdo;
+        $this->pathToImages    = BASE . 'images/ht_images/';
+        $this->urlPathToImages = HT_IMAGES_URL;
+        $this->is              = new Toolkit_Image_Server();
+    }
+    // }}}
+    // {{{ addFolderToDatabase()
+    function addFolderToDatabase($folder, $parent)
+    {
+        static $stmt;
+        if (!$stmt) {
+            $sql = "
+            INSERT INTO ckeditor_folders
+            (name, parent)
+            VALUES
+            (:name, :parent)
+            RETURNING id";
+            $stmt = $this->dbh->prepare($sql);
+        }
+        try {
+            $stmt->bindParam(":name",   $folder, PDO::PARAM_STR);
+            $stmt->bindParam(":parent", $parent, PDO::PARAM_STR);
+            $stmt->execute();
+            return $stmt->fetchColumn();
+        } catch(PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+    }
+    // }}}
+    // {{{ addImageToDatabase()
+    function addImageToDatabase($file, $folder)
+    {
+        static $stmt;
+        if (!$stmt) {
+            $sql = "
+            INSERT INTO ckeditor_images
+            (file_name, name_on_disk, original_width, original_height, folder)
+            VALUES
+            (:file_name, :name_on_disk, :original_width, :original_height, :folder)";
+            $stmt = $this->dbh->prepare($sql);
+        }
+        try {
+            $stmt->bindParam(":file_name",       $file['file_name'],       PDO::PARAM_STR);
+            $stmt->bindParam(":name_on_disk",    $file['name_on_disk'],    PDO::PARAM_STR);
+            $stmt->bindParam(":original_width",  $file['original_width'],  PDO::PARAM_STR);
+            $stmt->bindParam(":original_height", $file['original_height'], PDO::PARAM_STR);
+            $stmt->bindParam(":folder",          $folder,                  PDO::PARAM_INT);
+            $stmt->execute();
+        } catch(PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+    }// }}}
+    // {{{ processFilesArray
+    function processFilesArray($files, $parent = 1)
+    {
+        //echo '<pre>'.print_r($files, true).'</pre>';
+        foreach ($files as $folderName => $file) {
+            if (is_array($file)) {
+                $fKey = key($file);
+                $folderToAdd     = preg_replace("%/$%", "", $fKey);
+        //echo '<pre>'.print_r($folderToAdd, true).'</pre>';
+                $folder          = $this->addFolderToDatabase($folderToAdd, $parent);
+        //echo '<pre>'.print_r($folder, true).'</pre>';
+        //var_dump($files[$folderName][$fKey]);
+       // echo '<pre>'.print_r($file[$folderName][$fKey], true).'</pre>';
+                $processedFile[] = $this->processFilesArray($files[$folderName][$fKey], $folder);
+            } else {
+                $processedFile[] = $this->sendToImageServer($file, $parent);
+            }
+        }
+        return $processedFile;
+    }
+    // }}}
+    // {{{ readImageDir()
+    function readImageDir($path = '')
+    {
+        $dirName = $this->pathToImages . preg_replace("%/$%", "", $path);
+        //var_dump($dirName);echo '<br />';
+        $d = dir($dirName);
+        while (false !== ($entry = $d->read())) {
+            if (!preg_match("%^\.|^CVS%", $entry)) {
+                $entryPath = $this->pathToImages . $path . $entry;
+                //var_dump($entryPath); echo '<br />';
+                if ($dirCheck = is_dir($entryPath)) {
+                    $files[$path][] = $this->readImageDir($path . $entry . '/');
+                } else if ($fileCheck = is_file($entryPath)) {
+                    $files[$path][] = $path . $entry;
+                }
+            }
+        }
+        return $files;
+    }
+    // }}}
+    //{{{ runImport()
+    function runImport()
+    {
+        $files = $this->readImageDir();
+        echo '<pre>'.print_r($files, true).'</pre>';
+        $processedFiles = $this->processFilesArray($files['']);
+//        echo '<pre>'.print_r($processedFiles, true).'</pre>';
+    }
+    //}}}
+    // {{{ sendToImageServer()
+    function sendToImageServer($file, $folder)
+    {
+        $file_name    = basename($file);
+        $size         = getImageSize($this->pathToImages . $file);
+        try {
+            $urlToImage = $this->urlPathToImages . '/' . str_replace(' ', '%20', $file);
+            $name_on_disk = $this->is->imageUpload(
+                $urlToImage
+            );
+//            var_dump($name_on_disk);
+//            var_dump($this->urlPathToImages);
+//            exit;
+        } catch(PEAR_Exception $e) {
+            var_dump($file);
+            var_dump($urlToImage);
+            var_dump($e);
+            exit;
+        }
+        $image        = array(
+            'name_on_disk'    => $name_on_disk,
+            'file_name'       => $file_name,
+            'original_width'  => $size[0], 
+            'original_height' => $size[1], 
+        );
+        $this->addImageToDatabase($image, $folder);
+    }
+    // }}}
+}
+?>
diff --git a/Toolkit/CKImages/Database/application.sql b/Toolkit/CKImages/Database/application.sql
new file mode 100644 (file)
index 0000000..d3abc1e
--- /dev/null
@@ -0,0 +1,21 @@
+CREATE SCHEMA ckimages;
+GRANT ALL ON SCHEMA ckimages TO nobody;
+
+--
+-- Tables
+--
+\i ./tables/ckeditor_folders.sql
+\i ./tables/ckeditor_images.sql
+
+--
+-- Procedures
+--
+
+--
+-- Modules
+--
+
+--
+-- Data
+--
+\i ./data/applicationData.sql
diff --git a/Toolkit/CKImages/Database/data/applicationData.sql b/Toolkit/CKImages/Database/data/applicationData.sql
new file mode 100644 (file)
index 0000000..e4159a5
--- /dev/null
@@ -0,0 +1,2 @@
+INSERT INTO ckimages.ckeditor_folders(id, name, parent) VALUES(1, 'Home', 0);
+SELECT setval('"ckimages".ckeditor_folders_id_seq', 2, false);
diff --git a/Toolkit/CKImages/Database/importHtImages.php b/Toolkit/CKImages/Database/importHtImages.php
new file mode 100755 (executable)
index 0000000..5aaa481
--- /dev/null
@@ -0,0 +1,10 @@
+<?php
+require_once '../../../setup.phtml';
+// set this to the real sites url
+// is0.gaslightmedia.com will only fetch the images if
+// it has access and it cannot do http auth
+define('HT_IMAGES_URL', 'http://www.petoskeyarea.com/images/ht_images/');
+$dbh = Toolkit_Database::getInstance();
+$import = new Toolkit_CKImages_Database_Import($dbh);
+$import->runImport();
+?>
diff --git a/Toolkit/CKImages/Database/phpImageEditorUpdates.sql b/Toolkit/CKImages/Database/phpImageEditorUpdates.sql
new file mode 100644 (file)
index 0000000..28b86c2
--- /dev/null
@@ -0,0 +1,2 @@
+ALTER TABLE ckimages.ckeditor_images
+       ADD COLUMN original_image TEXT;
diff --git a/Toolkit/CKImages/Database/removeApplication.sql b/Toolkit/CKImages/Database/removeApplication.sql
new file mode 100644 (file)
index 0000000..4cd6978
--- /dev/null
@@ -0,0 +1,9 @@
+--
+--     This will drop everything in the ckimages schema
+--     Nothing better be in here except ckeditor related object
+--     or it will be dropp
+--
+--     The force is strong w/ this one, use it wisely.
+--
+
+DROP SCHEMA IF EXISTS ckimages CASCADE;
diff --git a/Toolkit/CKImages/Database/tables/ckeditor_folders.sql b/Toolkit/CKImages/Database/tables/ckeditor_folders.sql
new file mode 100644 (file)
index 0000000..1331175
--- /dev/null
@@ -0,0 +1,10 @@
+DROP TABLE IF EXISTS ckimage.ckeditor_folders CASCADE;
+
+CREATE TABLE ckimages.ckeditor_folders
+(id SERIAL,
+ name TEXT NOT NULL,
+ parent INTEGER DEFAULT 1 NOT NULL,
+ PRIMARY KEY (id));
+
+GRANT ALL ON ckimages.ckeditor_folders_id_seq TO nobody;
+GRANT ALL ON ckimages.ckeditor_folders TO nobody;
diff --git a/Toolkit/CKImages/Database/tables/ckeditor_images.sql b/Toolkit/CKImages/Database/tables/ckeditor_images.sql
new file mode 100644 (file)
index 0000000..a693799
--- /dev/null
@@ -0,0 +1,19 @@
+DROP TABLE IF EXISTS ckimages.ckeditor_images CASCADE;
+
+CREATE TABLE ckimages.ckeditor_images
+(id SERIAL,
+ create_date DATE DEFAULT CURRENT_DATE,
+ file_name text NOT NULL,
+ name_on_disk text NOT NULL,
+ original_width TEXT NOT NULL,
+ original_height TEXT NOT NULL,
+ original_image TEXT,
+ folder INTEGER DEFAULT 1
+       REFERENCES ckimages.ckeditor_folders (id)
+       ON UPDATE CASCADE
+       ON DELETE SET DEFAULT,
+ PRIMARY KEY (id)
+);
+
+GRANT ALL ON ckimages.ckeditor_images_id_seq TO nobody;
+GRANT ALL ON ckimages.ckeditor_images TO nobody;
diff --git a/Toolkit/CKImages/Exception.php b/Toolkit/CKImages/Exception.php
new file mode 100644 (file)
index 0000000..3ea1bbd
--- /dev/null
@@ -0,0 +1,3 @@
+<?php
+class Toolkit_CKImages_Exception extends Exception {}
+?>
diff --git a/Toolkit/CKImages/Factory.php b/Toolkit/CKImages/Factory.php
new file mode 100644 (file)
index 0000000..a693896
--- /dev/null
@@ -0,0 +1,83 @@
+<?php
+//     vim:set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker syntax=php:
+
+/**
+ * CKImage Factory
+ * 
+ * PHP version 5
+ * 
+ * @category  CKImages
+ * @package   Toolkit_CKImages
+ * @author    Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @version   CVS: $Id: Factory.php,v 1.1 2009/09/24 14:43:02 jamie Exp $
+ * @link      http://demo.gaslightmedia.com
+ * @see       References to other sections (if any)...
+ */
+
+
+/**
+ * CKImage Factory
+ * 
+ * @category  CKImages
+ * @package   Toolkit_CKImages
+ * @author    Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @version   Release: @package_version@
+ * @link      http://demo.gaslightmedia.com
+ * @see       References to other sections (if any)...
+ */
+class Toolkit_CKImages_Factory
+{
+    //  {{{ properties
+
+    /**
+     * array of instances
+     * @var    array 
+     * @access public
+     * @static
+     */
+    public static $instances = array();
+
+    //  }}} 
+    //  {{{ init()
+
+    /**
+     * initialize the factory
+     * 
+     * @return void  
+     * @access public
+     * @static
+     */
+    public static function init()
+    {
+    }
+
+    //  }}}
+    //  {{{ &instance()
+
+    /**
+     * Get a single instance of an object
+     * 
+     * @param string $class Class name to create
+     *
+     * @return mixed instantiated object
+     * @access public
+     * @static
+     */
+    public static function &instance($class)
+    {
+        $class = "Toolkit_CKImages_$class";
+
+        if (empty(Toolkit_CKImages_Factory::$instances)) {
+            Toolkit_CKImages_Factory::$instances[$class] = new $class;
+        }
+
+        return Toolkit_CKImages_Factory::$instances[$class];
+    }
+
+    //  }}}
+}
+?>
diff --git a/Toolkit/CKImages/Folders.php b/Toolkit/CKImages/Folders.php
new file mode 100644 (file)
index 0000000..2e24e47
--- /dev/null
@@ -0,0 +1,243 @@
+<?php
+//     vim:set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker syntax=php:
+
+/**
+ * Handles create a UL list to simulate a folder hierarchy
+ * 
+ * PHP version 5
+ * 
+ * @category  CKImages
+ * @package   Toolkit_CKImages
+ * @author    Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @version   CVS: $Id: Folders.php,v 1.6 2009/10/27 14:39:27 jamie Exp $
+ * @link      http://demo.gaslightmedia.com
+ * @see       References to other sections (if any)...
+ */
+
+/**
+ * Handles create a UL list to simulate a folder hierarchy
+ * 
+ * @category  CKImages
+ * @package   Toolkit_CKImages
+ * @author    Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @version   Release: @package_version@
+ * @link      http://demo.gaslightmedia.com
+ * @see       References to other sections (if any)...
+ */
+class Toolkit_CKImages_Folders
+{
+    //  {{{ properties
+
+
+    /**
+     * Levels deep we are into the hierarchy
+     * @var    integer
+     * @access private
+     */
+    private $_lvlsOpen = 0;
+
+    /**
+     * The previous level we were at
+     * @var    integer
+     * @access private
+     */
+    private $_prevLvl = 0;
+
+    //  }}}
+
+    //  {{{ __construct()
+
+    /**
+     * Constructor
+     * 
+     * @param PDO $pdo PHP Data Object
+     *                     
+     * @return void  
+     * @access public
+     */
+    public function __construct(PDO $pdo)
+    {
+        $this->dbh = $pdo;
+    }
+
+    //  }}}
+
+    //  {{{ closeFolder()
+
+
+    /**
+     * Closes a folder level
+     * 
+     * @return string    closing ul tag
+     * @access protected
+     */
+    protected function closeFolder()
+    {
+        return "\n</ul>";
+    }
+
+    //  }}}
+    //  {{{ closeNode()
+
+
+    /**
+     * Closes a node
+     * 
+     * @return string    closing li tag
+     * @access protected
+     */
+    protected function closeNode()
+    {
+        return "\n\t</li>";
+    }
+
+    //  }}}
+    //  {{{ createFolder()
+
+
+    /**
+     * Creates a folder level
+     * 
+     * @return string    opening ul tag
+     * @access protected
+     */
+    protected function createFolder()
+    {
+        return "\n<ul>";
+    }
+
+    //  }}}
+    //  {{{ createLink()
+
+
+    /**
+     * Creates a folder link so we can browse to each folder
+     * 
+     * @param array $row Folder information
+     *
+     * @return string anchor link for folder
+     * @access protected
+     */
+    protected function createLink(array $row)
+    {
+        $format = '<a class="%s" href="'.MEDIA_BASE_URL.'Toolkit/CKImages/browser.php?CKEditor=%s&CKEditorFuncNum=%s&langCode=%s&folder=%s">%s</a>';
+
+        $link = sprintf(
+            $format,
+            ($_GET['folder'] == $row['id']) ? 'clicked' : null,
+            $_GET['CKEditor'],
+            $_GET['CKEditorFuncNum'],
+            $_GET['langCode'],
+            $row['id'],
+            $row['name']
+        );
+
+        return $link;
+    }
+
+    //  }}}
+
+    //  {{{ fetchFoldersArray()
+
+
+    /**
+     * Fetches a tree hierarchy of the folder structure into a linear array
+     * 
+     * @param integer $start node to start at
+     *
+     * @return array folder structure with levels
+     * @access protected
+     */
+    protected function fetchFoldersArray($start)
+    {
+        return Toolkit_Common::getHierarchicalTreeStructure(
+            $this->dbh,
+            'ckeditor_folders',
+            'id',
+            'parent',
+            'id',
+            $start
+        );
+    }
+
+    //  }}}
+
+    //  {{{ getFolders()
+
+
+    /**
+     * Gets the folders ul list
+     * 
+     * @param integer $parent start at a certain level
+     *                         
+     * @return string  list of folders
+     * @access public 
+     */
+    public function getFolders($parent = 0)
+    {
+        $folders = $this->fetchFoldersArray($parent);
+        try {
+            $sql = "
+                SELECT *
+                  FROM ckeditor_folders
+                 WHERE id = :id";
+
+            $stmt = $this->dbh->prepare($sql);
+            foreach ($folders as $i => $j) {
+                $stmt->bindParam(':id', $i, PDO::PARAM_INT);
+                $stmt->execute();
+                $row = $stmt->fetch(PDO::FETCH_ASSOC);
+
+                if ($j == $this->_prevLvl) {
+                    $tree .= $this->openNode($row);
+                    $tree .= $this->createLink($row);
+                    $this->_prevLvl = $j;
+                } elseif ($j > $this->_prevLvl) {
+                    ++$this->_lvlsOpen;
+                    $tree .= $this->createFolder();
+                    $tree .= $this->openNode($row);
+                    $tree .= $this->createLink($row);
+                    $this->_prevLvl = $j;
+                } elseif ($j < $this->_prevLvl) {
+                    do {
+                        $tree .= $this->closeNode();
+                        $tree .= $this->closeFolder();
+                    } while (--$this->_lvlsOpen > $j);
+                    $tree .= $this->closeNode();
+                    $tree .= $this->openNode($row);
+                    $tree .= $this->createLink($row);
+                    $this->_prevLvl = $this->_lvlsOpen;
+                }
+            }
+            $tree .= $this->closeFolder();
+            return $tree;
+        } catch (PDOException $e) {
+            return Toolkit_Common::handleError($e);
+        }
+    }
+
+    //  }}}
+
+    //  {{{ openNode()
+
+
+    /**
+     * Opens a branch or leaf node
+     * 
+     * @param array $row Folder information
+     *
+     * @return string opening li tag
+     * @access protected
+     */
+    protected function openNode(array $row)
+    {
+        return "\n\t<li id=\"pred_{$row['id']}\">";
+    }
+
+    //  }}}
+}
+?>
diff --git a/Toolkit/CKImages/ImageUpdater.php b/Toolkit/CKImages/ImageUpdater.php
new file mode 100644 (file)
index 0000000..b0faaa4
--- /dev/null
@@ -0,0 +1,95 @@
+<?php
+class Toolkit_CKImages_ImageUpdater
+{
+       //      {{{     properties
+
+       private $_imageData = array();
+       private $_dbh;
+
+       //      }}}
+       //      {{{     __construct()
+
+       public function __construct(PDO $dbh)
+       {
+               $this->_dbh = $dbh;
+       }
+
+       //      }}}
+
+       //      {{{     load()
+
+       public function load($image)
+       {
+               try {
+                       $sql = "
+                               SELECT *
+                                 FROM ckeditor_images
+                                WHERE name_on_disk = :image";
+
+                       $stmt = $this->_dbh->prepare($sql);
+                       $stmt->bindParam(':image', $image, PDO::PARAM_STR);
+                       $stmt->setFetchMode(PDO::FETCH_INTO, $this);
+                       $stmt->execute();
+                       $this->_imageData = $stmt->fetch(PDO::FETCH_ASSOC);
+               } catch (PDOException $e) {
+                       Toolkit_Logger::logException('DB Error', $e);
+                       throw new Toolkit_CKImages_Exception(
+                               "Error fetching image row for image `$image`"
+                       );
+               }
+       }
+
+       //      }}}
+       //      {{{     update()
+
+       public function update(Toolkit_FileServer_ImageAdapter $ia)
+       {
+               if (   !is_array($this->_imageData)
+                       || empty($this->_imageData)
+               ) {
+                       throw new RuntimeException(
+                               'You must load an image first before trying to update'
+                       );
+               }
+
+        $targetImage = $this->_imageData['name_on_disk'];
+
+               $path = BASE . "Toolkit/CKImages/editedImages/$targetImage";
+               $newImage = $ia->uploadImageFile($path);
+               list($w, $h, ) = $ia->getImageSize(CKIMAGE_ORIGINAL . $newImage['name']);
+
+               try {
+                       $sql = "
+                UPDATE ckeditor_images
+                   SET original_image = COALESCE(original_image, name_on_disk),
+                       file_name = :image_name,
+                                          name_on_disk = :newFileName,
+                                          original_width = :newWidth,
+                                          original_height = :newHeight
+                 WHERE name_on_disk = :originalFileName
+                                   OR original_image = :originalFileName";
+
+                       $stmt = $this->_dbh->prepare($sql);
+                       $stmt->bindParam(':newFileName', $newImage['name'], PDO::PARAM_STR);
+            $stmt->bindParam(':image_name', $_REQUEST['image_name'], PDO::PARAM_STR);
+                       $stmt->bindParam(':newWidth', $w, PDO::PARAM_INT);
+                       $stmt->bindParam(':newHeight', $h, PDO::PARAM_INT);
+                       $stmt->bindParam(':originalFileName', $targetImage, PDO::PARAM_STR);
+                       $stmt->execute();
+               } catch (PDOException $e) {
+                       Toolkit_Logger::logException('DB Error', $e);
+                       throw new Toolkit_CKImages_Exception(
+                               "Error updating image `{$this->_imageData['name_on_disk']}`"
+                       );
+               }
+
+               //      If we have updated the file previously
+               //      then remove the old update - b/c it will now be orphaned.
+               if ($targetImage == $this->_imageData['original_image']) {
+                       $ia->delete($this->_imageData['name_on_disk']);
+               }
+       }
+
+       //      }}}
+}
+?>
diff --git a/Toolkit/CKImages/assets/.keepme b/Toolkit/CKImages/assets/.keepme
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/Toolkit/CKImages/assets/night-fate-stock2.jpg b/Toolkit/CKImages/assets/night-fate-stock2.jpg
new file mode 100755 (executable)
index 0000000..93fc375
Binary files /dev/null and b/Toolkit/CKImages/assets/night-fate-stock2.jpg differ
diff --git a/Toolkit/CKImages/browser.php b/Toolkit/CKImages/browser.php
new file mode 100644 (file)
index 0000000..f266df2
--- /dev/null
@@ -0,0 +1,49 @@
+<?php
+
+/**
+ * popup browser window
+ *
+ * This file creates the popup browser window to manage images
+ *
+ * PHP versions 4 and 5
+ *
+ * @category  CKImages
+ * @package   Toolkit_CKImages
+ * @author    Jamie Kahgee <jamie@gaslightmedia.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com/ Gaslightmedia
+ * @version   CVS: $Id: browser.php,v 1.4 2010/06/04 10:48:50 jamie Exp $
+ * @link      <>
+ * @see       References to other sections (if any)...
+ */
+
+/**
+ * bootstrap
+ */
+require_once '../../setup.phtml';
+
+// make sure the image directories exists for editor
+$imgDirs = array(
+    BASE . 'Toolkit/CKImages/editedImages',
+    BASE . 'Toolkit/PHPImageEditor/editimagesoriginal',
+    BASE . 'Toolkit/PHPImageEditor/editimagespng',
+    BASE . 'Toolkit/PHPImageEditor/editimagesworkwith',
+);
+$oldMask = umask(0);
+foreach ($imgDirs as $dir) {
+    if (!is_dir($dir)) {
+        mkdir($dir, 0770, true);
+    }
+}
+umask($oldMask);
+
+Toolkit_CKImages_Factory::init();
+$connector = Toolkit_CKImages_Factory::instance('Connector');
+$connector->setDbh(Toolkit_Database::getInstance());
+$flexyOpts = $GLOBALS['flexyOptions'];
+$flexyOpts['templateDir'] = BASE . 'Toolkit/CKImages/templates/';
+$flexyOpts['compileDir'] = BASE . 'Toolkit/CKImages/templates/compiled/';
+$tEngine = new HTML_Template_Flexy($flexyOpts);
+
+$connector->browseImages($tEngine);
+?>
diff --git a/Toolkit/CKImages/controller.php b/Toolkit/CKImages/controller.php
new file mode 100644 (file)
index 0000000..e99537e
--- /dev/null
@@ -0,0 +1,53 @@
+<?php
+
+/**
+ * Controller file
+ *
+ * PHP versions 4 and 5
+ *
+ * @category  CKImages
+ * @package   Toolkit_CKImages
+ * @author    Jamie Kahgee <jamie@gaslightmedia.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com/ Gaslightmedia
+ * @version   CVS: $Id: controller.php,v 1.3 2010/06/04 11:32:36 jamie Exp $
+ * @link      <>
+ * @see       References to other sections (if any)...
+ */
+
+/**
+ * bootstrap
+ */
+require_once '../../setup.phtml';
+
+Toolkit_CKImages_Factory::init();
+$controller = Toolkit_CKImages_Factory::instance('Connector');
+$controller->setDbh(Toolkit_Database::getInstance());
+
+switch ($_GET['command']) {
+case 'CreateFolder' :
+    $controller->createFolder();
+    break;
+
+case 'DeleteFile' :
+    $controller->deleteFile(new Toolkit_FileServer_ImageAdapter());
+    break;
+
+case 'DeleteFolder' :
+    $controller->deleteFolder();
+    break;
+
+case 'Upload' :
+    $controller->uploadFile(new Toolkit_FileServer_ImageAdapter());
+    break;
+
+case 'UploadFile' :
+    $controller->quickUpload(new Toolkit_FileServer_ImageAdapter());
+    break;
+
+default :
+       die('not yet implemented');
+    $controller->throwError();
+    break;
+}
+?>
diff --git a/Toolkit/CKImages/imageFetch.php b/Toolkit/CKImages/imageFetch.php
new file mode 100644 (file)
index 0000000..0c41ae8
--- /dev/null
@@ -0,0 +1,44 @@
+<?php
+if (!ctype_digit($_GET['file'])) {
+       exit();
+}
+
+require_once '../../setup.phtml';
+$dbh = Toolkit_Database::getInstance();
+
+try {
+       $sql = "
+               SELECT *
+                 FROM ckeditor_images
+                WHERE id = :id";
+
+       $stmt = $dbh->prepare($sql);
+       $stmt->bindParam(':id', $_GET['file'], PDO::PARAM_INT);
+       $stmt->execute();
+       $row = $stmt->fetch(PDO::FETCH_ASSOC);
+
+//     $fileToFetch = empty($row['original_image'])
+//             ? $row['name_on_disk']
+//             : $row['original_image'];
+    $fileToFetch = $row['name_on_disk'];
+} catch (PDOException $e) {
+       Toolkit_Logger::logException('DB Error', $e);
+}
+set_time_limit(0);
+
+//     file to save the info
+$fp = fopen(dirname(__FILE__) . "/editedImages/$fileToFetch", 'w+');
+
+//     file we are downloading
+$ch = curl_init(CKIMAGE_ORIGINAL . $fileToFetch);
+curl_setopt($ch, CURLOPT_TIMEOUT, 50);
+curl_setopt($ch, CURLOPT_FILE, $fp);
+curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
+curl_exec($ch);
+curl_close($ch);
+fclose($fp);
+
+$path = urlencode(BASE . "Toolkit/CKImages/editedImages/$fileToFetch");
+$image_name = urlencode($row['file_name']);
+header('Location: ' . MEDIA_BASE_URL . "Toolkit/PHPImageEditor/index.php?imagesrc=$path&image_name=$image_name");
+?>
diff --git a/Toolkit/CKImages/libjs/image_selector.js b/Toolkit/CKImages/libjs/image_selector.js
new file mode 100644 (file)
index 0000000..930bce2
--- /dev/null
@@ -0,0 +1,142 @@
+var IMAGE_SELECTOR =
+{
+    parentFolder: null,
+
+    init: function()
+    {
+               $('.tLeft').tipsy({gravity: 'sw'});
+               $('.tCenter').tipsy({gravity: 's'});
+               $('.tRight').tipsy({gravity: 'se'});
+               $('.tDelete').tipsy({gravity: 'se'});
+
+               $('a.CKImageEdit').click(function(event) {
+                       var $newWindow = window.open(
+                               $(this).attr('href'),
+                               '',
+                               'width=775,height=775',
+                               true
+                       );
+
+                       $newWindow.focus();
+                       return false;
+               });
+
+               tree1 = new tree_component();
+               tree1.init($("#folders"), {
+                       cookies : {
+                               prefix : "glm_image_browser",
+                               opts : { path : '/' }
+                       },
+                       ui : {
+                               animation : 500
+                       }
+               });
+
+        //  Make the tree default to all opened
+               $('#folders li.closed')
+            .toggleClass('open')
+                   .toggleClass('closed');
+
+               $('#folders a').click(IMAGE_SELECTOR.viewFolder);
+
+        //  Stop clicks on the LI elements, so the folders branches
+        //  will not close.
+        $('#folders li').click(function(event) {
+            event.stopImmediatePropagation();
+        });
+
+        $('div.thumb span.CKImageUse').click(function() {
+            var img = $(this).parent().siblings('img');
+                       var funcNum = $('#CKEditorFuncNum').text();
+
+            window.opener.CKEDITOR.tools.callFunction(funcNum, img.attr('rel'));
+            window.close();
+        });
+
+        $('div.thumb span.CKImageDelete').click(function(event) {
+            event.preventDefault();
+            if (confirm('Are you sure you want to delete this image?')) {
+                var img = $(this).parent().siblings('img').attr('rel');
+                var href = window.location.href.replace(/(.*)browser.php(.*)/, '$1controller.php$2&command=DeleteFile&img=' + img);
+
+                window.location.href = href;
+            }
+        });
+
+        $('#imageDialog').dialog({
+            bgiframe : true,
+            autoOpen : false,
+            resizable: false,
+            modal : true,
+            buttons : {
+                Cancel : function() {
+                    $(this).dialog('close');
+                },
+                'Upload Selected File' : function() {
+                    if ($('#imageDialog form input[type=file]').val()) {
+                        $('.ui-dialog-buttonpane button').attr('disabled', true);
+                        $('#imageDialog form').trigger('submit');
+                    } else {
+                        alert('You need to select a file first');
+                    }
+                }
+            }
+        });
+
+        $('#upload').click(function() {
+            $('#imageDialog').dialog('open');
+        });
+
+        $('#newFolder').submit(IMAGE_SELECTOR.createFolder);
+        $('#folderDialog').dialog({
+            bgiframe : true,
+            autoOpen : false,
+            resizable: false,
+            modal : true,
+            buttons: {
+                Cancel : function() {
+                    $('#folderName').removeClass('ui-state-error');
+                    $(this).dialog('close');
+                },
+                'Create' : IMAGE_SELECTOR.createFolder
+            }
+        });
+        $('#folderNew').click(function() {
+            $('#folderDialog').dialog('open');
+        });
+        $('#folderDelete').click(function() {
+            if (confirm('Are you sure you want to delete this folder?')) {
+                var href = window.location.href.replace(/(.*)browser.php(.*)/, '$1controller.php$2&command=DeleteFolder');
+
+                window.location.href = href;
+            }
+        });
+    },
+
+    createFolder: function(event)
+    {
+        event.preventDefault();
+        //  don't submit empty folder
+        var folderName = $('#folderName');
+        var val = folderName.val();
+        if (val == '') {
+            alert('You need to type a folder name first');
+            return false;
+        } else {
+            $('.ui-dialog-buttonpane button').attr('disabled', true);
+            var href = window.location.href.replace(/(.*)browser.php(.*)/, '$1controller.php$2&command=CreateFolder&name=' + $('#folderName').val());
+            if (IMAGE_SELECTOR.parentFolder != null) {
+                href += '&parentFolder=' + IMAGE_SELECTOR.parentFolder;
+                IMAGE_SELECTOR.parentFolder = null;
+            }
+            window.location.href = href;
+        }
+    },
+
+       viewFolder: function(event)
+       {
+               location.href = $(this).attr('href');
+       }
+};
+
+$(document).ready(IMAGE_SELECTOR.init);
diff --git a/Toolkit/CKImages/styles.css b/Toolkit/CKImages/styles.css
new file mode 100755 (executable)
index 0000000..8f93b21
--- /dev/null
@@ -0,0 +1,153 @@
+body {
+    font-family: arial, sans-serif;
+    font-size: 11px;
+    background: url(assets/night-fate-stock2.jpg) no-repeat fixed;/* #E3E3C7; */
+    margin: 0;
+    padding: 0;
+}
+#wrapper {
+    width: 750px;
+    height: 1%;
+    overflow: hidden;
+    margin: 10px 0px 5px 10px;
+}
+h1 {
+    font-size: 24px;
+    margin: 0;
+    padding: 10px;
+    background: rgba(255, 255, 255, 0.8);
+    position: fixed;
+    }
+
+#toolbar {
+    background: #F1F1E3;
+    width: 140px;
+    border: 1px solid #D5D59D;
+    float: left;
+    padding: 8px;
+    margin-right: 20px;
+    margin-top: 60px;
+    position: fixed;
+    }
+#toolbar > ul {
+    border-bottom: 1px solid #333;
+    padding: 0 0 6px 0;
+    margin: 0 0 6px 0;
+    }
+#toolbar > ul, #toolbar > ul li {
+    list-style: none;
+    display: block;
+}
+#toolbar > ul li {
+    padding: 2px 2px 2px 26px;
+    line-height: 18px;
+    border: 1px solid #F1F1E3;
+    margin: 0;
+    background-position: 2px center;
+    background-repeat: no-repeat;
+    text-decoration: underline;
+    cursor: hand;
+    cursor: pointer;
+}
+#toolbar > ul li:hover {
+    border: 1px solid #D5D59D;
+    text-decoration: none;
+}
+#toolbar #upload {
+    background-image: url(//app.gaslightmedia.com/assets/icons/add.png);
+}
+#toolbar #folderNew {
+    background-image: url(//app.gaslightmedia.com/assets/icons/folder_add.png);
+}
+#toolbar #folderDelete {
+    background-image: url(//app.gaslightmedia.com/assets/icons/folder_delete.png);
+}
+.tree ul {
+    margin: 0;
+    padding-top: 1px;
+}
+.tree li a {
+    background-image: url(//app.gaslightmedia.com/assets/icons/folder.png);
+}
+.tree li.open {
+    background: none;
+    }
+#pred_1 {
+    padding-left: 3px;
+    padding-top: 3px;
+}
+.tree li li.open {
+    background: transparent url(//app.gaslightmedia.com/libjs/jsTree/source/images/li.gif) no-repeat scroll 7px 7px;
+}
+/* Main Section */
+#photo-gallery {
+    height: 1%;
+    overflow: hidden;
+    padding-top: 10px;
+    margin: 50px 0 0 180px;
+    }
+.thumb {
+    width: 120px;
+    height: 160px;
+    float: left;
+    margin: 0 20px 20px 0;
+    background: #F1F1E3;
+    background: rgba(241, 241, 227, 0.5);
+    font-size: 10px;
+    border-radius:5px;
+    -moz-border-radius:5px;
+    -webkit-border-radius:5px;
+    border: 1px solid #D5D59D;
+    }
+.rowTop {
+    height: 1%;
+    overflow: hidden;
+}
+.thumb img {
+    display: block;
+    margin: 0px 10px 2px 10px;
+    border: 1px dotted #ccc;
+    clear: left;
+}
+
+.CKImageFunctions {
+    margin: 10px 5px;
+    }
+.CKImageFunctions a,
+.CKImageFunctions span {
+    display: block;
+    line-height: 16px;
+    margin: 0 5px 0 5px;
+    text-decoration: underline;
+    width: 16px;
+    height: 16px;
+    text-indent: -9000px;
+    float: left;
+    }
+    
+/* cursor */
+.CKImageUse, .CKImageEdit, .CKImageDelete {
+    cursor: hand;
+    cursor: pointer;
+    }
+
+.CKImageUse         {background: url(//app.gaslightmedia.com/assets/icons/accept.png) no-repeat;   }
+.CKImageEdit        {background: url(//app.gaslightmedia.com/assets/icons/image_edit.png) no-repeat;   }
+.CKImageView        {background: url(//app.gaslightmedia.com/assets/icons/image.png) no-repeat;}
+.CKImageDelete  {background: url(//app.gaslightmedia.com/assets/icons/image_delete.png) no-repeat;}
+
+.CKImageName {
+    display: none;
+}
+
+
+.CKImageProperties {
+    margin: 3px auto;
+    text-align: center;
+}
+.CKImageDate {
+    display: none;
+}
+#menuFolder, #menuImage, #column {
+    display: none;
+}
diff --git a/Toolkit/CKImages/templates/thumbnails.html b/Toolkit/CKImages/templates/thumbnails.html
new file mode 100755 (executable)
index 0000000..d8eaa64
--- /dev/null
@@ -0,0 +1,70 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
+"http://www.w3.org/TR/html4/strict.dtd">
+<html>
+<head>
+<title>Thumbnail Images</title>
+<meta http-equiv="content-type" content="text/html;charset=utf-8">
+<link rel="stylesheet" type="text/css" href="<?php echo MEDIA_APP_BASE_URL;?>libjs/jqueryui/1.8/css/smoothness/jquery-ui-1.8.custom.css">
+<link rel="stylesheet" type="text/css" href="<?php echo MEDIA_APP_BASE_URL;?>libjs/jsTree/source/tree_component.css">
+<link rel="stylesheet" type="text/css" href="<?php echo MEDIA_APP_BASE_URL;?>libjs/plugins/tipsy/0.1.7/tipsy.css">
+<link rel="stylesheet" type="text/css" href="styles.css">
+<script type="text/javascript" src="<?php echo MEDIA_APP_BASE_URL;?>libjs/jquery/jquery-1.4.2.min.js"></script>
+</head>
+<body>
+       <h1>GLM Image Browser</h1>
+       <div id="wrapper">
+               <div id="toolbar">
+                       <ul>
+                               <li id="upload">Upload New Image</li>
+                       </ul>
+                       <ul>
+                               <li id="folderNew">Create New Folder</li>
+                               <li id="folderDelete">Delete This Folder</li>
+                       </ul>
+                       <div id="folders" class="tree">
+                               Folders
+                               {folders:h}
+                       </div>
+               </div><!-- /#toolbar -->
+               <div id="photo-gallery" class="galleryRow">
+                       <div flexy:foreach="images,i" class="thumb">
+                               <div class="CKImageProperties">{i[original_width]:h}x{i[original_height]:h}</div>
+                               <img src="{imageManager}{i[name_on_disk]:h}" rel="{originalPath}{i[name_on_disk]:h}" title="{i[file_name]:h}" alt="{i[name_on_disk]:h}">
+                               <div class="CKImageFunctions">
+                                       <span class="CKImageUse tLeft" title="Insert Image">Insert this image</span>
+                                       <a class="CKImageView tCenter" href="{originalPath}{i[name_on_disk]:h}" target="_blank" title="View Image">View Full Image</a>
+                                       <a href="{baseUrl:h}Toolkit/CKImages/imageFetch.php?file={i[id]}" class="CKImageEdit tRight" target="_blank" title="Edit Image">Edit this image</a>
+                                       <span class="CKImageDelete tDelete" title="Delete Image">Delete Image</span>
+                               </div><!-- /#CKImageFunctions -->
+                               <div class="CKImageName">{i[file_name]:h}</div>
+                               <div class="CKImageDate">{i[create_date]:h}</div>
+                       </div>
+               </div><!-- /#photo-gallery -->
+       </div><!-- /#wrapper -->
+       <!-- dialog box for adding new folders -->
+       <div id="folderDialog" class="dialog" style="display: none;" title="Create new folder">
+               <form id="newFolder">
+                       <label for="folderName">Name</label>
+                       <input type="text" name="folderName" id="folderName">
+               </form>
+       </div>
+       <!-- dialog box for adding new images -->
+       <div id="imageDialog" class="dialog" style="display: none;" title="Upload new image">
+               {quickUploadForm:h}
+       </div>
+
+       <!-- Function number -->
+       <span style="display: none;" id="CKEditorFuncNum">{CKEditorFuncNum}</span>
+    
+    <script type="text/javascript" src="<?php echo MEDIA_APP_BASE_URL;?>libjs/jqueryui/1.8/development-bundle/ui/minified/jquery.ui.core.min.js"></script>
+    <script type="text/javascript" src="<?php echo MEDIA_APP_BASE_URL;?>libjs/jqueryui/1.8/development-bundle/ui/minified/jquery.ui.widget.min.js"></script>
+    <script type="text/javascript" src="<?php echo MEDIA_APP_BASE_URL;?>libjs/jqueryui/1.8/development-bundle/ui/minified/jquery.ui.position.min.js"></script>
+    <script type="text/javascript" src="<?php echo MEDIA_APP_BASE_URL;?>libjs/jqueryui/1.8/development-bundle/ui/minified/jquery.ui.dialog.min.js"></script>
+    <script type="text/javascript" src="<?php echo MEDIA_APP_BASE_URL;?>libjs/jsTree/libjs/css.js"></script>
+    <script type="text/javascript" src="<?php echo MEDIA_APP_BASE_URL;?>libjs/jquery.listen.js"></script>
+    <script type="text/javascript" src="<?php echo MEDIA_APP_BASE_URL;?>libjs/jquery.cookie.js"></script>
+    <script type="text/javascript" src="<?php echo MEDIA_APP_BASE_URL;?>libjs/jsTree/source/tree_component.js"></script>
+       <script type="text/javascript" src="libjs/image_selector.js"></script>
+    <script type="text/javascript" src="<?php echo MEDIA_APP_BASE_URL;?>libjs/plugins/tipsy/0.1.7/jquery.tipsy.js"></script>
+</body>
+</html>
diff --git a/Toolkit/Common.php b/Toolkit/Common.php
new file mode 100644 (file)
index 0000000..2ff5197
--- /dev/null
@@ -0,0 +1,1354 @@
+<?php
+//    vim:set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker syntax=php:
+
+/**
+ * Common Toolkit application function repository
+ *
+ * PHP version 5
+ *
+ * @category Toolkit
+ * @package  Common
+ * @author   Jamie Kahgee <steve@gaslightmedia.com>
+ * @license     http://www.gaslightmedia.com Gaslightmedia
+ * @release     CVS: $Id: Common.php,v 1.68 2010/08/15 19:31:47 jamie Exp $
+ * @link     http://demo.gaslightmedia.com
+ */
+
+/**
+ * Common collection of functions used throughout the GLM Toolkit
+ *
+ * @category  Toolkit
+ * @package   Common
+ * @author      Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2008 Gaslight Media
+ * @license      http://www.gaslightmedia.com Gaslightmedia
+ * @link      http://demo.gaslightmedia.com
+ */
+class Toolkit_Common
+{
+    //    {{{    properties
+
+    /**
+     * Who to send email to when a problem occurs on a live site
+     *
+     * This property doesn't matter for the development servers b/c the
+     * script will display the error encountered and terminate immediately.
+     * On the live site, a notification will be displayed to the user that
+     * an unexpected error has occured and will inform them to try again later.
+     * It will then send an email to the admin alerting them of the email.
+     *
+     * @var string
+     * @access protected
+     * @see Toolkit_Common::handleError()
+     */
+    protected static $admin = ERROR_EMAIL_ADDRESS;
+
+    /**
+     * Who to set the mail from when emailing errors
+     *
+     * @var string
+     * @access protected
+     * @see Toolkit_Common::handleError()
+     */
+    protected static $from = 'Gaslight Media Toolkit <server@gaslightmedia.com>';
+
+    //    }}}
+
+    //    {{{ arrayFlatten()
+
+    /**
+     * Recursively reduces multi-dimensional arrays to single-dimensional arrays
+     *
+     * @param array $array          The array to flatten.
+     * @param int    $preserveKeys How to handle array keys
+     *                               0 = Never
+     *                               1 = Strings
+     *                               2 = Always
+     * @param array    &$newArray      The new created from the flattening
+     *
+     * @return array Single dimensional array.
+     * @access public
+     */
+    public function arrayFlatten(
+        $array,
+        $preserveKeys = 1,
+        &$newArray = Array()
+    ) {
+        foreach ($array as $key => $child) {
+            if (is_array($child)) {
+                $newArray =& Toolkit_Common::arrayFlatten(
+                    $child,
+                    $preserveKeys,
+                    $newArray
+                );
+            } elseif ((int) $preserveKeys + (int) is_string($key) > 1) {
+                $newArray[$key] = $child;
+            } else {
+                $newArray[] = $child;
+            }
+        }
+        return $newArray;
+    }
+
+    //    }}}
+
+    //  {{{ cleanArray()
+
+    /**
+     * removes values from an array where the key ends in '_rmv'
+     *
+     * @param array &$values array to clean
+     *
+     * @return array  array w/ unneeded elements removed
+     * @access public
+     * @static
+     */
+    public static function cleanArray(&$values)
+    {
+        //    Get rid of any defined un-needed elements.
+        //    un-needed elements after the form is submitted are defined
+        //    by the ending _rmv name.
+        foreach ($values as $k => &$v) {
+            if (preg_match('/^.+_rmv$/', $k)) {
+                unset($values[$k]);
+            }
+        }
+
+        return $values;
+    }
+
+    //  }}}
+    //    {{{    createEmailBody()
+
+    /**
+     * Convert the form into an acceptable table to include in email
+     *
+     * This function can be called from any form class to generate
+     * a HTML table that we can use in an email. Elements can be dynamically
+     * rendered to meet your needs if you wish.
+     *
+     * If you wish to have any element(s) rendered differently than what the
+     * form already rendered them as, you need to define a public method
+     * named "emailRenderElement" in the calling class that will accept an
+     * elements name and you can create the rendering template in that class.
+     *
+     * Example:
+     * This example will turn groups like radio buttons or checkboxes
+     * from lists like:
+     * [ ] element_one [x] element_two [x] element_three
+     * ( ) element_one (x) element_two ( ) element_three
+     * into lists like:
+     * [ ] element_one        ( ) element_one
+     * [x] element_two        (x) element_two
+     * [x] element_three    ( ) element_three
+     * <code>
+     * public function &emailRenderElement($e)
+     * {
+     *        switch ($e) {
+     *            case 'element_one' :
+     *            case 'element_two' :
+     *            case 'element_three' :
+     *                $renderer =& $this->defaultRenderer();
+     *                $renderer->clearAllTemplates();
+     *                $renderer->setGroupTemplate('{content}', $e);
+     *                $renderer->setGroupElementTemplate('{element}<br>', $e);
+     *                break;
+     *
+     *            default :
+     *                $renderer = null;
+     *                break;
+     *        }
+     *
+     *        return $renderer;
+     *    }
+     * </code>
+     *
+     * @param array    $newLabels     Assoc array of element names and new
+     *                             labels to be used in the email form.
+     *                             eg [$newLabels['element'] => 'Label']
+     * @param array $excludeList Any element that needs to be removed
+     *                             from the form when creating the table
+     *                             eg [$list = array(e1, e2, e3, etc..)]
+     *
+     * @return mixed The table body for the email.
+     */
+    public function createEmailBody(
+        array $newLabels = array(),
+        array $excludeList = array()
+    ) {
+        $this->freeze();
+        //    Get the values corresponding to the elements present in the form.
+        $formData = $this->exportValues();
+        //    The array keys holds all the names of the elements.
+        $elements = array_keys($formData);
+        //    Remove any unwanted elements from our elements array.
+        foreach ($excludeList as $trgt) {
+            unset($elements[array_search($trgt, $elements)]);
+        }
+
+        //    Which row we are at.
+        $i     = 0;
+        $table = new HTML_Table(array('class' => 'data'));
+        //    Auto grow the table, since the forms will by dynamic in size.
+        $table->setAutoGrow(true);
+        //    Get the labels and values of the elements.
+        foreach ($elements as $name) {
+            $e =& $this->getElement($name);
+            //    Get the default HTML for each element.
+            $html = $e->toHtml();
+            //    If any elements need to have their html
+            //    changed for an email, this function in the
+            //    class should exist and will return a renderer
+            //    object of how the element should be rendered.
+            if (method_exists($this, 'emailRenderElement')) {
+                $renderer =& $this->emailRenderElement($name);
+                //    make sure we have an actual rendering object
+                //    if the element doesn't need to be altered it should
+                //    just return null.
+                if (is_object($renderer)) {
+                    $e->accept($renderer);
+                    $html = $renderer->toHtml($name);
+                    //    We have to reset the entire forms html
+                    //    otherwise we'll just keep adding to it everytime
+                    //    we render a new element.
+                    //    This is a bit of a hack to make this work (because
+                    //    the _html element is supposed to be a private
+                    //    property)
+                    $renderer->_html = null;
+                }
+            }
+            //    Get the label for the element.
+            $label = array_key_exists($name, $newLabels) ?
+                    $newLabels[$name] :
+                    $e->getLabel();
+
+            //    Make the row and increment the row counter.
+            $table->setCellContents($i, 0, $label);
+            $table->setCellAttributes($i, 0, array('class' => 'label'));
+            $table->setCellAttributes($i, 1, array('class' => 'field'));
+            $table->setCellContents($i++, 1, $html);
+        }
+        return $table->toHtml();;
+    }
+
+    //    }}}
+    //    {{{    createSQLInsert()
+
+    /**
+     * Generates a properly formatted sql query to insert data into a table
+     *
+     * @param string $table   Name of the table to insert into
+     * @param array  $columns Array of column names you want to set in the
+     *                          insert statement and bind names
+     *
+     * <code>
+     * Toolkit_Common::createSQLInsert('member', array('name', 'pos'));
+     * will create the sql statement
+     * INSERT INTO member (name, pos) VALUES(:name, :pos)
+     * </code>
+     *
+     * @return string Formatted SQL string
+     * @access public
+     * @static
+     */
+    public static function createSQLInsert($table, array $columns)
+    {
+        $params     = implode(', ', $columns);
+        $bindParams = ':' . implode(', :', $columns);
+
+        return "INSERT INTO $table ($params) VALUES ($bindParams)";
+    }
+
+    //    }}}
+    //    {{{    createSQLUpdate()
+
+    /**
+     * Generates a properly formatted sql query to update data in a table
+     *
+     * <code>
+     * Toolkit_Common::createSQLUpdate('member',
+     *                                   array('name', 'pos'),
+     *                                   array('id = :id');
+     * will create the sql statement
+     * UPDATE member SET name = :name, pos = :pos WHERE id = :id
+     * </code>
+     *
+     * @param string $table       Name of the table to update
+     * @param array  $columns     Array of columns names you want to update
+     * @param array  $constraints Constraints to apply to the table to prevent
+     *                              running the update on every row in the db
+     *
+     * @return string formatted query string
+     * @access public
+     * @static
+     */
+    public static function createSQLUpdate(
+        $table,
+        array $columns,
+        array $constraints = null
+    ) {
+        $length = count($columns);
+        for ($i = 0; $i < $length; ++$i) {
+            $bindParams .= "{$columns[$i]} = :{$columns[$i]}";
+            if ($i < ($length - 1)) {
+                $bindParams .= ', ';
+            }
+        }
+        $sql = "UPDATE $table SET $bindParams";
+
+        if (!empty($constraints)) {
+            $sql .= ' WHERE ' . implode(' AND ', $constraints);
+        }
+
+        return $sql;
+    }
+
+    //    }}}
+    //  {{{ createTables()
+
+    /**
+     * Read file from parameter and use the PDO parameter to create process file
+     *
+     * @param PDO    $pdo  PHP Data Object to use for DB calls
+     * @param string $file full path of file to parse
+     *
+     * @return void
+     * @access public
+     * @static
+     */
+    public static function createTables(PDO $pdo, $file)
+    {
+        $sql = file_get_contents($file);
+        //  break multiple queries apart by ';'
+        $tok = strtok($sql, ';');
+        try {
+            //  while we have valid tokens, execute the query
+            //  and grab the next token
+            while ($tok !== false) {
+                $pdo->query($tok);
+                $tok = strtok(';');
+            }
+        } catch (PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+    }
+
+    //  }}}
+
+    //    {{{ dieGracefully()
+
+    /**
+     * Gracefully exit from the script when an unrecoverable error is created
+     *
+     * @param string  $msg      Message to display to user
+     * @param mixed   $e        Error object
+     * @param boolean $moreInfo More debugging info when in development
+     *
+     * @return void
+     * @access public
+     */
+    public function dieGracefully($msg = null, $e = null, $moreInfo = false)
+    {
+        if (is_null($e)) {
+            if (is_null($msg)) {
+                die('There was an error, please try again later!');
+            } else {
+                die($msg);
+            }
+        } else {
+            echo $msg . '<br>';
+            echo 'Error Caught.  <br>';
+            echo 'Error: ' . $e->getMessage() . '<br>';
+            if ($moreInfo) {
+                echo 'Code: <pre>' . $e->getCode() . '</pre><br>';
+                echo 'Debug Info: <pre>' . $e->getDebugInfo() . '</pre><br>';
+            }
+        }
+    }
+
+    //    }}}
+
+    //    {{{    errorException()
+
+    /**
+     * Stops script on Exception error
+     *
+     * Stops the script when an Exception is raised inside a
+     * try/catch block.
+     *
+     * When a site is no longer in DEVELOPMENT, ie: its live on ws3.
+     * We don't show any error info, but let the user know an unexpected
+     * error has occured and then mail the error info the the admin.
+     *
+     * Example usage:
+     * <code>
+     * try {
+        if ($foo != $bar) {
+            throw new Exception ("Foo Doesn't equal Bar");
+        }
+     * } catch (Exception $e) {
+     *    return Toolkit_Common::handleError($e);
+     * }
+     * </code>
+     *
+     * @param Exception $e    Exception Object
+     * @param Mail      $mail What to use to send mail to admin
+     *
+     * @return false
+     * @access public
+     * @static
+     */
+    public static function errorException(Exception $e, Mail $mail = null)
+    {
+        if (!is_null($mail)) {
+            $subject = 'Exception Error for ' . SITENAME;
+            self::mailError($mail, $subject, $e);
+        } else {
+            echo self::getErrorInfo($e);
+        }
+
+        return false;
+    }
+
+    //    }}}
+    //    {{{    errorHTMLQuickFormError()
+
+    /**
+     * Handles PEAR Errors for our developers
+     *
+     * When a site is no longer in DEVELOPMENT, ie: its live on ws3.
+     * We don't show any error info, but let the user know an unexpected
+     * error has occured and then mail the error info the the admin.
+     *
+     * HTML_QuickForm Example usage:
+     * <code>
+     * $e =& $this->getElement('elementName');
+     * if (PEAR::isError($e)) {
+     *    return Toolkit_Common::handleError($e);
+     * }
+     * </code>
+     *
+     * @param HTML_QuickForm_Error $e    QuickFormError Object
+     * @param Mail                 $mail What to use to send mail to admin
+     *
+     * @return false
+     * @access public
+     * @since Method available since Release 1.0.1
+     * @static
+     */
+    public static function errorHTMLQuickFormError(
+        HTML_QuickForm_Error $e,
+        Mail $mail = null
+    ) {
+        if (!is_null($mail)) {
+            $subject = 'PEAR Error for ' . SITENAME;
+            self::mailError($mail, $subject);
+        } else {
+            echo self::getErrorInfo($e);
+        }
+
+        return false;
+    }
+
+    //    }}}
+    //    {{{    errorPDOException()
+
+    /**
+     * Stops script on database error
+     *
+     * Stops the script when a PDOException is raised inside a
+     * try/catch block.
+     *
+     * When a site is no longer in DEVELOPMENT, ie: its live on ws3.
+     * We don't show any error info, but let the user know an unexpected
+     * error has occured and then mail the error info the the admin.
+     *
+     * Example usage:
+     * <code>
+     * try {
+     *    $sql = "
+     *         SELECT *
+     *           FROM table_name
+     *          WHERE id = :id";
+     *
+     *    $stmt = $this->dbh->prepare($sql);
+     *    $stmt->bindParam(':id', $id, PDO::PARAM_INT);
+     *    return $stmt->execute();
+     * } catch (PDOException $e) {
+     *    return Toolkit_Common::handleError($e);
+     * }
+     * </code>
+     *
+     * @param PDOException $e    PDO Error Object.
+     * @param Mail         $mail Mail object used to send admin email
+     *
+     * @return false
+     * @access public
+     * @since Method available since Release 1.0.1
+     */
+    public function errorPDOException(PDOException $e, Mail $mail = null)
+    {
+        if (!is_null($mail)) {
+            $subject = 'SQL Error for ' . SITENAME;
+            self::mailError($mail, $subject, $e);
+        } else {
+            echo self::getErrorInfo($e);
+        }
+
+        return false;
+    }
+
+    //    }}}
+    //    {{{    errorPEARError()
+
+    /**
+     * Handles PEAR Errors for our developers
+     *
+     * When a site is no longer in DEVELOPMENT, ie: its live on ws3.
+     * We don't show any error info, but let the user know an unexpected
+     * error has occured and then mail the error info the the admin.
+     *
+     * @param PEAR_Error $e    PEARError Object
+     * @param Mail       $mail Mail object used to send admin email
+     *
+     * @return false
+     * @access public
+     * @since Method available since Release 1.0.1
+     */
+    public function errorPEARError(PEAR_Error $e, Mail $mail = null)
+    {
+        if (!is_null($mail)) {
+            $subject = 'PEAR Error for ' . SITENAME;
+            self::mailError($mail, $subject);
+        } else {
+            echo self::getErrorInfo($e);
+        }
+
+        return false;
+    }
+
+    //    }}}
+    //    {{{    errorPEARException()
+
+    /**
+     * Handles PEAR Exception for our developers
+     *
+     * When a site is no longer in DEVELOPMENT, ie: its live on ws3.
+     * We don't show any error info, but let the user know an unexpected
+     * error has occured and then mail the error info the the admin.
+     *
+     * @param PEAR_Exception $e    PEARException Object
+     * @param Mail           $mail Mail object used to send admin email
+     *
+     * @return false
+     * @access public
+     * @since Method available since Release 1.0.1
+     */
+    public function errorPEARException(PEAR_Exception $e, Mail $mail = null)
+    {
+        if (!is_null($mail)) {
+            $subject = 'SQL Error for ' . SITENAME;
+            self::mailError($mail, $subject, $e);
+        } else {
+            echo self::getErrorInfo($e);
+        }
+
+        return false;
+    }
+
+    //    }}}
+    //    {{{    errorRuntimeException()
+
+    /**
+     * Stops script on runtime error
+     *
+     * Stops the script when a runtimeException is raised inside a
+     * try/catch block.
+     *
+     * When a site is no longer in DEVELOPMENT, ie: its live on ws3.
+     * We don't show any error info, but let the user know an unexpected
+     * error has occured and then mail the error info the the admin.
+     *
+     * @param RuntimeException $e    PDO Error Object.
+     * @param Mail             $mail Mail object used to send admin email
+     *
+     * @return false
+     * @access public
+     * @since Method available since Release 1.0.2
+     */
+    public function errorRuntimeException(
+        RuntimeException $e,
+        Mail $mail = null
+    ) {
+        if (!is_null($mail)) {
+            $subject = 'Runtime Exception for ' . SITENAME;
+            self::mailError($mail, $subject, $e);
+        } else {
+            echo self::getErrorInfo($e);
+        }
+
+        return false;
+    }
+
+    //    }}}
+    //    {{{    errorBadMethodCallException()
+
+    /**
+     * Stops script on bad method call error
+     *
+     * Stops the script when a badMethodCallException is raised inside a
+     * try/catch block.
+     *
+     * When a site is no longer in DEVELOPMENT, ie: its live on ws3.
+     * We don't show any error info, but let the user know an unexpected
+     * error has occured and then mail the error info the the admin.
+     *
+     * @param BadMethodCallException $e    PDO Error Object.
+     * @param Mail                   $mail Mail object used to send admin email
+     *
+     * @return false
+     * @access public
+     * @since Method available since Release 1.0.3
+     */
+    public function errorBadMethodCallException(
+        BadMethodCallException $e,
+        Mail $mail = null
+    ) {
+        if (!is_null($mail)) {
+            $subject = 'Bad Method Call Exception for ' . SITENAME;
+            self::mailError($mail, $subject, $e);
+        } else {
+            echo self::getErrorInfo($e);
+        }
+
+        return false;
+    }
+
+    //    }}}
+
+    //  {{{ filterURI()
+
+    /**
+     * Filters uri's before they are validated
+     *
+     * @param string $uri URI to filter
+     *
+     * @return mixed new uri if missing scheme
+     * @access public
+     * @static
+     */
+    public static function filterURI($uri)
+    {
+        $validProtocol = '/^https?\:\/\/.*/';
+        $invalidProtocol = '/^.*?\:\/\/.*/';
+        if (empty($uri)) {
+            //    Empty field, just return
+            return $uri;
+        } elseif (preg_match($validProtocol, $uri)) {
+            //    has valid protocol, return unchanged
+            //    should pass validation.
+            return $uri;
+        } elseif (!preg_match($invalidProtocol, $uri)) {
+            //    missing protocol, prepend default
+            //    http:// protocol and return.
+            return "http://$uri";
+        } else {
+            //    has invalid protocol, return unchanged
+            //    validation should catch this and throw error.
+            return $uri;
+        }
+    }
+
+    //  }}}
+    //  {{{ filterPhone()
+
+    /**
+     * Filters phone numbers before they are validated
+     *
+     * @param string $phone number to filter
+     *
+     * @return mixed newly formatted phone number
+     * @access public
+     * @static
+     */
+    public static function filterPhone($phone)
+    {
+        //  Ditch anything that is not a number
+        $number = preg_replace('/[^0-9]/', '', $phone);
+
+        //  Invalid Number, validation will catch error
+        $len = strlen($number);
+        if (($len < 10) || ($len > 11)) {
+            return $phone;
+        }
+
+        //  subscriber number
+        $sn = substr($number, -4);
+        //  city code
+        $cc = substr($number, -7, 3);
+        //  area code
+        $ac = substr($number, -10, 3);
+        if ($len == 11) {
+            //  country prefix
+            $cp = $number[0];
+        }
+
+        $filteredNumber = "($ac) $cc-$sn";
+        if (!is_null($cp)) {
+            $filteredNumber = "$cp $filteredNumber";
+        }
+
+        return $filteredNumber;
+    }
+
+    //  }}}
+
+    //    {{{ getCities()
+
+    /**
+     * Get an array of cities from the database
+     *
+     * @param PDO     $dbh    Database handler
+     * @param integer $state  State id the city is in
+     * @param integer $county County id the city is in
+     * @param integer $region Region id the city is in
+     *
+     * @return array states
+     * @access public
+     * @static
+     */
+    public static function getCities(
+        PDO $dbh,
+        $state = null,
+        $county = null,
+        $region = null
+    ) {
+        $param = array();
+        if (ctype_digit((string)$state)) {
+            $param[] = 'state_id = ' . $dbh->quote($state);
+        }
+        if (ctype_digit((string)$county)) {
+            $param[] = 'county_id = ' . $dbh->quote($county);
+        }
+        if (ctype_digit((string)$region)) {
+            $param[] = 'region_id = ' . $dbh->quote($region);
+        }
+
+        try {
+            $sql = "
+                SELECT *
+                  FROM city";
+
+            if (!empty($param)) {
+                $sql .= ' WHERE ' . implode(' AND ', $param);
+            }
+            $sql .= ' ORDER BY city_name';
+
+            $stmt = $dbh->prepare($sql);
+            $stmt->execute();
+
+            $cities = array();
+            while ($row = $stmt->fetch()) {
+                $cities[$row['city_id']] = $row['city_name'];
+            }
+
+            return $cities;
+        } catch (PDOException $e) {
+            return Toolkit_Common::handleError($e);
+        }
+    }
+
+    //    }}}
+    //  {{{ getErrorInfo()
+
+    /**
+     * extract error info from error object
+     *
+     * @param mixed $obj Error object to get info for
+     *
+     * @return string formatted error information
+     * @access public
+     */
+    public function getErrorInfo($obj)
+    {
+        $state = '<b>' . get_class($obj) . ' error: </b>';
+
+        $state .= '<br>$_SERVER[\'QUERY_STRING\']: ' . $_SERVER['QUERY_STRING']
+            . "\n";
+        $state .= '<br>$_SERVER[\'HTTP_REFERER\']: ' . $_SERVER['HTTP_REFERER']
+            . "\n";
+        $state .= '<br>$_SERVER[\'REDIRECT_QUERY_STRING\']: '
+            . $_SERVER['REDIRECT_QUERY_STRING'] . "\n";
+        $state .= '<br>$_SERVER[\'REQUEST_URI\']: ' . $_SERVER['REQUEST_URI']
+            . "\n";
+
+        $state .= '<br>$_GET: <pre>' . print_r($_GET, true) . '</pre>';
+        $state .= '<br>$_POST: <pre>' . print_r($_POST, true) . '</pre>';
+        $state .= '<br>$_SESSION: <pre>' . print_r($_SESSION, true) . '</pre>';
+
+        if (method_exists($obj, 'getMessage')) {
+            $state .= $obj->getMessage();
+            if (method_exists($obj, 'getDebugInfo')) {
+                $state .= '; ' . $obj->getDebugInfo();
+            }
+        }
+        if (method_exists($obj, 'getFile')) {
+            $state .= ' in <b>' . $obj->getFile() . '</b>';
+        }
+        if (method_exists($obj, 'getLine')) {
+            $state .= ' on line <b>' . $obj->getLine() . '</b>';
+        }
+        if (method_exists($obj, 'getBacktrace')) {
+            $backtrace = print_r($obj->getBacktrace(), true);
+
+            $state .= "<br><pre>$backtrace</pre>";
+        }
+
+        return $state;
+    }
+
+    //  }}}
+    //    {{{    getHierarchicalTreeStructure()
+
+    /**
+     * Create a hierarchical tree stored in an linear array
+     *
+     * Produces a representation of a hierarchical tree structure into a
+     * linear array so you can iterate straight through to get the tree
+     * structure.
+     *
+     * @param PDO     $pdo         Database handler
+     * @param string  $table       Name of the source relation
+     * @param string  $key         Name of the key field
+     * @param string  $parent      Name of the parent-key field
+     * @param string  $order       Name of the field to order siblings by
+     * @param integer $start       Key value of the row to start at
+     * @param integer $maxDepth    Maximum depth to descend to, or zero
+     *                               for unlimited depth
+     * @param boolean $validParent exclude branches that have null
+     *                             parent values
+     *
+     * @return array   Linear array of tree structure
+     * @access public
+     * @see http://www.postgresql.org/doc/8.3/interactive/tablefunc.html#AEN104085
+     */
+    public function getHierarchicalTreeStructure(
+        PDO $pdo,
+        $table = 'pages',
+        $key = 'id',
+        $parent = 'parent',
+        $order = 'pos',
+        $start = 0,
+        $maxDepth = 0,
+        $validParent = true
+    ) {
+        try {
+            $tree = array();
+
+            $sql = "
+                SELECT *
+                  FROM connectby('{$table}', '{$key}', '{$parent}',
+                                 '{$order}', '{$start}', {$maxDepth})
+                        as t(id text, parent text, level int, pos int)";
+            if ($validParent) {
+                 $sql .= " WHERE parent is not null";
+            }
+            foreach ($pdo->query($sql) as $row) {
+                $tree[$row['id']] = $row['level'];
+            }
+
+            return $tree;
+        } catch (PDOException $e) {
+            return self::handleError($e);
+        }
+    }
+
+    //    }}}
+    //    {{{    getScripts()
+
+    /**
+     * Gets all scripts for the page
+     *
+     * adds version number to url for all local (non-app.glm) urls
+     * so we can use .htaccess cachebusting
+     *
+     * combines script by server id, so we can decrease http requests to fetch
+     * the needed scripts
+     *
+     * @param array $scripts The array of js scripts for the page
+     *
+     * @return string HTML markup for scripts
+     * @access public
+     * @static
+     */
+    public static function getScripts(array $scripts)
+    {
+        if (!is_array($scripts) || empty($scripts)) {
+            return false;
+        }
+
+        $uniqueScripts = array_unique($scripts);
+        //    Get the main jquery file
+        $jquery = JQUERY_CDN_JS;
+        $key = array_search($jquery, $uniqueScripts);
+        //    If it exists, remove it from its current location
+        //    and put at front of array.
+        if ($key !== false) {
+            unset($uniqueScripts[$key]);
+            array_unshift($uniqueScripts, $jquery);
+        }
+        $format = '<script type="text/javascript" src="%s"></script>';
+
+        $ret = '';
+        $baseUrlStrLen = strlen(MEDIA_BASE_URL);
+        $appUrlStrLen  = strlen(MEDIA_APP_BASE_URL);
+
+        //    Use versioning with local scripts for cachebusting
+        $localPath = MEDIA_BASE_URL . 'v/' . VERSION . '/javascript/';
+        $appPath   = MEDIA_APP_BASE_URL . 'javascript/';
+
+        $localScripts = array();
+        $appScripts   = array();
+        foreach ($uniqueScripts as $origScript) {
+            $ret .= sprintf($format, $origScript) . "\n";
+        }
+
+        if (!empty($appScripts)) {
+            $appPath = $appPath . implode(',', $appScripts);
+            $ret .= sprintf($format, $appPath) . "\n";
+        }
+
+        if (!empty($localScripts)) {
+            $localPath = $localPath . implode(',', $localScripts);
+            $ret .= sprintf($format, $localPath) . "\n";
+        }
+
+        return $ret;
+    }
+
+    //    }}}
+    //    {{{ getStates()
+
+
+    /**
+     * Get an array of states from the database
+     *
+     * @param PDO     $dbh             Database handler
+     * @param boolean $unionStatesOnly If we want to only retrieve
+     *                                 the 50 US states
+     *
+     * @return array states
+     * @access public
+     * @static
+     */
+    public static function getStates(PDO $dbh, $unionStatesOnly = false)
+    {
+        if ($unionStatesOnly) {
+            //    Just grab the 50 states of the union
+            $where = "WHERE us_state = :bool";
+        }
+
+        try {
+            $sql = "
+                SELECT *
+                  FROM state
+                $where
+                 ORDER BY state_name";
+
+            $stmt = $dbh->prepare($sql);
+            if ($unionStatesOnly) {
+                $stmt->bindValue(':bool', 1, PDO::PARAM_BOOL);
+            }
+            $stmt->execute();
+
+            $states = array();
+            while ($row = $stmt->fetch()) {
+                $states[$row['state_id']] = $row['state_name'];
+            }
+
+            return $states;
+        } catch (PDOException $e) {
+            return Toolkit_Common::handleError($e);
+        }
+    }
+
+    //    }}}
+    //    {{{    getStyleSheets()
+
+    /**
+     * Gets all style sheets for the page
+     *
+     * adds version number to url for all local (non-app.glm) urls
+     * so we can use .htaccess cachebusting
+     *
+     * combines style sheets by server id, so we can decrease http
+     * requests to fetch the needed style sheets
+     *
+     * @return string HTML markup for stylesheets
+     * @access public
+     * @static
+     */
+    public static function getStyleSheets()
+    {
+        if (   !isset($GLOBALS['styleSheets'])
+            || !is_array($GLOBALS['styleSheets'])
+            || empty($GLOBALS['styleSheets'])
+        ) {
+            return false;
+        }
+
+        $uniqueStyleSheets = array_unique($GLOBALS['styleSheets']);
+        $format = '<link type="text/css" rel="stylesheet" href="%s">';
+
+        $baseUrlStrLen = strlen(MEDIA_BASE_URL);
+        $appUrlStrLen  = strlen(MEDIA_APP_BASE_URL);
+        $ret = '';
+
+        $localPath = MEDIA_BASE_URL . 'v/' . VERSION . '/css/';
+        $appPath   = MEDIA_APP_BASE_URL . 'css/';
+
+        $localStyleSheets = array();
+        $appStyleSheets   = array();
+        foreach ($uniqueStyleSheets as $origStyleSheet) {
+            $ret .= sprintf($format, $origStyleSheet) . "\n";
+        }
+
+        if (!empty($appStyleSheets)) {
+            $appPath = $appPath . implode(',', $appStyleSheets);
+            $ret .= sprintf($format, $appPath) . "\n";
+        }
+        if (!empty($localStyleSheets)) {
+            $localPath = $localPath . implode(',', $localStyleSheets);
+            $ret .= sprintf($format, $localPath) . "\n";
+        }
+
+        return $ret;
+    }
+
+    //    }}}
+    //    {{{ getTableMetaData()
+
+    /**
+     * Gets the meta data of the calling classes table columns
+     *
+     * The table used when retrieving the meta data is defined
+     * in the class property $tableName. The class or parent class
+     * must also have a $tableMetaData property
+     *
+     * @param PDO    $pdo       Database Handler
+     * @param string $tableName The name of the table to get the meta data for.
+     * @param array  $clauses    Only retrieve meta data for certain column types
+     *
+     * @return array metadata for table
+     * @access protected
+     */
+    public function getTableMetaData(
+        PDO $pdo,
+        $tableName,
+        array $clauses = null
+    ) {
+        if (is_array($clauses)) {
+            while ($c = current($clauses)) {
+                $ands .= " data_type = '{$c}'";
+                if (false !== next($clauses)) {
+                    $ands .= " OR ";
+                }
+            }
+            $ands = " AND ($ands)";
+        }
+        try {
+            $sql  = "
+                SELECT column_name, data_type
+                  FROM information_schema.columns
+                 WHERE table_name = :tname
+                 $ands";
+            $stmt = $pdo->prepare($sql);
+            $stmt->bindParam(':tname', $tableName, PDO::PARAM_STR);
+
+            $stmt->execute();
+
+            $metaData = array();
+            while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                $metaData[$row['column_name']] = $row['data_type'];
+            }
+
+            return $metaData;
+        } catch (PDOException $e) {
+            return self::handleError($e);
+        }
+    }
+
+    //    }}}
+
+    //    {{{ handleError()
+
+    /**
+     * Handles various script error
+     *
+     * @param Object  $e                 Error Object
+     * @param boolean $developmentServer If we are on a development server
+     *
+     * @return mixed String, false, void
+     * @access public
+     * @static
+     */
+    public static function handleError($e, $developmentServer = DEVELOPMENT)
+    {
+        $errorType = str_replace('_', '', get_class($e));
+        $errorType = "error$errorType";
+
+        if (method_exists(__CLASS__, $errorType)) {
+            if (!$developmentServer) {
+                //    Tell the user we encountered an Error.
+                if (file_exists(BASE . '404.html')) {
+                    include_once BASE . "404.html";
+                }
+
+                $mail = Mail::factory('mail');
+                self::$errorType($e, $mail);
+                exit();
+            } else {
+                return self::$errorType($e, $mail);
+            }
+        } else {
+            echo '<p>The system has encountered an un-recoverable error!</p>';
+        }
+    }
+
+    //    }}}
+
+    //  {{{ mailError()
+
+    /**
+     * Mails the error to the site admin
+     *
+     * @param Mail      $mail    Mail object to use
+     * @param string    $subject Subject of email
+     * @param Exception $e       exception
+     *
+     * @return void
+     * @access public
+     * @static
+     */
+    public static function mailError(
+        Mail $mail,
+        $subject,
+        Exception $e = null
+    ) {
+        $msg = '<p>' . self::getErrorInfo($e) . '</p>';
+
+        $htmlMsg  = "<html><head></head><body>$msg</body></html>";
+        $crlf     = "\n";
+        $mimeMail = new Mail_mime($crlf);
+        $mimeMail->setFrom(self::$from);
+        $mimeMail->setSubject($subject);
+        $mimeMail->setHTMLBody($htmlMsg);
+        $mimeMail->setTXTBody($msg);
+
+        $body    = $mimeMail->get();
+        $headers = $mimeMail->headers();
+
+        $mail->send(self::$admin, $headers, $body);
+    }
+
+    //  }}}
+    //    {{{ multiDimArrayLocate()
+
+    /**
+     * Locates an array value in a multi-dimensional array
+     *
+     * @param array $array The Array which holds the value you need.
+     * @param mixed $text  The value that you are looking for.
+     *
+     * @return mixed The search result.
+     */
+    public function multiDimArrayLocate($array, $text)
+    {
+        if (!is_array($array)) {
+            return;
+        }
+        $arrayResult = array();
+        foreach ($array as $k => $v) {
+            if (is_array($v)) {
+                $temp[$k] = self::multiDimArrayLocate($v, $text);
+                if ($temp[$k]) {
+                    $arrayResult[$k] = $temp[$k];
+                }
+            } else {
+                if ($v == $text) {
+                    $arrayResult[$k] = $v;
+                }
+            }
+        }
+        return $arrayResult;
+    }
+
+    //    }}}
+
+    //    {{{    processQuery()
+    //The type hint of the argument is incorrect for the type
+    //definition of the @param tag with argument $dbh in processQuery()
+    /**
+     * Performs the sql insert statement
+     *
+     * If using a Postgresql driver for the PDO, metadata is used when
+     * binding parameters to the prepared statement.
+     *
+     * @param PDO    $dbh       Database handler object
+     * @param string $tableName table used in query
+     * @param string $sql       sql query string
+     * @param array  $values    associative array of key/value pairs that will
+     *                            be used to bind to the sql query string
+     *
+     * @return boolean result of the execute statement on the database
+     * @access public
+     * @static
+     */
+    public static function processQuery(
+        PDO $dbh,
+        $tableName,
+        $sql,
+        array $values = null
+    ) {
+        $dbType = $dbh->getAttribute(PDO::ATTR_DRIVER_NAME);
+        if ($dbType == 'pgsql') {
+            $md = Toolkit_Common::getTableMetaData($dbh, $tableName);
+        }
+        $stmt = $dbh->prepare($sql);
+        foreach ($values as $k => &$v) {
+            if ($dbType == 'pgsql') {
+                $metaData = $md[$k];
+                if ($metaData == 'integer') {
+                    $dataType = PDO::PARAM_INT;
+                } elseif ($metaData == 'boolean') {
+                    $dataType = PDO::PARAM_BOOL;
+                } else {
+                    $dataType = PDO::PARAM_STR;
+                }
+                //    for empty values that are not actually a zero (0), we
+                //    want to insert null's.
+                //    as empty values '', are not considered unique
+                if (   empty($v)
+                    && $dataType !== PDO::PARAM_BOOL
+                    && $dataType !== PDO::PARAM_INT
+                ) {
+                    $v        = null;
+                    $dataType = PDO::PARAM_NULL;
+                }
+                $stmt->bindParam(":$k", $v, $dataType);
+            } else {
+                $stmt->bindParam(":$k", $v);
+            }
+        }
+
+        return $stmt->execute();
+    }
+
+    //    }}}
+    //    {{{    prepareQuery()
+
+    /**
+     * Prepares the sql statement
+     *
+     * If using a Postgresql driver for the PDO, metadata is used when
+     * binding parameters to the prepared statement.
+     *
+     * @param PDO    &$dbh      Database handler object
+     * @param string $tableName table used in query
+     * @param string $sql       sql query string
+     * @param array  $values    associative array of key/value pairs that will
+     *                            be used to bind to the sql query string
+     *
+     * @return PDOStatement if successfully prepares statement
+     * @access public
+     * @static
+     */
+    public static function prepareQuery(
+        PDO &$dbh,
+        $tableName,
+        $sql,
+        array $values = null
+    ) {
+        $dbType = $dbh->getAttribute(PDO::ATTR_DRIVER_NAME);
+        if ($dbType == 'pgsql') {
+            $md = Toolkit_Common::getTableMetaData($dbh, $tableName);
+        }
+        $stmt = $dbh->prepare($sql);
+        foreach ($values as $k => &$v) {
+            if ($dbType == 'pgsql') {
+                $metaData = $md[$k];
+                if ($metaData == 'integer') {
+                    $dataType = PDO::PARAM_INT;
+                } elseif ($metaData == 'boolean') {
+                    $dataType = PDO::PARAM_BOOL;
+                } else {
+                    $dataType = PDO::PARAM_STR;
+                }
+                //    for empty values that are not actually a zero (0), we
+                //    want to insert null's.
+                //    as empty values '', are not considered unique
+                if (   empty($v)
+                    && $dataType !== PDO::PARAM_BOOL
+                    && $dataType !== PDO::PARAM_INT
+                ) {
+                    $v        = null;
+                    $dataType = PDO::PARAM_NULL;
+                } elseif ($v === '' && $dataType === PDO::PARAM_INT) {
+                    $v        = null;
+                    $dataType = PDO::PARAM_NULL;
+                }
+
+                $stmt->bindParam(":$k", $v, $dataType);
+            } else {
+                $stmt->bindParam(":$k", $v);
+            }
+        }
+
+        return $stmt;
+    }
+
+    //    }}}
+
+    //  {{{ recursiveArraySearch()
+
+    /**
+     * Recursive multi-dimensional array search
+     *
+     * @param string $n Needle
+     * @param array  $h Haystack
+     *
+     * @return mixed search result - false if not found
+     * @access public
+     * @static
+     */
+    public static function recursiveArraySearch($n, array $h)
+    {
+        foreach ($h as $i => $j) {
+            $curr = $i;
+            if (   $n === $j
+                || (is_array($j) && self::recursiveArraySearch($n, $j) !== false)
+            ) {
+                return $curr;
+            }
+        }
+
+        return false;
+    }
+
+    //  }}}
+
+    //    {{{ show()
+
+    /**
+     * Renders the calling class to the screen
+     *
+     * @access public
+     * @return string calls the toHTML function of calling class
+     */
+    public function show()
+    {
+        echo $this->toHTML();
+    }
+
+    //    }}}
+}
diff --git a/Toolkit/Contacts/Admin/EditContact.php b/Toolkit/Contacts/Admin/EditContact.php
new file mode 100644 (file)
index 0000000..ef49dbb
--- /dev/null
@@ -0,0 +1,1040 @@
+<?php
+
+/**
+ * EditContact.php
+ *
+ * PHP version 5
+ *
+ * @category  Toolkit
+ * @package   Contacts
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2010 Gaslight Media
+ * @license   Gaslight Media
+ * @version   CVS: $Id: EditContact.php,v 1.3 2010/05/13 20:18:38 matrix Exp $
+ * @link      http://pear.php.net/package/Contacts
+ * @see       References to other sections (if any)...
+ */
+/**
+ * Error codes for Toolkit_Contacts_ContactUs
+ *
+ * Codes are mapped to textual messaged by errorMessage() method,
+ * if you add a new code be sure to add a new message for it to errorMessage()
+ *
+ * @see Toolkit_Contacts_ContactUs::errorMessage()
+ */
+define('FORM_OK', 1);
+
+/**
+ * Description for define
+ */
+define('FORM_ERROR', -1);
+
+/**
+ * Description for define
+ */
+define('NO_RECORD', -2);
+
+/**
+ * Description for define
+ */
+define('INVALID_DB', -3);
+
+/**
+ * Description for define
+ */
+define('MISSING_CONSTANT', -4);
+
+/**
+ * Description for define
+ */
+define('MISSING_CONTACT_TYPE', -5);
+
+/**
+ * Toolkit_Contacts_Admin_EditContact
+ *
+ * Edit Process class to insert or update a contact
+ * can be used with customer table with little modification
+ *
+ * @category  Toolkit
+ * @package   Contacts
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2010 Steve Sutton
+ * @license   Gaslight Media
+ * @version   Release: @package_version@
+ * @link      http://pear.php.net/package/Contacts
+ * @see       References to other sections (if any)...
+ */
+class Toolkit_Contacts_Admin_EditContact
+    extends Toolkit_FormBuilder implements Toolkit_Form
+{
+       //      {{{     properties
+
+    /**
+     * Table in Database which holds the contact data
+        *
+     * @var    string
+     * @access public
+     */
+       public $tableName = 'contact';
+
+    /**
+     * Primary Key for the table
+        *
+     * @var    string
+     * @access public
+     */
+    public $primaryKey = 'id';
+
+    /**
+     * Sequence for the table
+        *
+     * @var    string
+     * @access public
+     */
+    public $sequence = 'contact_id_seq';
+
+    /**
+     * Table meta data
+        *
+        * This is used when inserting/updating data for the records
+        * so the PDO's can use explicit data types for the parameters.
+        *
+     * @var    array
+     * @access public
+     */
+       public $tableMetaData = array();
+
+    /**
+     * The interests from the contact db
+        *
+     * @var    array
+     * @access protected
+     */
+       protected $inquiries = array();
+
+       /**
+        * What do you want the error msg to be if the form doesn't validate
+        *
+        * @var string
+        * @access protected
+        */
+       protected $errorMsg
+        = '<div id="form-warning-top">
+                Warning: The form was not sent, please review the errors below.
+           </div>';
+
+       /**
+        * What do you want the success msg to be if the form validates successfully
+        *
+        * @var string
+        * @access protected
+        */
+       protected $successMsg
+        = '<div id="form-success-top">
+                The information below has been successfully submitted.
+           </div>';
+
+    /**
+     * Include a captcha on the form or not
+        *
+     * @var    boolean
+     * @access protected
+     */
+       protected $useCaptcha = false;
+
+       /**
+        * The default templates to inject into the form renderer
+        *
+        * @var string
+        * @access protected
+        */
+       protected $template;
+
+       /**
+        * The default rules to register for validating
+        *
+        * We have to register these rules, or any others we want, before
+        * we are able to use them in our forms.
+        *
+        * These rules can be removed in subclasses before the rules are configured
+        * if you want to omit any of them from validating input - just remember
+        * to not call them in your configured rules!
+        *
+        * Phone: validates input against US and CA style phone #'s
+        * <code>
+        * $rules[] = array('element'    => 'phone',
+        *                  'message'    => 'ERROR: Invalid Phone Format!',
+        *                  'type'       => 'phone',
+        *                  'format'     => null,
+        *                  'validation' => $this->validationType,
+        *                  'reset'      => true,
+        *                  'force'      => false);
+        * </code>
+        *
+        * Zip: Validates input against US and CA zip codes, if DB check is
+        *      set to true, validate zip codes against all the zip codes in the
+        *      DB.
+        * <code>
+        * $rules[] = array('element'    => 'zip',
+        *                  'message'    => 'ERROR: Invalid Zip!',
+        *                  'type'       => 'zip',
+        *                  'format'     => array('requireDBCheck' => true),
+        *                  'validation' => $this->validationType,
+        *                  'reset'      => true,
+        *                  'force'      => false);
+        * </code>
+        *
+        * Banwords: Make sure each each doesn't contain a banned word. Checking
+        *           against a DB of banned words.
+        *
+        * State: Validate input against US and CA region / province codes.  If DB
+        *        check is set to true, validate region / province against all the
+        *        regions / provinces in the DB.
+        * <code>
+        * $rules[] = array('element'    => 'state_id',
+        *                  'message'    => 'ERROR: Invalid State / Province!',
+        *                  'type'       => 'state',
+        *                  'format'     => array('requireDBCheck' => true),
+        *                  'validation' => $this->validationType,
+        *                  'reset'      => true,
+        *                  'force'      => false);
+        * </code>
+        *
+        * @var array
+        * @access protected
+        * @see app.gaslightmedia.com/glmPEAR/HTML/QuickForm/Rule/Zip.php
+        * @see app.gaslightmedia.com/glmPEAR/HTML/QuickForm/Rule/Phone.php
+        * @see app.gaslightmedia.com/glmPEAR/HTML/QuickForm/Rule/Banwords.php
+        * @see app.gaslightmedia.com/glmPEAR/HTML/QuickForm/Rule/State.php
+        */
+       protected $registeredRules = array('phone', 'zip', 'state');
+
+       //      }}}
+       //      {{{     __construct()
+
+       /**
+        * Class constructor
+        *
+     * @param object $pdo         PHP Data Object
+     * @param string $formName    Form's name.
+     * @param string $method      (optional)Form's method defaults to 'POST'
+     * @param string $action      (optional)Form's action
+     * @param string $target      (optional)Form's target defaults to '_self'
+     * @param mixed  $attributes  (optional)Extra attributes for <form> tag
+     * @param bool   $trackSubmit (optional)Whether to track if the form was
+        *                                                        submitted by adding a special hidden field
+        *
+        * @author Jamie Kahgee <jamie.kahgee@gmail.com>
+        * @access public
+        * @link   http://pear.php.net/package/HTML_QuickForm/docs/latest/HTML_QuickForm/HTML_QuickForm.html
+        * @see    HTML_QuickForm
+        */
+       public function __construct(
+        PDO $pdo,
+        $formName,
+        $method = 'post',
+        $action = '',
+        $target = '',
+        $attributes = null,
+        $trackSubmit = false
+    ) {
+               parent::__construct(
+            $formName,
+            $method,
+            $action,
+            $target,
+            $attributes,
+            $trackSubmit
+        );
+
+        $this->dbh = $pdo;
+
+               /**
+                * Where are the flexy templates stored at for this class.
+                */
+               define('TEMPLATES_DIR', BASE . 'Toolkit/Contacts/templates');
+
+               /**
+                * Where are the compiled flexy templates stored at for this class.
+                */
+               define('COMPILED_DIR', BASE . 'Toolkit/Contacts/templates/compiled');
+               $oldUmask = umask(0);
+               if (!is_dir(TEMPLATES_DIR)) {
+                       mkdir(TEMPLATES_DIR, 0770, true);
+               }
+               if (!is_dir(COMPILED_DIR)) {
+                       mkdir(COMPILED_DIR, 0770, true);
+               }
+               umask($oldUmask);
+
+               $this->flexyOptions                = $GLOBALS['flexyOptions'];
+               $this->flexyOptions['templateDir'] = TEMPLATES_DIR;
+               $this->flexyOptions['compileDir']  = COMPILED_DIR;
+
+       }
+
+       //      }}}
+
+       //      {{{ checkContactEmail()
+
+       /**
+        * Checks if the member name already exists in the database
+        *
+        * @param array $data The name of the member to check for.
+     *
+        * @return bool False on SQL Query error, otherwise true.
+        * @access      protected
+        */
+       public function checkContactEmail($data)
+       {
+               try {
+                       //      If we're editing a contact, they
+                       //      can save that member as its
+                       //      own name. so don't include that
+                       //      member in the check.
+                       if (ctype_digit($_REQUEST[$this->primaryKey])) {
+                               $and = "AND {$this->primaryKey} <> :id";
+                       }
+                       $sql = "
+                               SELECT count(*) AS total
+                                 FROM {$this->tableName}
+                                WHERE email = :email
+                                 $and";
+                       $stmt = $this->dbh->prepare($sql);
+                       $stmt->bindParam(':email', $data, PDO::PARAM_STR);
+                       if (ctype_digit($_REQUEST[$this->primaryKey])) {
+                               $stmt->bindParam(':id', $_REQUEST[$this->primaryKey], PDO::PARAM_STR);
+                       }
+                       $stmt->execute();
+            $stmt->bindColumn('total', $valid);
+                       $stmt->fetch();
+
+                       return !(bool) $valid;
+               } catch (PDOException $e) {
+                       return Toolkit_Common::handleError($e);
+               }
+       }
+
+       //      }}}
+       //      {{{     configureConstats()
+
+    /**
+     * Constant variables for the form
+        *
+        * These values won't get overridden by POST or GET vars
+     *
+     * @return void
+     * @access public
+     */
+       public function configureConstants()
+       {
+        if (ctype_digit($_REQUEST['id'])) {
+                   $constants = array('id' => $_REQUEST['id']);
+        } else {
+                   $constants = array();
+        }
+               $this->setupConstants($constants);
+       }
+
+       //      }}}
+       //      {{{     configureDefaults()
+
+    /**
+     * Initializes default form values
+     *
+     * @return void
+     * @access public
+     */
+       public function configureDefaults()
+       {
+        if (ctype_digit($_REQUEST[$this->primaryKey])) {
+            try {
+                $sql = "
+                SELECT ci.id,ig.name
+                  FROM contact_inq ci LEFT OUTER JOIN inq_group ig ON (ci.groupid = ig.id)
+                 WHERE ci.id = :id";
+                $getIntData = $this->dbh->prepare($sql);
+                $sql = "
+                SELECT *
+                  FROM {$this->tableName}
+                 WHERE {$this->primaryKey} = :id";
+                $stmt = $this->dbh->prepare($sql);
+                $stmt->bindParam(":id", $_REQUEST[$this->primaryKey], PDO::PARAM_INT);
+                $stmt->execute();
+                $defaults = $stmt->fetch(PDO::FETCH_ASSOC);
+                if ($defaults['interest']) {
+                    $pattern   = "/^:|:$/";
+                    $intString = preg_replace($pattern, "", $defaults['interest']);
+                    $intArray  = explode(":", $intString);
+                    if (is_array($intArray) && !empty($intArray)) {
+                        $newIntArray = array();
+                        foreach ($intArray as $intId) {
+                            if (is_numeric($intId)) {
+                                $getIntData->bindParam(":id", $intId, PDO::PARAM_INT);
+                                $getIntData->execute();
+                                $row = $getIntData->fetch(PDO::FETCH_ASSOC);
+                                // set the new interest array
+                                $newIntArray[$row['name']][$row['id']] = true;
+                            }
+                        }
+                        $defaults['interest'] = $newIntArray;
+                    }
+                }
+                if ($defaults['contact_type']) {
+                    $pattern      = "/^:|:$/";
+                    $cTypesString = preg_replace($pattern, "", $defaults['contact_type']);
+                    // set the new contact type array
+                    $defaults['contact_type'] = explode(":", $cTypesString);
+                }
+            } catch(PDOException $e) {
+                Toolkit_Common::handleError($e);
+            }
+        } else {
+            $defaults = array(
+                'state' => '',
+                'mail_ok' => 1
+            );
+        }
+
+               $this->setupDefaults($defaults);
+       }
+
+       //      }}}
+       //      {{{     configureElements()
+
+    /**
+     * Form element definitions
+     *
+     * @return void
+     * @access public
+     */
+       public function configureElements()
+       {
+               $e = array();
+               $this->setInterestFields();
+               //      Grouped Elements are defined here.
+               $this->interestsGroups =& $this->getInterestFields();
+
+        $contactTypesSetup
+            = $this->config->getItem('section', 'contact_types')
+                ->toArray();
+        $cType = $contactTypesSetup['contact_types'];
+               //      All Elements are created here.  This includes group element definitions.
+        $e[] = array(
+            'type'    => 'hidden',
+            'name'    => 'start',
+            'display' => $_REQUEST['start']
+        );
+               $e[] = array(
+                       'type'    => 'static',
+                       'req'     => false,
+            'name'    => 'user_agent',
+            'display' => 'User Agent'
+               );
+               $e[] = array(
+                       'type' => 'static',
+                       'req'  => false,
+                       'name' => 'remote_addr',
+            'display' => 'Remote Address'
+               );
+        if (ctype_digit($_REQUEST[$this->primaryKey])) {
+            $e[] = array(
+                'type' => 'hidden',
+                'req'  => false,
+                'name' => $this->primaryKey
+            );
+        }
+               $e[] = array(
+                       'type'    => 'text',
+                       'req'     => false,
+                       'name'    => 'fname',
+                       'display' => 'First Name'
+               );
+               $e[] = array(
+                       'type'    => 'text',
+                       'req'     => false,
+                       'name'    => 'lname',
+                       'display' => 'Last Name'
+               );
+        if ($this->tableName == 'customer') {
+            $add1 = 'add1';
+            $add2 = 'add2';
+        } else {
+            $add1 = 'address';
+            $add2 = 'address2';
+        }
+               $e[] = array(
+                       'type'    => 'text',
+                       'req'     => false,
+                       'name'    => $add1,
+                       'display' => 'Address 1'
+               );
+               $e[] = array(
+                       'type'    => 'text',
+                       'req'     => false,
+                       'name'    => $add2,
+                       'display' => 'Address 2'
+               );
+               $e[] = array(
+                       'type'    => 'text',
+                       'req'     => false,
+                       'name'    => 'city',
+                       'display' => 'City'
+               );
+        $e[] = array(
+                       'type'    => 'select',
+                       'req'     => false,
+                       'name'    => 'state',
+                       'display' => 'State/Province',
+                       'opts'    => $GLOBALS['states']
+               );
+        $e[] = array(
+                       'type'    => 'text',
+                       'req'     => false,
+                       'name'    => 'zip',
+                       'display' => 'ZIP/Postal Code'
+               );
+               $e[] = array(
+                       'type'    => 'text',
+                       'req'     => true,
+                       'name'    => 'email',
+                       'display' => 'Email'
+               );
+               $e[] = array(
+                       'type'    => 'text',
+                       'req'     => false,
+                       'name'    => 'phone',
+                       'display' => 'Phone'
+               );
+               $e[] = array(
+                       'type'    => 'text',
+                       'req'     => false,
+                       'name'    => 'fax',
+                       'display' => 'Fax'
+               );
+               $e[] = array(
+                       'type'    => 'advcheckbox',
+                       'req'     => false,
+                       'name'    => 'mail_ok',
+                       'display' => 'Mail Ok?',
+                       'opts'    => 'Yes',
+                       'val'     => array(0, 1)
+               );
+        if (is_array($this->interestsGroups)) {
+            foreach ($this->interestsGroups as $group => $gData) {
+                $this->myGroups[] = $gData;
+                $e[] = array(
+                    'type'       => 'group',
+                    'req'        => false,
+                    'name'       => 'interest['.$group.']',
+                    'group'         => $gData,
+                    'label'      => $group,
+                    'seperator'  => ' ',
+                    'appendName' => true
+                );
+            }
+        }
+        $e[] = array(
+            'type'    => 'select',
+            'req'     => false,
+            'name'    => 'contact_type',
+            'display' => 'Contact Type',
+            'opts'    => $cType,
+            'att'     => array('multiple' => 'multiple')
+        );
+               $e[] = array(
+                       'type'    => 'submit',
+                       'req'     => false,
+                       'name'    => 'submit_rmv',
+            'display' => 'Submit Form'
+               );
+
+               $this->setupElements($e);
+       }
+
+       //      }}}
+       //      {{{     configureRules()
+
+    /**
+     * Form rule definitions
+     *
+        * Adds validation rules for the given fields
+     *
+     * @return void
+     * @access public
+     */
+       public function configureRules()
+       {
+               $r = array();
+               //      Form Rules
+               $r[] = array(
+                       'element'    => 'email',
+                       'message'    => 'ERROR: Invalid Email Format!',
+                       'type'       => 'email',
+                       'format'     => array('use_rfc822' => true),
+                       'validation' => $this->validationType,
+                       'reset'      => true,
+                       'force'      => false
+        );
+               $r[] = array(
+                       'element'    => 'phone',
+                       'message'    => 'ERROR: Invalid number (xxx) xxx-xxxx!',
+                       'type'       => 'phone',
+                       'format'     => null,
+                       'validation' => $this->validationType,
+                       'reset'      => true,
+                       'force'      => false
+        );
+               $r[] = array(
+                       'element'    => 'fax',
+                       'message'    => 'ERROR: Invalid number (xxx) xxx-xxxx!',
+                       'type'       => 'phone',
+                       'format'     => null,
+                       'validation' => $this->validationType,
+                       'reset'      => true,
+                       'force'      => false
+        );
+               $r[] = array(
+                       'element'    => 'zip',
+                       'message'    => 'ERROR: Invalid Zip!',
+                       'type'       => 'zip',
+                       'format'     => array('requireDBCheck' => false),
+                       'validation' => $this->validationType,
+                       'reset'      => true,
+                       'force'      => false
+        );
+               $r[] = array(
+                       'element'    => 'mail_ok',
+                       'message'    => 'ERROR: Invalid Value!',
+                       'type'       => 'numeric',
+                       'format'     => null,
+                       'validation' => $this->validationType,
+                       'reset'      => true,
+                       'force'      => false
+        );
+               $r[] = array(
+                       'element'    => 'email',
+                       'message'    => 'ERROR: There is an existing account with this name!',
+                       'type'       => 'callback',
+                       'format'     => array($this, 'checkContactEmail'),
+                       'validation' => $this->validationType,
+                       'reset'      => false,
+                       'force'      => false
+               );
+
+               $this->setupRules($r);
+       }
+
+       //      }}}
+       //      {{{     configureFilters()
+
+    /**
+     * Form filter definitions
+     *
+        * Applies a data filter for the given fields when the form is submitted
+     *
+     * @return void
+     * @access public
+     */
+       public function configureFilters()
+       {
+        $f = array();
+
+               $f[] = array(
+            'element' => '__ALL__',
+            'filter' => 'trim'
+        );
+
+        $this->setupFilters($f);
+       }
+
+       //      }}}
+    //  {{{ configureForm()
+
+    /**
+     * Helper function, configures the entire form
+     *
+     * @return void
+     * @access public
+     */
+    public function configureForm()
+    {
+        $this->configureElements();
+        $this->configureFilters();
+        $this->configureRules();
+        $this->configureDefaults();
+        $this->configureConstants();
+    }
+
+    //  }}}
+    //  {{{ contactExists()
+
+    /**
+     * Checks for a duplicate email address already in the DB.
+     *
+     * @param string $email The email address to check for in the DB
+     *
+     * @return boolean   If the email address already exists
+     * @access protected
+     */
+    protected function contactExists($email)
+    {
+               //      Check if a contact w/ the submitted email already exists.
+               //      if so, then we need to update, if not then do a fresh insert.
+               try {
+                       $sql = "
+                               SELECT count(*) AS count
+                                 FROM {$this->tableName}
+                                WHERE email   = :email";
+
+                       $stmt = $this->dbh->prepare($sql);
+                       $stmt->bindParam(':email', $email, PDO::PARAM_STR);
+                       $stmt->execute();
+                       $stmt->bindColumn('count', $contactExists);
+                       $stmt->fetch();
+
+            return (bool) $contactExists;
+               } catch (PDOException $e) {
+                       return Toolkit_Common::handleError($e);
+               }
+    }
+
+    //  }}}
+
+       //      {{{     error()
+
+    /**
+     * Display errors with the form to the user
+     *
+     * @param object $error PEAR Error Object
+        *
+     * @return void
+     * @access public
+     */
+       public function error($error)
+       {
+               //      make the variables static so that is only has to do the defining
+               //      on the first call.
+               static $errorMessages;
+               static $className;
+
+               //      define the various error messages
+               if (!isset($errorMessages)) {
+                       $className     = get_class($this);
+                       $errorMessages = array(
+                               FORM_OK                      => 'No error',
+                               FORM_ERROR                   => 'Unknown error',
+                               NO_RECORD                    => 'Setting both properties (email and hasContactDB) to false in the <i>' . $className . '</i> class will result in no email record being sent and no record being inserted into the database.  There will be no record that this form was ever filled out.',
+                               INVALID_DB                   => 'Please set the property hasContactDB to a boolean value in the class <i>' . $className . '</i> before continuing',
+                               MISSING_CONSTANT     => 'You have failed to set a CONSTANT for the class <i>' . $className . '</i> to function properly',
+                               MISSING_CONTACT_TYPE => 'Please set the property contactType in the class <i>'. $className . '</i> before continuing',);
+               }
+
+               if (!is_null($error->message)) {
+                       $message = $error->message;
+               } elseif (isset($errorMessages[$error->code])) {
+                       $message = $errorMessages[$error->code];
+               } else {
+                       $message = $errorMessages[$error->code];
+                       $message = $errorMessages[FORM_ERROR];
+               }
+
+               echo "<div id=\"form-warning-top\">{$message}</div>";
+       }
+
+       //      }}}
+
+       //      {{{     getInterestFields()
+
+    /**
+     * Returns the field definitions of the contact db interest fields
+     *
+     * @return array     Group definitions for the interest fields
+     * @access protected
+     */
+       protected function getInterestFields()
+       {
+               if (is_array($this->inquiries)) {
+            foreach ($this->inquiries as $group => $data) {
+                foreach ($data as $k => $v) {
+                    $interests[$group][] = array(
+                        'type' => 'checkbox',
+                         'req' => false,
+                         'name' => $k,
+                         'opts' => $v
+                     );
+                }
+                       }
+               }
+
+               return $interests;
+       }
+
+       //      }}}
+
+       //      {{{     processData()
+
+    /**
+     * Handles how to process the form when submitted
+     *
+     * @param array $values Form submitted values
+        *
+     * @return array     Result of Insert / Update function
+     * @access protected
+     */
+       public function processData($values)
+       {
+               //      Form data used for the insert/update sql queries and
+               //      the form email.
+               $e = array(
+                       'user_agent',
+                       'remote_addr',
+                       'contact_type',
+               );
+               $this->setFormData($e);
+        $this->start = $values['start'];
+        unset($values['start']);
+        //  Get rid of any elements in the values array that
+        //  aren't going to be used when inserting/updating the db.
+        $values = Toolkit_Common::cleanArray($values);
+
+        $isUpdate = (ctype_digit($_REQUEST[$this->primaryKey])) ? true: false;
+
+        if (is_array($values['interest']) && !empty($values['interest'])) {
+            foreach ($values['interest'] as $iGroup => $interests) {
+                $newInterest[] = implode(':', array_keys($interests));
+            }
+            if (is_array($newInterest)) {
+                $values['interest'] = ':' . implode(':', $newInterest) . ':';
+            }
+        }
+        if (!isset($values['interest'])) {
+            $values['interest'] = '';
+        }
+        if (is_array($values['contact_type']) && !empty($values['contact_type'])) {
+            $values['contact_type'] = ':' . implode(':', $values['contact_type']) . ':';
+        }
+        if (!isset($values['contact_type'])) {
+            $values['contact_type'] = '';
+        }
+
+               if ($isUpdate) {
+            $this->Action = 'Contact Updated';
+               } else {
+            $this->Action = 'Contact Added';
+               }
+        $values['create_date'] = date('m/d/Y');
+        // build a contact or customer class object with the values from
+        // the Contact form
+        if ($this->tableName == 'contact') {
+            $contact = Toolkit_LeadManager_Contact::createByValues($values);
+        } else {
+            $contact = Toolkit_LeadManager_Customer::createByValues($values);
+        }
+
+        if ($contact->getMailOk()) {
+            if (   defined('STREAMSEND_FORMS_API')
+                && STREAMSEND_FORMS_API) {
+                $contact->attach(new Toolkit_LeadManager_StreamSend());
+            }
+            if (    defined('CONSTANT_CONTACT')
+                && CONSTANT_CONTACT) {
+                $contact->attach(new Toolkit_LeadManager_ConstantContact());
+            }
+        }
+
+        $contact->save($this->dbh, false);
+        return true;
+       }
+
+       //      }}}
+    // {{{ setConfig()
+    /**
+     * setConfig
+     *
+     * @param Config_Container $c instance of Config Container
+     *
+     * @access public
+     * @return string
+     */
+    function setConfig(Config_Container $c)
+    {
+        $this->config = $c;
+    }// }}}
+       //      {{{     setInterestFields()
+
+    /**
+     * Contact DB interests
+     *
+     * @return void
+     * @access protected
+     */
+       protected function setInterestFields()
+       {
+               try {
+                       $sql = "
+                SELECT contact_inq.*, inq_group.name
+                  FROM contact_inq LEFT OUTER JOIN inq_group ON (contact_inq.groupid = inq_group.id)
+                 ORDER BY groupid, pos";
+
+            $i = array();
+                       foreach ($this->dbh->query($sql) as $row) {
+                               $i[$row['name']][$row['id']] = $row['header'];
+                       }
+
+                       $this->inquiries = $i;
+               } catch (PDOException $e) {
+                       Toolkit_Common::handleError($e);
+               }
+       }
+
+       //      }}}
+    // {{{ setPrimaryKey()
+    /**
+     * setPrimaryKey
+     *
+     * @param mixed $fieldName Primary Key of Table
+     *
+     * @access public
+     * @return string
+     */
+    function setPrimaryKey($fieldName)
+    {
+        $this->primaryKey = $fieldName;
+    }// }}}
+    // {{{ setTableName()
+    /**
+     * setTableName
+     *
+     * @param mixed $tableName Database table name
+     *
+     * @access public
+     * @return string
+     */
+    function setTableName($tableName)
+    {
+        $this->tableName = $tableName;
+    }// }}}
+
+       //      {{{     setupRenderers()
+    //  @codeCoverageIgnoreStart
+
+    /**
+     * Custom rendering templates for special fields on the form
+     *
+     * @return void
+     * @access protected
+     */
+       protected function setupRenderers()
+       {
+               parent::setupRenderers();
+               $renderer =& $this->defaultRenderer();
+               $required = '<!-- BEGIN required -->
+            <span class="req"> * </span>
+            <!-- END required -->';
+               $error    = '<!-- BEGIN error -->
+            <div class="req"> {error} </div>
+            <!-- END error -->';
+               $renderer->setElementTemplate(
+            '<tr>
+               <td colspan="2" class="fieldcell checkbox">
+               '.$required.'{label}'.$error.'{element}
+               </td>
+             </tr>',
+            'interest'
+        );
+        if (is_array($this->interestsGroups)) {
+            foreach ($this->interestsGroups as $group => $gData) {
+                $renderer->setGroupTemplate(
+                    '<br>{content}',
+                    'interest['.$group.']'
+                );
+                $renderer->setGroupElementTemplate(
+                    '{element}',
+                    'interest['.$group.']'
+                );
+                       $renderer->setElementTemplate(
+                    '<tr>
+                       <td colspan="2">'.$required.'{label}'.$error.'{element}
+                       </td>
+                     </tr>',
+                    'interest['.$group.']'
+                 );
+            }
+        }
+               $renderer->setElementTemplate(
+            '<tr><td colspan="2">'.$required.'{label}'.$error.'{element}</td></tr>',
+            'comments'
+        );
+               $renderer->setElementTemplate(
+            '<tr align="center">
+                <td colspan="2">'.$required.'{label}'.$error.'{element}
+                    </td>
+             </tr>',
+            'submit'
+        );
+
+               $renderer->setElementTemplate(
+            '<tr>
+               <td class="labelcell">
+                 <label>{label}</label>
+               </td>
+               <td class="fieldcell captcha">
+                 {element}
+               </td>
+             </tr>',
+            'captcha_question');
+               $renderer->setElementTemplate(
+            '<tr>
+                <td class="labelcell">
+                  '.$required.'<label>{label}</label>
+                </td>
+                <td class="fieldcell">
+                  '.$error.'{element}
+                  <span class="tooltip" title="Verification Code|To help us
+                  distinguish between information submitted by individuals
+                  and those automatically entered by software robots, please
+                  type the letters shown.">What is this?</span>
+                </td>
+              </tr>',
+            'captcha_rmv'
+        );
+       }
+
+    //  @codeCoverageIgnoreEnd
+       //      }}}
+       //      {{{     toHtml()
+
+    /**
+     * Handles how to display the current step the user is at in the form
+     *
+        * destroying and resetting the captcha value dis-allows someone from
+        * re-sending a form on a previous captcha.
+        *
+     * @return string form HTML state
+     * @access public
+     */
+       public function toHtml()
+       {
+               $this->setupRenderers();
+               if ($this->validate()) {
+                       $this->cleanForm();
+                       if ($this->process(array(&$this, 'processData'), $this->mergeFiles)) {
+                               $this->freeze();
+                               $output = $this->successMsg;
+                header("Location: list_contact.phtml?back=1&start="
+                    . $this->start . "&Action=" . $this->Action);
+                exit;
+                       }
+               } elseif ($this->isSubmitted()) {
+                       $output  = $this->errorMsg;
+                       $output .= parent::toHTML();
+               } else {
+                       $output = parent::toHTML();
+               }
+               return $output;
+       }
+
+       //      }}}
+
+}
diff --git a/Toolkit/Contacts/ContactUs.php b/Toolkit/Contacts/ContactUs.php
new file mode 100755 (executable)
index 0000000..5a52256
--- /dev/null
@@ -0,0 +1,1137 @@
+<?php
+//    vim:set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker syntax=php:
+
+/**
+ * File Doc Comment
+ *
+ * PHP version 5
+ *
+ * @category Toolkit
+ * @package  Contacts
+ * @author   Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @license  http://www.gaslightmedia.com Gaslightmedia
+ * @version  CVS: $Id: ContactUs.php,v 1.36 2010/07/14 23:33:15 jamie Exp $
+ * @link     http://demo.gaslightmedia.com
+ */
+
+/**
+ * Error codes for Toolkit_Contacts_ContactUs
+ *
+ * Codes are mapped to textual messaged by errorMessage() method,
+ * if you add a new code be sure to add a new message for it to errorMessage()
+ *
+ * @see Toolkit_Contacts_ContactUs::errorMessage()
+ */
+define('FORM_OK', 1);
+define('FORM_ERROR', -1);
+define('NO_RECORD', -2);
+define('INVALID_DB', -3);
+define('MISSING_CONSTANT', -4);
+define('MISSING_CONTACT_TYPE', -5);
+
+/**
+ * GLM Contact Us form
+ *
+ * This form handles rendering and processing the contact us form.
+ * Controls the email functionality of the form, whether the client
+ * has a contact DB to store contact records and how to insert/update
+ * submitted form values.
+ *
+ * @category  Toolkit
+ * @package   Contacts
+ * @author    Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @link      http://demo.gaslightmedia.com
+ * @see       Toolkit_FormBuilder
+ */
+class Toolkit_Contacts_ContactUs
+    extends Toolkit_FormBuilder implements Toolkit_Form
+{
+
+    /**
+     * Table in Database which holds the contact data
+     *
+     * @var    string
+     * @access public
+     */
+    public $tableName = 'contact';
+
+    /**
+     * Table meta data
+     *
+     * This is used when inserting/updating data for the records
+     * so the PDO's can use explicit data types for the parameters.
+     *
+     * @var    array
+     * @access public
+     */
+    public $tableMetaData = array();
+
+    /**
+     * Contact type to be inserted into the DB as when the form is submitted
+     *
+     * This property is only valid when the [hasContactDB] property is set
+     * to true.
+     *
+     * N.B.
+     * If you subclass this class out to other forms that are
+     * inserted into the contact db, be sure to make each one of their
+     * contactType properties unique.  We don't check for duplicates.
+     *
+     * @var string
+     * @access protected
+     */
+    protected $contactType = '1';
+    /**
+     * which groups to use on form if empty will grab all groups
+     */
+    protected $contactInqGroups = array();
+
+    /**
+     * Who to send the email to when the contact form is submitted
+     *
+     * If you leave this blank, its value will get set to the OWNER_EMAIL
+     * in the constructor.
+     *
+     * If you ***DO NOT*** want any emails to go out when the form is submitted
+     * then set the value to false. Do not set it to 0 for false, because the
+     * check uses a strict type check to determine if the value is actually
+     * false. This is what allows for the empty value as an option, which sets
+     * the value to OWNER_EMAIL and won't override the $email property if
+     * this class gets subclassed and the value for this property gets set in
+     * the properties of the subclass and not in the constructor after this
+     * constructor function is called.
+     *
+     * tongue twister...I know.
+     * <code>
+     * protected $email = false;
+     * </code>
+     *
+     * @var    string
+     * @access protected
+     */
+    protected $email;
+
+    /**
+     * From header in the owner email
+     *
+     * This just sets the From header in the owner email
+     * SITENAME <from@email.com>
+     *
+     * It gets set to the constant SITENAME in the constructor if you leave
+     * empty here, but you can set it to something different here to override
+     * that if you desire.
+     *
+     * @var    string
+     * @access protected
+     */
+    protected $siteName;
+
+    /**
+     * Email subject and <h1> header in email
+     *
+     * It gets set in the constructor if you leave empty here, but you
+     * can set it to something different here to override that if you desire.
+     *
+     * @var    string
+     * @access protected
+     */
+    public $subject;
+
+    /**
+     * Whether the site has a contact DB to store contact records
+     *
+     * If this value is set to false, an email will still be sent to the
+     * addressed specified in the email property.
+     *
+     * @var    boolean
+     * @access protected
+     */
+    protected $hasContactDB = true;
+
+    /**
+     * The interests from the contact db
+     *
+     * @var    array
+     * @access protected
+     */
+    protected $inquiries = array();
+
+    /**
+     * Message to display if the form is successfully submitted
+     *
+     * @var    string
+     * @access protected
+     */
+    protected $successMsg = '
+        <style type="text/css">
+        #category {display:none;}
+        .listing {display:none;}
+        </style>
+        <div id="form-success-top">
+            Thank you for your questions or comments!
+        </div>';
+
+    /**
+     * Extra rules for processesing
+     *
+     * This registers the Zip validation rules (and any others listed) for
+     * QuickForm.
+     *
+     * Zip validation checks both US and Canadian Zip codes
+     *
+     * @var    array
+     * @access protected
+     * @see    app.gaslightmedia.com/glmPEAR/HTML/QuickForm/Rules/Zip.php
+     */
+    protected $registeredRules = array(
+        'zip',
+        'phone',
+        array(
+            'checkEmail',
+            'callback',
+            'email',
+            'Validate'
+        ),
+    );
+
+    /**
+     * Options for flexy templating engine
+     *
+     * Pulls the preset options from the setup.phtml file
+     * overwrites the templateDir and compileDir to match this classes needs
+     *
+     * @var    array
+     * @access protected
+     */
+    protected $flexyOptions;
+
+    /**
+     * FormTemplate to use
+     *
+     * @var string
+     */
+    public $formTemplate = 'contactForm.html';
+
+    /**
+     * Class constructor
+     *
+     * @param object $pdo         PHP Data Object
+     * @param string $formName    Form's name.
+     * @param string $method      (optional)Form's method defaults to 'POST'
+     * @param string $action      (optional)Form's action
+     * @param string $target      (optional)Form's target defaults to '_self'
+     * @param mixed  $attributes  (optional)Extra attributes for <form> tag
+     * @param bool   $trackSubmit (optional)Whether to track if the form was
+     *                              submitted by adding a special hidden field
+     *
+     * @author Jamie Kahgee <jamie.kahgee@gmail.com>
+     * @access public
+     * @link   http://pear.php.net/package/HTML_QuickForm/docs/latest/HTML_QuickForm/HTML_QuickForm.html
+     * @see    HTML_QuickForm
+     */
+    public function __construct(
+        PDO $pdo,
+        $formName,
+        $method = 'post',
+        $action = '',
+        $target = '',
+        $attributes = null,
+        $trackSubmit = false
+    ) {
+        //    Make sure these end cases are never met.
+        //    These are more for the development phase.
+        if ($this->email === false && $this->hasContactDB === false) {
+            PEAR::raiseError(
+                null,
+                NO_RECORD,
+                PEAR_ERROR_CALLBACK,
+                array(&$this, 'error')
+            );
+        }
+        if (!is_bool($this->hasContactDB)) {
+            PEAR::raiseError(
+                null,
+                INVALID_DB,
+                PEAR_ERROR_CALLBACK,
+                array(&$this, 'error')
+            );
+        }
+        //    we haven't defined the FROM_NEWS_EMAIL constant and
+        //    the email will default to relying on it.
+        if (   !defined('FROM_NEWS_EMAIL')
+            && (empty($this->email) && $this->email !== false)
+        ) {
+            PEAR::raiseError(
+                'You need to define the FROM_NEWS_EMAIL
+                 constant for this form to work correctly',
+                MISSING_CONSTANT,
+                PEAR_ERROR_CALLBACK,
+                array(&$this, 'error')
+            );
+        }
+        if (!is_numeric($this->contactType) && $this->hasContactDB) {
+            PEAR::raiseError(
+                null,
+                MISSING_CONTACT_TYPE,
+                PEAR_ERROR_CALLBACK,
+                array(&$this, 'error')
+            );
+        }
+        parent::__construct(
+            $formName,
+            $method,
+            $action,
+            $target,
+            $attributes,
+            $trackSubmit
+        );
+
+        $this->dbh = $pdo;
+
+        $conf = new Config;
+        $c =& $conf->parseConfig(
+            BASE . 'Toolkit/Contacts/config.ini',
+            'IniFile'
+        );
+        $table = $c->getItem('section', 'conf')
+            ->getItem('directive', 'table')
+            ->getContent();
+        $this->tableName = $table;
+
+        //    Default Renderer Form templates for QuickForm in respect to this
+        //    classes needs.
+        if ($this->email !== false && empty($this->email)) {
+            //    Set to false to turn off email function.
+            $this->email = CONTACT_EMAIL;
+        }
+        if (empty($this->siteName)) {
+            $this->siteName = SITENAME;
+        }
+        if (empty($this->subject)) {
+            $this->subject = 'Contact Request from website ' . SITENAME;
+        }
+
+        /**
+         * Where are the flexy templates stored at for this class.
+         */
+        define('TEMPLATES_DIR', BASE . 'Toolkit/Contacts/templates');
+
+        /**
+         * Where are the compiled flexy templates stored at for this class.
+         */
+        define('COMPILED_DIR', BASE . 'Toolkit/Contacts/templates/compiled');
+        $oldUmask = umask(0);
+        if (!is_dir(TEMPLATES_DIR)) {
+            mkdir(TEMPLATES_DIR, 0770, true);
+        }
+        if (!is_dir(COMPILED_DIR)) {
+            mkdir(COMPILED_DIR, 0770, true);
+        }
+        umask($oldUmask);
+
+        $this->flexyOptions                = $GLOBALS['flexyOptions'];
+        $this->flexyOptions['templateDir'] = TEMPLATES_DIR;
+        $this->flexyOptions['compileDir']  = COMPILED_DIR;
+
+        $var = basename(__FILE__, '.php');
+
+        $callbackUrl = ($_SERVER['HTTPS'] == 'on') ?
+                              BASE_SECURE_URL : MEDIA_BASE_URL;
+
+        $this->captchaOptions = array(
+            'width' => 100,
+            'height' => 50,
+            'callback' => "{$callbackUrl}Toolkit/qfcaptcha.php?var=$var",
+            'sessionVar' => $var,
+            'imageOptions' => array(
+                'font_size' => 20,
+                'font_path' => GLM_APP_BASE . 'glmPEAR/Image/Canvas/Fonts/',
+                'font_file' => 'times.ttf',
+                'background_color' => '#cccccc',
+                'obfuscation' => false,
+                'angle' => true,
+            ),
+        );
+            $this->useCaptcha(true);
+    }
+
+    /**
+     * Constant variables for the form
+     *
+     * These values won't get overridden by POST or GET vars
+     *
+     * @return void
+     * @access public
+     */
+    public function configureConstants()
+    {
+        $constants = array(
+            'user_agent' => $_SERVER['HTTP_USER_AGENT'],
+            'remote_addr' => $_SERVER['REMOTE_ADDR'],
+        );
+        $this->setupConstants($constants);
+    }
+
+    /**
+     * Initializes default form values
+     *
+     * @return void
+     * @access public
+     */
+    public function configureDefaults()
+    {
+        $defaults = array(
+            'state' => '',
+            'mail_ok' => 1
+        );
+        if (defined("MEMBERS_DB") && MEMBERS_DB) {
+            $defaults['members'] = 1;
+        }
+
+        $this->setupDefaults($defaults);
+    }
+
+    /**
+     * Form element definitions
+     *
+     * @return void
+     * @access public
+     */
+    public function configureElements()
+    {
+        $e = array();
+        if ($this->hasContactDB) {
+            $this->setInterestFields();
+            //    Grouped Elements are defined here.
+            $this->interestsGroups =& $this->getInterestFields();
+        }
+
+        //    All Elements are created here.  This includes group element definitions.
+        $e[] = array(
+            'type' => 'hidden',
+            'req'  => false,
+            'name' => 'user_agent'
+        );
+        $e[] = array(
+            'type' => 'hidden',
+            'req'  => false,
+            'name' => 'remote_addr'
+        );
+        $e[] = array(
+            'type'    => 'header',
+            'name'    => 'firstHdr_rmv',
+            'display' => ''
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => 'fname',
+            'display' => 'First Name'
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => 'lname',
+            'display' => 'Last Name'
+        );
+        if ($this->tableName == 'customer') {
+            $add1 = 'add1';
+            $add2 = 'add2';
+        } else {
+            $add1 = 'address';
+            $add2 = 'address2';
+        }
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => $add1,
+            'display' => 'Address 1'
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => $add2,
+            'display' => 'Address 2'
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => 'city',
+            'display' => 'City'
+        );
+        $e[] = array(
+            'type'    => 'select',
+            'req'     => false,
+            'name'    => 'state',
+            'display' => 'State/Province',
+            'opts'    => $GLOBALS['states']
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => 'zip',
+            'display' => 'ZIP/Postal Code'
+        );
+        $e[] = array(
+            'type'    => 'email',
+            'req'     => true,
+            'name'    => 'email',
+            'display' => 'Email'
+        );
+        $e[] = array(
+            'type'    => 'email',
+            'req'     => true,
+            'name'    => 'email_rmv',
+            'display' => 'Verify Email'
+        );
+        $e[] = array(
+            'type'    => 'tel',
+            'req'     => false,
+            'name'    => 'phone',
+            'display' => 'Phone'
+        );
+        $e[] = array(
+            'type'    => 'tel',
+            'req'     => false,
+            'name'    => 'fax',
+            'display' => 'Fax'
+        );
+        $e[] = array(
+            'type'    => 'textarea',
+            'req'     => false,
+            'name'    => 'comments',
+            'display' => 'Comments'
+        );
+        if ($this->hasContactDB) {
+            $e[] = array(
+                'type'    => 'advcheckbox',
+                'req'     => false,
+                'name'    => 'mail_ok',
+                'display' => '',
+                'opts'    => 'Yes, I would like to receive Email Newsletters',
+                'val'     => array(0, 1)
+            );
+            if (defined("MEMBERS_DB") && MEMBERS_DB) {
+                $e[] = array(
+                    'type'    => 'advcheckbox',
+                    'req'     => false,
+                    'name'    => 'members',
+                    'display' => '',
+                    'opts'    => 'Yes, I would like to receive information via e-mail from '.SITENAME.' Members',
+                    'val'     => array(0, 1)
+                );
+            }
+        }
+        if (is_array($this->interestsGroups)) {
+            foreach ($this->interestsGroups as $group => $gData) {
+                $this->myGroups[] = $gData;
+                $e[] = array(
+                    'type'       => 'group',
+                    'req'        => false,
+                    'name'       => 'interest['.$group.']',
+                    'group'         => $gData,
+                    'label'      => $group,
+                    'seperator'  => ' ',
+                    'appendName' => true
+                );
+            }
+        }
+        $e[] = array(
+            'type'    => 'CAPTCHA_Image',
+            'req'     => false,
+            'name'    => 'captcha_question',
+            'display' => 'Verification code',
+            'opts'    => $this->captchaOptions
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => true,
+            'name'    => 'captcha_rmv',
+            'display' => 'Enter verification code'
+        );
+        $e[] = array(
+            'type'    => 'submit',
+            'req'     => false,
+            'name'    => 'submit_rmv',
+            'display' => 'Submit Form'
+        );
+
+        $this->setupElements($e);
+    }
+
+    /**
+     * Form rule definitions
+     *
+     * Adds validation rules for the given fields
+     *
+     * @return void
+     * @access public
+     */
+    public function configureRules()
+    {
+        $r = array();
+        //    Form Rules
+        $r[] = array(
+            'element'    => array('email', 'email_rmv'),
+            'message'    => 'ERROR: Your Email Addresses Do Not Match!',
+            'type'       => 'compare',
+            'format'     => null,
+            'validation' => $this->validationType,
+            'reset'      => true,
+            'force'      => false
+        );
+        $r[] = array(
+            'element'    => 'email',
+            'message'    => 'ERROR: Invalid Email Format!',
+            'type'       => 'checkEmail',
+            'format'     => array('use_rfc822' => true),
+            'validation' => $this->validationType,
+            'reset'      => true,
+            'force'      => false
+        );
+        /*
+        $r[] = array(
+            'element'    => 'state',
+            'message'    => 'ERROR: Invalid State!',
+            'type'       => 'numeric',
+            'format'     => null,
+            'validation' => $this->validationType,
+            'reset'      => true,
+            'force'      => false
+        );
+        */
+        $r[] = array(
+            'element'    => 'phone',
+            'message'    => 'ERROR: Invalid number (xxx) xxx-xxxx!',
+            'type'       => 'phone',
+            'format'     => null,
+            'validation' => $this->validationType,
+            'reset'      => true,
+            'force'      => false
+        );
+        $r[] = array(
+            'element'    => 'fax',
+            'message'    => 'ERROR: Invalid number (xxx) xxx-xxxx!',
+            'type'       => 'phone',
+            'format'     => null,
+            'validation' => $this->validationType,
+            'reset'      => true,
+            'force'      => false
+        );
+        /*
+        $r[] = array(
+            'element'    => 'zip',
+            'message'    => 'ERROR: Invalid Zip!',
+            'type'       => 'zip',
+            'format'     => array('requireDBCheck' => false),
+            'validation' => $this->validationType,
+            'reset'      => true,
+            'force'      => false
+        );
+        */
+        $r[] = array(
+            'element'    => 'mail_ok',
+            'message'    => 'ERROR: Invalid Value!',
+            'type'       => 'numeric',
+            'format'     => null,
+            'validation' => $this->validationType,
+            'reset'      => true,
+            'force'      => false
+        );
+        $r[] = array(
+            'element'    => 'captcha_rmv',
+            'message'    => 'ERROR: Incorrect verification code!',
+            'type'       => 'CAPTCHA',
+            'format'     => $this->captchaQuestion,
+            'validation' => $this->validationType,
+            'reset'      => true,
+            'force'      => false
+        );
+
+        $this->setupRules($r);
+    }
+
+    /**
+     * Form filter definitions
+     *
+     * Applies a data filter for the given fields when the form is submitted
+     *
+     * @return void
+     * @access public
+     */
+    public function configureFilters()
+    {
+        $f = array();
+
+        $f[] = array(
+            'element' => '__ALL__',
+            'filter' => 'trim'
+        );
+
+        $this->setupFilters($f);
+    }
+
+    /**
+     * Helper function, configures the entire form
+     *
+     * @return void
+     * @access public
+     */
+    public function configureForm()
+    {
+        $this->configureElements();
+        $this->configureFilters();
+        $this->configureRules();
+        $this->configureDefaults();
+        $this->configureConstants();
+    }
+
+    /**
+     * Checks for a duplicate email address already in the DB.
+     *
+     * @param string $email The email address to check for in the DB
+     *
+     * @return boolean If the email address already exists
+     * @access protected
+     */
+    protected function contactExists($email)
+    {
+        //    Check if a contact w/ the submitted email already exists.
+        //    if so, then we need to update, if not then do a fresh insert.
+        try {
+            $sql = "
+                SELECT count(*) AS count
+                  FROM {$this->tableName}
+                 WHERE email   = :email";
+
+            $stmt = $this->dbh->prepare($sql);
+            $stmt->bindParam(':email', $email, PDO::PARAM_STR);
+            $stmt->execute();
+            $stmt->bindColumn('count', $contactExists);
+            $stmt->fetch();
+
+            return (bool) $contactExists;
+        } catch (PDOException $e) {
+            return Toolkit_Common::handleError($e);
+        }
+    }
+
+    /**
+     * Check for a contact or customer with given email address if found
+     * it returns the contact or customer id
+     *
+     * @param type $email Matching email for contact or customer
+     *
+     * @return integer
+     */
+    protected function getContactIdByEmail($email)
+    {
+        try {
+            $id = ($this->tableName == 'contact')
+                ? 'id'
+                : 'cust_id';
+            $sql = "
+            SELECT {$id}
+              FROM {$this->tableName}
+             WHERE email = :email";
+            $stmt = $this->dbh->prepare($sql);
+            $stmt->bindParam(':email', $email, PDO::PARAM_STR);
+            $stmt->execute();
+            return $stmt->fetchColumn();
+        } catch (PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+    }
+
+    /**
+     * Emails the owner the submitted data from the submitted form
+     *
+     * Uses a flexy template to render a nice looking html email.
+     * Fills in the supplied data from the form and doesn't add the
+     * empty fields the user didn't fill in.
+     *
+     * @param string $mailFactory What type of mail factory should we use
+     *
+     * @return boolean result of the mailing
+     * @access protected
+     */
+    protected function emailOwner($mailFactory = 'mail')
+    {
+        if (!$this->email) {
+            return;
+        }
+
+        $template = new HTML_Template_Flexy($this->flexyOptions);
+        $page     = new stdClass();
+        // for comments textarea need to replace newlines with br
+        $this->formData['comments']['element']
+            = nl2br($this->getSubmitValue('comments'));
+
+        $userEmail = $this->getSubmitValue('email');
+        $page->email_from  = GLM_TOOLBOX::valid_email(($userEmail))
+            ? $userEmail
+            : OWNER_EMAIL;
+        $page->subject     = $this->subject;
+        $page->client_info = $this->clientInfo;
+        $page->fname       = $this->getSubmitValue('fname');
+        $page->lname       = $this->getSubmitValue('lname');
+        $page->formData    = $this->formData;
+
+        if (   isset($this->newInterest)
+            && is_array($this->newInterest)
+            && !empty($this->newInterest)
+        ) {
+            //    Clean up the interests so they will look nice in the email
+            $page->formData['interest']['nowrap']  = 'nowrap';
+            $page->formData['interest']['element'] = null;
+
+            foreach ($this->newInterest as $group => $elements) {
+                $page->formData['interest['.$group.']']['label'] = $group;
+                foreach ($elements as $e => $vale) {
+                    $interest[] = $this->inquiries[$group][$e];
+                }
+                $page->formData['interest['.$group.']']['element']
+                    = implode("<br>", $interest);
+                unset($interest);
+            }
+        }
+
+        if ($this->elementExists('mail_ok')) {
+            //    Clean up the mail_ok flag so its human readable
+            $page->formData['mail_ok']['label'] = 'Emails';
+            $page->formData['mail_ok']['element']
+                = ($page->formData['mail_ok']['element']) ? 'Yes' : 'No';
+        }
+
+        if (defined("MEMBERS_DB") && MEMBERS_DB) {
+            if ($this->elementExists('members')) {
+                //    Clean up the mail_ok flag so its human readable
+                $page->formData['members']['label'] = 'Members';
+                $page->formData['members']['element']
+                    = ($page->formData['members']['element']) ? 'Yes' : 'No';
+            }
+        }
+
+        $template->compile('emailOwner.tpl');
+        $htmlMsg = $template->bufferedOutputObject($page);
+
+        if ($hasInterestField) {
+            //    Text version can't have HTML in it
+            //    so reset the interests to use commas
+            $page->formData['interest']['element'] = implode(", ", $interests);
+        }
+        $msg  = "{$page->subject}\n\n";
+        $msg .= "From {$page->fname} {$page->lname}\n\n";
+        $msg .= "Information\n\n";
+        foreach ($page->formData as $i) {
+            $msg .= "{$i['label']}: {$i['element']}\n";
+        }
+
+        $mimeMail = new Mail_mime("\n");
+        $fromName = preg_replace(
+            '/[^A-Z ]/i', '',
+            $page->fname.' '.$page->lname
+        );
+        $mimeMail->setFrom("Website Form <".SERVER_FROM_EMAIL.">");
+        $mimeMail->setSubject($this->subject);
+        $mimeMail->setHTMLBody($htmlMsg);
+        $mimeMail->setTXTBody($msg);
+
+        $mail =& Mail::factory($mailFactory);
+        $body = $mimeMail->get();
+        $setHeader['Reply-To'] = sprintf(
+            '%1$s %2$s <%3$s>',
+            trim($this->getSubmitValue('fname'), "\r"),
+            trim($this->getSubmitValue('lname'), "\r"),
+            trim($this->getSubmitValue('email'), "\r")
+        );
+
+        $headers = $mimeMail->headers($setHeader);
+
+        $res = $mail->send($this->email, $headers, $body);
+        if (PEAR::isError($res)) {
+            return Toolkit_Common::handleError($res);
+        } else {
+            return $res;
+        }
+    }
+
+    /**
+     * Display errors with the form to the user
+     *
+     * @param object $error PEAR Error Object
+     *
+     * @return void
+     * @access public
+     */
+    public function error($error)
+    {
+        //    make the variables static so that is only has to do the defining
+        //    on the first call.
+        static $errorMessages;
+        static $className;
+
+        //    define the various error messages
+        if (!isset($errorMessages)) {
+            $className     = get_class($this);
+            $errorMessages = array(
+                FORM_OK              => 'No error',
+                FORM_ERROR           => 'Unknown error',
+                NO_RECORD            => 'Setting both properties (email and hasContactDB) to false in the <i>' . $className . '</i> class will result in no email record being sent and no record being inserted into the database.  There will be no record that this form was ever filled out.',
+                INVALID_DB           => 'Please set the property hasContactDB to a boolean value in the class <i>' . $className . '</i> before continuing',
+                MISSING_CONSTANT     => 'You have failed to set a CONSTANT for the class <i>' . $className . '</i> to function properly',
+                MISSING_CONTACT_TYPE => 'Please set the property contactType in the class <i>'. $className . '</i> before continuing',);
+        }
+
+        if (!is_null($error->message)) {
+            $message = $error->message;
+        } elseif (isset($errorMessages[$error->code])) {
+            $message = $errorMessages[$error->code];
+        } else {
+            $message = $errorMessages[$error->code];
+            $message = $errorMessages[FORM_ERROR];
+        }
+
+        echo "<div id=\"form-warning-top\">{$message}</div>";
+    }
+
+    /**
+     * Returns the field definitions of the contact db interest fields
+     *
+     * @return array     Group definitions for the interest fields
+     * @access protected
+     */
+    protected function getInterestFields()
+    {
+        if (is_array($this->inquiries)) {
+            foreach ($this->inquiries as $group => $data) {
+                foreach ($data as $k => $v) {
+                    $interests[$group][] = array('type' => 'checkbox',
+                                             'req' => false,
+                                             'name' => $k,
+                                             'opts' => $v);
+                }
+            }
+        }
+
+        return $interests;
+    }
+
+    /**
+     * Handles how to process the form when submitted
+     *
+     * @param array $values Form submitted values
+     *
+     * @return array     Result of Insert / Update function
+     * @access protected
+     */
+    public function processData($values)
+    {
+        unset($values['catid']);
+        //    Form data used for the insert/update sql queries and
+        //    the form email.
+        $e = array(
+            'user_agent',
+            'remote_addr',
+            'contact_type',
+        );
+        $this->setFormData($e);
+
+
+        //    If no contact db, return true so we can send owner email.
+        if (!$this->hasContactDB) {
+            return true;
+        }
+
+        //  Get rid of any elements in the values array that
+        //  aren't going to be used when inserting/updating the db.
+        $values = Toolkit_Common::cleanArray($values);
+
+        //  Check if we are updating an existing contact or not
+        //  question: is this the best approach?  what if someone else
+        //  sends a form through w/ different data but the same emil addy
+        //  the old users data can be completely wiped out.
+        $existingContact = $this->contactExists($values['email']);
+        // setup newInterest aray with the interest values
+        // because their grouped can't be found with getElement('interest')
+        $this->newInterest = $values['interest'];
+        // update create_date for this contact
+        $values['create_date'] = date('m/d/Y');
+
+        // set up interest field values
+        if (is_array($values['interest']) && !empty($values['interest'])) {
+            foreach ($values['interest'] as $iGroup => $interests) {
+                $newInterest[] = implode(':', array_keys($interests));
+            }
+            if (is_array($newInterest)) {
+                $values['interest'] = ':' . implode(':', $newInterest) . ':';
+            }
+        }
+        if ($existingContact) {
+            $contactId = $this->getContactIdByEmail($values['email']);
+            $cType = $this->_getExistingContactTypes($contactId);
+            $idFieldName
+                = ($this->tableName == 'contact')
+                ? 'id'
+                : 'cust_id';
+            $values[$idFieldName] = $contactId;
+            $existingTypes
+                = (is_array($cType))
+                ? explode(':', $cType)
+                : array();
+            if (!in_array($this->contactType, $existingTypes)) {
+                $values['contact_type'] = "$cType{$this->contactType}:";
+            } else {
+                $values['contact_type'] = ":".implode(':', array_filter($existingTypes)).":";
+            }
+        } else {
+            $values['contact_type'] = ":{$this->contactType}:";
+        }
+        // build a contact or customer class object with the values from
+        // the Contact form
+        if ($this->tableName == 'contact') {
+            $contact = Toolkit_LeadManager_Contact::createByValues($values);
+        } else {
+            $contact = Toolkit_LeadManager_Customer::createByValues($values);
+        }
+        if ($contact->getMailOk()) {
+            if (   defined('STREAMSEND_FORMS_API')
+                && STREAMSEND_FORMS_API) {
+                $contact->attach(new Toolkit_LeadManager_StreamSend());
+            }
+            if (    defined('CONSTANT_CONTACT')
+                && CONSTANT_CONTACT) {
+                $contact->attach(new Toolkit_LeadManager_ConstantContact());
+            }
+        }
+        $contact->save($this->dbh, false);
+        return true;
+    }
+
+    /**
+     * Contact DB interests
+     *
+     * @return void
+     * @access protected
+     */
+    protected function setInterestFields()
+    {
+        try {
+            $groupWhere
+                = (!empty($this->contactInqGroups))
+                ? "WHERE contact_inq.groupid IN (".implode(',',$this->contactInqGroups).")"
+                : "";
+            $sql = "
+                SELECT contact_inq.*, inq_group.name
+                  FROM contact_inq
+                    LEFT OUTER JOIN inq_group
+                    ON (contact_inq.groupid = inq_group.id)
+                    $groupWhere
+                 ORDER BY inq_group.name, contact_inq.pos";
+
+            $i = array();
+            foreach ($this->dbh->query($sql) as $row) {
+                $i[$row['name']][$row['id']] = $row['header'];
+            }
+
+            $this->inquiries = $i;
+        } catch (PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+    }
+
+    //  @codeCoverageIgnoreStart
+
+    /**
+     * Custom rendering templates for special fields on the form
+     *
+     * @return void
+     * @access protected
+     */
+    protected function setupRenderers()
+    {
+        $formConfig = new Zend_Config_Ini(
+            BASE . 'Toolkit/Contacts/application.ini',
+            strtolower($_ENV['GLM_HOST_ID'])
+        );
+
+        $renderer = new HTML_QuickForm_Renderer_Object(true);
+        $this->accept($renderer);
+        $this->template = new HTML_Template_Flexy(
+            $formConfig->flexyOptions->toArray()
+        );
+
+        $this->view = $this;
+        $this->view->form = $renderer->toObject();
+        $this->template->compile($this->formTemplate);
+    }
+
+    //  @codeCoverageIgnoreEnd
+
+    /**
+     * Handles how to display the current step the user is at in the form
+     *
+     * destroying and resetting the captcha value dis-allows someone from
+     * re-sending a form on a previous captcha.
+     *
+     * @return string form HTML state
+     * @access public
+     */
+    public function toHtml()
+    {
+        if ($this->validate()) {
+            $this->captchaQuestion->destroy();
+            $this->cleanForm();
+            $this->setupRenderers();
+            if ($this->process(array(&$this, 'processData'), $this->mergeFiles)) {
+                $this->freeze();
+                $this->emailOwner();
+                $output = $this->successMsg;
+            }
+            $this->sent = true;
+        } elseif ($this->isSubmitted()) {
+            $this->captchaQuestion->destroy();
+            $this->captchaAnswer->setValue('');
+            $this->setupRenderers();
+            $output  = $this->errorMsg;
+            $output .= $this->template->bufferedOutputObject($this->view);
+        } else {
+            $this->captchaQuestion->destroy();
+            $this->captchaAnswer->setValue('');
+            $this->setupRenderers();
+            $output .= $this->template->bufferedOutputObject($this->view);
+        }
+        return $output;
+    }
+
+    /**
+     * Return the existing contact/customer contact_type
+     *
+     * @param type $contactId id of cust/contact
+     *
+     * @return type
+     */
+    protected function _getExistingContactTypes($contactId)
+    {
+        try {
+            $idField
+                = ($this->tableName == 'contact')
+                ? 'id'
+                : 'cust_id';
+            $sql = "
+            SELECT contact_type
+              FROM {$this->tableName}
+             WHERE {$idField} = :id";
+            $stmt = $this->dbh->prepare($sql);
+            $stmt->bindParam(':id', $contactId, PDO::PARAM_STR);
+            $stmt->execute();
+            return $stmt->fetchColumn();
+        } catch (PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+    }
+
+}
+
diff --git a/Toolkit/Contacts/Database/application.sql b/Toolkit/Contacts/Database/application.sql
new file mode 100644 (file)
index 0000000..f219edd
--- /dev/null
@@ -0,0 +1,15 @@
+CREATE SCHEMA contacts;
+GRANT ALL ON SCHEMA contacts TO nobody;
+
+--
+-- Tables
+--
+\i ./tables/contact.sql
+\i ./tables/contact_inq.sql
+\i ./tables/inq_group.sql
+\i ./tables/query_db.sql
+\i ./tables/news_response.sql
+
+--
+-- Procedures
+--
diff --git a/Toolkit/Contacts/Database/removeApplication.sql b/Toolkit/Contacts/Database/removeApplication.sql
new file mode 100644 (file)
index 0000000..f9c8178
--- /dev/null
@@ -0,0 +1,8 @@
+--
+--     This will drop everything in the contacts schema.
+--     Nothin better be in here except contact related object
+--     or it will be dropp
+--
+--     The force is strong w/ this one, use it wisely.
+--
+DROP SCHEMA IF EXISTS contacts CASCADE;
diff --git a/Toolkit/Contacts/Database/tables/contact.sql b/Toolkit/Contacts/Database/tables/contact.sql
new file mode 100644 (file)
index 0000000..fd43ab4
--- /dev/null
@@ -0,0 +1,45 @@
+DROP TABLE IF EXISTS contacts.contact CASCADE;
+
+CREATE TABLE contacts.contact
+(id SERIAL,
+ create_date DATE DEFAULT CURRENT_DATE,
+ fname text,
+ lname text,
+ company text,
+ address text,
+ address2 text,
+ city text,
+ state text,
+ zip text,
+ country text,
+ phone text,
+ fax text,
+ email text,
+ user_agent text,
+ remote_addr text,
+ interest text,
+ mail_ok BOOLEAN DEFAULT FALSE,
+ visitorguide BOOLEAN DEFAULT FALSE,
+ uidpdf text,
+ visitorguide_download BOOLEAN DEFAULT FALSE,
+ email_verified BOOLEAN DEFAULT true,
+ contact_type text,
+ discover text,
+ member_ok BOOLEAN DEFAULT FALSE,
+ staff BOOLEAN DEFAULT FALSE,
+ comments text,
+ password text,
+ verify_password text,
+ gift_cert BOOLEAN DEFAULT FALSE,
+ members BOOLEAN DEFAULT FALSE,
+ PRIMARY KEY (id));
+
+CREATE INDEX contact_create_date_indx ON contacts.contact USING btree(create_date);
+CREATE INDEX contact_email_indx ON contacts.contact USING btree(email);
+CREATE INDEX contact_fname_indx ON contacts.contact USING btree(fname);
+CREATE INDEX contact_lname_indx ON contacts.contact USING btree(lname);
+CREATE UNIQUE INDEX news_response_id_indx ON contacts.contact USING btree(id);
+CREATE UNIQUE INDEX query_db_id_indx ON contacts.contact USING btree(id);
+
+GRANT ALL ON contacts.contact_id_seq TO nobody;
+GRANT ALL ON contacts.contact TO nobody;
diff --git a/Toolkit/Contacts/Database/tables/contact_inq.sql b/Toolkit/Contacts/Database/tables/contact_inq.sql
new file mode 100644 (file)
index 0000000..410e809
--- /dev/null
@@ -0,0 +1,13 @@
+DROP TABLE IF EXISTS contacts.contact_inq CASCADE;
+
+CREATE TABLE contacts.contact_inq
+(id SERIAL,
+ header text,
+ pos integer,
+ description text,
+ image text,
+ groupid integer,
+ PRIMARY KEY (id));
+
+GRANT ALL ON contacts.contact_inq_id_seq TO nobody;
+GRANT ALL ON contacts.contact_inq TO nobody;
diff --git a/Toolkit/Contacts/Database/tables/inq_group.sql b/Toolkit/Contacts/Database/tables/inq_group.sql
new file mode 100644 (file)
index 0000000..01bcd2a
--- /dev/null
@@ -0,0 +1,11 @@
+DROP TABLE IF EXISTS contacts.inq_group CASCADE;
+
+CREATE TABLE contacts.inq_group
+(id SERIAL,
+ name text,
+ PRIMARY KEY (id));
+
+GRANT ALL ON contacts.inq_group_id_seq TO nobody;
+GRANT ALL ON contacts.inq_group TO nobody;
+
+INSERT INTO contacts.inq_group (name) VALUES ('Interests');
\ No newline at end of file
diff --git a/Toolkit/Contacts/Database/tables/news_response.sql b/Toolkit/Contacts/Database/tables/news_response.sql
new file mode 100644 (file)
index 0000000..a496353
--- /dev/null
@@ -0,0 +1,20 @@
+DROP TABLE IF EXISTS contacts.news_response;
+
+CREATE TABLE contacts.news_response
+(id SERIAL,
+ subject TEXT,
+ response TEXT,
+ image TEXT,
+ image2 TEXT,
+ image3 TEXT,
+ image_align TEXT,
+ image2_align TEXT,
+ image3_align TEXT,
+ mailout DATE DEFAULT CURRENT_DATE,
+ last_update DATE DEFAULT CURRENT_DATE,
+ PRIMARY KEY (id));
+
+GRANT ALL ON contacts.news_response_id_seq TO nobody;
+GRANT ALL ON contacts.news_response TO nobody;
+
+INSERT INTO contacts.news_response (subject, response) VALUES ('subject', 'response');
diff --git a/Toolkit/Contacts/Database/tables/query_db.sql b/Toolkit/Contacts/Database/tables/query_db.sql
new file mode 100644 (file)
index 0000000..3a2a0b3
--- /dev/null
@@ -0,0 +1,12 @@
+DROP TABLE IF EXISTS contacts.query_db;
+
+CREATE TABLE contacts.query_db
+(id SERIAL,
+ query_name TEXT,
+ query TEXT,
+ file TEXT,
+ delimiter text,
+ PRIMARY KEY (id));
+
+GRANT ALL ON contacts.query_db_id_seq TO nobody;
+GRANT ALL ON contacts.query_db TO nobody;
diff --git a/Toolkit/Contacts/ENewsSignup.php b/Toolkit/Contacts/ENewsSignup.php
new file mode 100755 (executable)
index 0000000..957d4ac
--- /dev/null
@@ -0,0 +1,219 @@
+<?php
+//    vim:set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker syntax=php:
+
+/**
+ * File Doc Comment
+ *
+ * PHP version 5
+ *
+ * @category Toolkit
+ * @package  Contacts
+ * @author   Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @license  http://www.gaslightmedia.com Gaslightmedia
+ * @version  CVS: $Id: ContactUs.php,v 1.36 2010/07/14 23:33:15 jamie Exp $
+ * @link     http://demo.gaslightmedia.com
+ */
+
+/**
+ * Error codes for Toolkit_Contacts_ContactUs
+ *
+ * Codes are mapped to textual messaged by errorMessage() method,
+ * if you add a new code be sure to add a new message for it to errorMessage()
+ *
+ * @see Toolkit_Contacts_ContactUs::errorMessage()
+ */
+
+/**
+ * GLM Contact Us form
+ *
+ * This form handles rendering and processing the contact us form.
+ * Controls the email functionality of the form, whether the client
+ * has a contact DB to store contact records and how to insert/update
+ * submitted form values.
+ *
+ * @category  Toolkit
+ * @package   Contacts
+ * @author    Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @link      http://demo.gaslightmedia.com
+ * @see       Toolkit_FormBuilder
+ */
+class Toolkit_Contacts_ENewsSignup
+    extends Toolkit_Contacts_ContactUs
+{
+    /**
+     * Contact type to be inserted into the DB as when the form is submitted
+     *
+     * This property is only valid when the [hasContactDB] property is set
+     * to true.
+     *
+     * N.B.
+     * If you subclass this class out to other forms that are
+     * inserted into the contact db, be sure to make each one of their
+     * contactType properties unique.  We don't check for duplicates.
+     *
+     * @var string
+     * @access protected
+     */
+    protected $contactType = '2';
+    /**
+     * Email subject and <h1> header in email
+     *
+     * It gets set in the constructor if you leave empty here, but you
+     * can set it to something different here to override that if you desire.
+     *
+     * @var    string
+     * @access protected
+     */
+    public $subject = "ENews Signup Form from website";
+    /**
+     * Message to display if the form is successfully submitted
+     *
+     * @var    string
+     * @access protected
+     */
+    protected $successMsg = '
+        <style type="text/css">
+        #category {display:none;}
+        .listing {display:none;}
+        </style>
+        <div id="form-success-top">
+            Thank you for requesting the ENewsetter.
+        </div>';
+    //    {{{    configureConstats()
+
+    /**
+     * Class constructor
+     *
+     * @param object $pdo         PHP Data Object
+     * @param string $formName    Form's name.
+     * @param string $method      (optional)Form's method defaults to 'POST'
+     * @param string $action      (optional)Form's action
+     * @param string $target      (optional)Form's target defaults to '_self'
+     * @param mixed  $attributes  (optional)Extra attributes for <form> tag
+     * @param bool   $trackSubmit (optional)Whether to track if the form was
+     *                              submitted by adding a special hidden field
+     *
+     * @author Jamie Kahgee <jamie.kahgee@gmail.com>
+     * @access public
+     * @link   http://pear.php.net/package/HTML_QuickForm/docs/latest/HTML_QuickForm/HTML_QuickForm.html
+     * @see    HTML_QuickForm
+     */
+    public function __construct(
+        PDO $pdo,
+        $formName,
+        $method = 'post',
+        $action = '',
+        $target = '',
+        $attributes = null,
+        $trackSubmit = false
+    ) {
+        parent::__construct(
+            $pdo,
+            $formName,
+            $method,
+            $action,
+            $target,
+            $attributes,
+            $trackSubmit
+        );
+        $this->email = ENEWS_EMAIL;
+    }
+
+    /**
+     * Constant variables for the form
+     *
+     * These values won't get overridden by POST or GET vars
+     *
+     * @return void
+     * @access public
+     */
+    public function configureConstants()
+    {
+        $constants = array(
+            'user_agent' => $_SERVER['HTTP_USER_AGENT'],
+            'remote_addr' => $_SERVER['REMOTE_ADDR'],
+            'mail_ok' => 1
+        );
+        $this->setupConstants($constants);
+    }
+
+    //    }}}
+    //    {{{    configureElements()
+
+    /**
+     * Form element definitions
+     *
+     * @return void
+     * @access public
+     */
+    public function configureElements()
+    {
+        $e = array();
+        if ($this->hasContactDB) {
+            $this->setInterestFields();
+            //    Grouped Elements are defined here.
+            $this->interestsGroups =& $this->getInterestFields();
+        }
+
+        //    All Elements are created here.  This includes group element definitions.
+        $e[] = array(
+            'type' => 'hidden',
+            'req'  => false,
+            'name' => 'user_agent'
+        );
+        $e[] = array(
+            'type' => 'hidden',
+            'req'  => false,
+            'name' => 'remote_addr'
+        );
+        $e[] = array(
+            'type'    => 'header',
+            'name'    => 'firstHdr_rmv',
+            'display' => ''
+        );
+        $e[] = array(
+            'type'    => 'email',
+            'req'     => true,
+            'name'    => 'email',
+            'display' => 'Email'
+        );
+        $e[] = array(
+            'type'    => 'email',
+            'req'     => true,
+            'name'    => 'email_rmv',
+            'display' => 'Verify Email'
+        );
+        if ($this->hasContactDB) {
+            $e[] = array(
+                'type'    => 'hidden',
+                'req'     => false,
+                'name'    => 'mail_ok',
+            );
+        }
+        $e[] = array(
+            'type'    => 'CAPTCHA_Image',
+            'req'     => false,
+            'name'    => 'captcha_question',
+            'display' => 'Verification code',
+            'opts'    => $this->captchaOptions
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => true,
+            'name'    => 'captcha_rmv',
+            'display' => 'Enter verification code'
+        );
+        $e[] = array(
+            'type'    => 'submit',
+            'req'     => false,
+            'name'    => 'submit_rmv',
+            'display' => 'Submit Form'
+        );
+
+        $this->setupElements($e);
+    }
+
+    //    }}}
+}
diff --git a/Toolkit/Contacts/SaveTripPlanner.php b/Toolkit/Contacts/SaveTripPlanner.php
new file mode 100755 (executable)
index 0000000..6ad0720
--- /dev/null
@@ -0,0 +1,643 @@
+<?php
+//    vim:set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker syntax=php:
+
+/**
+ * File Doc Comment
+ *
+ * PHP version 5
+ *
+ * @category Toolkit
+ * @package  Contacts
+ * @author   Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @license     http://www.gaslightmedia.com Gaslightmedia
+ * @link     <>
+ */
+
+/**
+ * Ask the concierge form
+ *
+ * This form handles rendering and processing the contact form.
+ *
+ * @category  Toolkit
+ * @package   Contacts
+ * @author    Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @version   CVS: $Id:
+ * @link      <>
+ * @see       Toolkit_FormBuilder
+ */
+class Toolkit_Contacts_SaveTripPlanner
+    extends Toolkit_Contacts_ContactUs
+{
+    //    {{{    properties
+
+    /**
+     * Contact type to be inserted into the DB as when the form is submitted
+     *
+     * This property is only valid when the [hasContactDB] property is set
+     * to true.
+     *
+     * N.B.
+     * If you subclass this class out to other forms that are
+     * inserted into the contact db, be sure to make each one of their
+     * contactType properties unique.  We don't check for duplicates.
+     *
+     * @var string
+     * @access public
+     */
+    public $contactType = '3';
+
+    /**
+     * Who to send the email to when the contact form is submitted
+     *
+     * If you leave this blank, its value will get set to the OWNER_EMAIL
+     * in the constructor.
+     *
+     * If you ***DO NOT*** want any emails to go out when the form is submitted
+     * then set the value to false. Do not set it to 0 for false, because the
+     * check uses a strict type check to determine if the value is actually
+     * false. This is what allows for the empty value as an option, which sets
+     * the value to OWNER_EMAIL and won't override the $email property if
+     * this class gets subclassed and the value for this property gets set in
+     * the properties of the subclass and not in the constructor after this
+     * constructor function is called.
+     *
+     * tongue twister...I know.
+     * <code>
+     * public $email = false;
+     * </code>
+     *
+     * @var    unknown
+     * @access public
+     */
+    public $email = false;
+
+    /**
+     * Message to display if the form is successfully submitted
+     *
+     * @var    string
+     * @access public
+     */
+    public $successMsg = '
+        <div id="form-success-top">
+            You have successfully created a new account.
+        </div>';
+
+    /**
+     * Email subject and <h1> header in email
+     *
+     * It gets set in the constructor if you leave empty here, but you
+     * can set it to something different here to override that if you desire.
+     *
+     * @var    string
+     * @access public
+     */
+    public $subject;
+
+    protected $plannerListPageId;
+    protected $plannerFormPageId;
+    //    }}}
+    //    {{{    __construct()
+
+    /**
+     * Class constructor
+     *
+     * @param string $pdo         PDO object
+     * @param string $formName    Form's name.
+     * @param string $method      (optional)Form's method defaults to 'POST'
+     * @param string $action      (optional)Form's action
+     * @param string $target      (optional)Form's target defaults to '_self'
+     * @param mixed  $attributes  (optional)Extra attributes for <form> tag
+     * @param bool   $trackSubmit (optional)Whether to track if the form was
+     *                              submitted by adding a special hidden field
+     *
+     * @access public
+     */
+    public function __construct(
+        PDO $pdo,
+        $formName,
+        $method = 'post',
+        $action = '',
+        $target = '',
+        $attributes = null,
+        $trackSubmit = false
+    ) {
+        parent::__construct(
+            $pdo,
+            $formName,
+            $method,
+            $action,
+            $target,
+            $attributes,
+            $trackSubmit
+        );
+        $this->subject           = 'Travel List from website ' . SITENAME;
+        $this->plannerListPageId = MEMBER_SESSION_PAGE;
+        $this->plannerFormPageId = MEMBER_SESSION_FORM;
+    }
+
+    //    }}}
+
+    //    {{{    configureElements()
+
+    /**
+     * Form element definitions
+     *
+     * @return void
+     * @access public
+     */
+    public function configureElements()
+    {
+        $e = array();
+        $this->setInterestFields();
+        //    Grouped Elements are defined here.
+        $this->interestsGroups =& $this->getInterestFields();
+
+        //    All Elements are created here.  This includes group element definitions.
+        $e[] = array(
+            'type' => 'hidden',
+            'req'  => false,
+            'name' => 'user_agent'
+        );
+        $e[] = array(
+            'type' => 'hidden',
+            'req'  => false,
+            'name' => 'remote_addr'
+        );
+        $e[] = array(
+            'type'    => 'header',
+            'name'    => 'infoHdr_rmv',
+            'display' => ''
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => 'fname',
+            'display' => 'First Name'
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => 'lname',
+            'display' => 'Last Name'
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => 'address',
+            'display' => 'Address 1'
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => 'address2',
+            'display' => 'Address 2'
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => 'city',
+            'display' => 'City'
+        );
+        $e[] = array(
+            'type'    => 'select',
+            'req'     => false,
+            'name'    => 'state',
+            'display' => 'State/Province',
+            'opts'    => $GLOBALS['states']
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => 'zip',
+            'display' => 'ZIP/Postal Code'
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => true,
+            'name'    => 'email',
+            'display' => 'Email'
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => true,
+            'name'    => 'email_rmv',
+            'display' => 'Verify Email'
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => 'phone',
+            'display' => 'Phone'
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => true,
+            'name'    => 'password',
+            'display' => 'Password'
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => true,
+            'name'    => 'verify_password',
+            'display' => 'Verify Password'
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => 'fax',
+            'display' => 'Fax'
+        );
+        $e[] = array(
+            'type'    => 'advcheckbox',
+            'req'     => false,
+            'name'    => 'mail_ok',
+            'display' => ', I would like to receive Email Newsletters',
+            'opts'    => 'Yes',
+            'val'     => array(0, 1)
+        );
+        if (is_array($this->interestsGroups)) {
+            foreach ($this->interestsGroups as $group => $gData) {
+                $this->myGroups[] = $gData;
+                $e[] = array(
+                    'type'       => 'group',
+                    'req'        => false,
+                    'name'       => 'interest['.$group.']',
+                    'group'         => $gData,
+                    'label'      => $group,
+                    'seperator'  => ' ',
+                    'appendName' => true
+                );
+            }
+        }
+        $e[] = array(
+            'type'    => 'CAPTCHA_Image',
+            'req'     => false,
+            'name'    => 'captcha_question',
+            'display' => 'Verification code',
+            'opts'    => $this->captchaOptions
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => true,
+            'name'    => 'captcha_rmv',
+            'display' => 'Enter verification code'
+        );
+        $e[] = array(
+            'type'    => 'submit',
+            'req'     => false,
+            'name'    => 'submit_rmv',
+            'display' => 'Submit Form'
+        );
+
+        $this->setupElements($e);
+    }
+
+    //    }}}
+    //    {{{    configureRules()
+
+    /**
+     * Form rule definitions
+     *
+     * Adds validation rules for the given fields
+     *
+     * @return void
+     * @access public
+     */
+    public function configureRules()
+    {
+        $r = array();
+        //    Form Rules
+        $r[] = array(
+            'element'    => array('email', 'email_rmv'),
+            'message'    => 'ERROR: Your Email Addresses Do Not Match!',
+            'type'       => 'compare',
+            'format'     => null,
+            'validation' => $this->validationType,
+            'reset'      => true,
+            'force'      => false
+        );
+        $r[] = array(
+            'element'    => array('password', 'verify_password'),
+            'message'    => 'ERROR: Your Passwords do not match!',
+            'type'       => 'compare',
+            'format'     => null,
+            'validation' => $this->validationType,
+            'reset'      => true,
+            'force'      => false
+        );
+        $r[] = array(
+            'element'    => 'email',
+            'message'    => 'ERROR: Invalid Email Format!',
+            'type'       => 'checkEmail',
+            'format'     => array('use_rfc822' => true),
+            'validation' => $this->validationType,
+            'reset'      => true,
+            'force'      => false
+        );
+        /*
+        $r[] = array(
+            'element'    => 'state',
+            'message'    => 'ERROR: Invalid State!',
+            'type'       => 'numeric',
+            'format'     => null,
+            'validation' => $this->validationType,
+            'reset'      => true,
+            'force'      => false
+        );
+        */
+        $r[] = array(
+            'element'    => 'phone',
+            'message'    => 'ERROR: Invalid number (xxx) xxx-xxxx!',
+            'type'       => 'phone',
+            'format'     => null,
+            'validation' => $this->validationType,
+            'reset'      => true,
+            'force'      => false
+        );
+        $r[] = array(
+            'element'    => 'fax',
+            'message'    => 'ERROR: Invalid number (xxx) xxx-xxxx!',
+            'type'       => 'phone',
+            'format'     => null,
+            'validation' => $this->validationType,
+            'reset'      => true,
+            'force'      => false
+        );
+        $r[] = array(
+            'element'    => 'zip',
+            'message'    => 'ERROR: Invalid Zip!',
+            'type'       => 'zip',
+            'format'     => array('requireDBCheck' => false),
+            'validation' => $this->validationType,
+            'reset'      => true,
+            'force'      => false
+        );
+        $r[] = array(
+            'element'    => 'mail_ok',
+            'message'    => 'ERROR: Invalid Value!',
+            'type'       => 'numeric',
+            'format'     => null,
+            'validation' => $this->validationType,
+            'reset'      => true,
+            'force'      => false
+        );
+        $r[] = array(
+            'element'    => 'captcha_rmv',
+            'message'    => 'ERROR: Incorrect verification code!',
+            'type'       => 'CAPTCHA',
+            'format'     => $this->captchaQuestion,
+            'validation' => $this->validationType,
+            'reset'      => true,
+            'force'      => false
+        );
+
+        $this->setupRules($r);
+    }
+
+    //    }}}
+    //  {{{ contactExists()
+
+    /**
+     * Checks for a duplicate email address already in the DB.
+     *
+     * @param string $email The email address to check for in the DB
+     *
+     * @return boolean If the email address already exists
+     * @access protected
+     */
+    protected function contactExists($email)
+    {
+        //    Check if a contact w/ the submitted email already exists.
+        //    if so, then we need to update, if not then do a fresh insert.
+        try {
+            $sql = "
+                SELECT id,password
+                  FROM contact
+                 WHERE email = :email";
+
+            $stmt = $this->dbh->prepare($sql);
+            $stmt->bindParam(':email', $email, PDO::PARAM_STR);
+            $stmt->execute();
+            //$stmt->bindColumn('id', $contactExists);
+            //$stmt->bindColumn('password', $password);
+            $row = $stmt->fetch();
+            //die('<pre>'.print_r($row, true).'</pre>');
+            if (!empty($row)) {
+                return array(
+                        'contactId' => $row['id'],
+                        'password' => $row['password']
+                    );
+            } else {
+                return false;
+            }
+
+        } catch (PDOException $e) {
+            return Toolkit_Common::handleError($e);
+        }
+    }
+
+    //  }}}
+
+    //    {{{    processData()
+
+    /**
+     * Handles how to process the form when submitted
+     *
+     * @param array $values Form submitted values
+     *
+     * @return array     Result of Insert / Update function
+     * @access protected
+     */
+    public function processData($values)
+    {
+        //    Form data used for the insert/update sql queries and
+        //    the form email.
+        $e = array(
+            'user_agent',
+            'remote_addr',
+            'contact_type',
+            'catid',
+            'cPage',
+        );
+        $this->setFormData($e);
+
+
+        //    If no contact db, return true so we can send owner email.
+        if (!$this->hasContactDB) {
+            return true;
+        }
+
+        //  Get rid of any elements in the values array that
+        //  aren't going to be used when inserting/updating the db.
+        $values = Toolkit_Common::cleanArray($values);
+
+        //  Check if we are updating an existing contact or not
+        //  question: is this the best approach?  what if someone else
+        //  sends a form through w/ different data but the same emil addy
+        //  the old users data can be completely wiped out.
+        $existingContact = $this->contactExists($values['email']);
+        // setup newInterest aray with the interest values
+        // because their grouped can't be found with getElement('interest')
+        $this->newInterest = $values['interest'];
+        // update create_date for this contact
+        $values['create_date'] = date('m/d/Y');
+
+        // set up interest field values
+        if (is_array($values['interest']) && !empty($values['interest'])) {
+            foreach ($values['interest'] as $iGroup => $interests) {
+                $newInterest[] = implode(':', array_keys($interests));
+            }
+            if (is_array($newInterest)) {
+                $values['interest'] = ':' . implode(':', $newInterest) . ':';
+            }
+        }
+
+        $values['password'] = md5($values['password']);
+        unset($values['catid']);
+        unset($values['cPage']);
+
+        $authContainer = new Toolkit_Members_TripPlanner_AuthContainer(
+            Toolkit_Database::getInstance(),
+            array(
+                'table'       => 'contact',
+                'usernamecol' => 'email',
+                'passwordcol' => 'password',
+                'db_fields'   => array('id', 'fname', 'lname'),
+                'cryptType'   => 'md5',
+            )
+        );
+
+        $tripPlannerAuth = new Toolkit_Members_TripPlanner_TripAuth(
+            $authContainer,
+            '',
+            false
+        );
+        $tripPlannerAuth->setIdle();
+        $tripPlannerAuth->start();
+        $sessionList = new Toolkit_Members_TripPlanner_Sessions(
+            Toolkit_Database::getInstance(),
+            $tripPlannerAuth
+        );
+        if (is_array($existingContact)) {
+            // if there already in the database then give a message that
+            //  they're already in and need to login
+            // this is true only if they already have a password set
+            if (!$existingContact['password']) {
+                $contactId = $existingContact['contactId'];
+                $cType = $this->_getExistingContactTypes($contactId);
+                $existingTypes
+                    = (is_array($cType))
+                    ? explode(':', $cType)
+                    : array();
+                if (!in_array($this->contactType, $existingTypes)) {
+                    $values['contact_type'] = "$cType{$this->contactType}:";
+                } else {
+                    $values['contact_type'] = ":".implode(
+                        ':', array_filter($existingTypes)
+                    ).":";
+                }
+                // build a contact or customer class object with the values from
+                // the Contact form
+                if ($this->tableName == 'contact') {
+                    $contact = Toolkit_LeadManager_Contact::createByValues($values);
+                } else {
+                    $contact = Toolkit_LeadManager_Customer::createByValues($values);
+                }
+                if (   defined('STREAMSEND_FORMS_API')
+                    && STREAMSEND_FORMS_API
+                    && $values['mail_ok']) {
+                    $contact->attach(new Toolkit_LeadManager_StreamSend());
+                }
+                $contact->save($this->dbh);
+                $tripPlannerAuth->setAuth($values['email']);
+                $tripPlannerAuth->setAuthData('id', $contactId);
+                $tripPlannerAuth->setAuthData('username', $values['email']);
+                $sessionList->saveList($contactId);
+                return true;
+            } else {
+                $seoUrl = Toolkit_Template_Page::getSeoUrl(
+                    $this->_pageGateway, $this->plannerListPageId
+                );
+                $this->errorMsg = "<p>Your Email address already exist. Please "
+                    . "<a href=\"$seoUrl\">sign-in</a>.</p>";
+                return false;
+            }
+        } else {
+            $values['contact_type'] = ":{$this->contactType}:";
+            // build a contact or customer class object with the values from
+            // the Contact form
+            if ($this->tableName == 'contact') {
+                $contact = Toolkit_LeadManager_Contact::createByValues($values);
+            } else {
+                $contact = Toolkit_LeadManager_Customer::createByValues($values);
+            }
+            if (   defined('STREAMSEND_FORMS_API')
+                && STREAMSEND_FORMS_API
+                && $values['mail_ok']) {
+                $contact->attach(new Toolkit_LeadManager_StreamSend());
+            }
+            $contact->save($this->dbh);
+            $contactId = $contact->getId();
+            // put in the creation of the contact record here
+            // and set contactId
+            $contactData = $this->contactExists($values['email']);
+            $contactId   = $contactData['contactId'];
+            $tripPlannerAuth->setAuth($values['email']);
+            $tripPlannerAuth->setAuthData('id', $contactId);
+            $tripPlannerAuth->setAuthData('username', $values['email']);
+            $sessionList->saveList($contactId);
+            return true;
+        }
+    }
+
+    //    }}}
+
+    public function setGateway(Toolkit_Toolbox_PageGatewayAbstract $gateway)
+    {
+        $this->_pageGateway = $gateway;
+    }
+
+    //    {{{    toHTML()
+
+    /**
+     * Handles how to display the current step the user is at in the form
+     *
+     * destroying and resetting the captcha value dis-allows someone from
+     * re-sending a form on a previous captcha.
+     *
+     * @return string form HTML state
+     * @access public
+     */
+    public function toHTML()
+    {
+        $this->setupRenderers();
+        if ($this->validate()) {
+            $this->captchaQuestion->destroy();
+            $this->cleanForm();
+
+            if ($this->process(array(&$this, 'processData'), $this->mergeFiles)) {
+                $this->freeze();
+                $output = $this->successMsg;
+            } else {
+                $output  = $this->errorMsg;
+                //$output .= parent::toHTML();
+            }
+            $this->sent = true;
+        } elseif ($this->isSubmitted()) {
+            $this->captchaQuestion->destroy();
+            $this->captchaAnswer->setValue('');
+            //$output  = $this->errorMsg;
+            $output .= parent::toHTML();
+        } else {
+            $this->captchaQuestion->destroy();
+            $this->captchaAnswer->setValue('');
+            $output .= parent::toHTML();
+        }
+        return $output;
+    }
+
+    //    }}}
+
+}
diff --git a/Toolkit/Contacts/SendTripPlanner.php b/Toolkit/Contacts/SendTripPlanner.php
new file mode 100755 (executable)
index 0000000..3bc3abf
--- /dev/null
@@ -0,0 +1,737 @@
+<?php
+//    vim:set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker syntax=php:
+
+/**
+ * File Doc Comment
+ *
+ * PHP version 5
+ *
+ * @category Toolkit
+ * @package  Contacts
+ * @author   Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @license     http://www.gaslightmedia.com Gaslightmedia
+ * @link     <>
+ */
+
+/**
+ * Ask the concierge form
+ *
+ * This form handles rendering and processing the contact form.
+ *
+ * @category  Toolkit
+ * @package   Contacts
+ * @author    Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @version   CVS: $Id:
+ * @link      <>
+ * @see       Toolkit_FormBuilder
+ */
+class Toolkit_Contacts_SendTripPlanner extends Toolkit_Contacts_ContactUs
+{
+    //    {{{    properties
+
+    /**
+     * Contact type to be inserted into the DB as when the form is submitted
+     *
+     * This property is only valid when the [hasContactDB] property is set
+     * to true.
+     *
+     * N.B.
+     * If you subclass this class out to other forms that are
+     * inserted into the contact db, be sure to make each one of their
+     * contactType properties unique.  We don't check for duplicates.
+     *
+     * @var string
+     * @access public
+     */
+    public $contactType = '3';
+
+    /**
+     * Who to send the email to when the contact form is submitted
+     *
+     * If you leave this blank, its value will get set to the OWNER_EMAIL
+     * in the constructor.
+     *
+     * If you ***DO NOT*** want any emails to go out when the form is submitted
+     * then set the value to false. Do not set it to 0 for false, because the
+     * check uses a strict type check to determine if the value is actually
+     * false. This is what allows for the empty value as an option, which sets
+     * the value to OWNER_EMAIL and won't override the $email property if
+     * this class gets subclassed and the value for this property gets set in
+     * the properties of the subclass and not in the constructor after this
+     * constructor function is called.
+     *
+     * tongue twister...I know.
+     * <code>
+     * public $email = false;
+     * </code>
+     *
+     * @var    unknown
+     * @access public
+     */
+    public $email = false;
+
+    /**
+     * Message to display if the form is successfully submitted
+     *
+     * @var    string
+     * @access public
+     */
+    public $successMsg = '
+        <style type="text/css">
+        #category {display:none;}
+        .listing {display:none;}
+        </style>
+        <div id="form-success-top">
+            <p>Thank you for adding these businesses to your Trip Planner.
+            Your information is currently being sent to each business so they
+            may contact you regarding your visit.</p>
+        </div>';
+
+    /**
+     * Email subject and <h1> header in email
+     *
+     * It gets set in the constructor if you leave empty here, but you
+     * can set it to something different here to override that if you desire.
+     *
+     * @var    string
+     * @access public
+     */
+    public $subject;
+
+    //    }}}
+    //    {{{    __construct()
+
+    /**
+     * Class constructor
+     *
+     * @param string $pdo         Passed in PDO object
+     * @param string $formName    Form's name.
+     * @param string $method      (optional)Form's method defaults to 'POST'
+     * @param string $action      (optional)Form's action
+     * @param string $target      (optional)Form's target defaults to '_self'
+     * @param mixed  $attributes  (optional)Extra attributes for <form> tag
+     * @param bool   $trackSubmit (optional)Whether to track if the form was
+     *                              submitted by adding a special hidden field
+     *
+     * @access public
+     */
+    public function __construct(
+        PDO $pdo,
+        $formName,
+        $method = 'post',
+        $action = '',
+        $target = '',
+        $attributes = null,
+        $trackSubmit = false
+    ) {
+        parent::__construct(
+            $pdo,
+            $formName,
+            $method,
+            $action,
+            $target,
+            $attributes,
+            $trackSubmit
+        );
+        $this->subject = 'Travel List from website ' . SITENAME;
+        $successMsg = '
+        <style type="text/css">
+        #category {display:none;}
+        .listing {display:none;}
+        </style>
+        <div id="form-success-top">
+            <p>Thank you for adding these businesses to your Trip Planner.
+            Your information is currently being sent to each business so they
+            may contact you regarding your visit to '.SITENAME.'.</p>
+        </div>';
+    }
+
+    //    }}}
+    //    {{{    configureDefaults()
+
+    /**
+     * Initializes default form values
+     *
+     * @return void
+     * @access public
+     */
+    public function configureDefaults()
+    {
+        $defaults = array(
+            'state'   => '',
+            'mail_ok' => 1,
+            'catid'   => $_REQUEST['catid'],
+            'cPage'   => $_REQUEST['cPage']
+        );
+
+        $this->setupDefaults($defaults);
+    }
+
+    //    }}}
+    //    {{{    configureElements()
+
+    /**
+     * Form element definitions
+     *
+     * @return void
+     * @access public
+     */
+    public function configureElements()
+    {
+        $e = array();
+        $this->setInterestFields();
+        //    Grouped Elements are defined here.
+        $this->interestsGroups =& $this->getInterestFields();
+
+        //    All Elements are created here.  This includes group element definitions.
+        $e[] = array(
+            'type' => 'hidden',
+            'req'  => false,
+            'name' => 'catid'
+        );
+        $e[] = array(
+            'type' => 'hidden',
+            'req'  => false,
+            'name' => 'cPage'
+        );
+        $e[] = array(
+            'type' => 'hidden',
+            'req'  => false,
+            'name' => 'user_agent'
+        );
+        $e[] = array(
+            'type' => 'hidden',
+            'req'  => false,
+            'name' => 'remote_addr'
+        );
+        $e[] = array(
+            'type'    => 'header',
+            'name'    => 'infoHdr_rmv',
+            'display' => ''
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => 'fname',
+            'display' => 'First Name'
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => 'lname',
+            'display' => 'Last Name'
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => 'address',
+            'display' => 'Address 1'
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => 'address2',
+            'display' => 'Address 2'
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => 'city',
+            'display' => 'City'
+        );
+        $e[] = array(
+            'type'    => 'select',
+            'req'     => false,
+            'name'    => 'state',
+            'display' => 'State/Province',
+            'opts'    => $GLOBALS['states']
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => 'zip',
+            'display' => 'ZIP/Postal Code'
+        );
+        $e[] = array(
+            'type'    => 'email',
+            'req'     => true,
+            'name'    => 'email',
+            'display' => 'Email'
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => true,
+            'name'    => 'email_rmv',
+            'display' => 'Verify Email'
+        );
+        $e[] = array(
+            'type'    => 'tel',
+            'req'     => false,
+            'name'    => 'phone',
+            'display' => 'Phone'
+        );
+        $e[] = array(
+            'type'    => 'tel',
+            'req'     => false,
+            'name'    => 'fax',
+            'display' => 'Fax'
+        );
+        $e[] = array(
+            'type'    => 'textarea',
+            'req'     => false,
+            'name'    => 'comments',
+            'display' => 'Comments'
+        );
+        $e[] = array(
+            'type'    => 'advcheckbox',
+            'req'     => false,
+            'name'    => 'mail_ok',
+            'display' => 'I would like to receive Email Newsletters',
+            'opts'    => 'Yes',
+            'val'     => array(0, 1)
+        );
+//        if (is_array($this->interestsGroups)) {
+//            foreach ($this->interestsGroups as $group => $gData) {
+//                $this->myGroups[] = $gData;
+//                $e[] = array(
+//                    'type'       => 'group',
+//                    'req'        => false,
+//                    'name'       => 'interest['.$group.']',
+//                    'group'         => $gData,
+//                    'label'      => $group,
+//                    'seperator'  => ' ',
+//                    'appendName' => true
+//                );
+//            }
+//        }
+        $e[] = array(
+            'type'    => 'CAPTCHA_Image',
+            'req'     => false,
+            'name'    => 'captcha_question',
+            'display' => 'Verification code',
+            'opts'    => $this->captchaOptions
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => true,
+            'name'    => 'captcha_rmv',
+            'display' => 'Enter verification code'
+        );
+        $e[] = array(
+            'type'    => 'submit',
+            'req'     => false,
+            'name'    => 'submit_rmv',
+            'display' => 'Submit Form'
+        );
+
+        $this->setupElements($e);
+    }
+
+    //    }}}
+    //    {{{    configureRules()
+
+    /**
+     * Form rule definitions
+     *
+     * Adds validation rules for the given fields
+     *
+     * @return void
+     * @access public
+     */
+    public function configureRules()
+    {
+        $r = array();
+        //    Form Rules
+        $r[] = array(
+            'element'    => array('email', 'email_rmv'),
+            'message'    => 'ERROR: Your Email Addresses Do Not Match!',
+            'type'       => 'compare',
+            'format'     => null,
+            'validation' => $this->validationType,
+            'reset'      => true,
+            'force'      => false
+        );
+        $r[] = array(
+            'element'    => 'email',
+            'message'    => 'ERROR: Invalid Email Format!',
+            'type'       => 'checkEmail',
+            'format'     => array('use_rfc822' => true),
+            'validation' => $this->validationType,
+            'reset'      => true,
+            'force'      => false
+        );
+        /*
+        $r[] = array(
+            'element'    => 'state',
+            'message'    => 'ERROR: Invalid State!',
+            'type'       => 'numeric',
+            'format'     => null,
+            'validation' => $this->validationType,
+            'reset'      => true,
+            'force'      => false
+        );
+        */
+        $r[] = array(
+            'element'    => 'phone',
+            'message'    => 'ERROR: Invalid number (xxx) xxx-xxxx!',
+            'type'       => 'phone',
+            'format'     => null,
+            'validation' => $this->validationType,
+            'reset'      => true,
+            'force'      => false
+        );
+        $r[] = array(
+            'element'    => 'fax',
+            'message'    => 'ERROR: Invalid number (xxx) xxx-xxxx!',
+            'type'       => 'phone',
+            'format'     => null,
+            'validation' => $this->validationType,
+            'reset'      => true,
+            'force'      => false
+        );
+        $r[] = array(
+            'element'    => 'zip',
+            'message'    => 'ERROR: Invalid Zip!',
+            'type'       => 'zip',
+            'format'     => array('requireDBCheck' => false),
+            'validation' => $this->validationType,
+            'reset'      => true,
+            'force'      => false
+        );
+        $r[] = array(
+            'element'    => 'mail_ok',
+            'message'    => 'ERROR: Invalid Value!',
+            'type'       => 'numeric',
+            'format'     => null,
+            'validation' => $this->validationType,
+            'reset'      => true,
+            'force'      => false
+        );
+        $r[] = array(
+            'element'    => 'captcha_rmv',
+            'message'    => 'ERROR: Incorrect verification code!',
+            'type'       => 'CAPTCHA',
+            'format'     => $this->captchaQuestion,
+            'validation' => $this->validationType,
+            'reset'      => true,
+            'force'      => false
+        );
+
+        $this->setupRules($r);
+    }
+
+    //    }}}
+
+    // {{{ sendEmails()
+
+    /**
+     * sendEmails
+     *
+     * From the list of emails contained in $_SESSIEN['wish_list']
+     * send out emails to each email address
+     * only if we're on the production server
+     * if on the development server send to OWNER_EMAIL
+     *
+     * @return void
+     * @access public
+     */
+    function sendEmails()
+    {
+        $hasInterestField = $this->elementExists('interest');
+
+        $template = new HTML_Template_Flexy($this->flexyOptions);
+        $page     = new stdClass();
+        // for comments textarea need to replace newlines with br
+        $this->formData['comments']['element'] = nl2br($this->getSubmitValue('comments'));
+
+        $page->email_from  = OWNER_EMAIL;
+        $page->subject     = $this->subject;
+        $page->client_info = $this->clientInfo;
+        $page->fname       = $this->getSubmitValue('fname');
+        $page->lname       = $this->getSubmitValue('lname');
+        $page->formData    = $this->formData;
+
+        // unset some extra fields
+        unset(
+            $page->formData['catid'],
+            $page->formData['cPage']
+        );
+
+        if ($hasInterestField) {
+            //    Clean up the interests so they will look nice in the email
+            $page->formData['interest']['nowrap']  = 'nowrap';
+            $page->formData['interest']['element'] = null;
+            $g  =& $this->getElement('interest');
+            $ge =& $g->getElements();
+            $interests = array();
+            foreach ($ge as $e) {
+                if ($e->getChecked()) {
+                    $interests[] = $e->getText();
+                }
+            }
+            $page->formData['interest']['element'] = implode('<br>', $interests);
+        }
+
+        if ($this->elementExists('mail_ok')) {
+            //    Clean up the mail_ok flag so its human readable
+            $page->formData['mail_ok']['element']
+                = ($page->formData['mail_ok']['element']) ? 'Yes' : 'No';
+        }
+
+        $template->compile('emailOwner.tpl');
+        $htmlMsg = $template->bufferedOutputObject($page);
+
+        if ($hasInterestField) {
+            //    Text version can't have HTML in it
+            //    so reset the interests to use commas
+            $page->formData['interest']['element'] = implode(", ", $interests);
+        }
+        $msg  = "{$page->subject}\n\n";
+        $msg .= "From {$page->fname} {$page->lname}\n\n";
+        $msg .= "Information\n\n";
+        foreach ($page->formData as $i) {
+            $msg .= "{$i['label']}: {$i['element']}\n";
+        }
+
+        $crlf     = "\n";
+        $mimeMail = new Mail_mime($crlf);
+        $mimeMail->setFrom("Online Form <".SERVER_FROM_EMAIL.">");
+        $mimeMail->setSubject($this->subject);
+        $mimeMail->setHTMLBody($htmlMsg);
+        $mimeMail->setTXTBody($msg);
+
+        $mail    =& Mail::factory('mail');
+        $body    = $mimeMail->get();
+        $headerFormat = "%s %s <%s>";
+        $setHeader['Reply-To'] = sprintf(
+            $headerFormat,
+            $this->getSubmitValue('fname'),
+            $this->getSubmitValue('lname'),
+            $this->getSubmitValue('email')
+        );
+        $headers = $mimeMail->headers($setHeader);
+        $sql = "
+        SELECT member_contact_email
+          FROM member
+         WHERE member_id = :member_id";
+        $getMemberEmail = $this->dbh->prepare($sql);
+
+        if (defined('DEVELOPMENT') && DEVELOPMENT) {
+            // no emails sent
+        } else {
+            if (is_array($_SESSION['wish_list'])
+                && !empty($_SESSION['wish_list'])
+            ) {
+                foreach ($_SESSION['wish_list'] as $memberId => $row) {
+                    try {
+                        $getMemberEmail->bindParam(
+                            ':member_id',
+                            $memberId,
+                            PDO::PARAM_INT
+                        );
+                        $getMemberEmail->execute();
+                        $memberEmail = $getMemberEmail->fetchColumn();
+                    } catch(PDOException $e) {
+                        return Toolkit_Common::handleError($e);
+                    }
+                    // code for sending emails
+                    if (GLM_TOOLBOX::valid_email($memberEmail)) {
+                        $res = $mail->send($memberEmail, $headers, $body);
+                        if (PEAR::isError($res)) {
+                            return Toolkit_Common::handleError($res);
+                        }
+                    }
+                }
+            }
+        }
+        $authContainer = new Toolkit_Members_TripPlanner_AuthContainer(
+            Toolkit_Database::getInstance(),
+            array(
+                'table'       => 'contact',
+                'usernamecol' => 'email',
+                'passwordcol' => 'password',
+                'db_fields'   => array('id', 'fname', 'lname'),
+                'cryptType'   => 'md5',
+            )
+        );
+        $tripPlannerAuth = new Toolkit_Members_TripPlanner_TripAuth(
+            $authContainer,
+            '',
+            false
+        );
+        $tripPlannerAuth->setIdle();
+        $tripPlannerAuth->start();
+        $sessionList = new Toolkit_Members_TripPlanner_Sessions(
+            $this->dbh,
+            $tripPlannerAuth
+        );
+        $sessionList->dumpList();
+        return $res;
+    }
+
+    // }}}
+    // {{{ showDirectList()
+
+    /**
+     * showDirectList
+     *
+     * Display the list of members in $_SESSION['wish_list']
+     *
+     * @return void
+     * @access public
+     */
+    function showDirectList()
+    {
+        // Initiate HTML_Template_Flexy.
+        $template           = new HTML_Template_Flexy($this->flexyOptions);
+        $page               = new stdClass();
+        $page->memberDirect = $page->memberDirectNoEmail = array();
+        if (is_array($_SESSION['wish_list']) && !empty($_SESSION['wish_list'])) {
+            $sql   = "
+            SELECT member_name,member_contact_email,phone,toll_free
+              FROM member
+             WHERE member_id = :member_id";
+            $getMemberData = $this->dbh->prepare($sql);
+            $mACount = $mBCount = 0;
+            foreach ($_SESSION['wish_list'] as $memberId => $row) {
+                try {
+                    $getMemberData->bindParam(
+                        ':member_id',
+                        $memberId,
+                        PDO::PARAM_INT
+                    );
+                    $getMemberData->execute();
+                    $memberData     = $getMemberData->fetch();
+                    $memberName     = $memberData['member_name'];
+                    $memberPhone    = $memberData['phone'];
+                    $memberTollFree = $memberData['toll_free'];
+                } catch(PDOException $e) {
+                    Toolkit_Common::handleError($e);
+                }
+                $name  = preg_replace(
+                    '/[\/#&?\'"]|amp;/',
+                    '',
+                    strip_tags(
+                        strtolower(
+                            trim(
+                                str_replace(
+                                    ' ',
+                                    '-',
+                                    $memberData['member_name']
+                                )
+                            )
+                        )
+                    )
+                );
+                $memberUrl = MEDIA_BASE_URL
+                    . "member-{$row['catid']}/" . htmlspecialchars($name)
+                    . "-{$memberId}.html";
+                if ($memberData['member_contact_email']) {
+                    $page->memberDirect[] = array(
+                        'memberName'     => $memberName,
+                        'memberPhone'    => $memberPhone,
+                        'memberTollFree' => $memberTollFree,
+                        'memberLink'     => $memberUrl,
+                        'deleteLink'     => MEDIA_BASE_URL
+                            .'Toolkit/Members/TripPlanner/'
+                            .'wish-list.php?catid='
+                            .$_REQUEST['catid'].'&member_id=' . $memberId
+                            . '&cPage=' . urlencode($_REQUEST['cPage']),
+                        'trclass' => (($mACount % 2 == 0) ? 'tr1': 'tr2')
+                    );
+                    $mACount++;
+                } else {
+                    $page->memberDirectNoEmail[] = array(
+                        'memberName'     => $memberName,
+                        'memberPhone'    => $memberPhone,
+                        'memberTollFree' => $memberTollFree,
+                        'memberLink'     => $memberUrl,
+                        'deleteLink'     => MEDIA_BASE_URL
+                            .'Toolkit/Members/TripPlanner/'
+                            . 'wish-list.php?catid='
+                            . $_REQUEST['catid'].'&member_id=' . $memberId
+                            . '&cPage=' . urlencode($_REQUEST['cPage']),
+                        'trclass' => (($mBCount % 2 == 0) ? 'tr1': 'tr2')
+                    );
+                    $mBCount++;
+                }
+            }
+        }
+        $this->totalMembersEmails   = $mACount;
+        $this->totalMembersNoEmails = $mBCount;
+        // Compile the direct-list.html from the templates directory.
+        $template->compile('direct-list.html');
+
+        // Merge compiled template with the $page object.
+        $out = $template->bufferedOutputObject($page);
+        return $out;
+    }
+    // }}}
+    // {{{ showNoList()
+
+    /**
+     * showNoList
+     *
+     * What to display If nothing is in $_SESSION['wish_list']
+     *
+     * @return void
+     * @access public
+     */
+    function showNoList()
+    {
+        // Initiate HTML_Template_Flexy.
+        $template      = new HTML_Template_Flexy($this->flexyOptions);
+        $page          = new stdClass();
+        $page->baseUrl = MEDIA_BASE_URL;
+        // Compile the direct-nolist.html from the templates directory.
+        $template->compile('direct-nolist.html');
+
+        // Merge compiled template with the $page object.
+        $out = $template->bufferedOutputObject($page);
+        return $out;
+    }
+    // }}}
+    //    {{{    toHTML()
+
+    /**
+     * Handles how to display the current step the user is at in the form
+     *
+     * destroying and resetting the captcha value dis-allows someone from
+     * re-sending a form on a previous captcha.
+     *
+     * @return string form HTML state
+     * @access public
+     */
+    public function toHTML()
+    {
+        $this->setupRenderers();
+        if ($this->validate()) {
+            $this->captchaQuestion->destroy();
+            $this->cleanForm();
+
+            if ($this->process(array(&$this, 'processData'), $this->mergeFiles)) {
+                $this->freeze();
+                $this->sendEmails();
+                $output = $this->successMsg;
+            }
+            $this->sent = true;
+        } elseif ($this->isSubmitted()) {
+            $this->captchaQuestion->destroy();
+            $this->captchaAnswer->setValue('');
+            $output .= parent::toHTML();
+        } else {
+            $this->captchaQuestion->destroy();
+            $this->captchaAnswer->setValue('');
+            $output = $this->showDirectList();
+            if ($this->totalMembersEmails > 0) {
+                $output .= parent::toHTML();
+            }
+        }
+        return $output;
+    }
+
+    //    }}}
+}
+
diff --git a/Toolkit/Contacts/StreamSend.php b/Toolkit/Contacts/StreamSend.php
new file mode 100755 (executable)
index 0000000..50dec94
--- /dev/null
@@ -0,0 +1,212 @@
+<?php
+/**
+ * StreamSend.php
+ *
+ * PHP version 5
+ *
+ * All Right Reserved
+ *
+ * @category  Toolkit
+ * @package   Contacts
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2009 Gaslight Media
+ * @license   Gaslight Media
+ * @version   CVS: $Id: StreamSend.php,v 1.5 2010/04/30 19:02:10 matrix Exp $
+ * @link      <>
+ */
+
+/**
+ * Default parameters for contact create operations.
+ * Note that these are strings for use in XML data not true/false values.
+ * If false, the person will be created with a status of pending
+ */
+define('STREAMSEND_DEFAULT_ACTIVATE', 'false');
+/**
+  * If activate is false, setting this to true will trigger the sending of the built-in
+  * activation notification; if activate is true, this setting has no effect
+  */
+define('STREAMSEND_DEFAULT_DELIVER_ACTIVATION', 'true');
+/**
+  * If activate is true, setting this to true will trigger the sending of the built-in
+  * welcome notification; if activate is false, this setting has no effect
+  */
+define('STREAMSEND_DEFAULT_DELIVER_WELCOME', 'false');
+/**
+  * URI for streamsend API
+  */
+define('STREAMSEND_BASE_URL', "https://app.streamsend.com");
+
+/**
+ * Toolkit_Contacts_StreamSend
+ *
+ * Contact support class for inergration with StreamSend API
+ *
+ * @category  Toolkit
+ * @package   Contacts
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2009 Gaslight Media
+ * @license   Gaslight Media
+ * @link      <>
+ */
+class Toolkit_Contacts_StreamSend
+{
+    // {{{ Class Properties
+
+
+    /**
+     * Description for public
+     * @var    boolean
+     * @access public
+     */
+    public $debug = false;
+
+    /**
+     * streamSendFields
+     *
+     * array with key values matching the gaslight contact tabel to the
+     * StreamSend field (normal fields)
+     *
+     * @var    array
+     * @access protected
+     */
+    protected $streamSendFields = array(
+        'email'        => 'email-address',
+        'fname'        => 'first-name',
+        'lname'        => 'last-name',
+        'address'      => 'address1',
+        'address2'     => 'address2',
+        'city'         => 'city',
+        'state'        => 'stateprovince',
+        'zip'          => 'postal-code',
+        'phone'        => 'phone-number',
+               'interest'     => 'interests',
+               'contact_type' => 'contact-type'
+    );
+
+    /**
+     * contactInqTypes
+     *
+     * array with key values matching the gaslight contact_inq tabel to the
+     * StreamSend field (Radio fields)
+     * key   = id from contact_inq table
+     * value = name from contact_inq_table
+     * value is same name as the StreamSend fieldname
+     *
+     * @var    array
+     * @access protected
+     */
+    protected $contactInqTypes = array();
+
+    /**
+     * booleanTypes
+     *
+     * StreamSend fields (boolean fields) an array of field names for the
+     * boolean field types
+     *
+     * @var    array
+     * @access protected
+     */
+    protected $booleanTypes = array();
+    // }}}
+    // {{{ __construct()
+
+
+    /**
+     *  __construct(
+     *
+     * @return void
+     * @access public
+     */
+    public function __construct()
+    {
+    }
+
+
+    // }}}
+    // {{{ addContact()
+
+
+    /**
+     * addContact
+     *
+     * Given $values from a Toolkit_Contacts Form add Contact
+     *
+     * @param array $values Parameter description (if any) ...
+     *
+     * @return boolean Return true if successfull
+     * @access public
+     */
+    public function addContact($values)
+    {
+        // check the array $values to make sure it is correct
+        if (is_array($values) && !empty($values)) {
+            include_once GLM_APP_BASE.'StreamSend/class_streamsend_api.inc';
+            // initialize the streamsend object
+            $ss = new streamSend (STREAMSEND_BASE_URL, STREAMSEND_LOGIN_ID,
+                STREAMSEND_KEY);
+            $ss->debug = $this->debug;
+            $ret = $ss->contactSearch($values['email']);
+
+            $contactData = array();
+            $values[$type] = 1;
+            foreach ($this->streamSendFields as $glmName => $ssName) {
+                switch ($glmName) {
+                case "state":
+                    $contactData[$ssName] = ($values[$glmName]) ? $GLOBALS['states'][$values[$glmName]]: '';
+                    break;
+                default:
+                    $contactData[$ssName] = $values[$glmName];
+                    break;
+                }
+            }
+            foreach ($this->contactInqTypes as $contactInqId => $name) {
+                $slug = str_replace(" ", "-", strtolower($this->contactInqTypes[$contactInqId]));
+                if ($values['interest'][$contactInqId]) {
+                    $contactData[$slug] = 'Yes';
+                } else {
+                    $contactData[$slug] = 'No';
+                }
+            }
+            foreach ($this->booleanTypes as $name) {
+                if ($values[$name]) {
+                    $contactData[$name] = 'Yes';
+                } else {
+                    $contactData[$name] = 'No';
+                }
+            }
+            if ($ret->contact) {
+                $contacts = $ss->contactUpdate(
+                    $ret->contact->id,
+                    $contactData
+                );
+            } else {
+                $contacts = $ss->contactCreate(
+                    $contactData,
+                    STREAMSEND_DEFAULT_ACTIVATE,
+                    STREAMSEND_DEFAULT_DELIVER_ACTIVATION,
+                    STREAMSEND_DEFAULT_DELIVER_WELCOME
+                );
+            }
+            if (!$contacts) {
+
+                // show errors if on development server
+                switch (GLM_HOST_ID) {
+                case "DEVELOPMENT":
+                    echo "<p>A total and complete failure occured.";
+                    break;
+                case "PRODUCTION":
+                    break;
+                }
+            }
+            if ($ss->debug == true) {
+                echo '<p><h3>Debug Results</h3>'.$ss->debugBuffer.'</p>';
+            }
+            return true;
+        }
+        return false;
+    }
+
+
+    // }}}
+}
+?>
diff --git a/Toolkit/Contacts/VisitorGuide.php b/Toolkit/Contacts/VisitorGuide.php
new file mode 100755 (executable)
index 0000000..2755e94
--- /dev/null
@@ -0,0 +1,313 @@
+<?php
+//    vim:set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker syntax=php:
+
+/**
+ * File Doc Comment
+ *
+ * PHP version 5
+ *
+ * @category Toolkit
+ * @package  Contacts
+ * @author   Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @license  http://www.gaslightmedia.com Gaslightmedia
+ * @version  CVS: $Id: ContactUs.php,v 1.36 2010/07/14 23:33:15 jamie Exp $
+ * @link     http://demo.gaslightmedia.com
+ */
+
+/**
+ * Error codes for Toolkit_Contacts_ContactUs
+ *
+ * Codes are mapped to textual messaged by errorMessage() method,
+ * if you add a new code be sure to add a new message for it to errorMessage()
+ *
+ * @see Toolkit_Contacts_ContactUs::errorMessage()
+ */
+
+/**
+ * GLM Contact Us form
+ *
+ * This form handles rendering and processing the contact us form.
+ * Controls the email functionality of the form, whether the client
+ * has a contact DB to store contact records and how to insert/update
+ * submitted form values.
+ *
+ * @category  Toolkit
+ * @package   Contacts
+ * @author    Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @link      http://demo.gaslightmedia.com
+ * @see       Toolkit_FormBuilder
+ */
+class Toolkit_Contacts_VisitorGuide
+    extends Toolkit_Contacts_ContactUs
+{
+    /**
+     * Contact type to be inserted into the DB as when the form is submitted
+     *
+     * This property is only valid when the [hasContactDB] property is set
+     * to true.
+     *
+     * N.B.
+     * If you subclass this class out to other forms that are
+     * inserted into the contact db, be sure to make each one of their
+     * contactType properties unique.  We don't check for duplicates.
+     *
+     * @var string
+     * @access protected
+     */
+    protected $contactType = '4';
+    /**
+     * Message to display if the form is successfully submitted
+     *
+     * @var    string
+     * @access protected
+     */
+    protected $successMsg = '
+        <style type="text/css">
+        #category {display:none;}
+        .listing {display:none;}
+        </style>
+        <div id="form-success-top">
+            Thank you for requesting the Visitors guide.
+        </div>';
+    /**
+     * Email subject and <h1> header in email
+     *
+     * It gets set in the constructor if you leave empty here, but you
+     * can set it to something different here to override that if you desire.
+     *
+     * @var    string
+     * @access protected
+     */
+    public $subject = "Visitor Guide Form from website";
+
+    /**
+     * Class constructor
+     *
+     * @param object $pdo         PHP Data Object
+     * @param string $formName    Form's name.
+     * @param string $method      (optional)Form's method defaults to 'POST'
+     * @param string $action      (optional)Form's action
+     * @param string $target      (optional)Form's target defaults to '_self'
+     * @param mixed  $attributes  (optional)Extra attributes for <form> tag
+     * @param bool   $trackSubmit (optional)Whether to track if the form was
+     *                              submitted by adding a special hidden field
+     *
+     * @author Jamie Kahgee <jamie.kahgee@gmail.com>
+     * @access public
+     * @link   http://pear.php.net/package/HTML_QuickForm/docs/latest/HTML_QuickForm/HTML_QuickForm.html
+     * @see    HTML_QuickForm
+     */
+    public function __construct(
+        PDO $pdo,
+        $formName,
+        $method = 'post',
+        $action = '',
+        $target = '',
+        $attributes = null,
+        $trackSubmit = false
+    ) {
+        parent::__construct(
+            $pdo,
+            $formName,
+            $method,
+            $action,
+            $target,
+            $attributes,
+            $trackSubmit
+        );
+        $this->email = VISITOR_GUIDE_EMAIL;
+    }
+    //    {{{    configureConstants()
+
+    /**
+     * Constant variables for the form
+     *
+     * These values won't get overridden by POST or GET vars
+     *
+     * @return void
+     * @access public
+     */
+    public function configureConstants()
+    {
+        $constants = array(
+            'user_agent' => $_SERVER['HTTP_USER_AGENT'],
+            'remote_addr' => $_SERVER['REMOTE_ADDR']
+        );
+        $this->setupConstants($constants);
+    }
+
+    //    }}}
+    //    {{{    configureElements()
+
+    /**
+     * Form element definitions
+     *
+     * @return void
+     * @access public
+     */
+    public function configureElements()
+    {
+        $e = array();
+        if ($this->hasContactDB) {
+            $this->setInterestFields();
+            //    Grouped Elements are defined here.
+            $this->interestsGroups =& $this->getInterestFields();
+        }
+
+        //    All Elements are created here.  This includes group element definitions.
+        $e[] = array(
+            'type' => 'hidden',
+            'req'  => false,
+            'name' => 'user_agent'
+        );
+        $e[] = array(
+            'type' => 'hidden',
+            'req'  => false,
+            'name' => 'remote_addr'
+        );
+        $e[] = array(
+            'type'    => 'header',
+            'name'    => 'firstHdr_rmv',
+            'display' => ''
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => true,
+            'name'    => 'fname',
+            'display' => 'First Name'
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => true,
+            'name'    => 'lname',
+            'display' => 'Last Name'
+        );
+        if ($this->tableName == 'customer') {
+            $add1 = 'add1';
+            $add2 = 'add2';
+        } else {
+            $add1 = 'address';
+            $add2 = 'address2';
+        }
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => true,
+            'name'    => $add1,
+            'display' => 'Address 1'
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => $add2,
+            'display' => 'Address 2'
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => true,
+            'name'    => 'city',
+            'display' => 'City'
+        );
+        $e[] = array(
+            'type'    => 'select',
+            'req'     => true,
+            'name'    => 'state',
+            'display' => 'State/Province',
+            'opts'    => $GLOBALS['states']
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => true,
+            'name'    => 'zip',
+            'display' => 'ZIP/Postal Code'
+        );
+        $e[] = array(
+            'type'    => 'email',
+            'req'     => true,
+            'name'    => 'email',
+            'display' => 'Email'
+        );
+        $e[] = array(
+            'type'    => 'email',
+            'req'     => true,
+            'name'    => 'email_rmv',
+            'display' => 'Verify Email'
+        );
+        $e[] = array(
+            'type'    => 'tel',
+            'req'     => false,
+            'name'    => 'phone',
+            'display' => 'Phone'
+        );
+        $e[] = array(
+            'type'    => 'tel',
+            'req'     => false,
+            'name'    => 'fax',
+            'display' => 'Fax'
+        );
+        $e[] = array(
+            'type'    => 'textarea',
+            'req'     => false,
+            'name'    => 'comments',
+            'display' => 'Comments'
+        );
+        if ($this->hasContactDB) {
+            $e[] = array(
+                'type'    => 'advcheckbox',
+                'req'     => false,
+                'name'    => 'mail_ok',
+                'display' => 'I would like to receive Email Newsletters',
+                'opts'    => 'Yes, ',
+                'val'     => array(0, 1)
+            );
+            if (defined("MEMBERS_DB") && MEMBERS_DB) {
+                $e[] = array(
+                    'type'    => 'advcheckbox',
+                    'req'     => false,
+                    'name'    => 'members',
+                    'display' => 'I would like to receive information via e-mail from '.SITENAME.' Members',
+                    'opts'    => 'Yes, ',
+                    'val'     => array(0, 1)
+                );
+            }
+        }
+        if (is_array($this->interestsGroups)) {
+            foreach ($this->interestsGroups as $group => $gData) {
+                $this->myGroups[] = $gData;
+                $e[] = array(
+                    'type'       => 'group',
+                    'req'        => false,
+                    'name'       => 'interest['.$group.']',
+                    'group'         => $gData,
+                    'label'      => $group,
+                    'seperator'  => ' ',
+                    'appendName' => true
+                );
+            }
+        }
+        $e[] = array(
+            'type'    => 'CAPTCHA_Image',
+            'req'     => false,
+            'name'    => 'captcha_question',
+            'display' => 'Verification code',
+            'opts'    => $this->captchaOptions
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => true,
+            'name'    => 'captcha_rmv',
+            'display' => 'Enter verification code'
+        );
+        $e[] = array(
+            'type'    => 'submit',
+            'req'     => false,
+            'name'    => 'submit_rmv',
+            'display' => 'Submit Form'
+        );
+
+        $this->setupElements($e);
+    }
+
+    //    }}}
+}
diff --git a/Toolkit/Contacts/application.ini b/Toolkit/Contacts/application.ini
new file mode 100644 (file)
index 0000000..5512735
--- /dev/null
@@ -0,0 +1,26 @@
+; Production server configuration data
+[production]
+previousYearSince = 2010
+
+; flexyOptions
+flexyOptions.templateDir  = BASE "Toolkit/Contacts/templates"
+flexyOptions.compileDir   = BASE "Toolkit/Contacts/templates/compiled"
+flexyOptions.forceCompile = Off
+flexyOptions.url_rewrite  = "baseurl/::" BASE_URL ",glmappbaseurl/::"  MEDIA_APP_BASE_URL
+flexyOptions.allowPHP     = On
+
+; development server configuration data inherits from production and
+; overrides values as necessary
+[development : production]
+
+; chuck's server configuration data inherits from development
+; and overrides values as necessary
+[chuck : development]
+
+; steve's server configuration data inherits from development
+; and overrides values as necessary
+[steve : development]
+
+; Vagrant server configuration inherits from development and
+; overrides values as needed
+[vagrant : development]
diff --git a/Toolkit/Contacts/assets/.keepme b/Toolkit/Contacts/assets/.keepme
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/Toolkit/Contacts/config.ini b/Toolkit/Contacts/config.ini
new file mode 100644 (file)
index 0000000..c77d0d2
--- /dev/null
@@ -0,0 +1,14 @@
+; Contact Database configuration file
+[conf]
+; Table Name for records
+table = "contact"
+; Primary Key
+primarykey = "id"
+; Sequence
+sequence = "contact_id_seq"
+
+[contact_types]
+1 = "Web Contact"
+2 = "E-News"
+3 = "Travel Planner"
+4 = "Visitor Guide"
diff --git a/Toolkit/Contacts/database-table-modifiers.sql b/Toolkit/Contacts/database-table-modifiers.sql
new file mode 100644 (file)
index 0000000..b539d26
--- /dev/null
@@ -0,0 +1,46 @@
+CREATE SEQUENCE contact_id_seq
+    INCREMENT BY 1
+    NO MAXVALUE
+    NO MINVALUE
+    CACHE 1;
+
+ALTER TABLE contact ALTER COLUMN id SET DEFAULT nextval('contact_id_seq'::regclass);
+
+ALTER TABLE ONLY contact
+    ADD CONSTRAINT contact_pkey PRIMARY KEY (id);
+
+CREATE INDEX contact_create_date_indx ON contact USING btree (create_date);
+
+CREATE INDEX contact_email_indx ON contact USING btree (email);
+
+CREATE INDEX contact_fname_indx ON contact USING btree (fname);
+
+CREATE UNIQUE INDEX contact_id_indx ON contact USING btree (id);
+
+CREATE INDEX contact_lname_indx ON contact USING btree (lname);
+
+CREATE UNIQUE INDEX news_response_id_indx ON contact USING btree (id);
+
+CREATE UNIQUE INDEX query_db_id_indx ON contact USING btree (id);
+
+CREATE SEQUENCE contact_inq_id_seq
+    INCREMENT BY 1
+    NO MAXVALUE
+    NO MINVALUE
+    CACHE 1;
+
+ALTER TABLE contact_inq ALTER COLUMN id SET DEFAULT nextval('contact_inq_id_seq'::regclass);
+
+ALTER TABLE ONLY inq_group
+    ADD CONSTRAINT inq_group_pkey PRIMARY KEY (id);
+
+CREATE SEQUENCE inq_group_id_seq
+    INCREMENT BY 1
+    NO MAXVALUE
+    NO MINVALUE
+    CACHE 1;
+
+ALTER TABLE inq_group ALTER COLUMN id SET DEFAULT nextval('inq_group_id_seq'::regclass);
+
+ALTER TABLE ONLY inq_group
+    ADD CONSTRAINT inq_group_pkey PRIMARY KEY (id);
diff --git a/Toolkit/Contacts/database-tables.sql b/Toolkit/Contacts/database-tables.sql
new file mode 100644 (file)
index 0000000..1b71879
--- /dev/null
@@ -0,0 +1,45 @@
+CREATE TABLE contact (
+    id integer NOT NULL,
+    create_date date DEFAULT CURRENT_DATE,
+    fname text,
+    lname text,
+    company text,
+    address text,
+    address2 text,
+    city text,
+    state text,
+    zip text,
+    country text,
+    phone text,
+    fax text,
+    email text,
+    user_agent text,
+    remote_addr text,
+    interest text,
+    mail_ok boolean DEFAULT false,
+    visitorguide boolean DEFAULT false,
+    uidpdf text,
+    visitorguide_download boolean DEFAULT false,
+    email_verified boolean DEFAULT true,
+    contact_type text,
+    discover text,
+    member_ok boolean DEFAULT false,
+    staff boolean DEFAULT false,
+    comments text,
+    password text,
+    verify_password text
+);
+
+CREATE TABLE inq_group (
+    id integer NOT NULL,
+    name text
+);
+
+CREATE TABLE contact_inq (
+    id integer NOT NULL,
+    header text,
+    pos integer,
+    description text,
+    image text,
+    groupid integer
+);
diff --git a/Toolkit/Contacts/templates/brochurePage.html b/Toolkit/Contacts/templates/brochurePage.html
new file mode 100755 (executable)
index 0000000..d145ab2
--- /dev/null
@@ -0,0 +1,9 @@
+<link type="text/css" rel="stylesheet" href="<?php echo MEDIA_BASE_URL;?>css/contactform.css">
+<div flexy:if="pdfForm"><strong>DOWNLOAD:</strong>
+  <p>We will email you a link to the PDF version of the {brochure}.</p>
+</div>
+<div>
+  {pdfForm:h}
+</div>
+<p style="clear: both;" flexy:if="contactForm"><strong>ORDER BY MAIL:</strong></p>
+{contactForm:h}
diff --git a/Toolkit/Contacts/templates/contactForm.html b/Toolkit/Contacts/templates/contactForm.html
new file mode 100644 (file)
index 0000000..2f2f84a
--- /dev/null
@@ -0,0 +1,92 @@
+<div id="contact">
+    {form.javascript:h}
+    {form.outputHeader():h}
+    {form.hidden:h}
+    {hidden}
+    <table>
+    {foreach:form.sections,sec}
+    <tr>
+        <td class="header" colspan="2">
+        <b>{sec.header:h}</b></td>
+    </tr>
+        {foreach:sec.elements,elem}
+            {if:elem.style}
+              {elem.outputStyle():h}
+            {else:}
+                {if:elem.isButton()}
+                    {if:elem.notFrozen()}
+                    <tr>
+                        <td class="labelcell">&nbsp;</td>
+                        <td class="fieldcell">{elem.html:h}</td>
+                    </tr>
+                    {end:}
+                {else:}
+                    <tr>
+                    {if:elem.isType(#textarea#)}
+                        <td colspan="2">
+                            {if:elem.required}<span class="req">*</span>{end:}
+                            {if:elem.error}<div class="req">{end:}
+                            {elem.label:h}<br>
+                            {if:elem.error}</div>{end:}
+                    {else:}
+                        {if:elem.isType(#CAPTCHA_Image#)}
+                            <td class="labelcell captcha">
+                                {if:elem.required}<span class="req">*</span>{end:}
+                                {if:elem.error}<div class="req">{end:}
+                                {elem.label:h}
+                                {if:elem.error}</div>{end:}
+                            </td>
+                            <td class="fieldcell">
+                        {else:}
+                            {if:elem.isType(#group#)}
+                                <td colspan="2">
+                                    {if:elem.required}<span class="req">*</span>{end:}
+                                    {if:elem.error}<div class="req">{end:}
+                                    {elem.label:h}<br>
+                                    {if:elem.error}</div>{end:}
+                            {else:}
+                                <td class="labelcell">
+                                    {if:elem.required}<span class="req">*</span>{end:}
+                                    {if:elem.error}<div class="req">{end:}
+                                    {elem.label:h}
+                                    {if:elem.error}</div>{end:}
+                                </td>
+                                {if:elem.isName(#interest#)}
+                                    <td class="fieldcell checkbox">
+                                {else:}
+                                    <td class="fieldcell">
+                                {end:}
+                            {end:}
+                        {end:}
+                    {end:}
+                    {if:elem.error}<div class="error">{elem.error}</div>{end:}
+                    {if:elem.isType(#group#)}
+                        {foreach:elem.elements,gitem}
+                            {gitem.label:h}
+                            {gitem.html:h}{if:gitem.required}<div class="req">*</div>{end:}
+                            {if:elem.separator}{elem.separator:h}{end:}
+                        {end:}
+                    {else:}
+                        {elem.html:h}
+                        {if:elem.isName(#captcha_rmv#)}
+                            <span
+                                class="tooltip"
+                                title="Verification Code|To help us distinguish between
+                                information submitted by individuals and those automatically
+                                entered by software robots, please type the letters shown.">What is this?
+                            </span>
+                        {end:}
+                    {end:}
+                </td>
+              </tr>
+                {end:}
+            {end:}
+        {end:}  <!-- end for foreach:sec.element,elem -->
+    {end:} <!-- end for foreach:form.section,sec -->
+
+               </table>
+       </form>
+       {if:form.requirednote}
+    <div>{form.requirednote:h}</div>
+    {end:}
+</div>
diff --git a/Toolkit/Contacts/templates/currentTables/Element.tpl b/Toolkit/Contacts/templates/currentTables/Element.tpl
new file mode 100755 (executable)
index 0000000..595457b
--- /dev/null
@@ -0,0 +1,14 @@
+<tr>
+       <td class="labelcell">
+               <!-- BEGIN required -->
+               <span class="req">*</span>
+               <!-- END required -->
+               <label>{label}</label>
+       </td>
+       <td class="fieldcell">
+               <!-- BEGIN error -->
+               <div class="req"> {error} </div>
+               <!-- END error -->
+               {element}
+       </td>
+</tr>
diff --git a/Toolkit/Contacts/templates/currentTables/Form.tpl b/Toolkit/Contacts/templates/currentTables/Form.tpl
new file mode 100755 (executable)
index 0000000..8875737
--- /dev/null
@@ -0,0 +1,8 @@
+<div id="contact">
+       <form{attributes}>
+               {hidden}
+               <table>
+                       {content}
+               </table>
+       </form>
+</div>
\ No newline at end of file
diff --git a/Toolkit/Contacts/templates/currentTables/Group.tpl b/Toolkit/Contacts/templates/currentTables/Group.tpl
new file mode 100755 (executable)
index 0000000..cdd24cf
--- /dev/null
@@ -0,0 +1,5 @@
+<table class="group">
+       <tbody>
+               {content}
+       </tbody>
+</table>
diff --git a/Toolkit/Contacts/templates/currentTables/GroupElement.tpl b/Toolkit/Contacts/templates/currentTables/GroupElement.tpl
new file mode 100755 (executable)
index 0000000..1a4ba27
--- /dev/null
@@ -0,0 +1,9 @@
+<tr>
+       <td>
+               {element}
+               <!-- BEGIN required -->
+               <span class="req">*</span>
+               <!-- END required -->
+               {label}
+       </td>
+</tr>
diff --git a/Toolkit/Contacts/templates/currentTables/Header.tpl b/Toolkit/Contacts/templates/currentTables/Header.tpl
new file mode 100755 (executable)
index 0000000..64ac244
--- /dev/null
@@ -0,0 +1,5 @@
+<tr class="hdr">
+       <td colspan="2">
+               {header}
+       </td>
+</tr>
diff --git a/Toolkit/Contacts/templates/currentTables/RequiredNote.tpl b/Toolkit/Contacts/templates/currentTables/RequiredNote.tpl
new file mode 100755 (executable)
index 0000000..dad5d0b
--- /dev/null
@@ -0,0 +1 @@
+<span class="req">* = Required Fields</span>
diff --git a/Toolkit/Contacts/templates/direct-list.html b/Toolkit/Contacts/templates/direct-list.html
new file mode 100755 (executable)
index 0000000..eefc842
--- /dev/null
@@ -0,0 +1,29 @@
+<h1>Your Trip Planner List</h1>
+<p>Below is the list of businesses you have added to your Trip Planner. The form below will be sent to each individual business listing that has an email address listed with their business listing. For those businesses with no email address, we have included their phone numbers for you to call directly and request additional information.</p>
+<h2 flexy:if="memberDirectNoEmail">Request Information by Phone from:</h2>
+<p>These business listings have no current email address on file. To receive additional information please call the phone numbers listed next to each business name.</p>
+<table flexy:if="memberDirectNoEmail">
+    <tr>
+        <th>Name</td>
+        <th>Phone</td>
+        <th>&nbsp;</td>
+    </tr>
+    {foreach:memberDirectNoEmail,member}
+    <tr class="{member[trclass]}">
+        <td><a href="{member[memberLink]}">{member[memberName]}</a></td>
+        <td>{member[memberPhone]}</td>
+        <td class="rowDelete"><a href="{member[deleteLink]}" style="text-decoration: underline;">Remove</a></td>
+    </tr>
+    {end:}
+</table>
+<h2 flexy:if="memberDirect">Request Information by Email from:</h2>
+<p flexy:if="memberDirect">This list of businesses will receive the following information form directly to their email account.</p>
+<table flexy:if="memberDirect">
+    {foreach:memberDirect,member}
+    <tr class="{member[trclass]}">
+        <td><a href="{member[memberLink]}">{member[memberName]}</a></td>
+        <td class="rowDelete"><a href="{member[deleteLink]}" style="text-decoration: underline;">Remove</a></td>
+    </tr>
+    {end:}
+</table>
+<h2 flexy:if="memberDirect">Plan Your Trip Information Request Form</h2>
diff --git a/Toolkit/Contacts/templates/emailOwner.tpl b/Toolkit/Contacts/templates/emailOwner.tpl
new file mode 100755 (executable)
index 0000000..e574342
--- /dev/null
@@ -0,0 +1,61 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+       <meta http-equiv="content-type" content="text/html;charset=utf-8">
+       <title>{title:h}</title>
+</head>
+<body>
+       <!-- {{{ body -->
+       <p>
+               <font size="4" face="arial, sans-serif">
+                       <b>{subject:h}</b>
+               </font>
+       </p>
+       <p>
+               <font size="3" face="arial, sans-serif">
+                       <b>From {fname:h} {lname:h}</b>
+               </font>
+       </p>
+       <table cellspacing="0" cellpadding="0" bgcolor="#c0c0c0" border="0">
+               <tr>
+                       <td>
+                               <table cellspacing="1" cellpadding="5" border="0" bgcolor="#c0c0c0" width="400">
+                                       {foreach:formData,v}
+                                               <tr flexy:if="v[element]" bgcolor="#c0c0c0">
+                                                       <td align="right" bgcolor="#ffffff">
+                                                               <font size="2" face="arial, sans-serif">
+                                                                       <b>{v[label]:h}</b>
+                                                               </font>
+                                                       </td>
+                                                       {if:v[nowrap]}
+                                                               <td nowrap bgcolor="#ffffff">
+                                                                       <font size="2" face="arial, sans-serif">{v[element]:h}</font>
+                                                               </td>
+                                                       {else:}
+                                                               <td bgcolor="#ffffff">
+                                                                       <font size="2" face="arial, sans-serif">{v[element]:h}</font>
+                                                               </td>
+                                                       {end:}
+                                               </tr>
+                                       {end:}
+                               </table>
+                       </td>
+               </tr>
+               <tr>
+                       <td>
+                               <table cellspacing="0" cellpadding="10" border="0" width="400">
+                                       <tr>
+                                               <td bgcolor="#eeeeee">
+                                                       <font size="1" face="arial, sans-serif">
+                                                               To ensure the delivery of these e-mails to your inbox, please add {email_from:h} to your e-mail Address Book or Safe List.
+                                                       </font>
+                                               </td>
+                                       </tr>
+                               </table>
+                       </td>
+               </tr>                           
+                                                       
+       </table>
+       <!-- }}} -->
+</body>
+</html>
diff --git a/Toolkit/Contacts/templates/friendEmail.html b/Toolkit/Contacts/templates/friendEmail.html
new file mode 100644 (file)
index 0000000..358ded6
--- /dev/null
@@ -0,0 +1,49 @@
+<html>
+               <style type="text/css">
+               <!--
+               body { background-color: #fff;
+                       color: black;
+                       font-family: verdana, arial, helvetica, sans-serif;
+                       }
+               h1, h2 {font-family: arial, helvetica, sans-serif;}     
+               h1 {font-size: 18px; }
+               h2 {font-size: 16px; margin-bottom: 5px;}
+               p {font-size: 12px;}
+               .label {
+                       font-weight: bold; 
+                       background-color: transparent;
+                       text-align: right;
+                       width: 200px;
+                       padding: 5px;
+                       }
+               .field {
+                       //background-color: #F2F7FB;
+                       background-color: #fff;
+                       padding: 3px;
+                       }
+               table.data { 
+                       //background-color: #F9FBFD; 
+                       background-color: #fff; 
+                       color: #000; 
+                       width: 500px;  
+                       //border: 1px solid #D7E5F2; 
+                       border: 1px solid #ccc; 
+                       border-collapse: collapse; 
+                       margin-left: 20px;
+                       }
+               table.data td { 
+                       //border: 1px solid #D7E5F2; 
+                       border: 1px solid #ccc; 
+                       padding-left: 4px; 
+                       font-size: 12px;
+                       }       
+               -->
+</style>
+<body>
+               
+    <p>Dear {fname}</p>
+    <p>Your friend {yname} has been to <a href="baseurl">demo.gaslightmedia.com</a>, and thought you might be interested in it. </p>
+    <p>Message here.</p>
+    <div>&nbsp;</div><br clear="all">
+</body>
+</html>
diff --git a/Toolkit/Contacts/templates/pdfDownloadEmail.html b/Toolkit/Contacts/templates/pdfDownloadEmail.html
new file mode 100755 (executable)
index 0000000..6f01198
--- /dev/null
@@ -0,0 +1,19 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+       <meta http-equiv="content-type" content="text/html;charset=utf-8">
+       <title>{title:h}</title>
+</head>
+<body>
+               <!-- {{{ body -->
+        <b>{subject:h}</b>
+        <p>Thank you for requesting the {pdfName} Link.  Here is the link to the Adobe PDF version:
+            </p>
+        <p><a href="{pdfLinkURL}">Brochure</a> ({pdfFileSize})</p>
+        <p>Thank you for your interest in {client_info[name]:h}'!</p>
+        <p>Sincerely,</p>
+        <p>{client_info[name]:h}</p>
+        <p>{client_info[url]:h}</p>
+               <!-- }}} -->
+</body>
+</html>
diff --git a/Toolkit/DataGridBuilder.php b/Toolkit/DataGridBuilder.php
new file mode 100644 (file)
index 0000000..ced09b6
--- /dev/null
@@ -0,0 +1,288 @@
+<?php
+//    vim:set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker syntax=php:
+
+/**
+ * Create Datagrids for displaying data
+ *
+ * PHP version 5
+ *
+ * @category Structures
+ * @package  Toolkit_DataGridBuilder
+ * @author   Jamie Kahgee <steve@gaslightmedia.com>
+ * @license     http://www.gaslightmedia.com Gaslightmedia
+ * @link     http://demo.gaslightmedia.com
+ */
+
+/**
+ * Create Datagrids for displaying data
+ *
+ * This abstract class handles all the base functionality of creating
+ * handeling all the details associated w/ a regular dataGrid.
+ * 1. Creation
+ * 2. Sorting (via column headers or sortform)
+ * 3. Pagenation
+ *
+ * @category  Structures
+ * @package      Toolkit_DataGridBuilder
+ * @author      Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2008 Gaslight Media
+ * @license      http://www.gaslightmedia.com Gaslightmedia
+ * @Release      CVS: $Id: DataGridBuilder.php,v 1.13 2009/09/16 00:07:44 jamie Exp $
+ * @link      http://demo.gaslightmedia.com
+ * @see          Toolkit_SortForm, Structures_DataGrid
+ */
+abstract class Toolkit_DataGridBuilder extends Structures_DataGrid
+{
+    //    {{{ properties
+
+    /**
+     * Options to pass to DataGrid
+     *
+     * @var    array
+     * @access protected
+     */
+    protected $options;
+
+    /**
+     * Rendering options for DataGrid
+     *
+     * @var    array
+     * @access protected
+     */
+    protected $rendererOptions = array(
+        'sortIconASC' => '&uArr;',
+        'sortIconDESC'    => '&dArr;',
+        'evenRowAttributes' => 'class="even"',
+        'oddRowAttributes' => 'class="odd"',
+        'sortingResetsPaging' => false,
+    );
+
+    /**
+     * Rendering options for DataGrid paging element
+     *
+     * @var    array
+     * @access protected
+     */
+    protected $pagerOptions = array(
+        'nextImg' => 'Next',
+        'prevImg' => 'Previous',
+        'separator' => '',
+        'spacesBeforeSeparator' => 0,
+        'spacesAfterSeparator' => 0,
+        'containerClass' => 'paging',
+    );
+
+    /**
+     * SQL query used to obtain the DataGrid
+     *
+     * @var    unknown
+     * @access protected
+     */
+    protected $sql;
+
+    /**
+     * How many records must exist in the Datagrid before the sort form shows up
+     *
+     * @var    integer
+     * @access protected
+     */
+    protected $sortableAfter = 10;
+
+    /**
+     * The HTML table id of the DataGrid
+     *
+     * @var    string
+     * @access protected
+     */
+    protected $tableId = 'dataGrid';
+
+    /**
+     * The HTML class name of the DataGrid
+     *
+     * @var    string
+     * @access protected
+     */
+    protected $tableClass = 'dataGrid';
+
+    /**
+     * The HTML id of the DataGrid sorting form (when present)
+     *
+     * @var    string
+     * @access protected
+     */
+    protected $sortFormId = 'gridSorter';
+
+    /**
+     * Message to display to users if no records were found
+     *
+     * @var        String
+     * @access    Protected
+     * @see        Toolkit_DataGridBuilder::setNoRecordMessage()
+     */
+    protected $noRecMessage = 'No Records';
+
+    //    }}}
+    //    {{{ __construct()
+
+    /**
+     * DataGrid constructor
+     *
+     * Instantiates a DataGrid and sets up when to make the grid sortable
+     *
+     * @param PDO     $pdo          PDO object used in the datagrid
+     * @param integer $limit        The number of records to display per page.
+     * @param integer $page         The current page view. In most cases,
+     *                                this is useless. Note: if you specify
+     *                                this, the "page"GET variable will be ignored.
+     * @param string  $rendererType The type of renderer to use. You may
+     *                                prefer to use the $type argument of
+     *                                render, fill or getOutput.
+     *
+     * @return void
+     * @access public
+     */
+    public function __construct(
+        PDO $pdo,
+        $limit = null,
+        $page = null,
+        $rendererType = null
+    ) {
+        parent::__construct($limit, $page, $rendererType);
+
+        $this->options = array('dbc' => $pdo);
+        if (!is_null($limit)) {
+            $this->sortableAfter = $limit;
+        }
+        if ($_GET['page_id']) {
+            $this->pagerOptions['path'] = MEDIA_BASE_URL . 'members-only-area/';
+        }
+    }
+
+    //    }}}
+
+    //  {{{ configureColumns()
+
+    /**
+     * configure retrieved columns
+     *
+     * Tells the DataGrid how to render the retrieved columns
+     *
+     * @return void
+     * @access protected
+     */
+    abstract protected function configureColumns();
+
+    //  }}}
+
+    //    {{{ setNoRecordMessage()
+
+    /**
+     * The message to display if no results were found from the sql query
+     *
+     * @param string $msg No result message.
+     *
+     * @return void
+     * @access public
+     */
+    public function setNoRecordMessage($msg)
+    {
+        $this->noRecMessage = $msg;
+    }
+
+    //    }}}
+    //    {{{ setQuery()
+
+    /**
+     * Sets the sql query to use in the DataGrid to get the results
+     *
+     * @param string $sql The SQL query
+     *
+     * @return void
+     * @access public
+     */
+    public function setQuery($sql)
+    {
+        $this->sql = $sql;
+    }
+
+    //    }}}
+    //    {{{ show()
+
+    /**
+     * Displays the DataGrid results
+     *
+     * @return void
+     * @access public
+     */
+    public function show()
+    {
+        echo $this->toHTML();
+    }
+
+    //    }}}
+
+    //    {{{ toHTML()
+
+    /**
+     * returns a HTML table of the datagrid
+     *
+     * @return string
+     * @access public
+     */
+    public function toHTML()
+    {
+        $this->configureColumns();
+
+        try {
+            $bind = $this->bind($this->sql, $this->options, 'PDO');
+        } catch (PDOException $e) {
+            return Toolkit_Common::handleError($e);
+        }
+
+        if (PEAR::isError($bind)) {
+            return Toolkit_Common::handleError($bind);
+        } elseif (($recCount = $this->getRecordCount()) > 0) {
+            $this->setRendererOptions($this->rendererOptions);
+            $renderer =& $this->getRenderer();
+            //    Allows us to turn off the id name for the table,
+            //    when we subclass this class out.
+            if ($this->tableId) {
+                $renderer->setTableAttribute('id', $this->tableId);
+            }
+            //    Allows us to turn off the class name for the table,
+            //    when we subclass this class out.
+            if ($this->tableClass) {
+                $renderer->setTableAttribute('class', $this->tableClass);
+            }
+            $gridBody = $this->getOutput();
+
+            if (PEAR::isError($gridBody)) {
+                return Toolkit_Common::handleError($gridBody);
+            }
+
+            $gridPager = $this->getOutput(
+                DATAGRID_RENDER_PAGER,
+                array('pagerOptions' => $this->pagerOptions)
+            );
+            if (PEAR::isError($gridPager)) {
+                return Toolkit_Common::handleError($gridPager);
+            }
+
+            if ($recCount > $this->sortableAfter) {
+                $form    = new Toolkit_SortForm($this->sortFormId);
+                $options = array('directionStyle' => 'radio');
+                $this->fill($form, $options, DATAGRID_RENDER_SORTFORM);
+                //    Datagrid never ads a submit button.
+                $form->addElement('submit', null, 'Submit');
+                $gridSorter = $form->toHTML();
+            }
+
+            return $gridPager . $gridSorter . $gridBody . $gridPager;
+        } else {
+            return "<h2>{$this->noRecMessage}</h2>";
+        }
+    }
+
+    //    }}}
+}
+?>
diff --git a/Toolkit/Database.php b/Toolkit/Database.php
new file mode 100644 (file)
index 0000000..f6e6521
--- /dev/null
@@ -0,0 +1,268 @@
+<?php
+
+/**
+ * Database Singleton
+ *
+ * PHP version 5
+ *
+ * @category Database
+ * @package  Database
+ * @author   Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @license     http://www.gaslightmedia.com Gaslightmedia
+ * @version  CVS: $Id: Database.php,v 1.8 2010/05/25 14:09:12 jamie Exp $
+ * @link     http://demo.gaslightmedia.com
+ */
+
+/**
+ * Database Singleton class
+ *
+ * Used to create a one and only one instance of a databsae PDO object.
+ * Only uses the PHP PDO. Later additions may be worked on the
+ * use master/slave database setups. Hopefully!
+ *
+ * @category  Database
+ * @package   Database
+ * @author    Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2008 Gaslight Media
+ * @license      http://www.gaslightmedia.com Gaslightmedia
+ * @link      http://demo.gaslightmedia.com
+ */
+class Toolkit_Database
+{
+    //    {{{    properties
+
+    /**
+     * Master database handler object
+     *
+     * Handles insert/update/delete requests
+     *
+     * @var PDO
+     * @access private
+     */
+    private $_masterDbh;
+
+    /**
+     * Slave database handler object
+     *
+     * Handles read requests
+     *
+     * @var PDO
+     * @access private
+     */
+    private $_slaveDbh;
+
+    /**
+     * Class instance
+     *
+     * @var Toolkit_Database
+     * @access private
+     */
+    private static $_instance;
+
+    //    }}}
+    //    {{{    __construct()
+
+    /**
+     * Class constructor
+     *
+     * Make the database handler connection for the class
+     *
+     * @param boolean $master Whether Use connection for master of slave
+     *
+     * @access private
+     */
+    private function __construct($master)
+    {
+        try {
+            $dsn = 'pgsql:' . CONN_STR;
+            //    Keep the fetch mode set to FETCH_BOTH, we use
+            //    associative arrays throughout our apps, but some PEAR lib
+            //    apps require numerical indicies to work correctly.
+            //    i.e. (DataGrids working w/ PDO's)
+            $driverOptions = array(
+                PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_BOTH,
+            );
+            $this->_masterDbh = new PDO($dsn, null, null, $driverOptions);
+            $this->_masterDbh->setAttribute(
+                PDO::ATTR_ERRMODE,
+                PDO::ERRMODE_EXCEPTION
+            );
+            $this->_setDateStyle($this->_masterDbh);
+            $this->_setSearchPath($this->_masterDbh);
+        } catch (PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+    }
+
+    //    }}}
+    //    {{{    __clone()
+
+    /**
+     * Clone magic method
+     *
+     * Don't allow cloning of instances of this class
+     * which could potentially duplicate system resources
+     * and make extra DB connections
+     *
+     * @return void
+     * @access private
+     */
+    private function __clone()
+    {
+        //    Do nothing so we don't create duplicate resources
+    }
+
+    //    {{{    _setDateStyle()
+
+    /**
+     * Set the default date style
+     * @param PDO $dbh
+     */
+    private function _setDateStyle(PDO &$dbh)
+    {
+        $dbh->query("SET DATESTYLE TO 'SQL, US'");
+    }
+
+    //    }}}
+    //    {{{    _setSearchPath()
+
+    /**
+     * Set the search path for schemas
+     * @param PDO $dbh
+     */
+    private function _setSearchPath(PDO &$dbh)
+    {
+        //    Add schemas to search path.
+        $sql = "
+            SELECT set_config(
+                'search_path',
+                current_setting('search_path') || ', ' || :schema,
+                false
+            )";
+
+        $stmt = $dbh->prepare($sql);
+        $stmt->bindValue(':schema', 'toolbox', PDO::PARAM_STR);
+        $stmt->execute();
+
+        $stmt->bindValue(':schema', 'ckimages', PDO::PARAM_STR);
+        $stmt->execute();
+
+        if (defined('RETAIL_SHOP') && RETAIL_SHOP) {
+            $stmt->bindValue(':schema', 'retail_shop', PDO::PARAM_STR);
+            $stmt->execute();
+        }
+
+        if (defined('GLM_BLOCKS') && GLM_BLOCKS) {
+            //    define seasonator search path
+            $stmt->bindValue(':schema', 'blocks', PDO::PARAM_STR);
+            $stmt->execute();
+        }
+
+        if (defined('CONTACT_DB') && CONTACT_DB) {
+            //    define banner search path
+            $stmt->bindValue(':schema', 'contacts', PDO::PARAM_STR);
+            $stmt->execute();
+        }
+
+        if (defined('BANNERS') && BANNERS) {
+            //    define banner search path
+            $stmt->bindValue(':schema', 'banners', PDO::PARAM_STR);
+            $stmt->execute();
+        }
+
+        if (defined('ROTATING_IMAGES') && ROTATING_IMAGES) {
+            //    define rotating images search path
+            $stmt->bindValue(':schema', 'rotatingImages', PDO::PARAM_STR);
+            $stmt->execute();
+        }
+
+        if (defined('PHOTO_GALLERY') && PHOTO_GALLERY) {
+            //    define phot gallery search path
+            $stmt->bindValue(':schema', 'photos', PDO::PARAM_STR);
+            $stmt->execute();
+        }
+
+        if (defined('POSTCARD_DB') && POSTCARD_DB) {
+            //    define phot gallery search path
+            $stmt->bindValue(':schema', 'postcards', PDO::PARAM_STR);
+            $stmt->execute();
+        }
+
+        if (defined('EVENT_DB') && EVENT_DB) {
+            //    define members search path
+            $stmt->bindValue(':schema', 'events', PDO::PARAM_STR);
+            $stmt->execute();
+        }
+
+        if (defined('MEMBERS_DB') && MEMBERS_DB) {
+            //    define members search path
+            $stmt->bindValue(':schema', 'members', PDO::PARAM_STR);
+            $stmt->execute();
+        }
+
+        if (defined('COUPONS') && COUPONS) {
+            //    define coupon search path
+            $stmt->bindValue(':schema', 'coupons', PDO::PARAM_STR);
+            $stmt->execute();
+        }
+
+        if (defined('PRESS_DB') && PRESS_DB) {
+            //    define press/archive search path
+            $stmt->bindValue(':schema', 'press', PDO::PARAM_STR);
+            $stmt->execute();
+        }
+        if (defined('EMPLOYMENT') && EMPLOYMENT) {
+            //    define press/archive search path
+            $stmt->bindValue(':schema', 'employment', PDO::PARAM_STR);
+            $stmt->execute();
+        }
+        if (defined('VIDEOS') && VIDEOS) {
+            // define videos search path
+            $stmt->bindValue(':schema', 'videos', PDO::PARAM_STR);
+            $stmt->execute();
+        }
+        if (defined('EVENT_MANAGEMENT') && EVENT_MANAGEMENT) {
+            //    define event management search path
+            $stmt->bindValue(':schema', 'reservations', PDO::PARAM_STR);
+            $stmt->execute();
+        }
+
+        if (defined('SEASONS') && SEASONS) {
+            //    define seasonator search path
+            $stmt->bindValue(':schema', 'seasons', PDO::PARAM_STR);
+            $stmt->execute();
+        }
+    }
+
+    //    }}}
+
+    //    {{{    getInstance()
+
+    /**
+     * Get an instance of this class
+     *
+     * If this object hasn't been instantiated once already
+     * then create the object and return the dbh.
+     * Otherwise just return the already instantiated dbh.
+     *
+     * @param boolean $master Whether Use connection for master or slave
+     *
+     * @return PDO $_instance database handler instance
+     * @access public
+     * @static
+     */
+    public static function getInstance($master = true)
+    {
+        settype($master, 'bool');
+
+        if (!(self::$_instance instanceof self)) {
+            self::$_instance = new self($master);
+        }
+
+        return $master
+            ? self::$_instance->_masterDbh
+            : self::$_instance->_slaveDbh;
+    }
+
+    //    }}}
+}
diff --git a/Toolkit/Events/AddCommonEventForm.php b/Toolkit/Events/AddCommonEventForm.php
new file mode 100644 (file)
index 0000000..4fc56f0
--- /dev/null
@@ -0,0 +1,1061 @@
+<?php
+
+//     vim:set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker syntax=php:
+
+/**
+ * New Event Form
+ *
+ * PHP version 5
+ *
+ * @category Toolkit
+ * @package  Events
+ * @author   Steve Sutton <steve@gaslightmedia.com>
+ * @license  http://www.gaslightmedia.com Gaslightmedia
+ * @version  CVS: $Id: AddEventForm.php,v 1.20 2010/07/04 23:58:22 jamie Exp $
+ * @link     http://demo.gaslightmedia.com
+ */
+
+/**
+ * New Event Form
+ *
+ * @category  Toolkit
+ * @package   Events
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @link      http://demo.gaslightmedia.com
+ */
+class Toolkit_Events_AddCommonEventForm
+    extends Toolkit_FormBuilder
+    implements Toolkit_Form
+{
+    // {{{     properties
+
+    /**
+     * Table in Database which holds the contact data
+     *
+     * @var    string
+     * @access public
+     */
+    public $tableName = 'events.events';
+
+    /**
+     * Table meta data
+     *
+     * This is used when inserting/updating data for the records
+     * so the PDO's can use explicit data types for the parameters.
+     *
+     * @var    array
+     * @access public
+     */
+    public $tableMetaData;
+
+    /**
+     * Who to send the email to when the contact form is submitted
+     *
+     * If you leave this blank, its value will get set to the OWNER_EMAIL
+     * in the constructor.
+     *
+     * If you ***DO NOT*** want any emails to go out when the form is submitted
+     * then set the value to false. Do not set it to 0 for false, because the
+     * check uses a strict type check to determine if the value is actually
+     * false. This is what allows for the empty value as an option, which sets
+     * the value to OWNER_EMAIL and won't override the $email property if
+     * this class gets subclassed and the value for this property gets set in
+     * the properties of the subclass and not in the constructor after this
+     * constructor function is called.
+     *
+     * tongue twister...I know.
+     * <code>
+     * protected $email = false;
+     * </code>
+     *
+     * @var    unknown
+     * @access protected
+     */
+    protected $email;
+
+    /**
+     * From header in the owner email
+     *
+     * This just sets the From header in the owner email
+     * SITENAME <from@email.com>
+     *
+     * It gets set to the constant SITENAME in the constructor if you leave
+     * empty here, but you can set it to something different here to override
+     * that if you desire.
+     *
+     * @var    unknown
+     * @access protected
+     */
+    protected $siteName;
+
+    /**
+     * Email subject and <h1> header in email
+     *
+     * It gets set in the constructor if you leave empty here, but you
+     * can set it to something different here to override that if you desire.
+     *
+     * @var    string
+     * @access protected
+     */
+    protected $subject = 'New Event Submission';
+    public $formTemplate = 'form.html';
+
+    /**
+     * Message to display if the form is successfully submitted
+     *
+     * @var    string
+     * @access protected
+     */
+    protected $successMsg = '
+    <style type="text/css">
+        #category {display:none};
+        .listings {display:none};
+    </style>
+               <div id="form-sucess-top">
+            Your event has been successfully added to the events calendar,
+            however will not be visible until it has been approved by
+            the Web site administrator. Thank You.
+               </div>';
+
+    /**
+     * Extra rules for processesing
+     *
+     * This registers the Zip validation rules (and any others listed) for
+     * QuickForm.
+     *
+     * Zip validation checks both US and Canadian Zip codes
+     *
+     * @var    array
+     * @access protected
+     */
+    protected $registeredRules = array(
+        'phone',
+        array(
+            'checkEmail',
+            'callback',
+            'email',
+            'Validate'
+        ),
+        array(
+            'checkURI',
+            'callback',
+            'uri',
+            'Validate'
+        )
+    );
+
+    /**
+     * Options for flexy templating engine
+     *
+     * Pulls the preset options from the setup.phtml file
+     * overwrites the templateDir and compileDir to match this classes needs
+     *
+     * @var    array
+     * @access protected
+     */
+    protected $flexyOptions;
+
+    protected $eventMapper;
+
+    // }}}
+    // {{{     __construct()
+
+    /**
+     * Class constructor
+     *
+     * @param PDO    $pdo         PHP Data Object
+     * @param string $formName    Form's name.
+     * @param string $method      (optional)Form's method defaults to 'POST'
+     * @param string $action      (optional)Form's action
+     * @param string $target      (optional)Form's target defaults to '_self'
+     * @param mixed  $attributes  (optional)Extra attributes for <form> tag
+     * @param bool   $trackSubmit (optional)Whether to track if the form was
+     *                                                           submitted by adding a special hidden field
+     *
+     * @access public
+     */
+    public function __construct(
+        PDO $pdo,
+        $formName,
+        $method = 'post',
+        $action = '',
+        $target = '',
+        $attributes = null,
+        $trackSubmit = false,
+        EventMapper $eventMapper
+    ) {
+        parent::__construct(
+            $formName, $method, $action, $target, $attributes, $trackSubmit
+        );
+
+        $this->dbh = $pdo;
+        $this->eventMapper = $eventMapper;
+
+        if ($this->email !== false && empty($this->email)) {
+            // Set to false to turn off email function.
+            $this->email = ADD_EVENT_EMAIL;
+        }
+        if (empty($this->siteName)) {
+            $this->siteName = SITENAME;
+        }
+        if (empty($this->subject)) {
+            $this->subject = 'Contact Request from website ' . SITENAME;
+        }
+
+        $this->flexyOptions = $GLOBALS['flexyOptions'];
+        $this->flexyOptions['templateDir'] = dirname(__FILE__) . "/templates/";
+        $this->flexyOptions['compileDir'] = dirname(__FILE__) . "/templates/compiled/";
+
+        $var = basename(__FILE__, '.php');
+
+        $callbackUrl = MEDIA_BASE_URL;
+
+        $this->captchaOptions = array(
+            'width'        => 100,
+            'height'       => 50,
+            'callback'     => "{$callbackUrl}Toolkit/qfcaptcha.php?var=$var",
+            'sessionVar'   => $var,
+            'imageOptions' => array(
+                'font_size'        => 16,
+                'font_path'        => GLM_APP_BASE . 'glmPEAR/Image/Canvas/Fonts/',
+                'font_file'        => 'times.ttf',
+                'background_color' => '#cccccc',
+                'obfuscation'      => false,
+                'angle'            => true,
+            ),
+        );
+    }
+
+    // }}}
+    //  {{{ checkDate()
+
+    /**
+     * Validate date input
+     *
+     * allows for empty dates to be valid
+     *
+     * @param array $date date group from form
+     *
+     * @return boolean true if valid, false if not
+     * @access public
+     */
+    public function checkDate($date)
+    {
+        if (!$date) {
+            return true;
+        } else {
+            return Validate::date($date, array('format' => '%m/%d/%Y'));
+        }
+    }
+
+    //  }}}
+    //  {{{ checkDateRange()
+
+    /**
+     * Validate date input
+     *
+     * allows for empty end date to be valid
+     *
+     * @param array $d date group from form
+     *
+     * @return boolean true if valid, false if not
+     * @access public
+     */
+    public function checkDateRange(array $d)
+    {
+        if (!$this->hasEndDate($d[1])) {
+            //  no end date is a valid date range
+            return true;
+        }
+        $pattern = '/([0-9]{2})\/([0-9]{2})\/([0-9]{4})/';
+        if (preg_match($pattern, $d[0], $m)) {
+            $t1 = mktime(0, 0, 0, (int) $m[1], (int) $m[2], (int) $m[3]);
+            $bdate = new Date($t1);
+        }
+        if (preg_match($pattern, $d[1], $m)) {
+            $t2    = mktime(0, 0, 0, (int) $m[1], (int) $m[2], (int) $m[3]);
+            $edate = new Date($t2);
+        }
+        if ($bdate && $edate) {
+            //  0 if the dates are equal - valid
+            // -1 if $bdate is before $edate - valid
+            //  1 if $bdate is after $edate - invalid
+            $res = Date::compare($bdate, $edate);
+            return ($res !== 1);
+        }
+        return true;
+    }
+
+    //  }}}
+    // {{{     configureElements()
+
+    /**
+     * Form element definitions
+     *
+     * @return void
+     * @access public
+     */
+    public function configureElements()
+    {
+        $e = array();
+
+        //     All Elements are created here.  This includes group element definitions.
+        $e[] = array(
+            'type'    => 'header',
+            'name'    => 'eventNameHdr',
+            'display' => 'Event Name'
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => true,
+            'name'    => 'header',
+            'display' => 'Event Name'
+        );
+        $e[] = array(
+            'type'    => 'header',
+            'name'    => 'eventDateHdr',
+            'display' => 'Event Date / Time'
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => true,
+            'name'    => 'starting',
+            'display' => 'Start Date',
+            'opts'    => array('id' => 'sdate')
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => true,
+            'name'    => 'ending',
+            'display' => 'End Date',
+            'opts'    => array('id' => 'edate')
+        );
+        $e[] = array(
+            'type'    => 'date',
+            'req'     => false,
+            'name'    => 'btime',
+            'display' => 'Start Time',
+            'opts'    => array(
+                'format'           => 'h : i A',
+                'addEmptyOption'   => true,
+                'emptyOptionValue' => '',
+                'emptyOptionText'  => array(
+                    'h' => 'hh',
+                    'i' => 'mm',
+                    'A' => 'am/pm'
+                ),
+                'optionIncrement' => array(
+                    'i' => 15,
+                ),
+            ),
+            'error' => 'ERROR: You must select a start time!',
+        );
+        $e[] = array(
+            'type'    => 'date',
+            'req'     => false,
+            'name'    => 'etime',
+            'display' => 'End Time',
+            'opts'    => array(
+                'format'           => 'h : i A',
+                'addEmptyOption'   => true,
+                'emptyOptionValue' => '',
+                'emptyOptionText'  => array(
+                    'h' => 'hh',
+                    'i' => 'mm',
+                    'A' => 'am/pm'
+                ),
+                'optionIncrement' => array(
+                    'i'  => 15,
+                ),
+            ),
+        );
+        $e[] = array(
+            'type'    => 'header',
+            'name'    => 'eventInfoHdr',
+            'display' => 'Event Information'
+        );
+        $e[] = array(
+            'type'    => 'select',
+            'req'     => true,
+            'name'    => 'category',
+            'display' => 'Category',
+            'opts'    => $this->getTopicFields(),
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => 'website',
+            'display' => 'Event Website'
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => 'facebook',
+            'display' => 'Facebook',
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => 'twitter',
+            'display' => 'Twitter',
+        );
+
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => 'cost',
+            'display' => 'Cost',
+        );
+
+        $e[] = array(
+            'type'    => 'textarea',
+            'req'     => false,
+            'name'    => 'description',
+            'display' => 'Event Description',
+            'opts'    => array('id' => 'description')
+        );
+        $e[] = array(
+            'type'    => 'header',
+            'name'    => 'eventLocationInfoHeader_rmv',
+            'display' => 'Event Location Information
+                <div id="map-dialog">
+                    <div id="map_canvas" style="width:500px; height:400px"></div>
+                </div>
+                <a id="map-it" href="#">Map It</a>'
+        );
+        $e[] = array(
+            'type' => 'hidden',
+            'req'  => false,
+            'name' => 'lat',
+            'opts' => array('id' => 'lat')
+        );
+        $e[] = array(
+            'type' => 'hidden',
+            'req'  => false,
+            'name' => 'lon',
+            'opts' => array('id' => 'lon')
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => true,
+            'name'    => 'place',
+            'display' => 'Place'
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => 'address',
+            'display' => 'Address',
+            'opts'    => array('id' => 'address')
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => true,
+            'name'    => 'city',
+            'display' => 'City',
+            'opts'    => array('id' => 'city')
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => 'state',
+            'display' => 'State',
+            'opts'    => array('id' => 'state')
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => 'zip',
+            'display' => 'ZIP',
+            'opts'    => array('id' => 'zip')
+        );
+        $e[] = array(
+            'type'    => 'header',
+            'name'    => 'eventContactHeader_rmv',
+            'display' => 'Event Contact Information'
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => 'contact_name',
+            'display' => 'Event Contact Person<br>(published on Web site)'
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => 'contact_email',
+            'display' => 'Contact Email<br>(published on Web site)'
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => true,
+            'name'    => 'contact_phone',
+            'display' => 'Contact Phone<br>(published on Web site)'
+        );
+
+        $e[] = array(
+            'type'    => 'header',
+            'name'    => 'adminInfoHdr',
+            'display' => 'Event Admin Information'
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => true,
+            'name'    => 'admin_contact',
+            'display' => 'Contact Name Submitting Event'
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => true,
+            'name'    => 'admin_org',
+            'display' => 'Organization Name Submitting Event'
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => true,
+            'name'    => 'admin_phone',
+            'display' => 'Phone'
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => true,
+            'name'    => 'admin_email',
+            'display' => 'Email Address'
+        );
+
+        if ($this->useCaptcha) {
+            $e[] = array(
+                'type'    => 'CAPTCHA_Image',
+                'req'     => false,
+                'name'    => 'captcha_question',
+                'display' => 'Verification code',
+                'opts'    => $this->captchaOptions
+            );
+            $e[] = array(
+                'type'    => 'text',
+                'req'     => true,
+                'name'    => 'captcha_rmv',
+                'display' => 'Enter verification code',
+            );
+        }
+        $e[] = array(
+            'type'    => 'submit',
+            'req'     => false,
+            'name'    => 'submit_rmv',
+            'display' => 'Submit',
+            'opts'    => array('class' => 'button')
+        );
+
+        $this->setupElements($e);
+    }
+
+    // }}}
+    // {{{     configureFilters()
+
+    /**
+     * Form filter definitions
+     *
+     * Applies a data filter for the given fields when the form is submitted
+     *
+     * @return void
+     * @access public
+     */
+    public function configureFilters()
+    {
+        $f = array();
+
+        $f[] = array(
+            'element' => '__ALL__',
+            'filter'  => 'trim'
+        );
+        $f[] = array(
+            'element' => 'website',
+            'filter'  => array('Toolkit_Common', 'filterURI')
+        );
+
+        $this->setupFilters($f);
+    }
+
+    // }}}
+    //  {{{ configureForm()
+
+    /**
+     * Helper function to handle setting up the form
+     *
+     * @return void
+     * @access public
+     */
+    public function configureForm()
+    {
+        $this->configureElements();
+        $this->configureFilters();
+        $this->configureRules();
+    }
+
+    //  }}}
+    // {{{     configureRules()
+
+    /**
+     * Form rule definitions
+     *
+     * Adds validation rules for the given fields
+     *
+     * @return void
+     * @access public
+     */
+    public function configureRules()
+    {
+        //     Form Rules
+        $r[] = array(
+            'element'    => 'topicid',
+            'message'    => 'ERROR: Invalid Category!',
+            'type'       => 'numeric',
+            'format'     => null,
+            'validation' => $this->validationType,
+            'reset'      => true,
+            'force'      => false
+        );
+        $r[]         = array(
+            'element' => 'email',
+            'message' => 'ERROR: Invalid Email Format!',
+            'type'    => 'checkEmail',
+            'format'  => array('use_rfc822' => true),
+            'validation' => $this->validationType,
+            'reset'      => true,
+            'force'      => false
+        );
+        $r[]         = array(
+            'element' => array('starting', 'ending'),
+            'message' => 'ERROR: Starting Date must be before Ending Date',
+            'type'    => 'callback',
+            'format'  => array(&$this, 'checkDateRange'),
+            'validation' => $this->validationType,
+            'reset'      => false,
+            'force'      => false
+        );
+        $r[]         = array(
+            'element' => 'starting',
+            'message' => 'ERROR: Invalid date!',
+            'type'    => 'callback',
+            'format'  => array(&$this, 'checkDate'),
+            'validation' => $this->validationType,
+            'reset'      => false,
+            'force'      => false
+        );
+        $r[]         = array(
+            'element' => 'ending',
+            'message' => 'ERROR: Invalid date!',
+            'type'    => 'callback',
+            'format'  => array(&$this, 'checkDate'),
+            'validation' => $this->validationType,
+            'reset'      => false,
+            'force'      => false
+        );
+        $r[]         = array(
+            'element' => 'website',
+            'message' => 'ERROR: Invalid URL format',
+            'type'    => 'checkURI',
+            'format'  => array(
+                'allowed_schemes' => array('http', 'https'),
+                'strict'     => false
+            ),
+            'validation' => $this->validationType,
+            'reset'      => false,
+            'force'      => false
+        );
+        $r[]         = array(
+            'element'    => 'contact_phone',
+            'message'    => 'ERROR: Invalid Phone Format (xxx) xxx - xxxx!',
+            'type'       => 'phone',
+            'format'     => null,
+            'validation' => $this->validationType,
+            'reset'      => true,
+            'force'      => false
+        );
+        if ($this->useCaptcha) {
+            $r[] = array(
+                'element'    => 'captcha_rmv',
+                'message'    => 'ERROR: What you entered didn\'t match!',
+                'type'       => 'CAPTCHA',
+                'format'     => $this->captchaQuestion,
+                'validation' => $this->validationType,
+                'reset'      => true,
+                'force'      => false
+            );
+        }
+
+        $this->setupRules($r);
+    }
+
+    // }}}
+    // {{{     getTopicFields()
+
+    /**
+     * get event topics
+     *
+     * @return array topics
+     * @access protected
+     */
+    protected function getTopicFields()
+    {
+        $categories = array('' => '- select -');
+        $catData = $this->eventMapper->fetchAllCategories();
+        foreach ($catData as $category) {
+            $categories[$category->getId()] = $category->getName();
+        }
+        return $categories;
+    }
+
+    // }}}
+    //  {{{ hasEndDate()
+
+    /**
+     * verifies if we have a valid end date to work with
+     *
+     * @param array $d end date
+     *
+     * @return boolean if the end date is
+     */
+    protected function hasEndDate($d)
+    {
+        $pattern = '/([0-9]{2})\/([0-9]{2})\/([0-9]{4})/';
+        if (preg_match($pattern, $d, $m)) {
+            return checkdate((int) $m[1], (int) $m[2], (int) $m[3]);
+        } else {
+            return false;
+        }
+    }
+
+    //  }}}
+    // {{{     emailOwner()
+
+    /**
+     * Emails the owner the submitted data from the submitted form
+     *
+     * Uses a flexy template to render a nice looking html email.
+     * Fills in the supplied data from the form and doesn't add the
+     * empty fields the user didn't fill in.
+     *
+     * @return boolean   result of the mailing
+     * @access protected
+     */
+    protected function emailOwner()
+    {
+        if (!$this->email) {
+            return;
+        }
+
+        $template = new HTML_Template_Flexy($this->flexyOptions);
+        $page     = new stdClass();
+        // for comments textarea need to replace newlines with br
+        $this->formData['comments']['element'] = nl2br($this->getSubmitValue('comments'));
+        unset($this->formData['lat'], $this->formData['lon']);
+
+        //  these values are required, therefor will always be part of
+        //  the formData array
+        $bdate = explode('/', $this->formData['bdate']['element']);
+        $foo   = array_map('trim', $bdate);
+        $this->formatValue($foo, '%02d/%02d/%d');
+        $this->formData['bdate']['element'] = $foo;
+
+        $btime = explode('/', $this->formData['btime']['element']);
+        $foo   = array_map('trim', $btime);
+        $this->formatValue($foo, '%02d:%02d %s');
+        $this->formData['btime']['element'] = $foo;
+
+        //  not required, so check to make sure it exists before trying
+        //  to format the value
+        if (isset($this->formData['edate'])) {
+            $edate = explode('/', $this->formData['edate']['element']);
+            $foo   = array_map('trim', $edate);
+            $this->formatValue($foo, '%02d/%02d/%d');
+            $this->formData['edate']['element'] = $foo;
+        }
+        if (isset($this->formData['etime'])) {
+            $etime = explode('/', $this->formData['etime']['element']);
+            $foo   = array_map('trim', $etime);
+            $this->formatValue($foo, '%02d:%02d %s');
+            $this->formData['etime']['element'] = $foo;
+        }
+
+        $category = $this->eventMapper->fetchCategory(
+            $this->formData['category']['element']
+        );
+        if ($category) {
+            $this->formData['category']['element'] = $category->getName();
+        }
+
+        $page->email_from = FROM_NEWS_EMAIL;
+        $page->subject = $this->subject;
+        $page->formData = $this->formData;
+        $page->eventAdminURL = MEDIA_BASE_URL . 'admin/CommonEvents/index.php?pending=1';
+
+        $template->compile('emailOwner.tpl');
+        $htmlMsg = $template->bufferedOutputObject($page);
+
+        //     Text version can't have HTML in it
+        $msg = "{$page->subject}\n\n";
+        $msg .= "From {$page->fname} {$page->lname}\n\n";
+        $msg .= "Information\n\n";
+        foreach ($page->formData as $i) {
+            $msg .= "{$i['label']}: {$i['element']}\n";
+        }
+
+        $crlf     = "\n";
+        $mimeMail = new Mail_mime($crlf);
+        $mimeMail->setFrom("{$this->siteName} <{$page->email_from}>");
+        $mimeMail->setSubject($this->subject);
+        $mimeMail->setHTMLBody($htmlMsg);
+        $mimeMail->setTXTBody($msg);
+
+        $mail    = Mail::factory('mail');
+        $body    = $mimeMail->get();
+        $headers = $mimeMail->headers();
+
+        $res = $mail->send($this->email, $headers, $body);
+        if (PEAR::isError($res)) {
+            return Toolkit_Common::handleError($res);
+        } else {
+            return $res;
+        }
+    }
+
+    // }}}
+    //  {{{ formatValue()
+
+    /**
+     * Format an array into an acceptable string
+     *
+     * @param mixed  &$i     array values to format or null value for
+     *                       element that was not filled in
+     * @param string $format string to format values into
+     *
+     * @return string formatted string
+     * @access public
+     */
+    public function formatValue(&$i, $format)
+    {
+        //  Allow for 0 to be not empty.  This allows for minutes in the
+        //  time arrays to be valid if they are on the hour ie. (1:00 pm)
+        $notEmpty = create_function('$v', 'return strlen($v) > 0;');
+        if (is_array($i) && count(array_filter($i, $notEmpty)) == 3) {
+            list($x, $y, $z) = array_values($i);
+            eval("\$i = sprintf('$format', $x, $y, $z);");
+        } else {
+            $i = null;
+        }
+    }
+
+    //  }}}
+    // {{{     insertData()
+
+    /**
+     * Inserts contact data into the contact db
+     *
+     * @param array $values submitted values
+     *
+     * @return object result of db insert query
+     * @access protected
+     */
+    protected function insertData($values)
+    {
+        $values = $this->_geocode($values);
+
+        try {
+            // need to set the dates up first
+            $this->formatValue($values['btime'], '%d:%02d %s');
+            $this->formatValue($values['etime'], '%d:%02d %s');
+            $values['description'] = nl2br($values['description']);
+            $values['website']   = preg_replace("/^(http:\/\/)/", "", $values['website']);
+            if ($values['btime']) {
+                $values['starthour'] = $values['btime'];
+                unset($values['btime']);
+            }
+            if ($values['etime']) {
+                $values['endhour'] = $values['etime'];
+                unset($values['etime']);
+            }
+            if ($values['category']) {
+                $values['category'] = $this->eventMapper->fetchCategory(
+                    $values['category']
+                );
+            }
+            if (defined('MEMBERS_DB') && MEMBERS_DB) {
+                $event = MemberEvent::createByValues($values);
+            } else {
+                $event = Event::createByValues($values);
+            }
+            return $this->eventMapper->saveEvent($event);
+
+            return false;
+        } catch (PDOException $e) {
+            return Toolkit_Common::handleError($e);
+        }
+    }
+
+    // }}}
+    private function _geocode(array $values)
+    {
+        $geocoder = new GeocodeYahoo();
+        if (!$values['address'] && !$values['city'] && !$values['state']) {
+            return $values;
+        }
+        $address = array(
+                       'city'  => $values['city'],
+                       'state' => $values['state'],
+                       'zip'   => $values['zip'],
+               );
+               if (!empty($values['address'])) {
+                       $address['street'] = $values['address'];
+               }
+        try {
+            $response = $geocoder->geocodeAddress($address);
+            $responseArray = unserialize($response);
+            if ($responseArray['ResultSet']['Result'][0]['Latitude']) {
+                $values['lat'] = $responseArray['ResultSet']['Result'][0]['Latitude'];
+                $values['lon'] = $responseArray['ResultSet']['Result'][0]['Longitude'];
+            } else {
+                $values['lat'] = $responseArray['ResultSet']['Result']['Latitude'];
+                $values['lon'] = $responseArray['ResultSet']['Result']['Longitude'];
+            }
+
+            return $values;
+        } catch (BadMethodCallException $e) {
+            Toolkit_Logger::logException('Invalid Arg', $e);
+        } catch (Exception $e) {
+            Toolkit_Logger::logException('Yahoo GeoCode', $e);
+        }
+
+    }
+    // {{{     processData()
+
+    /**
+     * Handles how to process the form when submitted
+     *
+     * @param array $values Form submitted values
+     *
+     * @return array Result of Insert / Update function
+     * @access protected
+     */
+    public function processData($values)
+    {
+        //     Form data used for the insert/update sql queries and
+        //     the form email.
+        $e = array();
+        $this->setFormData($e);
+
+        //     Get rid of any defined un-needed elements.
+        //     un-needed elements after the form is submitted are defined
+        //     by the ending _rmv name.
+        foreach ($values as $k => &$v) {
+            if (!is_array($v)) {
+                $values[$k] = preg_replace("/\r/", "\n", $v);
+            }
+            if (preg_match('/^.+_rmv$/', $k)) {
+                unset($values[$k]);
+            }
+        }
+        $values['create_date'] = date('m/d/Y');
+
+        return $this->insertData($values);
+    }
+
+    // }}}
+    // {{{     setupRenderers()
+
+    /**
+     * Custom rendering templates for special fields on the form
+     *
+     * @return void
+     * @access protected
+     */
+    protected function setupRenderers()
+    {
+        $formConfig = new Zend_Config_Ini(
+            BASE . 'Toolkit/Events/application.ini',
+            strtolower($_ENV['GLM_HOST_ID'])
+        );
+
+        $renderer = new HTML_QuickForm_Renderer_Object(true);
+        $this->accept($renderer);
+        $this->template = new HTML_Template_Flexy(
+            $formConfig->flexyOptions->toArray()
+        );
+
+        $this->view = $this;
+        $this->view->form = $renderer->toObject();
+        $this->template->compile($this->formTemplate);
+    }
+
+    // }}}
+    // {{{     toHtml()
+
+    /**
+     * Handles how to display the current step the user is at in the form
+     *
+     * destroying and resetting the captcha value dis-allows someone from
+     * re-sending a form on a previous captcha.
+     *
+     * @return string form HTML state
+     * @access public
+     */
+    public function toHtml()
+    {
+        if ($this->validate()) {
+            $this->captchaQuestion->destroy();
+            $this->cleanForm();
+
+            if ($this->process(array(&$this, 'processData'), $this->mergeFiles)) {
+                $this->freeze();
+                $this->emailOwner();
+                $output = $this->successMsg;
+            }
+            $this->sent = true;
+        } elseif ($this->isSubmitted()) {
+            if ($this->useCaptcha) {
+                $this->captchaQuestion->destroy();
+                $this->captchaAnswer->setValue('');
+            }
+            $output                     = $this->errorMsg;
+            $GLOBALS['topScripts'][]
+                = '//code.jquery.com/ui/1.11.1/jquery-ui.min.js';
+            $GLOBALS['styleSheets'][]
+                = '//code.jquery.com/ui/1.11.1/themes/smoothness/jquery-ui.css';
+            $GLOBALS['bottomScripts'][] = MEDIA_APP_BASE_URL . 'libjs/textlimit.js';
+            $GLOBALS['topScripts'][]
+                = 'http://maps.googleapis.com/maps/api/js?sensor=true';
+            $GLOBALS['bottomScripts'][]
+                = MEDIA_BASE_URL . 'Toolkit/Maps/geoCoder.js';
+            $GLOBALS['bottomScripts'][]
+                = MEDIA_BASE_URL . 'Toolkit/Events/libjs/addEvent.js';
+            $output  = $this->errorMsg;
+            $this->setupRenderers();
+            $output .= $this->template->bufferedOutputObject($this->view);
+        } else {
+            if ($this->useCaptcha) {
+                $this->captchaQuestion->destroy();
+                $this->captchaAnswer->setValue('');
+            }
+            $GLOBALS['topScripts'][]
+                = '//code.jquery.com/ui/1.11.1/jquery-ui.min.js';
+            $GLOBALS['styleSheets'][]
+                = '//code.jquery.com/ui/1.11.1/themes/smoothness/jquery-ui.css';
+            $GLOBALS['bottomScripts'][] = MEDIA_APP_BASE_URL . 'libjs/textlimit.js';
+            $GLOBALS['topScripts'][]
+                = 'http://maps.googleapis.com/maps/api/js?sensor=true';
+            $GLOBALS['bottomScripts'][]
+                = MEDIA_BASE_URL . 'Toolkit/Maps/geoCoder.js';
+            $GLOBALS['bottomScripts'][]
+                = MEDIA_BASE_URL . 'Toolkit/Events/libjs/addEvent.js';
+            $this->setupRenderers();
+            $output .= $this->template->bufferedOutputObject($this->view);
+        }
+        return $output;
+    }
+
+    // }}}
+}
+
+?>
diff --git a/Toolkit/Events/AddEventForm.php b/Toolkit/Events/AddEventForm.php
new file mode 100755 (executable)
index 0000000..a034874
--- /dev/null
@@ -0,0 +1,1037 @@
+<?php
+
+//     vim:set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker syntax=php:
+
+/**
+ * New Event Form
+ *
+ * PHP version 5
+ *
+ * @category Toolkit
+ * @package  Events
+ * @author   Steve Sutton <steve@gaslightmedia.com>
+ * @license  http://www.gaslightmedia.com Gaslightmedia
+ * @version  CVS: $Id: AddEventForm.php,v 1.20 2010/07/04 23:58:22 jamie Exp $
+ * @link     http://demo.gaslightmedia.com
+ */
+
+/**
+ * New Event Form
+ *
+ * @category  Toolkit
+ * @package   Events
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @link      http://demo.gaslightmedia.com
+ */
+class Toolkit_Events_AddEventForm
+    extends Toolkit_FormBuilder
+    implements Toolkit_Form
+{
+    // {{{     properties
+
+    /**
+     * Table in Database which holds the contact data
+     *
+     * @var    string
+     * @access public
+     */
+    public $tableName = 'event';
+
+    /**
+     * Table meta data
+     *
+     * This is used when inserting/updating data for the records
+     * so the PDO's can use explicit data types for the parameters.
+     *
+     * @var    array
+     * @access public
+     */
+    public $tableMetaData;
+
+    /**
+     * Who to send the email to when the contact form is submitted
+     *
+     * If you leave this blank, its value will get set to the OWNER_EMAIL
+     * in the constructor.
+     *
+     * If you ***DO NOT*** want any emails to go out when the form is submitted
+     * then set the value to false. Do not set it to 0 for false, because the
+     * check uses a strict type check to determine if the value is actually
+     * false. This is what allows for the empty value as an option, which sets
+     * the value to OWNER_EMAIL and won't override the $email property if
+     * this class gets subclassed and the value for this property gets set in
+     * the properties of the subclass and not in the constructor after this
+     * constructor function is called.
+     *
+     * tongue twister...I know.
+     * <code>
+     * protected $email = false;
+     * </code>
+     *
+     * @var    unknown
+     * @access protected
+     */
+    protected $email;
+
+    /**
+     * From header in the owner email
+     *
+     * This just sets the From header in the owner email
+     * SITENAME <from@email.com>
+     *
+     * It gets set to the constant SITENAME in the constructor if you leave
+     * empty here, but you can set it to something different here to override
+     * that if you desire.
+     *
+     * @var    unknown
+     * @access protected
+     */
+    protected $siteName;
+
+    /**
+     * Email subject and <h1> header in email
+     *
+     * It gets set in the constructor if you leave empty here, but you
+     * can set it to something different here to override that if you desire.
+     *
+     * @var    string
+     * @access protected
+     */
+    protected $subject = 'New Event Submission';
+
+    /**
+     * Message to display if the form is successfully submitted
+     *
+     * @var    string
+     * @access protected
+     */
+    protected $successMsg = '
+    <style type="text/css">
+        #category {display:none};
+        .listings {display:none};
+    </style>
+               <div id="form-sucess-top">
+            Your event has been successfully added to the events calendar,
+            however will not be visible until it has been approved by
+            the Web site administrator. Thank You.
+               </div>';
+
+    /**
+     * Extra rules for processesing
+     *
+     * This registers the Zip validation rules (and any others listed) for
+     * QuickForm.
+     *
+     * Zip validation checks both US and Canadian Zip codes
+     *
+     * @var    array
+     * @access protected
+     */
+    protected $registeredRules = array(
+        'phone',
+        array(
+            'checkEmail',
+            'callback',
+            'email',
+            'Validate'
+        ),
+        array(
+            'checkURI',
+            'callback',
+            'uri',
+            'Validate'
+        )
+    );
+
+    /**
+     * Options for flexy templating engine
+     *
+     * Pulls the preset options from the setup.phtml file
+     * overwrites the templateDir and compileDir to match this classes needs
+     *
+     * @var    array
+     * @access protected
+     */
+    protected $flexyOptions;
+
+    // }}}
+    // {{{     __construct()
+
+    /**
+     * Class constructor
+     *
+     * @param PDO    $pdo         PHP Data Object
+     * @param string $formName    Form's name.
+     * @param string $method      (optional)Form's method defaults to 'POST'
+     * @param string $action      (optional)Form's action
+     * @param string $target      (optional)Form's target defaults to '_self'
+     * @param mixed  $attributes  (optional)Extra attributes for <form> tag
+     * @param bool   $trackSubmit (optional)Whether to track if the form was
+     *                                                           submitted by adding a special hidden field
+     *
+     * @access public
+     */
+    public function __construct(
+        PDO $pdo,
+        $formName,
+        $method = 'post',
+        $action = '',
+        $target = '',
+        $attributes = null,
+        $trackSubmit = false
+    ) {
+        parent::__construct(
+            $formName, $method, $action, $target, $attributes, $trackSubmit
+        );
+
+        $this->dbh = $pdo;
+
+        if ($this->email !== false && empty($this->email)) {
+            // Set to false to turn off email function.
+            $this->email = ADD_EVENT_EMAIL;
+        }
+        if (empty($this->siteName)) {
+            $this->siteName = SITENAME;
+        }
+        if (empty($this->subject)) {
+            $this->subject = 'Contact Request from website ' . SITENAME;
+        }
+
+        $this->flexyOptions = $GLOBALS['flexyOptions'];
+        $this->flexyOptions['templateDir'] = dirname(__FILE__) . "/templates/";
+        $this->flexyOptions['compileDir'] = dirname(__FILE__) . "/templates/compiled/";
+
+        $var = basename(__FILE__, '.php');
+
+        $callbackUrl = ($_SERVER['HTTPS'] == 'on')
+            ?
+            BASE_SECURE_URL
+            : MEDIA_BASE_URL;
+
+        $this->captchaOptions = array(
+            'width'        => 100,
+            'height'       => 50,
+            'callback'     => "{$callbackUrl}Toolkit/qfcaptcha.php?var=$var",
+            'sessionVar'   => $var,
+            'imageOptions' => array(
+                'font_size'        => 16,
+                'font_path'        => GLM_APP_BASE . 'glmPEAR/Image/Canvas/Fonts/',
+                'font_file'        => 'times.ttf',
+                'background_color' => '#cccccc',
+                'obfuscation'      => false,
+                'angle'            => true,
+            ),
+        );
+    }
+
+    // }}}
+    //  {{{ checkDate()
+
+    /**
+     * Validate date input
+     *
+     * allows for empty dates to be valid
+     *
+     * @param array $date date group from form
+     *
+     * @return boolean true if valid, false if not
+     * @access public
+     */
+    public function checkDate($date)
+    {
+        if (!$date) {
+            return true;
+        } else {
+            return Validate::date($date, array('format' => '%m/%d/%Y'));
+        }
+    }
+
+    //  }}}
+    //  {{{ checkDateRange()
+
+    /**
+     * Validate date input
+     *
+     * allows for empty end date to be valid
+     *
+     * @param array $d date group from form
+     *
+     * @return boolean true if valid, false if not
+     * @access public
+     */
+    public function checkDateRange(array $d)
+    {
+        if (!$this->hasEndDate($d[1])) {
+            //  no end date is a valid date range
+            return true;
+        }
+        $pattern = '/([0-9]{2})\/([0-9]{2})\/([0-9]{4})/';
+        if (preg_match($pattern, $d[0], $m)) {
+            $t1 = mktime(0, 0, 0, (int) $m[1], (int) $m[2], (int) $m[3]);
+            $bdate = new Date($t1);
+        }
+        if (preg_match($pattern, $d[1], $m)) {
+            $t2    = mktime(0, 0, 0, (int) $m[1], (int) $m[2], (int) $m[3]);
+            $edate = new Date($t2);
+        }
+        if ($bdate && $edate) {
+            //  0 if the dates are equal - valid
+            // -1 if $bdate is before $edate - valid
+            //  1 if $bdate is after $edate - invalid
+            $res = Date::compare($bdate, $edate);
+            return ($res !== 1);
+        }
+        return true;
+    }
+
+    //  }}}
+    // {{{     configureElements()
+
+    /**
+     * Form element definitions
+     *
+     * @return void
+     * @access public
+     */
+    public function configureElements()
+    {
+        $e = array();
+
+        //     All Elements are created here.  This includes group element definitions.
+        $e[] = array(
+            'type'    => 'header',
+            'name'    => 'eventInfoHdr',
+            'display' => 'Event Information'
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => true,
+            'name'    => 'header',
+            'display' => 'Event Name'
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => true,
+            'name'    => 'bdate',
+            'display' => 'Start Date',
+            'opts'     => array('id' => 'sdate')
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => true,
+            'name'    => 'edate',
+            'display' => 'End Date',
+            'opts'     => array('id' => 'edate')
+        );
+        $e[] = array(
+            'type'    => 'date',
+            'req'     => false,
+            'name'    => 'btime',
+            'display' => 'Start Time',
+            'opts'    => array(
+                'format'           => 'h : i A',
+                'addEmptyOption'   => true,
+                'emptyOptionValue' => '',
+                'emptyOptionText'  => array(
+                    'h'               => 'hh',
+                    'i'               => 'mm',
+                    'A'               => 'am/pm'
+                ),
+                'optionIncrement' => array(
+                    'i'     => 15,
+                ),
+            ),
+            'error' => 'ERROR: You must select a start time!',
+        );
+        $e[] = array(
+            'type'    => 'date',
+            'req'     => false,
+            'name'    => 'etime',
+            'display' => 'End Time',
+            'opts'    => array(
+                'format'           => 'h : i A',
+                'addEmptyOption'   => true,
+                'emptyOptionValue' => '',
+                'emptyOptionText'  => array(
+                    'h'               => 'hh',
+                    'i'               => 'mm',
+                    'A'               => 'am/pm'
+                ),
+                'optionIncrement' => array(
+                    'i'  => 15,
+                ),
+            ),
+        );
+        $e[] = array(
+            'type'    => 'select',
+            'req'     => true,
+            'name'    => 'topicid',
+            'display' => 'Category',
+            'opts'    => $this->getTopicFields(),
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => 'url',
+            'display' => 'Event Website'
+        );
+
+        $e[] = array(
+            'type'    => 'text',
+            'req' => false,
+            'name' => 'cost',
+            'display' => 'Cost',
+        );
+        $e[] = array(
+            'type'    => 'textarea',
+            'req'     => false,
+            'name'    => 'descr',
+            'display' => 'Event Description',
+            'opts'    => array('id' => 'description')
+        );
+        $e[] = array(
+            'type'    => 'header',
+            'name'    => 'eventLocationInfoHeader_rmv',
+            'display' => 'Event Location Information
+                <div id="map-dialog">
+                    <div id="map_canvas" style="width:500px; height:400px"></div>
+                </div>
+                <a id="map-it" href="#">Map It</a>'
+        );
+        $e[] = array(
+            'type' => 'hidden',
+            'req'  => false,
+            'name' => 'lat',
+            'opts' => array('id' => 'lat')
+        );
+        $e[] = array(
+            'type' => 'hidden',
+            'req'  => false,
+            'name' => 'lon',
+            'opts' => array('id' => 'lon')
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => true,
+            'name'    => 'loc',
+            'display' => 'Place'
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => 'address',
+            'display' => 'Address'
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => true,
+            'name'    => 'city',
+            'display' => 'City'
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => 'state',
+            'display' => 'State'
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => 'zip',
+            'display' => 'ZIP'
+        );
+        $e[] = array(
+            'type'    => 'header',
+            'name'    => 'eventContactHeader_rmv',
+            'display' => 'Event Contact Information'
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => 'contact',
+            'display' => 'Event Contact Person<br>(published on Web site)'
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => 'email',
+            'display' => 'Contact Email<br>(published on Web site)'
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => true,
+            'name'    => 'phone',
+            'display' => 'Contact Phone<br>(published on Web site)'
+        );
+
+        $e[] = array(
+            'type'    => 'header',
+            'name'    => 'adminInfoHdr',
+            'display' => 'Event Admin Information'
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => true,
+            'name'    => 'admin_contact_name',
+            'display' => 'Contact Name Submitting Event'
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => true,
+            'name'    => 'admin_org_name',
+            'display' => 'Organization Name Submitting Event'
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => true,
+            'name'    => 'admin_phone',
+            'display' => 'Phone'
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => true,
+            'name'    => 'admin_email',
+            'display' => 'Email Address'
+        );
+
+        if ($this->useCaptcha) {
+            $e[] = array(
+                'type'    => 'CAPTCHA_Image',
+                'req'     => false,
+                'name'    => 'captcha_question',
+                'display' => 'Verification code',
+                'opts'    => $this->captchaOptions
+            );
+            $e[] = array(
+                'type'    => 'text',
+                'req'     => true,
+                'name'    => 'captcha_rmv',
+                'display' => 'Enter verification code',
+            );
+        }
+        $e[] = array(
+            'type'    => 'submit',
+            'req'     => false,
+            'name'    => 'submit_rmv',
+            'display' => 'Submit'
+        );
+
+        $this->setupElements($e);
+    }
+
+    // }}}
+    // {{{     configureFilters()
+
+    /**
+     * Form filter definitions
+     *
+     * Applies a data filter for the given fields when the form is submitted
+     *
+     * @return void
+     * @access public
+     */
+    public function configureFilters()
+    {
+        $f = array();
+
+        $f[] = array(
+            'element' => '__ALL__',
+            'filter'  => 'trim'
+        );
+        $f[]      = array(
+            'element' => 'url',
+            'filter'  => array('Toolkit_Common', 'filterURI')
+        );
+
+        $this->setupFilters($f);
+    }
+
+    // }}}
+    //  {{{ configureForm()
+
+    /**
+     * Helper function to handle setting up the form
+     *
+     * @return void
+     * @access public
+     */
+    public function configureForm()
+    {
+        $this->configureElements();
+        $this->configureFilters();
+        $this->configureRules();
+    }
+
+    //  }}}
+    // {{{     configureRules()
+
+    /**
+     * Form rule definitions
+     *
+     * Adds validation rules for the given fields
+     *
+     * @return void
+     * @access public
+     */
+    public function configureRules()
+    {
+        //     Form Rules
+        $r[] = array(
+            'element'    => 'topicid',
+            'message'    => 'ERROR: Invalid Category!',
+            'type'       => 'numeric',
+            'format'     => null,
+            'validation' => $this->validationType,
+            'reset'      => true,
+            'force'      => false
+        );
+        $r[]         = array(
+            'element' => 'email',
+            'message' => 'ERROR: Invalid Email Format!',
+            'type'    => 'checkEmail',
+            'format'  => array('use_rfc822' => true),
+            'validation' => $this->validationType,
+            'reset'      => true,
+            'force'      => false
+        );
+        $r[]         = array(
+            'element' => array('bdate', 'edate'),
+            'message' => 'ERROR: Starting Date must be before Ending Date',
+            'type'    => 'callback',
+            'format'  => array(&$this, 'checkDateRange'),
+            'validation' => $this->validationType,
+            'reset'      => false,
+            'force'      => false
+        );
+        $r[]         = array(
+            'element' => 'bdate',
+            'message' => 'ERROR: Invalid date!',
+            'type'    => 'callback',
+            'format'  => array(&$this, 'checkDate'),
+            'validation' => $this->validationType,
+            'reset'      => false,
+            'force'      => false
+        );
+        $r[]         = array(
+            'element' => 'edate',
+            'message' => 'ERROR: Invalid date!',
+            'type'    => 'callback',
+            'format'  => array(&$this, 'checkDate'),
+            'validation' => $this->validationType,
+            'reset'      => false,
+            'force'      => false
+        );
+        $r[]         = array(
+            'element' => 'url',
+            'message' => 'ERROR: Invalid URL format',
+            'type'    => 'checkURI',
+            'format'  => array(
+                'allowed_schemes' => array('http', 'https'),
+                'strict'     => true
+            ),
+            'validation' => $this->validationType,
+            'reset'      => false,
+            'force'      => false
+        );
+        $r[]         = array(
+            'element'    => 'phone',
+            'message'    => 'ERROR: Invalid Phone Format (xxx) xxx - xxxx!',
+            'type'       => 'phone',
+            'format'     => null,
+            'validation' => $this->validationType,
+            'reset'      => true,
+            'force'      => false
+        );
+        if ($this->useCaptcha) {
+            $r[] = array(
+                'element'    => 'captcha_rmv',
+                'message'    => 'ERROR: What you entered didn\'t match!',
+                'type'       => 'CAPTCHA',
+                'format'     => $this->captchaQuestion,
+                'validation' => $this->validationType,
+                'reset'      => true,
+                'force'      => false
+            );
+        }
+
+        $this->setupRules($r);
+    }
+
+    // }}}
+    // {{{     getTopicFields()
+
+    /**
+     * get event topics
+     *
+     * @return array topics
+     * @access protected
+     */
+    protected function getTopicFields()
+    {
+        try {
+            $sql = "
+                SELECT *
+                  FROM topic
+                 ORDER BY descr";
+
+            $topics = array('' => '-- Select --');
+            foreach ($this->dbh->query($sql) as $row) {
+                $topics[$row['id']] = $row['descr'];
+            }
+
+            return $topics;
+        } catch (PDOException $e) {
+            return Toolkit_Common::handleError($e);
+        }
+    }
+
+    // }}}
+    //  {{{ hasEndDate()
+
+    /**
+     * verifies if we have a valid end date to work with
+     *
+     * @param array $d end date
+     *
+     * @return boolean if the end date is
+     */
+    protected function hasEndDate($d)
+    {
+        $pattern = '/([0-9]{2})\/([0-9]{2})\/([0-9]{4})/';
+        if (preg_match($pattern, $d, $m)) {
+            return checkdate((int) $m[1], (int) $m[2], (int) $m[3]);
+        } else {
+            return false;
+        }
+    }
+
+    //  }}}
+    // {{{     emailOwner()
+
+    /**
+     * Emails the owner the submitted data from the submitted form
+     *
+     * Uses a flexy template to render a nice looking html email.
+     * Fills in the supplied data from the form and doesn't add the
+     * empty fields the user didn't fill in.
+     *
+     * @return boolean   result of the mailing
+     * @access protected
+     */
+    protected function emailOwner()
+    {
+        if (!$this->email) {
+            return;
+        }
+
+        $template = new HTML_Template_Flexy($this->flexyOptions);
+        $page     = new stdClass();
+        // for comments textarea need to replace newlines with br
+        $this->formData['comments']['element'] = nl2br($this->getSubmitValue('comments'));
+        unset($this->formData['lat'], $this->formData['lon']);
+
+        //  these values are required, therefor will always be part of
+        //  the formData array
+        $bdate = explode('/', $this->formData['bdate']['element']);
+        $foo   = array_map('trim', $bdate);
+        $this->formatValue($foo, '%02d/%02d/%d');
+        $this->formData['bdate']['element'] = $foo;
+
+        $btime = explode('/', $this->formData['btime']['element']);
+        $foo   = array_map('trim', $btime);
+        $this->formatValue($foo, '%02d:%02d %s');
+        $this->formData['btime']['element'] = $foo;
+
+        //  not required, so check to make sure it exists before trying
+        //  to format the value
+        if (isset($this->formData['edate'])) {
+            $edate = explode('/', $this->formData['edate']['element']);
+            $foo   = array_map('trim', $edate);
+            $this->formatValue($foo, '%02d/%02d/%d');
+            $this->formData['edate']['element'] = $foo;
+        }
+        if (isset($this->formData['etime'])) {
+            $etime = explode('/', $this->formData['etime']['element']);
+            $foo   = array_map('trim', $etime);
+            $this->formatValue($foo, '%02d:%02d %s');
+            $this->formData['etime']['element'] = $foo;
+        }
+
+        try {
+            $sql = "
+                SELECT descr
+                  FROM topic
+                 WHERE id = :id";
+
+            $stmt = $this->dbh->prepare($sql);
+            $stmt->bindParam(
+                ':id', $this->formData['topicid']['element'], PDO::PARAM_INT
+            );
+            $stmt->execute();
+            $stmt->bindColumn('descr', $this->formData['topicid']['element']);
+            $stmt->fetch();
+        } catch (PDOException $e) {
+            return Toolkit_Common::handleError($e);
+        }
+
+        $page->email_from = FROM_NEWS_EMAIL;
+        $page->subject = $this->subject;
+        $page->formData = $this->formData;
+        $page->eventAdminURL = MEDIA_BASE_URL . 'admin/Events/list_events.phtml?pending=t';
+
+        $template->compile('emailOwner.tpl');
+        $htmlMsg = $template->bufferedOutputObject($page);
+
+        //     Text version can't have HTML in it
+        $msg = "{$page->subject}\n\n";
+        $msg .= "From {$page->fname} {$page->lname}\n\n";
+        $msg .= "Information\n\n";
+        foreach ($page->formData as $i) {
+            $msg .= "{$i['label']}: {$i['element']}\n";
+        }
+
+        $crlf     = "\n";
+        $mimeMail = new Mail_mime($crlf);
+        $mimeMail->setFrom("{$this->siteName} <{$page->email_from}>");
+        $mimeMail->setSubject($this->subject);
+        $mimeMail->setHTMLBody($htmlMsg);
+        $mimeMail->setTXTBody($msg);
+
+        $mail    = Mail::factory('mail');
+        $body    = $mimeMail->get();
+        $headers = $mimeMail->headers();
+
+        $res = $mail->send($this->email, $headers, $body);
+        if (PEAR::isError($res)) {
+            return Toolkit_Common::handleError($res);
+        } else {
+            return $res;
+        }
+    }
+
+    // }}}
+    //  {{{ formatValue()
+
+    /**
+     * Format an array into an acceptable string
+     *
+     * @param mixed  &$i     array values to format or null value for
+     *                       element that was not filled in
+     * @param string $format string to format values into
+     *
+     * @return string formatted string
+     * @access public
+     */
+    public function formatValue(&$i, $format)
+    {
+        //  Allow for 0 to be not empty.  This allows for minutes in the
+        //  time arrays to be valid if they are on the hour ie. (1:00 pm)
+        $notEmpty = create_function('$v', 'return strlen($v) > 0;');
+        if (is_array($i) && count(array_filter($i, $notEmpty)) == 3) {
+            list($x, $y, $z) = array_values($i);
+            eval("\$i = sprintf('$format', $x, $y, $z);");
+        } else {
+            $i = null;
+        }
+    }
+
+    //  }}}
+    // {{{     insertData()
+
+    /**
+     * Inserts contact data into the contact db
+     *
+     * @param array $values submitted values
+     *
+     * @return object result of db insert query
+     * @access protected
+     */
+    protected function insertData($values)
+    {
+        $values = $this->_geocode($values);
+
+        try {
+            // need to set the dates up first
+            $this->formatValue($values['btime'], '%02d:%02d %s');
+            $this->formatValue($values['etime'], '%02d:%02d %s');
+            $values['descr'] = nl2br($values['descr']);
+            $values['url']   = preg_replace("/^(http:\/\/)/", "", $values['url']);
+
+            $sql = Toolkit_Common::createSQLInsert(
+                $this->tableName, array_keys($values)
+            );
+
+            return Toolkit_Common::processQuery(
+                $this->dbh, $this->tableName, $sql, $values
+            );
+        } catch (PDOException $e) {
+            return Toolkit_Common::handleError($e);
+        }
+    }
+
+    // }}}
+    private function _geocode(array $values)
+    {
+        $geocoder = new GeocodeYahoo();
+        if (!$values['address'] && !$values['city'] && !$values['state']) {
+            return $values;
+        }
+        $address = array(
+                       'city'  => $values['city'],
+                       'state' => $values['state'],
+                       'zip'   => $values['zip'],
+               );
+               if (!empty($values['address'])) {
+                       $address['street'] = $values['address'];
+               }
+        try {
+            $response = $geocoder->geocodeAddress($address);
+            $responseArray = unserialize($response);
+            if ($responseArray['ResultSet']['Result'][0]['Latitude']) {
+                $values['lat'] = $responseArray['ResultSet']['Result'][0]['Latitude'];
+                $values['lon'] = $responseArray['ResultSet']['Result'][0]['Longitude'];
+            } else {
+                $values['lat'] = $responseArray['ResultSet']['Result']['Latitude'];
+                $values['lon'] = $responseArray['ResultSet']['Result']['Longitude'];
+            }
+
+            return $values;
+        } catch (BadMethodCallException $e) {
+            Toolkit_Logger::logException('Invalid Arg', $e);
+        } catch (Exception $e) {
+            Toolkit_Logger::logException('Yahoo GeoCode', $e);
+        }
+
+    }
+    // {{{     processData()
+
+    /**
+     * Handles how to process the form when submitted
+     *
+     * @param array $values Form submitted values
+     *
+     * @return array Result of Insert / Update function
+     * @access protected
+     */
+    public function processData($values)
+    {
+        //     Form data used for the insert/update sql queries and
+        //     the form email.
+        $e = array();
+        $this->setFormData($e);
+
+        //     Get rid of any defined un-needed elements.
+        //     un-needed elements after the form is submitted are defined
+        //     by the ending _rmv name.
+        foreach ($values as $k => &$v) {
+            if (!is_array($v)) {
+                $values[$k] = preg_replace("/\r/", "\n", $v);
+            }
+            if (preg_match('/^.+_rmv$/', $k)) {
+                unset($values[$k]);
+            }
+        }
+        $values['create_date'] = date('m/d/Y');
+
+        return $this->insertData($values);
+    }
+
+    // }}}
+    // {{{     setupRenderers()
+
+    /**
+     * Custom rendering templates for special fields on the form
+     *
+     * @return void
+     * @access protected
+     */
+    protected function setupRenderers()
+    {
+        parent::setupRenderers();
+        $renderer = $this->defaultRenderer();
+        $required = '<!-- BEGIN required --><span class="req">*</span><!-- END required -->';
+        $error    = '<!-- BEGIN error --><div class="req">{error}</div><!-- END error -->';
+
+        $renderer->setElementTemplate('<tr><td colspan="2">' . $required . '{label}' . $error . '{element}</td></tr>', 'descr');
+        $renderer->setElementTemplate('<tr align="center"><td colspan="2">' . $required . '{label}' . $error . '{element}</td></tr>', 'submit_rmv');
+
+        if ($this->useCaptcha) {
+            $renderer->setElementTemplate('<tr><td class="labelcell"><label>{label}</label></td><td class="fieldcell captcha">{element}</td></tr>', 'captcha_question');
+            $renderer->setElementTemplate('<tr><td class="labelcell">' . $required . '<label>{label}</label></td><td class="fieldcell">' . $error . '{element}<span class="tooltip" title="Verification Code|To help us distinguish between information submitted by individuals and those automatically entered by software robots, please type the letters shown.">What is this?</span></td></tr>', 'captcha_rmv');
+        }
+    }
+
+    // }}}
+    // {{{     toHtml()
+
+    /**
+     * Handles how to display the current step the user is at in the form
+     *
+     * destroying and resetting the captcha value dis-allows someone from
+     * re-sending a form on a previous captcha.
+     *
+     * @return string form HTML state
+     * @access public
+     */
+    public function toHtml()
+    {
+        $this->setupRenderers();
+        if ($this->validate()) {
+            $this->captchaQuestion->destroy();
+            $this->cleanForm();
+
+            if ($this->process(array(&$this, 'processData'), $this->mergeFiles)) {
+                $this->freeze();
+                $this->emailOwner();
+                $output = $this->successMsg;
+            }
+            $this->sent = true;
+        } elseif ($this->isSubmitted()) {
+            if ($this->useCaptcha) {
+                $this->captchaQuestion->destroy();
+                $this->captchaAnswer->setValue('');
+            }
+            $output                     = $this->errorMsg;
+            $GLOBALS['topScripts'][]
+                = MEDIA_APP_BASE_URL
+                . 'libjs/jqueryui/1.8.13/js/jquery-ui-1.8.13.custom.min.js';
+            $GLOBALS['styleSheets'][]
+                = MEDIA_APP_BASE_URL
+                . 'libjs/jqueryui/1.8.13/development-bundle/themes/base/jquery.ui.all.css';
+            $GLOBALS['bottomScripts'][] = MEDIA_APP_BASE_URL . 'libjs/textlimit.js';
+            $GLOBALS['topScripts'][]
+                = 'http://maps.googleapis.com/maps/api/js?sensor=true';
+            $GLOBALS['bottomScripts'][]
+                = MEDIA_BASE_URL . 'Toolkit/Maps/geoCoder.js';
+            $GLOBALS['bottomScripts'][]
+                = MEDIA_BASE_URL . 'Toolkit/Events/libjs/addEvent.js';
+            $output .= parent::toHtml();
+        } else {
+            if ($this->useCaptcha) {
+                $this->captchaQuestion->destroy();
+                $this->captchaAnswer->setValue('');
+            }
+            $GLOBALS['topScripts'][]
+                = MEDIA_APP_BASE_URL
+                . 'libjs/jqueryui/1.8.13/js/jquery-ui-1.8.13.custom.min.js';
+            $GLOBALS['styleSheets'][]
+                = MEDIA_APP_BASE_URL
+                . 'libjs/jqueryui/1.8.13/development-bundle/themes/base/jquery.ui.all.css';
+            $GLOBALS['bottomScripts'][] = MEDIA_APP_BASE_URL . 'libjs/textlimit.js';
+            $GLOBALS['topScripts'][]
+                = 'http://maps.googleapis.com/maps/api/js?sensor=true';
+            $GLOBALS['bottomScripts'][]
+                = MEDIA_BASE_URL . 'Toolkit/Maps/geoCoder.js';
+            $GLOBALS['bottomScripts'][]
+                = MEDIA_BASE_URL . 'Toolkit/Events/libjs/addEvent.js';
+            $output                     = parent::toHtml();
+        }
+        return $output;
+    }
+
+    // }}}
+}
+
+?>
diff --git a/Toolkit/Events/Ajax.php b/Toolkit/Events/Ajax.php
new file mode 100644 (file)
index 0000000..bd66920
--- /dev/null
@@ -0,0 +1,25 @@
+<?php
+/**
+ * Ajax.php
+ *
+ * PHP version 5.2
+ *
+ * @category  Toolkit
+ * @package   Events
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @version   SVN: $id$
+ * @link      <>
+ */
+require_once '../../setup.phtml';
+$month = ($_REQUEST['month']) ? $_REQUEST['month']: date('n');
+$year  = ($_REQUEST['year']) ? $_REQUEST['year']: date('Y');
+$cal   = new Toolkit_Events_SmallCal(
+    Toolkit_Database::getInstance(),
+    $month,
+    $year
+);
+// build the small calendar
+echo $cal->toHtml();
+?>
diff --git a/Toolkit/Events/Auxiliary.php b/Toolkit/Events/Auxiliary.php
new file mode 100644 (file)
index 0000000..7f2ddce
--- /dev/null
@@ -0,0 +1,79 @@
+<?php
+
+/**
+ * Auxiliary.php
+ *
+ * PHP version 5.2
+ *
+ * @category  Toolkit
+ * @package   Events
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @version   SVN: (0.1)
+ * @link      <>
+ */
+
+/**
+ * Toolkit_Events_Auxiliary
+ *
+ * Setup the event class so it can use the flexy setting for Events
+ * Sets up the flexyOptions for this class to use the templates directory
+ * in Toolkit_Events
+ *
+ * @category  Toolkit
+ * @package   Events
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @release   Release: (0.1)
+ * @link      <>
+ */
+class Toolkit_Events_Auxiliary
+{
+    /**
+     * Database Connection
+     * @var    PDO
+     * @access protected
+     */
+    protected $dbh;
+
+    /**
+     * Options for Flexy Templates
+     * @var    array
+     * @access protected
+     */
+    protected $flexyOptions;
+
+    /**
+     * __construct()
+     *
+     * @param PDO $pdo Database Connection
+     *
+     * @return void
+     * @access public
+     */
+    public function __construct(PDO $pdo)
+    {
+        // assign the global dbh to $this->dbh;
+        $this->dbh = $pdo;
+        $this->_setFlexyOptions();
+    }
+
+    /**
+     * setOptions
+     *
+     * @access public
+     * @return string
+     */
+    private function _setFlexyOptions()
+    {
+        $this->flexyOptions = $GLOBALS['flexyOptions'];
+        // set paths for templates
+        $this->flexyOptions['templateDir'] = dirname(__FILE__)
+            . '/templates';
+        $this->flexyOptions['compileDir']  = dirname(__FILE__)
+            . '/templates/compiled';
+    }
+}
+
diff --git a/Toolkit/Events/Calendar.php b/Toolkit/Events/Calendar.php
new file mode 100755 (executable)
index 0000000..2dfdf47
--- /dev/null
@@ -0,0 +1,750 @@
+<?php
+/**
+ * Calendar.php
+ *
+ * Event Calendar display
+ * used by class_events.inc to display calendar output.
+ *
+ * PHP version 5
+ *
+ * @category Toolkit
+ * @package  Events
+ * @author   Steve Sutton <steve@gaslightmedia.com>
+ * @license  Gaslight Media
+ * @version  Release: SVN:<svn_id>
+ * @link     <>
+ */
+
+/**
+ * Toolkit_Events_Calendar
+ *
+ * Display the events in a list view
+ *
+ * @category  Toolkit
+ * @package   Events
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2009 Gaslight Media
+ * @license   Gaslight Media
+ * @version   Release: @package_version@
+ * @link      <>
+ */
+class Toolkit_Events_Calendar
+{
+
+    // {{{ Class Properties
+    /**
+     * Description of $_rightLink
+     * @var    mixed
+     * @access private
+     */
+    private $_rightLink;
+
+    /**
+     * Description of $_leftLink
+     * @var    mixed
+     * @access private
+     */
+    private $_leftLink;
+
+    /**
+     * Description of $_eventsData
+     * @var    mixed
+     * @access private
+     */
+    private $_eventsData;
+
+    /**
+     * Description of $_colorArray
+     * @var    array
+     * @access private
+     */
+    private $_colorArray;
+
+    // }}}
+    // {{{ __construct()
+    /**
+     * Class constructor
+     *
+     * @param PDO $pdo PDO
+     *
+     * @access protected
+     * @return string
+     */
+    function __construct(PDO $pdo)
+    {
+        $this->dbh = $pdo;
+        $this->_leftLink = '<img src="'
+            . MEDIA_BASE_URL . 'images/left.gif" class="calleftarrow" border="0">';
+        $this->_rightLink = '<img src="'
+            . MEDIA_BASE_URL . 'images/right.gif" class="calrightarrow" border="0">';
+        /**
+         * $this->colorArray is used for the colors for this application.
+         * If these need to be changed then it might be better
+         * putting this in setup.phtml.
+         */
+        $this->_colorArray = array(
+            '#FFCCCC' => 'color1',
+            '#CC9999' => 'color2',
+            '#FF9999' => 'color3',
+            '#FFCCFF' => 'color4',
+            '#CC99CC' => 'color5',
+            '#FF99FF' => 'color6',
+            '#CCCCFF' => 'color7',
+            '#9999CC' => 'color8',
+            '#9999FF' => 'color9',
+            '#CCFFFF' => 'color10',
+            '#99CCCC' => 'color11',
+            '#99FFFF' => 'color12',
+            '#CCFFCC' => 'color13',
+            '#99CC99' => 'color14',
+            '#99FF99' => 'color15',
+            '#FFFFCC' => 'color16',
+            '#CCCC99' => 'color17',
+            '#FFFF99' => 'color18',
+            '#FFCC99' => 'color19',
+            '#FF99CC' => 'color20',
+            '#CC99FF' => 'color21',
+            '#99CCFF' => 'color22',
+            '#99FFCC' => 'color23',
+            '#CCFF99' => 'color24',
+            '#CCCCCC' => 'color25'
+        );
+    }
+
+    // }}}
+    // {{{ calendar()
+    /**
+     * calendar
+     *
+     * @param int    $month    month of the calendar
+     * @param int    $year     year of the calendar
+     * @param int    $size     size is 1 small 2 large
+     * @param string $href     href for the prev link
+     * @param string $href2    href for the next link
+     * @param mixed  $eventObj Pass the EventCalendar Obj
+     *
+     * @access public
+     * @return string
+     */
+    function calendar($month, $year, $size, $href, $href2, $eventObj)
+    {
+        // Change to month/year to display
+        if (!isset($month)) {
+            $month = date('m');
+        }
+        if (!isset($year)) {
+            $year      = date('Y');
+        }
+        $this->getEvents($month, $year);
+        $prev      = ($month - 1 < 1)
+            ? 12
+            : $month - 1;
+        $prev_year = ($month - 1 < 1)
+            ? $year - 1
+            : $year;
+        $next      = ($month + 1 > 12)
+            ? 1
+            : $month + 1;
+        $next_year = ($month + 1 > 12)
+            ? $year + 1
+            : $year;
+        switch ($size) {
+        case 1:
+            // small calendar
+            $caltable     = 'caltablesmall';
+            $caldayheader = 'caldayheadersmall';
+            $caltitle     = 'caltitle';
+            $calmonth     = 'calmonthsmall';
+            $calspacer    = 'calspacersmall';
+            $calday       = 'caldaysmall';
+            $calendar     = '<a href="' . $eventObj->pageName
+                . '&amp;year=' . $year . '&amp;month=' . $month . '">';
+            $calendar .= date("F", mktime(0, 0, 0, $month + 1, 0, $year));
+            $calendar .= '</a>';
+            break;
+        case 2:
+            $in_future    = date('Y') + 3;
+            $in_past      = date('Y') - 3;
+            if ($prev_year > $in_past) {
+                $prev_month = '<a href="'
+                    . $href . $prev . '&amp;year=' . $prev_year . '">'
+                    . $this->_leftLink . '&nbsp;Previous&nbsp;Month</a>';
+            }
+            if ($next_year < $in_future) {
+                $next_month   = '<a href="' . $href . $next . '&amp;year='
+                    . $next_year . '">' . $this->_rightLink . 'Next&nbsp;Month</a>';
+            }
+            // large calendar
+            $caltable     = 'caltable';
+            $caldayheader = 'caldayheader';
+            $caltitle     = 'caltitle';
+            $calmonth     = 'calmonth';
+            $calspacer    = 'calspacer';
+            $calday       = 'calday';
+            $week_titles  = array(
+                0 => "Sunday",
+                1 => "Monday",
+                2 => "Tuesday",
+                3 => "Wednesday",
+                4 => "Thursday",
+                5 => "Friday",
+                6 => "Saturday"
+            );
+            $calendar = date("F", mktime(0, 0, 0, $month + 1, 0, $year));
+            break;
+        }
+        // determine total number of days in a month
+        $totaldays = date(
+            "t", mktime(0, 0, 1, $month, 1, $year)
+        );
+        // build table
+        $out       = '';
+        $out .= '<table class="' . $caltable . '">';
+        if ($size == 1) {
+            $out .= '
+            <tr>
+                <td colspan="7" class="' . $calmonth . '"> ' . $calendar . ' '
+                . (($size == 2)
+                    ? $year
+                    : '') . '</td>
+            </tr>
+            ';
+        } else {
+            $out .= '
+            <tr>
+                <td class="' . $caltitle . '" colspan="2">' . $prev_month . '</td>
+                <td colspan="3" class="' . $calmonth . '"> ' . $calendar . ' '
+                . (($size == 2)
+                    ? $year
+                    : '') . '</td>
+                <td class="' . $caltitle . '" colspan="2">' . $next_month . '</td>
+            </tr>
+            ';
+        }
+        if ($size == 2) {
+            $out .= '<tr>';
+            for ($x = 0; $x < 7; $x++) {
+                $out .= '
+                <td class="' . $caldayheader . '">' . $week_titles[$x] . '</td>
+                ';
+            }
+            $out .= '
+            </tr>
+            ';
+        }
+        /**
+         * ensure that a number of blanks are put in so
+         * that the first day of the month
+         * lines up with the proper day of the week
+         */
+        $offset = date("w", mktime(0, 0, 0, $month, 0, $year)) + 1;
+        $out .= '
+        <tr>
+            ';
+        if ($offset > 0 && $offset != 7) {
+            $out .= str_repeat(
+                "<td class=\"" . $calspacer . "\">&nbsp;</td>", $offset
+            );
+        }
+        $offset = ($offset == 7)
+            ? 0
+            : $offset;
+        // start entering in the information
+        for ($day    = 1; $day <= $totaldays; $day++) {
+            $out .= '
+            <td class="' . $calday . '" valign="top">';
+            $datelink = $this->getEventData($month, $day, $year, $eventObj);
+            if ($size == 1 && $datelink) {
+                $out .= '<a href="' . $eventObj->pageName
+                    . '&amp;flat=1&amp;month=' . $month
+                    . '&amp;day=' . $day . '&amp;year=' . $year . '">'
+                    . $day . '</a>';
+            } elseif ($datelink) {
+                $out .= $day . '';
+                $out .= $datelink;
+            } else {
+                $out .= $day . '';
+            }
+            $out .= '</td>
+            ';
+            $offset++;
+            // if we're on the last day of the week, wrap to the other side
+            if ($offset > 6) {
+                $offset = 0;
+                $out .= '
+                </tr>
+                ';
+                if ($day < $totaldays) {
+                    $out .= '
+                    <tr>
+                    ';
+                }
+            }
+        }
+        // fill in the remaining spaces for the end of the month
+        // , just to make it look pretty
+        if ($offset > 0) {
+            $offset = 7 - $offset;
+        }
+        if ($offset > 0) {
+            $out .= str_repeat(
+                "<td class=\"" . $calspacer . "\">&nbsp;</td>", $offset
+            );
+        }
+        // end the table
+        $out .= '
+        </tr></table>
+        ';
+        return $out;
+    }
+
+    // }}}
+    // {{{ calTime()
+    /**
+     * calTime
+     *
+     * @param mixed $time timestamp
+     *
+     * @access public
+     * @return array
+     */
+
+    function calTime($time)
+    {
+        if (preg_match("/([0-9]{1,2}):?([0-9]{2})? ?([AP]M)/i", $time, $temp)) {
+            $out = (int) $temp[1];
+            $out
+                .= (!$temp[2])
+                ? ':00'
+                : ':' . $temp[2];
+            $out .= ' ' . $temp[3];
+        } else if (strtolower($time) == 'noon') {
+            $out = '12:00 pm';
+        }
+        return $out;
+    }
+
+    // }}}
+    // {{{ getEventData()
+    /**
+     * getEventData
+     *
+     * @param int   $month    number of monht
+     * @param int   $day      number of day
+     * @param int   $year     number of year
+     * @param mixed $eventObj Pass the EventCalendar Obj
+     *
+     * @access public
+     * @return boolean|array
+     */
+
+    function getEventData($month, $day, $year, $eventObj)
+    {
+        $month = (int) $month;
+        if ((int) $month < 10) {
+            $month = '0' . $month;
+        }
+        $day   = (int) $day;
+        if ((int) $day < 10) {
+            $day  = '0' . $day;
+        }
+        $date = $month . '-' . $day . '-' . $year;
+        if ($this->_eventsData[$date]) {
+            return $this->showEventHeaders($date, $eventObj);
+        } else {
+            return false;
+        }
+    }
+
+    // }}}
+    // {{{ getEventDates()
+    /**
+     * getEventDates
+     *
+     * @param mixed   $starttime unix timestamp start
+     * @param mixed   $endtime   unix timestamp end
+     * @param boolean $recur     bool true or false
+     * @param mixed   $format    php date format
+     *
+     * @access public
+     * @return array|void
+     */
+
+    function getEventDates($starttime, $endtime, $recur, $format = 'm-d-Y')
+    {
+        $events = $eWeeks = array();
+        if (is_array($recur)) {
+            if ($starttime == $endtime) {
+                return;
+            }
+            $daysow = $recur['dow'];
+            if ($recur['recur_week'] == 9) {
+                $fWeekNum = date("W", $starttime);
+                if (date('w', $starttime) == 0) {
+                    $fWeekNum++;
+                }
+                $lWeekNum = date("W", $endtime);
+                if (date('w', $endtime) == 0) {
+                    $lWeekNum++;
+                }
+                for ($fi = $fWeekNum; $fi <= $lWeekNum; $fi = $fi + 2) {
+                    $eWeeks[] = $fi;
+                }
+            }
+            $ord      = array();
+            for ($i = $starttime; $i <= $endtime; $i += 86400) {
+                if ($recur['recur_week'] != '') {
+                    if ($recur['dow']) {
+                        $ri = (int) 1;
+                        for ($r  = 0; $r < 7; $r++) {
+                            if ($daysow & $ri) {
+                                $ord[] = $this->ordinalDay(
+                                    $recur['recur_week'], $r, date('n', $i), date('Y', $i)
+                                );
+                            }
+                            $ri    = $ri << 1;
+                        }
+                    }
+                }
+                if ($recur['dom']) {
+                    if (date("j", $i) == $recur['dom']) {
+                        $events[] = date($format, $i);
+                    }
+                } elseif ($daysow) {
+                    $cur_dow = date("w", $i);
+                    switch ($cur_dow) {
+                    case 0:
+                        $cur_dow = 1;
+                        break;
+                    case 1:
+                        $cur_dow = 2;
+                        break;
+                    case 2:
+                        $cur_dow = 4;
+                        break;
+                    case 3:
+                        $cur_dow = 8;
+                        break;
+                    case 4:
+                        $cur_dow = 16;
+                        break;
+                    case 5:
+                        $cur_dow = 32;
+                        break;
+                    case 6:
+                        $cur_dow = 64;
+                        break;
+                    }
+                    if ((int) $cur_dow & $daysow) {
+                        $cDateWeek = date("W", $i);
+                        if (date('w', $time) == 0) {
+                            $cDateWeek++;
+                        }
+                        if ($recur['recur_week'] == 9) {
+                            if (in_array($cDateWeek, $eWeeks)) {
+                                $events[] = date($format, $i);
+                            }
+                        } elseif ($recur['recur_week'] != '') {
+                            foreach ($ord as $o) {
+                                if ($recur['recur_week'] && $i && $o) {
+                                    if ($i == $o) {
+                                        $events[] = date($format, $i);
+                                    }
+                                }
+                            }
+                        } else {
+                            $events[] = date($format, $i);
+                        }
+                    }
+                }
+            }
+        }
+        return $events;
+    }
+
+    // }}}
+    // {{{ getEvents()
+    /**
+     * getEvents
+     *
+     * @param int $month number of monht
+     * @param int $year  number of year
+     *
+     * @access public
+     * @return string
+     */
+
+    function getEvents($month, $year)
+    {
+        $this->_leftLink = '<img src="' . MEDIA_BASE_URL
+            . 'images/left.gif" class="calleftarrow" border="0">';
+        $this->_rightLink = '<img src="'
+            . MEDIA_BASE_URL . 'images/right.gif" class="calrightarrow" border="0">';
+
+        /**
+         * set up the month day and year vars if not already set.
+         */
+        $month = (!isset($month) && $month != "All")
+            ? date("n")
+            : $month;
+        $year  = (!isset($year))
+            ? date("Y")
+            : $year;
+        if ($year <= 2000) {
+            return false;
+        }
+        $month = (preg_match("/^0([0-9]).*/", $month, $part))
+            ? $part[1]
+            : $month;
+        if (!is_numeric($month) || !is_numeric($year)) {
+            return false;
+        }
+        $st1 = mktime(0, 0, 0, $month, 1, $year);
+        $st2 = mktime(0, 0, 0, $month + 1, 1, $year);
+
+        /**
+         * build start and end timestamps for getting an array of events for the
+         * month.
+         */
+        $starting = date("m/d/Y", $st1);
+        $ending   = date("m/d/Y", $this->lastDayOfMonth($st2));
+
+        /**
+         * Grab the events for this month.
+         * NOTE: case statement in this query will order
+         * the ones that span more than 1 day.
+         */
+        $query = "
+        SELECT e.id,e.dayom,e.header,
+        bdate as sdate, e.edate as edate,e.weekom,
+        e.btime,e.etime,e.descr,e.loc,e.contact,e.email,e.url,
+        e.img,e.daysow,e.reacur,t.topiccolor,
+        case when e.reacur = cast(1 as boolean) then 0
+        when (e.edate - e.bdate) > 1
+               then (e.edate - e.bdate)
+        else 0
+        end as sortflag,e.all_day
+        FROM event e left outer join  topic t on (t.id = e.topicid)
+        WHERE e.visable = cast(1 as boolean)";
+        $query .= " AND         e.bdate <= '$ending'
+        AND         e.edate >= '$starting' $addpart";
+        $query .= "ORDER BY sortflag desc,e.bdate DESC,e.btime ASC";
+        try {
+            $events_data = $this->dbh->query($query)->fetchAll(PDO::FETCH_ASSOC);
+        } catch (PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+        // {{{ selection of events part
+        /**
+         * End of query and now we have the data and must arrange
+         * this to place in the calendar.
+         * This will result in an array like the following:
+         * $this->_eventsData['x-date'] = $row;
+         * in which x-data is the date of the event (ie.)$months['08-07-2005']
+         * and 4row is the $rom from the $evwents_data array
+         */
+        if (is_array($events_data)) {
+            foreach ($events_data as $key => $row) {
+                if ($row['reacur']) {
+                    $eData = $this->getEventDates(
+                        $st1, $st2, array(
+                        'dow'        => $row['daysow'],
+                        'recur_week' => $row['weekom'],
+                        'dom'        => $row['dayom']
+                        )
+                    );
+                    if (is_array($eData) && !empty($eData)) {
+
+                        $eventStartDate = strtotime($row['sdate']);
+                        $eventEndDate   = strtotime($row['edate']);
+                        foreach ($eData as $event) {
+                            list($eM, $eD, $eY) = explode("-", $event);
+                            $checkTime = strtotime("{$eM}/{$eD}/{$eY}");
+                            if ($checkTime >= $eventStartDate
+                                && $checkTime <= $eventEndDate
+                            ) {
+                                $this->_eventsData[$event][$row['id']] = $row;
+                            }
+                        }
+                    }
+                } else {
+                    for ($i = 1; $i <= 31; ++$i) {
+                        $checkTime      = mktime(
+                            0, 0, 0, $month, $i, $year
+                        );
+                        $eventStartDate = strtotime($row['sdate']);
+                        $eventEndDate   = strtotime($row['edate']);
+                        if ($checkTime >= $eventStartDate
+                            && $checkTime <= $eventEndDate
+                        ) {
+                            $date = date("m-d-Y", $checkTime);
+                            $this->_eventsData[$date][$row['id']] = $row;
+                        }
+                    }
+                }
+            }
+        }// }}}
+    }
+
+    // }}}
+    // {{{ disColor()
+    /**
+     * disColor
+     *
+     * <p>Goes back to color array and grabs the class for that peticular color.</p>
+     *
+     * @param string $color Color to use
+     *
+     * @return string class to use for style.
+     */
+
+    function disColor($color)
+    {
+        // get style class for this color
+        return $this->_colorArray[$color];
+    }
+
+    // }}}
+    // {{{ lastDayOfMonth()
+    /**
+     * lastDayOfMonth
+     *
+     * <p>given timestamp get the last day of the month it
+     * fall in</p>
+     *
+     * @param string $time Timestamp
+     *
+     * @return string timestamp
+     */
+    function lastDayOfMonth($time = '')
+    {
+        $timestamp = ($time == '')
+            ? time()
+            : $time;
+        $timePart  = date("Y-m", $timestamp);
+        return strtotime($timePart . ' last day');
+    }
+
+    // }}}
+    // {{{ myGetTimeStamp()
+
+    /**
+     * gets time stamp
+     *
+     * Long description (if any)...
+     *
+     * @param unknown $time    Parameter description (if any)...
+     * @param boolean $all_day Parameter description (if any)...
+     *
+     * @return integer Return description (if any)...
+     * @access public
+     */
+    function myGetTimeStamp($time, $all_day = false)
+    {
+        static $count, $count2;
+        $count   = ($count)
+            ? $count
+            : 1;
+        $count2  = ($count2)
+            ? $count2
+            : 1;
+        $pattern = "/^([0-9]{1,2}):?([0-9]{1,2})? ?([AP]M)/i";
+        if (!$all_day && preg_match($pattern, trim($time), $tar1)) {
+            if (($tar1[3] == 'PM' || $tar1[3] == 'pm') && (int) $tar1[1] < 12) {
+                $a1 = (int) $tar1[1] + 12;
+            } else {
+                $a1    = (int) $tar1[1];
+            }
+            $a2    = $tar1[2];
+            $time1 = mktime($a1, $a2, $count++, 1, 1, 2000);
+            return $time1;
+        } else {
+            if ($all_day) {
+                $time1 = $count2++;
+            } else {
+                $time1 = 999999999 + $count++;
+            }
+            return $time1;
+        }
+    }
+
+    // }}}
+    // {{{ ordinalDay()
+    /**
+     * ordinalDay
+     * get ordinal (th)day for given timestamp
+     *
+     * @param int $ord   ordinal number
+     * @param int $day   number of day
+     * @param int $month number of month
+     * @param int $year  number of year
+     *
+     * @access public
+     * @return int
+     */
+
+    function ordinalDay($ord, $day, $month, $year)
+    {
+        $firstOfMonth = strtotime("{$month}/01/{$year}");
+        $lastOfMonth  = $firstOfMonth + date("t", $firstOfMonth) * 86400;
+        $dayOccurs    = 0;
+        for ($i            = $firstOfMonth; $i < $lastOfMonth; $i += 86400) {
+            if (date("w", $i) == $day) {
+                $dayOccurs++;
+                if ($dayOccurs == $ord) {
+                    $ordDay = $i;
+                }
+            }
+        }
+        return $ordDay;
+    }
+
+    // }}}
+    // {{{ showEventHeaders()
+    /**
+     * showEventHeaders
+     *
+     * @param string $date     date string
+     * @param mixed  $eventObj Pass the EventCalendar Obj
+     *
+     * @access public
+     * @return string
+     */
+
+    function showEventHeaders($date, $eventObj)
+    {
+        $pattern = "/([0-9]{1,2})[-\/]([0-9]{1,2})[-\/]([0-9]{4})/";
+        if (preg_match($pattern, $date, $dpart)) {
+            $month = (int) $dpart[1];
+            $year  = (int) $dpart[3];
+        }
+        if (is_array($this->_eventsData[$date])) {
+            foreach ($this->_eventsData[$date] as $num => $data) {
+                $btime              = $this->calTime($data['btime']);
+                $etime              = $this->calTime($data['etime']);
+                $all_day            = ($data['all_day'] == 't')
+                    ? 1
+                    : 0;
+                $stime              = (int) $this->myGetTimeStamp($btime, $all_day);
+                $data2[(int) $stime] = '<div class="topic">
+                    <div class="' . $this->disColor($data['topiccolor'])
+                    . '">' . '<a href="' . $eventObj->pageName . '&amp;month='
+                    . $month . '&amp;year=' . $year . '&amp;eventid='
+                    . $data['id'] . '">' . $data['header'] . '</a>' . ' </div>
+                </div>
+                ';
+            }
+            ksort($data2, SORT_REGULAR);
+            ob_start();
+            $out                = ob_get_contents();
+            ob_end_clean();
+            echo $out;
+            $output             = implode(" ", $data2);
+        }
+        return $output;
+    }
+
+    // }}}
+}
+
+?>
diff --git a/Toolkit/Events/CategoryBlocks.php b/Toolkit/Events/CategoryBlocks.php
new file mode 100644 (file)
index 0000000..a3fe413
--- /dev/null
@@ -0,0 +1,37 @@
+<?php
+
+/**
+ * CategoryBlocks.php
+ *
+ * PHP version 5.2
+ *
+ * @category  Toolkit
+ * @package   Events
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @version   SVN: (0.1)
+ * @link      <>
+ */
+
+/**
+ * Toolkit_Events_CategoryBlocks
+ *
+ * Description of CategoryBlocks
+ *
+ * @category  Toolkit
+ * @package   Events
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @release   Release: (0.1)
+ * @link      <>
+ */
+class Toolkit_Events_CategoryBlocks
+    extends Toolkit_Events_Auxiliary
+{
+    public function toHtml()
+    {
+        $tpl = new HTML_Template_Flexy($this->_flexyOptions);
+    }
+}
diff --git a/Toolkit/Events/Database/addJustHideAddress.sql b/Toolkit/Events/Database/addJustHideAddress.sql
new file mode 100644 (file)
index 0000000..5416a78
--- /dev/null
@@ -0,0 +1,3 @@
+ALTER TABLE events.event ADD hide_address BOOLEAN;
+ALTER TABLE events.event ALTER hide_address SET DEFAULT false;
+UPDATE events.event SET hide_address = false;
\ No newline at end of file
diff --git a/Toolkit/Events/Database/addJustHideAddressNoSchema.sql b/Toolkit/Events/Database/addJustHideAddressNoSchema.sql
new file mode 100644 (file)
index 0000000..0532a40
--- /dev/null
@@ -0,0 +1,3 @@
+ALTER TABLE event ADD hide_address BOOLEAN;
+ALTER TABLE event ALTER hide_address SET DEFAULT false;
+UPDATE event SET hide_address = false;
\ No newline at end of file
diff --git a/Toolkit/Events/Database/application.sql b/Toolkit/Events/Database/application.sql
new file mode 100644 (file)
index 0000000..9979e66
--- /dev/null
@@ -0,0 +1,9 @@
+CREATE SCHEMA events;
+GRANT ALL ON SCHEMA events TO nobody;
+
+--
+-- Tables
+--
+\i ./tables/event.sql
+\i ./tables/topic.sql
+\i ./tables/event_recur.sql
diff --git a/Toolkit/Events/Database/applicationWithMembers.sql b/Toolkit/Events/Database/applicationWithMembers.sql
new file mode 100644 (file)
index 0000000..1092c9f
--- /dev/null
@@ -0,0 +1,9 @@
+CREATE SCHEMA events;
+GRANT ALL ON SCHEMA events TO nobody;
+
+--
+-- Tables
+--
+\i ./tables/event_with_members.sql
+\i ./tables/topic.sql
+\i ./tables/event_recur.sql
diff --git a/Toolkit/Events/Database/removeApplication.sql b/Toolkit/Events/Database/removeApplication.sql
new file mode 100644 (file)
index 0000000..b371ad0
--- /dev/null
@@ -0,0 +1,8 @@
+--
+--  This will drop everything in the events schema.
+--  Nothing better be in here except events related objects
+--  or it will be dropped
+--
+--  The force is strong w/ this one, use it wisely.
+-- 
+DROP SCHEMA IF EXISTS events CASCADE;
diff --git a/Toolkit/Events/Database/tables/event.sql b/Toolkit/Events/Database/tables/event.sql
new file mode 100644 (file)
index 0000000..f64a81f
--- /dev/null
@@ -0,0 +1,45 @@
+DROP TABLE IF EXISTS events.event CASCADE;
+
+CREATE TABLE events.event
+(id SERIAL,
+ topicid INTEGER DEFAULT 0,
+ bdate DATE,
+ edate DATE,
+ loc TEXT,
+ header TEXT,
+ descr TEXT,
+ img TEXT,
+ url TEXT,
+ contact TEXT,
+ phone TEXT,
+ email TEXT,
+ btime TEXT,
+ etime TEXT,
+ visable BOOLEAN DEFAULT false,
+ home BOOLEAN DEFAULT false,
+ ds TIMESTAMP,
+ daysow INTEGER,
+ dayom INTEGER,
+ reacur BOOLEAN DEFAULT false,
+ weekom TEXT,
+ all_day BOOLEAN DEFAULT false,
+ file TEXT,
+ filename TEXT,
+ cost TEXT,
+ admin_contact_name TEXT,
+ admin_org_name TEXT,
+ admin_phone TEXT,
+ admin_email TEXT,
+ create_date DATE DEFAULT now(),
+ approved_date DATE,
+ notes TEXT,
+ address TEXT,
+ city TEXT,
+ state TEXT,
+ zip TEXT,
+ lat DOUBLE PRECISION,
+ lon DOUBLE PRECISION,
+ PRIMARY KEY (id));
+
+GRANT ALL ON events.event_id_seq TO nobody;
+GRANT ALL ON events.event TO nobody;
diff --git a/Toolkit/Events/Database/tables/event_recur.sql b/Toolkit/Events/Database/tables/event_recur.sql
new file mode 100644 (file)
index 0000000..b04d4f1
--- /dev/null
@@ -0,0 +1,12 @@
+DROP TABLE IF EXISTS events.event_recur CASCADE;
+
+CREATE TABLE events.event_recur
+(id SERIAL,
+ event_id INTEGER
+   REFERENCES events.event(id)
+   ON UPDATE CASCADE
+   ON DELETE CASCADE,
+ event_day DATE);
+
+GRANT ALL ON events.event_recur TO nobody;
+GRANT ALL ON events.event_recur_id_seq TO nobody;
diff --git a/Toolkit/Events/Database/tables/event_with_members.sql b/Toolkit/Events/Database/tables/event_with_members.sql
new file mode 100644 (file)
index 0000000..ee733a7
--- /dev/null
@@ -0,0 +1,49 @@
+DROP TABLE IF EXISTS events.event CASCADE;
+
+CREATE TABLE events.event
+(id SERIAL,
+ topicid INTEGER DEFAULT 0,
+ bdate DATE,
+ edate DATE,
+ loc TEXT,
+ header TEXT,
+ descr TEXT,
+ img TEXT,
+ url TEXT,
+ contact TEXT,
+ phone TEXT,
+ email TEXT,
+ btime TEXT,
+ etime TEXT,
+ visable BOOLEAN DEFAULT false,
+ home BOOLEAN DEFAULT false,
+ ds TIMESTAMP,
+ daysow INTEGER,
+ dayom INTEGER,
+ reacur BOOLEAN DEFAULT false,
+ weekom TEXT,
+ all_day BOOLEAN DEFAULT false,
+ file TEXT,
+ filename TEXT,
+ cost TEXT,
+ admin_contact_name TEXT,
+ admin_org_name TEXT,
+ admin_phone TEXT,
+ admin_email TEXT,
+ create_date DATE DEFAULT now(),
+ approved_date DATE,
+ notes TEXT,
+ address TEXT,
+ city TEXT,
+ state TEXT,
+ zip TEXT,
+ lat DOUBLE PRECISION,
+ lon DOUBLE PRECISION,
+ member_id INTEGER
+       REFERENCES members.member (member_id)
+       ON UPDATE CASCADE
+       ON DELETE CASCADE,
+ PRIMARY KEY (id));
+
+GRANT ALL ON events.event_id_seq TO nobody;
+GRANT ALL ON events.event TO nobody;
diff --git a/Toolkit/Events/Database/tables/topic.sql b/Toolkit/Events/Database/tables/topic.sql
new file mode 100644 (file)
index 0000000..5c35838
--- /dev/null
@@ -0,0 +1,10 @@
+DROP TABLE IF EXISTS events.topic CASCADE;
+
+CREATE TABLE events.topic
+(id SERIAL, 
+ descr TEXT,
+ topiccolor TEXT,
+ PRIMARY KEY (id));
+
+GRANT ALL ON events.topic_id_seq TO nobody;
+GRANT ALL ON events.topic TO nobody;
diff --git a/Toolkit/Events/Database/updateEventsTable.sql b/Toolkit/Events/Database/updateEventsTable.sql
new file mode 100644 (file)
index 0000000..8e6e1ae
--- /dev/null
@@ -0,0 +1,18 @@
+ALTER TABLE events.event ADD cost TEXT;
+ALTER TABLE events.event ADD admin_contact_name TEXT;
+ALTER TABLE events.event ADD admin_org_name TEXT;
+ALTER TABLE events.event ADD admin_phone TEXT;
+ALTER TABLE events.event ADD admin_email TEXT;
+ALTER TABLE events.event ADD create_date DATE;
+ALTER TABLE events.event ADD approved_date DATE;
+ALTER TABLE events.event ALTER create_date SET DEFAULT now();
+ALTER TABLE events.event ADD notes TEXT;
+ALTER TABLE events.event ADD address TEXT;
+ALTER TABLE events.event ADD city TEXT;
+ALTER TABLE events.event ADD state TEXT;
+ALTER TABLE events.event ADD zip TEXT;
+ALTER TABLE events.event ADD lat DOUBLE PRECISION;
+ALTER TABLE events.event ADD lon DOUBLE PRECISION;
+ALTER TABLE events.event ADD hide_address BOOLEAN;
+ALTER TABLE events.event ALTER hide_address SET DEFAULT false;
+UPDATE events.event SET hide_address = false;
\ No newline at end of file
diff --git a/Toolkit/Events/Database/updateEventsTableNoSchema.sql b/Toolkit/Events/Database/updateEventsTableNoSchema.sql
new file mode 100644 (file)
index 0000000..b2ea37c
--- /dev/null
@@ -0,0 +1,18 @@
+ALTER TABLE event ADD cost TEXT;
+ALTER TABLE event ADD admin_contact_name TEXT;
+ALTER TABLE event ADD admin_org_name TEXT;
+ALTER TABLE event ADD admin_phone TEXT;
+ALTER TABLE event ADD admin_email TEXT;
+ALTER TABLE event ADD create_date DATE;
+ALTER TABLE event ADD approved_date DATE;
+ALTER TABLE event ALTER create_date SET DEFAULT now();
+ALTER TABLE event ADD notes TEXT;
+ALTER TABLE event ADD address TEXT;
+ALTER TABLE event ADD city TEXT;
+ALTER TABLE event ADD state TEXT;
+ALTER TABLE event ADD zip TEXT;
+ALTER TABLE event ADD lat DOUBLE PRECISION;
+ALTER TABLE event ADD lon DOUBLE PRECISION;
+ALTER TABLE event ADD hide_address BOOLEAN;
+ALTER TABLE event ALTER hide_address SET DEFAULT false;
+UPDATE event SET hide_address = false;
\ No newline at end of file
diff --git a/Toolkit/Events/Display.php b/Toolkit/Events/Display.php
new file mode 100755 (executable)
index 0000000..475f164
--- /dev/null
@@ -0,0 +1,116 @@
+<?php
+
+/**
+ * Display.php
+ *
+ * PHP version 5
+ *
+ * @category  Toolkit
+ * @package   Events
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2009 Gaslight Media
+ * @license   Gaslight Media
+ * @version   CVS: $Id: Display.php,v 1.4 2010/08/04 13:04:50 jamie Exp $
+ * @link      <>
+ */
+
+/**
+ * Toolkit_Events_Display
+ *
+ * Display the events in a list view and optionally a calendar view
+ * This is set in Toolkit/Events/config.ini
+ *
+ * @category  Toolkit
+ * @package   Events
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2009 Gaslight Media
+ * @license   Gaslight Media
+ * @version   Release: @package_version@
+ * @link      <>
+ */
+class Toolkit_Events_Display
+    extends Toolkit_Events_Auxiliary
+{
+    /**
+     * toHTML()
+     *
+     * call to listEvents function for display of events
+     *
+     * @param Toolkit_Template_KeywordReplacement $keywordReplacement Reference
+     *
+     * @return void
+     * @access public
+     */
+    public function toHTML(
+        Toolkit_Template_KeywordReplacement $keywordReplacement
+    ) {
+        $baseUrl
+            = ($_SERVER['HTTPS'] == 'on')
+            ? BASE_SECURE_URL
+            : MEDIA_BASE_URL;
+        $glmAppBaseUrl
+            = ($_SERVER['HTTPS'] == 'on')
+            ? GLM_APP_BASE_SECURE_URL
+            : MEDIA_APP_BASE_URL;
+        $GLOBALS['styleSheets'][]
+            = $glmAppBaseUrl . 'libjs/jqueryui/1.8.13/development-bundle/themes/base/jquery.ui.all.css';
+        $GLOBALS['styleSheets'][]
+            = $baseUrl . 'Toolkit/Events/css/event.css';
+        $GLOBALS['topScripts'][]
+            = $glmAppBaseUrl . 'libjs/jqueryui/1.8.13/js/jquery-ui-1.8.13.custom.min.js';
+        $GLOBALS['bottomScripts'][]
+            = $baseUrl . 'Toolkit/Events/libjs/events.js';
+        $GLOBALS['styleSheets'][]
+            = $glmAppBaseUrl . 'libjs/plugins/nivoslider/themes/default/default.css';
+
+        $template          = new HTML_Template_Flexy($this->flexyOptions);
+        $page              = new stdClass;
+        $page->todayUrl    = MEDIA_BASE_URL . 'events/'.EVENT_PAGE.'/?t=today';
+        $page->tomorrowUrl = MEDIA_BASE_URL . 'events/'.EVENT_PAGE.'/?t=tomorrow';
+        $page->nextUrl     = MEDIA_BASE_URL . 'events/'.EVENT_PAGE.'/?t=next';
+
+        $template->compile('eventPage.html');
+
+        // create user search form
+        $userSearchForm
+            = new Toolkit_Events_UserSearchForm($this->dbh);
+        $page->eventSearchForm = $userSearchForm->toHtml();
+
+        // add home events
+        $homeEvents   = new Toolkit_Events_HomeEvents($this->dbh);
+        $page->events = $homeEvents->getHomeEvents();
+        $page->hasHomeEvents = !empty($page->events);
+        // setup query
+        $queryBuilder = new Toolkit_Events_QueryBuilder();
+        $eventId      = filter_var($_REQUEST['eventid'], FILTER_VALIDATE_INT);
+        $t            = filter_var($_REQUEST['t'], FILTER_SANITIZE_STRING);
+
+        if ($eventId) {
+            $GLOBALS['topScripts'][]
+                = 'http://maps.googleapis.com/maps/api/js?sensor=true';
+            $GLOBALS['topScripts'][]
+            = $baseUrl . 'Toolkit/Events/libjs/geoCoder.js';
+            $eventList = new Toolkit_Events_ListEvents($this->dbh);
+            $eventList->setQuery($queryBuilder->buildQuery());
+            $page->eventContent = $eventList->toHtml(
+                $keywordReplacement,
+                'eventDetail.html'
+            );
+        } else if ($_REQUEST['search']) {
+            $eventList = new Toolkit_Events_ListEvents($this->dbh);
+            $eventList->setQuery($queryBuilder->buildQuery());
+            $eventList->setSearchFor($queryBuilder);
+            $page->eventContent = $eventList->toHtml($keywordReplacement);
+        } else if ($t) {
+            $eventList = new Toolkit_Events_ListEvents($this->dbh);
+            $eventList->setQuery($queryBuilder->buildQuery());
+            $eventList->setSearchFor($queryBuilder);
+            $page->eventContent = $eventList->toHtml($keywordReplacement);
+        } else {
+            $homePage = new Toolkit_Events_EventHomePage($this->dbh);
+            $page->eventContent = $homePage->toHtml();
+        }
+
+        return $template->bufferedOutputObject($page);
+    }
+}
diff --git a/Toolkit/Events/EventCalendar.php b/Toolkit/Events/EventCalendar.php
new file mode 100755 (executable)
index 0000000..db06938
--- /dev/null
@@ -0,0 +1,798 @@
+<?php
+
+/**
+ * Short description for file
+ *
+ * Long description (if any) ...
+ *
+ * PHP version 5
+ *
+ * The license text...
+ *
+ * @category  Toolkit
+ * @package   Events
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2010 Gaslight Media
+ * @license   Gaslight Media
+ * @version   CVS: $Id: EventCalendar.php,v 1.3 2010/08/04 14:39:58 jamie Exp $
+ * @link      <>
+ */
+
+/**
+ * Handles the event calendar
+ *
+ * Long description (if any) ...
+ *
+ * @category  Toolkit
+ * @package   Events
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2010 Gaslight Media
+ * @license   Gaslight Media
+ * @version   Release: @package_version@
+ * @link      <>
+ */
+class Toolkit_Events_EventCalendar
+{
+    // {{{     properties
+
+    /**
+     * Description of $topicid
+     * @access public
+     * @var integer
+     */
+    public $topicid;
+    /*     * #@+
+     * @access public
+     * @var string
+     */
+
+    /**
+     * Description of $pageName
+     *
+     * @var    string
+     * @access public
+     */
+    public $pageName;
+
+    /**
+     * Type of display flat or calendar based on weather or not a topicid exists.
+     *
+     * @var mixed
+     * @access public
+     */
+    public $displayType;
+
+    /**
+     * PDO
+     *
+     * @var    mixed
+     * @access public
+     */
+    public $dbh;
+
+    /**
+     * Array of colors
+     * @var    array
+     * @access public
+     */
+    public $colorArray = array(
+        '#FFCCCC' => 'color1',
+        '#CC9999' => 'color2',
+        '#FF9999' => 'color3',
+        '#FFCCFF' => 'color4',
+        '#CC99CC' => 'color5',
+        '#FF99FF' => 'color6',
+        '#CCCCFF' => 'color7',
+        '#9999CC' => 'color8',
+        '#9999FF' => 'color9',
+        '#CCFFFF' => 'color10',
+        '#99CCCC' => 'color11',
+        '#99FFFF' => 'color12',
+        '#CCFFCC' => 'color13',
+        '#99CC99' => 'color14',
+        '#99FF99' => 'color15',
+        '#FFFFCC' => 'color16',
+        '#CCCC99' => 'color17',
+        '#FFFF99' => 'color18',
+        '#FFCC99' => 'color19',
+        '#FF99CC' => 'color20',
+        '#CC99FF' => 'color21',
+        '#99CCFF' => 'color22',
+        '#99FFCC' => 'color23',
+        '#CCFF99' => 'color24',
+        '#CCCCCC' => 'color25'
+        );
+
+    // }}}
+    // {{{ __construct()
+
+    /**
+     * Class constructor
+     *
+     * @param PDO $pdo Database Instance
+     *
+     * @access public
+     */
+    public function __construct(PDO $pdo)
+    {
+        $this->dbh = & $pdo;
+        $this->topicid = $GLOBALS['topicid'];
+        $this->pageName = MEDIA_BASE_URL . 'index.php?catid=' . $_REQUEST['catid'];
+        $this->displayType = $this->getType(); // Calendar or Flat
+    }
+
+    // }}}
+    // {{{ advancedSearch()
+    /**
+     * advancedSearch
+     *
+     * <p>Show an advanced search form</p>
+     *
+     * @return string
+     */
+    function advancedSearch()
+    {
+        $out = '';
+        $out .= $this->getEventSearch(1);
+        $out .= '
+                       <h4>Advanced Search Option:</h4>
+                       <form action="' . $this->pageName . '" method="post">
+                       <table id="event-advanced-search">
+                         <tr>
+                           <td>Search For:</td>
+                               <td>
+                                       <input name="EventName" value="' . $_POST['EventName'] . '">
+                               </td>
+                         </tr>
+                         <tr>
+                           <td>Search By Category:</td>
+                               <td>
+                       ';
+        $sql = "
+        SELECT id,descr
+        FROM topic";
+        try {
+            $data = $this->dbh->query($sql)->fetchAll(PDO::FETCH_ASSOC);
+        } catch (PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+        if ($data) {
+            foreach ($data as $key => $val) {
+                $out .= '
+                               <input type="checkbox" name="topics_id[]" id="chxbox_'
+                    . $val["id"] . '" value="' . $val["id"] . '">
+                               <label for="chxbox_' . $val["id"] . '">'
+                    . $val["descr"] . '</label><br>';
+            }
+        }
+        $out .= '
+                           </td>
+                         </tr>
+                         <tr>
+                           <td>From:</td>
+                               <td>
+                                       <input name="EventDateFrom" value="'
+            . $_POST['EventDateFrom'] . '"> (MM/DD/YYYY)
+                               </td>
+                         </tr>
+                         <tr>
+                           <td>To:</td>
+                               <td>
+                                       <input name="EventDateTo" value="'
+            . $_POST['EventDateTo'] . '"> (MM/DD/YYYY)
+                               </td>
+                         </tr>
+                         <tr>
+                           <td colspan="2" align="right">
+                           <input type="submit" value="Search"></td>
+                         </tr>
+                       </table>
+                       </form>';
+        return $out;
+    }
+
+    // }}}
+    // {{{ disColor()
+
+    /**
+     * Get style class for color
+     *
+     * Long description (if any) ...
+     *
+     * @param unknown $color Parameter description (if any) ...
+     *
+     * @return array Return description (if any) ...
+     * @access public
+     */
+    function disColor($color)
+    {
+        // get style class for this color
+        return $this->colorArray[$color];
+    }
+
+    // }}}
+    // {{{ firstDayOfMonth()
+    /**
+     * firstDayOfMonth
+     *
+     * Given a unix timestamp return the first day of the month
+     *
+     * @param string $time unix timestamp
+     *
+     * @return string
+     */
+
+    function firstDayOfMonth($time = '')
+    {
+        $timestamp = ($time == '')
+            ? time()
+            : $time;
+        list($month, $day, $year) = explode("/", date('m/d/Y', $timestamp));
+        return mktime(0, 0, 0, $month, 1, $year);
+    }
+
+    // }}}
+    // {{{ toHtml()
+    /**
+     * toHtml:
+     *
+     * @param Toolkit_Template_KeywordReplacement $keywordReplacement Keyword
+     *
+     * @uses BASE
+     * @uses GLM_EVENT::getEventSearch()
+     * @uses GLM_EVENT::$displayType
+     * @uses calendar()
+     * @return mixed
+     * */
+
+    function toHtml(Toolkit_Template_KeywordReplacement $keywordReplacement)
+    {
+        if ($_GET['advance'] == 1) {
+            return $this->advancedSearch();
+        }
+        $out = '';
+        if ($_POST['month'] && $_POST['month'] != 'All') {
+            $month = (int) substr($_POST['month'], 0, 2);
+            $year  = (int) substr($_POST['month'], 3, 6);
+        } elseif (preg_match("/([0-9]{1,2}) ([0-9]{4})/", $_GET['month'])) {
+            $month = (int) substr($_GET['month'], 0, 2);
+            $year  = (int) substr($_GET['month'], 3, 6);
+        } elseif ($_GET['month'] && is_numeric($_GET['month'])) {
+            $month = (int) $_GET['month'];
+            $year  = (int) $_GET['year'];
+        } elseif ($_REQUEST['month'] && $_REQUEST['month'] != 'All') {
+            $month = (int) date("m");
+            $year  = (int) date("Y");
+        }
+        if (isset($year) && (int) $year < 2000) {
+            return false;
+        }
+
+        // this runs when a user clicks on the day in one of the small calendars
+        if ($_GET['flat'] == 1 && $_GET['day'] && $_GET['month'] && $_GET['year']) {
+            $out .= $this->getEventSearch(1);
+            $req_timestamp = mktime(
+                           0,
+                           0,
+                           1,
+                           $_GET['month'],
+                           $_GET['day'],
+                           $_GET['year']
+            );
+            $mydate        = date("m-d-Y", $req_timestamp);
+            $myDayOw       = date("w", $req_timestamp);
+            switch ($myDayOw) {
+            case 0:
+                $myDayOw   = 1;
+                break;
+            case 1:
+                $myDayOw   = 2;
+                break;
+            case 2:
+                $myDayOw   = 4;
+                break;
+            case 3:
+                $myDayOw   = 8;
+                break;
+            case 4:
+                $myDayOw   = 16;
+                break;
+            case 5:
+                $myDayOw   = 32;
+                break;
+            case 6:
+                $myDayOw   = 64;
+                break;
+            }
+            $qs        = "
+              SELECT e.*,
+                            t.descr AS topicname
+                FROM event e LEFT OUTER JOIN topic t ON (e.topicid = t.id)
+               WHERE e.visable = 't'
+                 AND ((e.reacur != 't'
+                        AND e.edate >= '$mydate'
+                        AND e.bdate <= '$mydate'
+                     ) OR (e.reacur = 't'
+                        AND e.id in (
+                        SELECT event_id
+                          FROM event_recur
+                         WHERE event_day = '$mydate')
+                     ))
+            ORDER BY e.all_day desc,e.bdate,e.edate,e.btime,e.etime;";
+            //$out .= $qs;
+            $eventList = new Toolkit_Events_ListEvents($this->dbh);
+            $eventList->setQuery($qs);
+            $out .= $eventList->toHtml($keywordReplacement);
+            // default calendar view
+        } elseif ($this->displayType == "Calendar") {
+            $cal = new Toolkit_Events_Calendar($this->dbh);
+            if (!$_REQUEST['eventid']) {
+                $out .= $this->getEventSearch(1);
+                $out .= '' . "\n";
+                $out .= $cal->calendar(
+                                   $month,
+                                   $year,
+                                   2,
+                                   $this->pageName.'&amp;event=1&amp;month=',
+                                   $this->pageName.'&amp;event=1&amp;eventid=1&amp;month='.$month,
+                                   $this
+                );
+            } else {
+                $eventid   = (int) $_REQUEST['eventid'];
+                $qs        = "
+                SELECT event.*,topic.descr as topicname
+                  FROM event LEFT OUTER JOIN topic ON (topic.id = event.topicid)
+                 WHERE event.id = {$eventid}";
+                $eventList = new Toolkit_Events_ListEvents($this->dbh);
+                $eventList->setQuery($qs);
+                $out .= $this->getEventSearch(1);
+                $out .= $eventList->toHtml($keywordReplacement);
+
+                $out .= '<div class="clearer"></div>' . "\n";
+                $out .= $cal->calendar(
+                                   $month,
+                                   $year,
+                                   2,
+                                   $this->pageName.'&amp;event=1&amp;month=',
+                                   $this->pageName.'&amp;eventid=1&month='.$month,
+                                   $this
+                );
+            }
+            // used when the end user does a advanced search
+        } else {
+            $noSearch  = true;
+            $searching = '';
+            $out .= $this->getEventSearch(1);
+            $qs        = "
+            SELECT e.*,t.descr as topicname
+              FROM event e LEFT OUTER JOIN topic t ON (e.topicid = t.id)
+             WHERE e.visable = 't'";
+            if (ctype_digit($_POST['topicid'])) {
+                $noSearch = false;
+                $query    = "select descr from topic where id = " . $_POST['topicid'];
+                try {
+                    $row2 = $this->dbh->query($query)->fetchAll(PDO::FETCH_ASSOC);
+                } catch (PDOException $e) {
+                    Toolkit_Common::handleError($e);
+                }
+                $qs .= " and e.topicid = " . $_POST['topicid'];
+                if ($searching) {
+                    $searching .= ', ' . $row2[0]['descr'] . '';
+                } else {
+                    $searching = 'Showing all ' . $row2[0]['descr'] . '';
+                }
+            }
+            if ($this->displayType == 'flat' && $month && $year) {
+                $noSearch  = false;
+                $timestmp  = mktime(
+                    0,
+                    0,
+                    1,
+                    $month,
+                    1,
+                    $year
+                );
+                $bdate     = date("m/d/Y", $timestmp);
+                $timestmp2 = mktime(
+                    0,
+                    0,
+                    1,
+                    $month,
+                    date("t", $timestmp),
+                    $year
+                );
+                $edate     = date("m/d/Y", $timestmp2);
+                if ($searching == '') {
+                    $searching = "Searching for All events in "
+                        . date("F, Y", $timestmp);
+                } else {
+                    $searching .= " in " . date("F, Y", $timestmp);
+                }
+                $qs .= "
+                               and e.edate > current_date
+                               and e.edate >= '{$bdate}'
+                               and e.bdate <= '{$edate}'";
+            }
+            if (isset($_GET['topicid']) && $_GET['topicid'] != 'All' && ctype_digit($_GET['topicid'])) {
+                $noSearch = false;
+                $query    = "select descr from topic where id = " . $_GET['topicid'];
+                try {
+                    $row2 = $this->dbh->query($query)->fetchAll(PDO::FETCH_ASSOC);
+                } catch (PDOException $e) {
+                    Toolkit_Common::handleError($e);
+                }
+                $qs .= " and e.topicid = " . $_GET['topicid'];
+                $searching .= 'Showing all ' . $row2[0]['descr'] . '';
+            }
+            if (is_array($_POST['topics_id']) || $_POST['EventName']) {
+                $noSearch = false;
+                if ($_POST['EventName'] != '') {
+                    $qs .= " and ( lower(e.header) like '%"
+                        . strtolower($_POST['EventName']) . "%'";
+                    $qs .= " or lower(e.descr) like '%"
+                        . strtolower($_POST['EventName']) . "%') ";
+                    $searching .= 'Searching for &quot;'
+                        . $_POST['EventName'] . '&quot; ';
+                } else {
+                    $searching .= 'Searching for anything ';
+                }
+                if (is_array($_POST['topics_id'])
+                    && count($_POST['topics_id']) > 0
+                ) {
+                    $topics_list = implode(",", $_POST['topics_id']);
+                    $qs .= " and e.topicid in ({$topics_list})";
+                    $query       = "select descr from topic where id  in ({$topics_list})";
+                    $order_by    = "t.descr,";
+                    try {
+                        $row2 = $this->dbh->query($query)
+                            ->fetchAll(PDO::FETCH_ASSOC);
+                    } catch (PDOException $e) {
+                        Toolkit_Common::handleError($e);
+                    }
+                    if ($row2) {
+                        foreach ($row2 as $k => $v) {
+                            $tps[] = $v['descr'];
+                        }
+                    }
+                    if (is_array($tps)) {
+                        $searching .= 'in ' . implode(', ', $tps) . ' ';
+                    }
+                }
+            }
+            if ($_POST['EventDateTo'] && $_POST['EventDateFrom']
+                && (strtotime($_POST['EventDateFrom']) != -1)
+                && (strtotime($_POST['EventDateTo']) != -1)
+            ) {
+                $noSearch = false;
+                if ($_POST['EventName'] == '' && $searching == '') {
+                    $searching .= 'Searching for anything ';
+                }
+                $begin_time = date('n/j/Y', strtotime($_POST['EventDateFrom']));
+                $end_time   = date('n/j/Y', strtotime($_POST['EventDateTo']));
+                $qs .= " and e.bdate <= '$end_time' and e.edate >= '$begin_time'";
+                $searching .= ' between ' . $begin_time . ' and ' . $end_time;
+            }
+            if ($noSearch) {
+                $qs = "
+                                 SELECT e.*
+                                   FROM event e
+                                  where e.visable = 't'
+                                    and e.edate >= current_date
+                               order by e.bdate,e.edate";
+            } else {
+                $qs .= '
+                               and e.edate >= current_date
+                               order by ' . $order_by . 'e.bdate,e.edate';
+            }
+            if ($searching) {
+                $out .= '<div id="searching"><h4>' . $searching . '</h4>';
+                $out .= '</div>';
+            }
+            $eventList = new Toolkit_Events_ListEvents($this->dbh);
+            $eventList->setQuery($qs);
+            $out .= $eventList->toHtml($keywordReplacement);
+        }
+        return $out;
+    }
+
+    // }}}
+    // {{{ getEventSearch()
+    /**
+     * getEventSearch
+     *
+     * @uses PGSQL_ASSOC
+     * @uses GLM_EVENT::$pageName
+     * @uses calendar()
+     * @return string
+     * */
+
+    function getEventSearch()
+    {
+        $out      = '';
+        //        unset($emonths);
+        $month_id = array(
+            "Jan",
+            "Feb",
+            "Mar",
+            "Apr",
+            "May",
+            "Jun",
+            "Jul",
+            "Aug",
+            "Sep",
+            "Oct",
+            "Nov",
+            "Dec"
+        );
+
+        // Month part do not change
+        $qs = "
+          SELECT bdate,edate
+            FROM event
+           WHERE visable = 't'
+        ORDER BY bdate ASC;";
+
+        try {
+            $res = $this->dbh->query($qs)->fetchAll(PDO::FETCH_ASSOC);
+        } catch (PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+
+        $page = $this->pageName;
+
+        foreach ($res as $data) {
+            $beginDateParts = explode("/", $data['bdate']);
+            $endDateParts   = explode("/", $data['edate']);
+            // parse the month and year out of the start and end dates
+            $s_month        = $beginDateParts[0];
+            $s_year         = $beginDateParts[2];
+            $e_month        = $endDateParts[0];
+            $e_year         = $endDateParts[2];
+
+            //            $start_time_stamp = mktime(
+            //                0,
+            //                0,
+            //                0,
+            //                $s_month,
+            //                1,
+            //                $s_year
+            //            );
+            $end_time_stamp   = mktime(
+                0,
+                0,
+                0,
+                $e_month,
+                1,
+                $e_year
+            );
+            $watchdog         = 20;
+            $cur_time_stamp   = mktime(
+                0,
+                0,
+                0,
+                $m,
+                1,
+                $y
+            );
+            for ($y = $s_year, $m = $s_month; $cur_time_stamp <= $end_time_stamp;) {
+                if ($m == 13) {
+                    $y++;
+                    $m = 1;
+                }
+
+                $emonth = sprintf(
+                    "%02d %04d",
+                    $m,
+                    $y
+                );
+                if (!isset($emonths["$emonth"])) {
+                    $emonths["$emonth"] = 0;
+                }
+                $emonths["$emonth"]++;
+
+                $cur_time_stamp = mktime(
+                    0,
+                    0,
+                    0,
+                    $m,
+                    1,
+                    $y
+                );
+                if ($watchdog-- == 0) {
+                    break 1;
+                }
+                $m++;
+                $cur_time_stamp = mktime(
+                    0,
+                    0,
+                    0,
+                    $m,
+                    1,
+                    $y
+                );
+            }
+        }
+        $out .= '<a name="event"></a>';
+        // topic search part
+        $qs             = "
+            SELECT distinct descr,id,topiccolor
+                       FROM topic
+            WHERE id in (
+                SELECT topicid
+                  FROM event
+                 WHERE visable = 't'
+                  AND edate >= current_date)
+                       ORDER BY descr;";
+        try {
+            $result = $this->dbh->query($qs)->fetchAll(PDO::FETCH_ASSOC);
+        } catch (PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+        if ($result) {
+            if ($this->displayType == 'Calendar') {
+                $out .= '<div class="clearer"></div><div id="topicsearch">';
+                foreach ($result as $tkey => $trow) {
+                    $out .= '<div class="topicsearchrow"><div class="'
+                        . $this->discolor($trow['topiccolor'])
+                        . ' topicsearchheader">&nbsp;</div>
+                        <a href="'
+                        . $this->pageName . '&amp;topicid='
+                        . $trow['id'] . '">'
+                        . $trow['descr'] . '</a></div>';
+                }
+                $out .= '</div>';
+            } else {
+                $out .= '<form action="' . $page . '#event" method="POST">';
+                $out .= '<select name="topicid">';
+                $out .= '<option value="">All Categories</option>';
+                foreach ($result as $tkey => $trow) {
+                    $out .= '<option value="' . $trow["id"] . '"';
+                    if ($_POST['topicid'] == $trow['id']) {
+                        $out .= ' selected';
+                    }
+                    $out .= '>' . $trow["descr"] . '</option>';
+                }
+                $out .= '</select>';
+            }
+        }
+
+        if ($_POST['month']) {
+            $month = (int) substr($_POST['month'], 0, 2);
+            $year  = (int) substr($_POST['month'], 3, 6);
+        } elseif ($_GET['month']) {
+            $month = (int) $_GET['month'];
+            $year  = (int) $_GET['year'];
+        } else {
+            $month      = (int) date("m");
+            $year       = (int) date("Y");
+        }
+        $out .= '<div id="smallcals">';
+        $prev_month = ($month - 1 < 1)
+            ? 12
+            : $month - 1;
+        $prev_year  = ($month - 1 < 1)
+            ? $year - 1
+            : $year;
+        $next_month = ($month + 1 > 12)
+            ? 1
+            : $month + 1;
+        $next_year  = ($month + 1 > 12)
+            ? $year + 1
+            : $year;
+        if ($this->displayType == 'Calendar') {
+            $calObj   = $this;
+            $calendar = new Toolkit_Events_Calendar($this->dbh);
+            // setup the calendars for the search on the top
+            $out .= $calendar->calendar(
+                $prev_month,
+                $prev_year,
+                1,
+                $this->pageName.'&amp;event=1&amp;month=',
+                $this->pageName.'&amp;event=1&amp;eventid=1&amp;month='.$month,
+                $calObj
+            );
+            $out .= $calendar->calendar(
+                $month,
+                $year,
+                1,
+                $this->pageName.'&amp;event=1&amp;month=',
+                $this->pageName.'&amp;event=1&amp;eventid=1&amp;month='.$month,
+                $calObj
+            );
+            $out .= $calendar->calendar(
+                $next_month,
+                $next_year,
+                1,
+                $this->pageName.'&amp;event=1&amp;month=',
+                $this->pageName.'&amp;event=1&amp;eventid=1&amp;month='.$month,
+                $calObj
+            );
+        }
+        $out .= '</div>';
+        $out .= '<div id="monthsearch">';
+        if ($this->displayType == 'Calendar') {
+            $out .= '<form action="'
+                . $page . '#event" method="POST" name="glm-cal-search">';
+        }
+        if ($this->displayType == 'Calendar') {
+            $out .= 'By Month: <select name="month">';
+        } else {
+            $out .= '<select name="month"><option value="All">All Months</option>';
+        }
+        if (isset($emonths) && is_array($emonths)) {
+            // below FOR equivalent to: foreach (array_keys($emonths) as $key) {
+            // assignment intentional
+            for (reset($emonths); $key = key($emonths); next($emonths)) {
+                $date = mktime(
+                                   0,
+                                   0,
+                                   0,
+                                   (integer)substr($key, 0, 2),
+                                   1,
+                                   substr($key, 3, 4)
+                );
+                $now  = mktime(0, 0, 0, date("m"), 1, date("Y"));
+                if ($date >= $now) {
+                    $m       = substr($key, 0, 2);
+                    $mymonth = $month_id[$m - 1] . " " . substr($key, 3, 4);
+                    $out .= '<option value="' . $key . '"';
+                    $out .= ($_POST['month'] == $key)
+                        ? 'selected'
+                        : '';
+                    $out .= '>';
+                    $out .= $mymonth;
+                    $out .= '</option>';
+                }
+            }
+        }
+        $out .= '</select>';
+        $out .= '<input type="SUBMIT" value="GO" name="SUBMIT">';
+        $out .= '<a href="' . $this->pageName . '&amp;advance=1">Advanced Search</a>';
+        $out .= '</form><div class="clearer"></div></div>';
+        return $out;
+    }
+
+    // }}}
+    // {{{ getType()
+    /**
+     * getType
+     *
+     * @return string
+     */
+
+    function getType()
+    {
+        if (($_POST['EventDateTo'] && $_POST['EventDateFrom'])
+            && (strtotime($_POST['EventDateTo']) != -1
+            && strtotime($_POST['EventDateFrom']) != -1)
+        ) {
+            return 'flat';
+        }
+        if ($_POST['topicid'] || $_GET['topicid'] || $_POST['topic']
+            || $_POST['topics_id'] || $_POST['EventName']
+        ) {
+            return 'flat';
+        }
+        return 'Calendar';
+    }
+
+    // }}}
+    // {{{ lastDayOfMonth()
+    /**
+     * lastDayOfMonth
+     *
+     * @param string $time unix timestamp
+     *
+     * @return string
+     */
+    function lastDayOfMonth($time = '')
+    {
+        $timestamp = ($time == '')
+            ? time()
+            : $time;
+        list($month, $day, $year) = explode("/", date('m/t/Y', $timestamp));
+        return strtotime("$month/$day/$year");
+    }
+
+    // }}}
+}
diff --git a/Toolkit/Events/EventHomePage.php b/Toolkit/Events/EventHomePage.php
new file mode 100644 (file)
index 0000000..41b0ae7
--- /dev/null
@@ -0,0 +1,168 @@
+<?php
+
+/**
+ * EventHomePage.php
+ *
+ * PHP version 5.2
+ *
+ * @category  Toolkit
+ * @package   Package
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @version   SVN: (0.1)
+ * @link      <>
+ */
+
+/**
+ * Toolkit_Package_EventHomePage
+ *
+ * Displays the content for Event Home Area
+ *
+ * @category  Toolkit
+ * @package   Package
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @release   Release: (0.1)
+ * @link      <>
+ */
+class Toolkit_Events_EventHomePage
+    extends Toolkit_Events_Auxiliary
+{
+    /**
+     * Create the html for the Event Home Page content
+     *
+     * @return string
+     */
+    public function toHtml()
+    {
+        $glmAppBaseUrl = MEDIA_APP_BASE_URL;
+        $GLOBALS['styleSheets'][]
+            = $glmAppBaseUrl . 'libjs/plugins/nivoslider/3.1/' . CSS_ENV
+            . '/nivo-slider.css';
+        $GLOBALS['bottomScripts'][]
+            = $glmAppBaseUrl . 'libjs/plugins/nivoslider/3.1/' . LIBJS_ENV
+            . '/jquery.nivo.slider.js';
+        $tpl = new HTML_Template_Flexy($this->flexyOptions);
+        $tpl->compile('eventHomePage.html');
+        $page = new stdClass;
+        $page->url = MEDIA_BASE_URL;
+        $page->topicSearchUrl = MEDIA_BASE_URL . 'index.php?catid='.EVENT_PAGE.'&search=1&category=' ;
+        $page->events = $this->_getFeaturedEvents();
+        $page->hasFeaturedEvents = !empty($page->events);
+        $page->blocks = $this->_getCategoryBlocks();
+        return $tpl->bufferedOutputObject($page);
+    }
+
+    /**
+     * Return array of Events that are marked featured or Home
+     *
+     * @return array
+     */
+    private function _getFeaturedEvents()
+    {
+        $homeEvents = new Toolkit_Events_HomeEvents($this->dbh);
+        return $homeEvents->getHomeEvents();
+    }
+
+    /**
+     * Returns the Events Category Block Arrays
+     *
+     * @return array
+     */
+    private function _getCategoryBlocks()
+    {
+        $blocks = array();
+        try {
+            $sql = "
+            SELECT *
+              FROM topic
+             WHERE id IN (
+                   SELECT topicid
+                     FROM event
+                    WHERE current_date <= edate
+                      AND visable
+                   )
+            ORDER BY descr";
+            $stmt = $this->dbh->query($sql);
+            while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                $blocks[$row['descr']]
+                    = array(
+                        'id'   => $row['id'],
+                        'data' => $this->_getUpcomingEventsByTopics($row['id'])
+                    );
+            }
+        } catch (PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+        return $blocks;
+    }
+
+    /**
+     * Returns array of events by topic (limited)
+     *
+     * @param int $topicId Topic id for the events
+     *
+     * @return array Events
+     */
+    private function _getUpcomingEventsByTopics($topicId)
+    {
+        $events = array();
+        $sql = "
+          SELECT event.id,header,img,descr,event.reacur,
+                 CASE
+                    WHEN reacur is true THEN event_day
+                    ELSE bdate
+                 END as event_starting_date,event.bdate,event.edate
+            FROM event
+                 LEFT OUTER JOIN event_recur
+                 ON (event.id = event_recur.event_id)
+           WHERE visable
+             AND ((reacur is true AND event_recur.event_day >= current_date)
+                 OR
+                 (reacur is not true AND event.edate >= current_date))
+             AND event.topicid = {$topicId}
+        ORDER BY event_starting_date,header
+           LIMIT 3
+          OFFSET 0";
+        $result = $this->dbh->query($sql);
+        Zend_Date::setOptions(array('format_type' => 'php'));
+        while ($row = $result->fetch()) {
+            $eventDateUsed = new Zend_Date(
+                strtotime($row['event_starting_date']),
+                Zend_Date::TIMESTAMP
+            );
+            $hrefFormat
+                = 'events/%d/%d/';
+            $href = MEDIA_BASE_URL . sprintf(
+                $hrefFormat,
+                EVENT_PAGE,
+                $row['id']
+            );
+            $sdate = strtotime($row['bdate']);
+            $edate = strtotime($row['edate']);
+            $dates
+                = ($event_day)
+                ? $event_day
+                : GLM_TEMPLATE::get_event_date(
+                    $sdate,
+                    $edate,
+                    'timestamp'
+                );
+            if ($row['reacur']) {
+                $dates = $eventDateUsed->toString('M - jS');
+            }
+            // strip out tags and add slashes to $descr
+            $events[] = array(
+                'href'   => $href,
+                'bdate'  => $eventDateUsed->toString('M - jS'),
+                'dates'  => $dates,
+                'header' => strip_tags($row['header']),
+                'img'   => FILE_SERVER_URL . IS_OWNER_ID . '/eventSlider/' . $row['img'],
+                'descr'         => substr(strip_tags($row['descr']), 0, 150)
+            );
+        }
+        return $events;
+    }
+}
diff --git a/Toolkit/Events/Forms/Admin/EditEvent.php b/Toolkit/Events/Forms/Admin/EditEvent.php
new file mode 100755 (executable)
index 0000000..679f43b
--- /dev/null
@@ -0,0 +1,1571 @@
+<?php
+
+/**
+ * Handles editing of events
+ *
+ * PHP version 5
+ *
+ * @category Toolkit
+ * @package  Events
+ * @author   Jamie Kahgee <steve@gaslightmedia.com>
+ * @license  http://www.gaslightmedia.com Gaslightmedia
+ * @version  CVS: $Id: AddEventForm.php,v 1.20 2010/07/04 23:58:22 jamie Exp $
+ * @link     http://demo.gaslightmedia.com
+ */
+/**
+ *
+ */
+require_once BASE . 'Toolkit/Forms/Rules/Image.php';
+
+/**
+ * Handles editing of events
+ *
+ * @category  Toolkit
+ * @package   Events
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @link      http://demo.gaslightmedia.com
+ */
+class Toolkit_Events_Forms_Admin_EditEvent
+    extends Toolkit_FormBuilder
+    implements Toolkit_Form
+{
+
+    /**
+     * Table in Database which holds the contact data
+     *
+     * @var    string
+     * @access public
+     */
+    public $tableName = 'event';
+
+    /**
+     * Table meta data
+     *
+     * This is used when inserting/updating data for the records
+     * so the PDO's can use explicit data types for the parameters.
+     *
+     * @var    array
+     * @access public
+     */
+    public $tableMetaData;
+
+    /**
+     * Who to send the email to when the contact form is submitted
+     *
+     * If you leave this blank, its value will get set to the OWNER_EMAIL
+     * in the constructor.
+     *
+     * If you ***DO NOT*** want any emails to go out when the form is submitted
+     * then set the value to false. Do not set it to 0 for false, because the
+     * check uses a strict type check to determine if the value is actually
+     * false. This is what allows for the empty value as an option, which sets
+     * the value to OWNER_EMAIL and won't override the $email property if
+     * this class gets subclassed and the value for this property gets set in
+     * the properties of the subclass and not in the constructor after this
+     * constructor function is called.
+     *
+     * tongue twister...I know.
+     * <code>
+     * protected $email = false;
+     * </code>
+     *
+     * @var    unknown
+     * @access protected
+     */
+    protected $email;
+
+    /**
+     * From header in the owner email
+     *
+     * This just sets the From header in the owner email
+     * SITENAME <from@email.com>
+     *
+     * It gets set to the constant SITENAME in the constructor if you leave
+     * empty here, but you can set it to something different here to override
+     * that if you desire.
+     *
+     * @var    unknown
+     * @access protected
+     */
+    protected $siteName;
+
+    /**
+     * Email subject and <h1> header in email
+     *
+     * It gets set in the constructor if you leave empty here, but you
+     * can set it to something different here to override that if you desire.
+     *
+     * @var    string
+     * @access protected
+     */
+    protected $subject = 'New Event Submission';
+
+    /**
+     * Message to display if the form is successfully submitted
+     *
+     * @var    string
+     * @access protected
+     */
+    protected $successMsg = '
+    <style type="text/css">
+        #category {display:none};
+        .listings {display:none};
+    </style>
+        <div id="form-sucess-top">
+            Your event has been successfully added to the events calendar,
+            however will not be visible until it has been approved by
+            the Web site administrator. Thank You.
+        </div>';
+
+    /**
+     * Extra rules for processesing
+     *
+     * This registers the Zip validation rules (and any others listed) for
+     * QuickForm.
+     *
+     * Zip validation checks both US and Canadian Zip codes
+     *
+     * @var    array
+     * @access protected
+     */
+    protected $registeredRules = array(
+        'phone',
+        array(
+            'checkEmail',
+            'callback',
+            'email',
+            'Validate'
+        ),
+        array(
+            'checkURI',
+            'callback',
+            'uri',
+            'Validate'
+        )
+    );
+
+    /**
+     * Options for flexy templating engine
+     *
+     * Pulls the preset options from the setup.phtml file
+     * overwrites the templateDir and compileDir to match this classes needs
+     *
+     * @var    array
+     * @access protected
+     */
+    protected $flexyOptions;
+
+    /**
+     * Class constructor
+     *
+     * @param PDO    $pdo         PHP Data Object
+     * @param string $formName    Form's name.
+     * @param string $method      (optional)Form's method defaults to 'POST'
+     * @param string $action      (optional)Form's action
+     * @param string $target      (optional)Form's target defaults to '_self'
+     * @param mixed  $attributes  (optional)Extra attributes for <form> tag
+     * @param bool   $trackSubmit (optional)Whether to track if the form was
+     *                            submitted by adding a special hidden field
+     *
+     * @access public
+     */
+    public function __construct(
+        PDO $pdo,
+        $formName,
+        $method = 'post',
+        $action = '',
+        $target = '',
+        $attributes = null,
+        $trackSubmit = false
+    ) {
+        parent::__construct(
+            $formName, $method, $action, $target, $attributes, $trackSubmit
+        );
+
+        $this->dbh = $pdo;
+
+        if ($this->email !== false && empty($this->email)) {
+            //  Set to false to turn off email function.
+            $this->email = OWNER_EMAIL;
+        }
+        if (empty($this->siteName)) {
+            $this->siteName = SITENAME;
+        }
+        if (empty($this->subject)) {
+            $this->subject = 'Contact Request from website ' . SITENAME;
+        }
+
+        $this->flexyOptions = $GLOBALS['flexyOptions'];
+        $this->flexyOptions['templateDir'] = dirname(__FILE__) . "/templates/";
+        $this->flexyOptions['compileDir']
+            = dirname(__FILE__) . "/templates/compiled/";
+
+        $var = basename(__FILE__, '.php');
+
+        $callbackUrl = ($_SERVER['HTTPS'] == 'on')
+            ?
+            BASE_SECURE_URL
+            : MEDIA_BASE_URL;
+
+        $this->captchaOptions = array(
+            'width'        => 100,
+            'height'       => 50,
+            'callback'     => "{$callbackUrl}Toolkit/qfcaptcha.php?var=$var",
+            'sessionVar'   => $var,
+            'imageOptions' => array(
+                'font_size'        => 16,
+                'font_path'        => GLM_APP_BASE . 'glmPEAR/Image/Canvas/Fonts/',
+                'font_file'        => 'times.ttf',
+                'background_color' => '#cccccc',
+                'obfuscation'      => false,
+                'angle'            => true,
+            ),
+        );
+    }
+
+    /**
+     * Get the array of members used for the select list
+     *
+     * @param PDO $dbh Database handler
+     *
+     * @return array members used to populate a select list element
+     * @access protected
+     */
+    private function _getMembers(PDO $dbh)
+    {
+        //  Get only the active members from
+        $sql = "
+            SELECT *
+              FROM member
+             ORDER BY member_name";
+
+        $members = array();
+        try {
+            foreach ($dbh->query($sql, PDO::FETCH_ASSOC) as $row) {
+                $members[$row['member_id']] = $row['member_name'];
+            }
+        } catch (PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+
+        return $members;
+    }
+
+    /**
+     * Validate date input
+     *
+     * allows for empty dates to be valid
+     *
+     * @param array $date date group from form
+     *
+     * @return boolean true if valid, false if not
+     * @access public
+     */
+    public function checkDate($date)
+    {
+        if (!$date) {
+            return true;
+        } else {
+            return Validate::date($date, array('format' => '%m/%d/%Y'));
+        }
+    }
+
+    /**
+     * Validate date input
+     *
+     * allows for empty end date to be valid
+     *
+     * @param array $d date group from form
+     *
+     * @return boolean true if valid, false if not
+     * @access public
+     */
+    public function checkDateRange(array $d)
+    {
+        if (!$this->hasEndDate($d[1])) {
+            //  no end date is a valid date range
+            return true;
+        }
+        $pattern = '/([0-9]{2})\/([0-9]{2})\/([0-9]{4})/';
+        if (preg_match($pattern, $d[0], $m)) {
+            $t1 = mktime(0, 0, 0, (int) $m[1], (int) $m[2], (int) $m[3]);
+            $bdate = new Date($t1);
+        }
+        if (preg_match($pattern, $d[1], $m)) {
+            $t2    = mktime(0, 0, 0, (int) $m[1], (int) $m[2], (int) $m[3]);
+            $edate = new Date($t2);
+        }
+        if ($bdate && $edate) {
+            //  0 if the dates are equal - valid
+            // -1 if $bdate is before $edate - valid
+            //  1 if $bdate is after $edate - invalid
+            $res = Date::compare($bdate, $edate);
+            return ($res !== 1);
+        }
+        return true;
+    }
+
+    /**
+     * Form element definitions
+     *
+     * @param PDO $dbh Database handle
+     *
+     * @return void
+     * @access public
+     */
+    public function configureElements(PDO $dbh)
+    {
+        $e = array();
+        if (defined('MEMBERS_DB') && MEMBERS_DB) {
+            $members             = $this->_getMembers($dbh);
+            $conf                = new Config;
+            $memberDbListingType = & $conf->parseConfig(
+                BASE . 'Toolkit/Members/config.ini', 'IniFile'
+            )->getItem('section', 'listing type')
+                ->getItem('directive', 'singular')
+                ->getContent();
+        }
+
+        //  All Elements are created here.  This includes group element definitions.
+        $e[] = array(
+            'type'    => 'header',
+            'name'    => 'eventInfoHeader_rmv',
+            'display' => 'Event Information'
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => true,
+            'name'    => 'header',
+            'display' => 'Event Name'
+        );
+        $e[] = array(
+            'type'    => 'select',
+            'req'     => false,
+            'name'    => 'topicid',
+            'display' => 'Category',
+            'opts'    => $this->getTopicFields(),
+        );
+        $e[] = array(
+            'type' => 'hidden',
+            'req'  => false,
+            'name' => 'id'
+        );
+        $e[] = array(
+            'type' => 'hidden',
+            'req'  => false,
+            'name' => 'oldVisable'
+        );
+        $e[] = array(
+            'type'    => 'advcheckbox',
+            'req'     => false,
+            'name'    => 'visable',
+            'display' => 'Active?',
+            'opts'    => 'Yes',
+            'val'     => array(0, 1)
+        );
+        if (defined('MEMBERS_DB') && MEMBERS_DB) {
+            $e[] = array(
+                'type'    => 'select',
+                'req'     => false,
+                'name'    => 'member_id',
+                'display' => $memberDbListingType,
+                'opts'    => array(''   => '-- Select --') + $members
+            );
+            $e[] = array(
+                'type' => 'hidden',
+                'req'  => false,
+                'name' => 'pending',
+            );
+        }
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => true,
+            'name'    => 'bdate',
+            'display' => 'Start Date',
+            'opts'     => array('id' => 'sdate')
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => true,
+            'name'    => 'edate',
+            'display' => 'End Date',
+            'opts'     => array('id' => 'edate')
+        );
+        $e[] = array(
+            'type'    => 'advcheckbox',
+            'req'     => false,
+            'name'    => 'all_day',
+            'display' => 'All Day Event?',
+            'opts'    => 'Yes',
+            'val'     => array(0, 1)
+        );
+        $e[] = array(
+            'type'    => 'date',
+            'req'     => false,
+            'name'    => 'btime',
+            'display' => 'Start Time',
+            'opts'    => array(
+                'format'           => 'h : i A',
+                'addEmptyOption'   => true,
+                'emptyOptionValue' => '',
+                'emptyOptionText'  => array(
+                    'h'               => 'hh',
+                    'i'               => 'mm',
+                    'A'               => 'am/pm'
+                ),
+                'optionIncrement' => array(
+                    'i'     => 15,
+                ),
+            ),
+            'error' => 'ERROR: You must select a start time!',
+        );
+        $e[] = array(
+            'type'    => 'date',
+            'req'     => false,
+            'name'    => 'etime',
+            'display' => 'End Time',
+            'opts'    => array(
+                'format'           => 'h : i A',
+                'addEmptyOption'   => true,
+                'emptyOptionValue' => '',
+                'emptyOptionText'  => array(
+                    'h'               => 'hh',
+                    'i'               => 'mm',
+                    'A'               => 'am/pm'
+                ),
+                'optionIncrement' => array(
+                    'i'  => 15,
+                ),
+            ),
+        );
+        $e[] = array(
+            'type'    => 'advcheckbox',
+            'req'     => false,
+            'name'    => 'reacur',
+            'display' => 'Recurring Event',
+            'opts'    => 'Is this a recurring event?',
+            'val'     => array(0, 1)
+        );
+        $daysOm = array('' => '');
+        for ($i = 1; $i <= 31; ++$i) {
+            $daysOm[$i] = $i;
+        }
+        $e[] = array(
+            'type'    => 'select',
+            'name'    => 'dayom',
+            'display' => 'Every Month on',
+            'opts'    => $daysOm
+        );
+        $e[] = array(
+            'type'    => 'select',
+            'req'     => false,
+            'name'    => 'weekom',
+            'display' => 'Recurs',
+            'opts'    => array(
+                ''        => 'Every Week of Month',
+                '9'       => 'Every Other Week',
+                '1'       => 'Every First Week of Month',
+                '2'       => 'Every Second Week of Month',
+                '3'       => 'Every Third Week of Month',
+                '4'       => 'Every Fourth Week of Month'
+            ),
+        );
+        $weekdays = array(
+            1 => 'Sunday',
+            'Monday',
+            'Tuesday',
+            'Wednesday',
+            'Thursday',
+            'Friday',
+            'Saturday'
+        );
+        $ri = 1;
+        for ($i = 1; $i <= 7; ++$i) {
+            $daysOw[] = array(
+                'type' => 'advcheckbox',
+                'req'  => false,
+                'name' => $i,
+                'opts' => $weekdays[$i],
+                'val'  => array('', $ri)
+            );
+            $ri  = $ri << 1;
+        }
+        $e[] = array(
+            'type'       => 'group',
+            'req'        => false,
+            'name'       => 'daysow',
+            'group'      => $daysOw,
+            'label'      => 'Days Of Week',
+            'seperator'  => ' ',
+            'appendName' => true
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => 'url',
+            'display' => 'Website'
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => 'cost',
+            'display' => 'Cost'
+        );
+        $e[] = array(
+            'type'    => 'static',
+            'req'     => false,
+            'name'    => 'current_img_rmv',
+            'display' => 'Current Image'
+        );
+        $e[] = array(
+            'type' => 'hidden',
+            'req'  => false,
+            'name' => 'img'
+        );
+        $e[] = array(
+            'type'    => 'file',
+            'req'     => false,
+            'name'    => 'img_file_rmv',
+            'display' => 'Event Image'
+        );
+        $e[]      = array(
+            'type' => 'static',
+            'req'  => false,
+            'name' => 'img_instructions_rmv',
+            'opts' => '.jpg or .gif images only'
+        );
+        $e[] = array(
+            'type'    => 'textarea',
+            'req'     => false,
+            'name'    => 'descr',
+            'display' => 'Description',
+            'opts'    => array('id' => 'descr'),
+            'noCharLimit' => true
+        );
+        $e[] = array(
+            'type'    => 'header',
+            'name'    => 'eventLocationInfoHeader_rmv',
+            'display' => 'Event Location Information
+                <div id="map-dialog">
+                    <div id="map_canvas" style="width:500px; height:400px"></div>
+                </div>
+                <a id="map-it" href="#">Map It</a>'
+        );
+        $e[] = array(
+            'type'    => 'advcheckbox',
+            'req'     => false,
+            'name'    => 'hide_address',
+            'display' => 'Hide Event Address',
+            'opts'    => 'Yes',
+            'val'     => array(0, 1)
+        );
+        $e[] = array(
+            'type' => 'hidden',
+            'req'  => false,
+            'name' => 'lat',
+            'opts' => array('id' => 'lat')
+        );
+        $e[] = array(
+            'type' => 'hidden',
+            'req'  => false,
+            'name' => 'lon',
+            'opts' => array('id' => 'lon')
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => 'loc',
+            'display' => 'Place'
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => 'address',
+            'display' => 'Address',
+            'opts'     => array('id' => 'address')
+        );
+        $e[]         = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => 'city',
+            'display' => 'City',
+            'opts'     => array('id' => 'city')
+        );
+        $e[]         = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => 'state',
+            'display' => 'State',
+            'opts'     => array('id' => 'state')
+        );
+        $e[]         = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => 'zip',
+            'display' => 'ZIP',
+            'opts'     => array('id' => 'zip')
+        );
+        $e[] = array(
+            'type'    => 'header',
+            'name'    => 'eventContactHeader_rmv',
+            'display' => 'Event Contact Information'
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => 'contact',
+            'display' => 'Contact Person'
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => 'email',
+            'display' => 'Contact Email'
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => 'phone',
+            'display' => 'Contact Phone'
+        );
+        $e[] = array(
+            'type'    => 'static',
+            'req'     => false,
+            'name'    => 'current_file_rmv',
+            'display' => 'Current File'
+        );
+        $e[] = array(
+            'type' => 'hidden',
+            'req'  => false,
+            'name' => 'file'
+        );
+        $e[] = array(
+            'type'    => 'file',
+            'req'     => false,
+            'name'    => 'file_rmv',
+            'display' => 'Event File'
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => 'filename',
+            'display' => 'File Name'
+        );
+        $e[] = array(
+            'type'    => 'header',
+            'name'    => 'eventInfoHeader_rmv',
+            'display' => 'Event Admin Information'
+        );
+        $e[]      = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => 'admin_contact_name',
+            'display' => 'Contact Name Submitting Event'
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => 'admin_org_name',
+            'display' => 'Organization Name Submitting Event'
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => 'admin_phone',
+            'display' => 'Phone'
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => 'admin_email',
+            'display' => 'Email Address'
+        );
+        $e[] = array(
+            'type'    => 'textarea',
+            'req'     => false,
+            'name'    => 'notes',
+            'display' => 'Notes'
+        );
+        if ($_REQUEST['id']) {
+            $e[] = array(
+                'type'    => 'static',
+                'req'     => false,
+                'name'    => 'create_date',
+                'display' => 'Create Date'
+            );
+            $e[] = array(
+                'type'    => 'static',
+                'req'     => false,
+                'name'    => 'approved_date',
+                'display' => 'Approved Date'
+            );
+        }
+        $e[] = array(
+            'type'    => 'submit',
+            'req'     => false,
+            'name'    => 'submit_rmv',
+            'display' => 'Submit'
+        );
+
+        $this->setupElements($e);
+    }
+
+    /**
+     * Form filter definitions
+     *
+     * Applies a data filter for the given fields when the form is submitted
+     *
+     * @return void
+     * @access public
+     */
+    public function configureFilters()
+    {
+        $f = array();
+
+        $f[] = array(
+            'element' => '__ALL__',
+            'filter'  => 'trim'
+        );
+        $f[]      = array(
+            'element' => 'url',
+            'filter'  => array('Toolkit_Common', 'filterURI')
+        );
+
+        $this->setupFilters($f);
+    }
+
+    /**
+     * Helper function to handle setting up the form
+     *
+     * @param PDO $dbh PDO database connection
+     *
+     * @return void
+     * @access public
+     */
+    public function configureForm(PDO $dbh)
+    {
+        $this->configureElements($dbh);
+        $this->configureDefaults();
+        $this->configureFilters();
+        $this->configureRules();
+    }
+
+    /**
+     * Form rule definitions
+     *
+     * Adds validation rules for the given fields
+     *
+     * @return void
+     * @access public
+     */
+    public function configureRules()
+    {
+        //  Form Rules
+        $r = array();
+
+        $mimeTypes = array(
+            'image/jpe',
+            'image/jpeg',
+            'image/jpg',
+            'image/jfif',
+            'image/pjpeg',
+            'image/pjp',
+            'image/gif',
+            'image/png',
+            'image/x-png',
+        );
+
+        $r[] = array(
+            'element'    => 'topicid',
+            'message'    => 'ERROR: Invalid Category!',
+            'type'       => 'numeric',
+            'format'     => null,
+            'validation' => $this->validationType,
+            'reset'      => true,
+            'force'      => false
+        );
+        $r[]         = array(
+            'element' => 'email',
+            'message' => 'ERROR: Invalid Email Format!',
+            'type'    => 'checkEmail',
+            'format'  => array('use_rfc822' => true),
+            'validation' => $this->validationType,
+            'reset'      => true,
+            'force'      => false
+        );
+        $r[]         = array(
+            'element' => array('bdate', 'edate'),
+            'message' => 'ERROR: Starting Date must be before Ending Date',
+            'type'    => 'callback',
+            'format'  => array(&$this, 'checkDateRange'),
+            'validation' => $this->validationType,
+            'reset'      => false,
+            'force'      => false
+        );
+        $r[]         = array(
+            'element' => 'bdate',
+            'message' => 'ERROR: Invalid date!',
+            'type'    => 'callback',
+            'format'  => array(&$this, 'checkDate'),
+            'validation' => $this->validationType,
+            'reset'      => false,
+            'force'      => false
+        );
+        $r[]         = array(
+            'element' => 'edate',
+            'message' => 'ERROR: Invalid date!',
+            'type'    => 'callback',
+            'format'  => array(&$this, 'checkDate'),
+            'validation' => $this->validationType,
+            'reset'      => false,
+            'force'      => false
+        );
+        $r[]         = array(
+            'element' => 'url',
+            'message' => 'ERROR: Invalid URL format',
+            'type'    => 'checkURI',
+            'format'  => array(
+                'allowed_schemes' => array('http', 'https'),
+                'strict'     => false
+            ),
+            'validation' => $this->validationType,
+            'reset'      => false,
+            'force'      => false
+        );
+        if ($this->useCaptcha) {
+            $r[] = array(
+                'element'    => 'captcha_rmv',
+                'message'    => 'ERROR: What you entered didn\'t match!',
+                'type'       => 'CAPTCHA',
+                'format'     => $this->captchaQuestion,
+                'validation' => $this->validationType,
+                'reset'      => true,
+                'force'      => false
+            );
+        }
+        if (is_uploaded_file($_FILES['img_file_rmv']['tmp_name'])) {
+            $r[] = array(
+                'element'    => 'img_file_rmv',
+                'message'    => 'ERROR: Incorrect File Type (.gif, .png, .jpg) only!',
+                'type'       => 'mimetype',
+                'format'     => $mimeTypes,
+                'validation' => $this->validationType,
+                'reset'      => false,
+                'force'      => false
+            );
+        }
+        $r[]         = array(
+            'element' => 'img_file_rmv',
+            'message' => 'ERROR: Error uploading image!',
+            'type'    => 'Image',
+            'format'  => array(
+                'form'                => $this,
+                'fieldName'           => 'img_file_rmv',
+                'imageField'          => 'img',
+                'is'                  => new Toolkit_FileServer_ImageAdapter(),
+                'deleteExistingImage' => false,
+                'injectImage'         => array('tgtElement' => 'current_img_rmv')
+            ),
+            'validation' => 'server',
+            'reset'      => false,
+            'force'      => false
+        );
+
+        $this->setupRules($r);
+    }
+
+    /**
+     * Form defaults
+     *
+     * @return void
+     * @access public
+     */
+    public function configureDefaults()
+    {
+        if (ctype_digit($_GET['id'])) {
+            $sql                   = "
+            SELECT *
+              FROM event
+             WHERE id = :id";
+            $stmt                  = $this->dbh->prepare($sql);
+            $stmt->bindParam(":id", $_GET['id'], PDO::PARAM_INT);
+            $stmt->execute();
+            $d                     = $stmt->fetch(PDO::FETCH_ASSOC);
+            $d['current_file_rmv']
+                = ($d['file'])
+                ? '<a href="' . UPLOADED_FILES . 'original/' . $d['file'] . '">
+                ' . $d['file'] . '</a>
+                <input type="hidden" name="del_file_rmv" value="0">
+                <input type="checkbox" name="del_file_rmv" value="1">Delete File?'
+                : 'File not yet uploaded';
+            $d['current_img_rmv']
+                = ($d['img'])
+                ? '<img src="' . THUMB . $d['img'] . '">
+                <input type="hidden" name="del_img_rmv" value="0">
+                <input type="checkbox" name="del_img_rmv" value="1">Delete Image?'
+                : 'Image not yet uploaded';
+            $ri                    = 1;
+            for ($r                     = 1; $r <= 7; ++$r) {
+                if ($d['daysow'] & $ri) {
+                    $daysow[$r]  = $ri;
+                }
+                $ri          = $ri << 1;
+            }
+            $d['daysow'] = $daysow;
+            $d['oldVisable'] = $d['visable'];
+        } else {
+            $d['current_img_rmv']  = 'Image not yet uploaded';
+            $d['current_file_rmv'] = 'File not yet uploaded';
+        }
+        $this->setupDefaults($d);
+    }
+
+    /**
+     * get event topics
+     *
+     * @return array     topics
+     * @access protected
+     */
+    protected function getTopicFields()
+    {
+        try {
+            $sql = "
+                SELECT *
+                  FROM topic
+                 ORDER BY descr";
+
+            $topics = array('' => '-- Select --');
+            foreach ($this->dbh->query($sql) as $row) {
+                $topics[$row['id']] = $row['descr'];
+            }
+
+            return $topics;
+        } catch (PDOException $e) {
+            return Toolkit_Common::handleError($e);
+        }
+    }
+
+    /**
+     * verifies if we have a valid end date to work with
+     *
+     * @param string $d end date
+     *
+     * @return boolean if the end date is
+     */
+    protected function hasEndDate($d)
+    {
+        $pattern = '/([0-9]{2})\/([0-9]{2})\/([0-9]{4})/';
+        if (preg_match($pattern, $d, $m)) {
+            return checkdate((int) $m[1], (int) $m[2], (int) $m[3]);
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * Format an array into an acceptable string
+     *
+     * @param mixed  &$i     array values to format or null value for
+     *                       element that was not filled in
+     * @param string $format string to format values into
+     *
+     * @return string formatted string
+     * @access public
+     */
+    public function formatValue(&$i, $format)
+    {
+        //  Allow for 0 to be not empty.  This allows for minutes in the
+        //  time arrays to be valid if they are on the hour ie. (1:00 pm)
+        $notEmpty = create_function('$v', 'return strlen($v) > 0;');
+        if (is_array($i) && count(array_filter($i, $notEmpty)) == 3) {
+            list($x, $y, $z) = array_values($i);
+            eval("\$i = sprintf('$format', $x, $y, $z);");
+        } else {
+            $i = null;
+        }
+    }
+
+    /**
+     * Inserts contact data into the contact db
+     *
+     * @param array $values submitted values
+     *
+     * @return object    result of db insert query
+     * @access protected
+     */
+    protected function insertData($values)
+    {
+        $values = $this->_geocode($values);
+
+        try {
+            // need to set the dates up first
+            unset($values['id'], $values['oldVisable']);
+            $values['visable'] = 1;
+            $sql               = Toolkit_Common::createSQLInsert(
+                $this->tableName, array_keys($values)
+            );
+            $sql .= " RETURNING id";
+            $stmt              = Toolkit_Common::prepareQuery(
+                $this->dbh, $this->tableName, $sql, $values
+            );
+            $stmt->execute();
+            return $stmt->fetchColumn();
+        } catch (PDOException $e) {
+            return Toolkit_Common::handleError($e);
+        }
+    }
+
+    /**
+     * Handles how to process the form when submitted
+     *
+     * @param array $values Form submitted values
+     *
+     * @return boolean|void     Result of Insert / Update function
+     * @access public
+     */
+    public function processData($values)
+    {
+        //  Form data used for the insert/update sql queries and
+        //  the form email.
+        $e = array();
+        $this->setFormData($e);
+
+        unset(
+            $values['MAX_FILE_SIZE'], $values['pending'], $values['archived']
+        );
+        if ($values['del_img_rmv'] && $values['img']) {
+            $is            = new Toolkit_Image_Server();
+            $is->imageDelete($values['img']);
+            $values['img'] = '';
+        }
+        if ($values['del_file_rmv'] && $values['file']) {
+            $is             = new Toolkit_Image_Server();
+            $is->imageDelete($values['file']);
+            $values['file'] = '';
+        }
+        if ($values['file_rmv']['name']) {
+            $fs = new Toolkit_FileServer_FileAdapter();
+            try {
+                $res = $fs->upload('file_rmv');
+            } catch (Toolkit_FileServer_Exception $e) {
+                Toolkit_Logger::logException('File Server', $e);
+                echo -1;
+                return;
+            }
+            $values['file'] = $res['name'];
+        }
+
+        //  Get rid of any defined un-needed elements.
+        //  un-needed elements after the form is submitted are defined
+        //  by the ending _rmv name.
+        foreach ($values as $k => &$v) {
+            if (!is_array($v)) {
+                $values[$k] = str_replace("\r", "", $v);
+            }
+            if (preg_match('/^.+_rmv$/', $k)) {
+                unset($values[$k]);
+            }
+        }
+        $values['url']   = preg_replace("/^(http:\/\/)/", "", $values['url']);
+        if ($values['reacur']) {
+            if ($values['dayom']) {
+                $values['weekom'] = null;
+                $values['daysow'] = null;
+            } else {
+                $values['dayom']  = null;
+                $values['daysow'] = array_sum($values['daysow']);
+            }
+        } else {
+            $values['dayom']  = null;
+            $values['weekom'] = null;
+            $values['daysow'] = null;
+        }
+        if (defined('MEMBERS_DB')
+            && MEMBERS_DB
+            && !$values['member_id']
+        ) {
+            $values['member_id'] = null;
+        }
+        if (!$values['topicid']) {
+            $values['topicid'] = null;
+        }
+        $bdate = $values['bdate'];
+        $edate = $values['edate'];
+        // still need to reformat the time fields
+        $this->formatValue($values['btime'], '%02d:%02d %s');
+        $this->formatValue($values['etime'], '%02d:%02d %s');
+        if ($values['reacur']) {
+            $recur['dow'] = $values['daysow'];
+            $recur['dom'] = $values['dayom'];
+            if ($recur['dow']) {
+                $recur['recur_week'] = ($values['weekom'])
+                    ? $values['weekom']
+                    : null;
+            } else {
+                $recur['recur_week'] = null;
+            }
+        }
+        $eventId = $_REQUEST['id'];
+        if (ctype_digit($eventId)) {
+            $this->updateData($values);
+        } else {
+            $eventId = $this->insertData($values);
+        }
+        $this->insertEventRecur($bdate, $edate, $eventId, $recur);
+        return true;
+    }
+
+    /**
+     * Custom rendering templates for special fields on the form
+     *
+     * @return void
+     * @access protected
+     */
+    protected function setupRenderers()
+    {
+        parent::setupRenderers();
+        $renderer  = & $this->defaultRenderer();
+        $required  = '<!-- BEGIN required --><span class="req">*</span>
+        <!-- END required -->';
+        $error     = '<!-- BEGIN error --><div class="req">{error}</div>
+        <!-- END error -->';
+        $recurTmpl = '<tr class="recur-event">
+        <td class="labelcell">' . $required . '<label>{label}</label></td>
+        <td class="fieldcell">' . $error . '{element}</td></tr>';
+        $renderer->setElementTemplate($recurTmpl, 'dayom');
+        $renderer->setElementTemplate($recurTmpl, 'weekom');
+        $renderer->setElementTemplate($recurTmpl, 'daysow');
+        $renderer->setElementTemplate(
+            '<tr><td colspan="2">' . $required . '{label}'
+            . $error . '{element}</td></tr>', 'descr'
+        );
+        $renderer->setElementTemplate(
+            '<tr align="center"><td colspan="2">'
+            . $required . '{label}' . $error . '{element}</td></tr>', 'submit_rmv'
+        );
+    }
+
+    /**
+     * Handles how to display the current step the user is at in the form
+     *
+     * destroying and resetting the captcha value dis-allows someone from
+     * re-sending a form on a previous captcha.
+     *
+     * @return string form HTML state
+     * @access public
+     */
+    public function toHtml()
+    {
+        $GLOBALS['topScripts'][]
+            = MEDIA_APP_BASE_URL
+            . 'libjs/jqueryui/1.8.13/js/jquery-ui-1.8.13.custom.min.js';
+        $GLOBALS['topScripts'][]  = CKEDITOR_JS . '';
+        $GLOBALS['styleSheets'][]
+            = MEDIA_APP_BASE_URL
+            . 'libjs/jqueryui/1.8.13/development-bundle/themes/base/jquery.ui.all.css';
+        $this->setupRenderers();
+        if ($this->validate()) {
+            $this->cleanForm();
+            if ($this->process(array(&$this, 'processData'), $this->mergeFiles)) {
+                $this->freeze();
+                $output  = $this->successMsg;
+                $pageUrl
+                    = MEDIA_BASE_URL . 'admin/Events/list_events.phtml';
+                $params  = array();
+                if ($_GET['archived']) {
+                    $params[] = 'archived=' . $_GET['archived'];
+                }
+                if ($_GET['pending']) {
+                    $params[] = 'pending=' . $_GET['pending'];
+                }
+                if ($_GET['start']) {
+                    $params[] = 'start=' . $_GET['start'];
+                }
+                if ($_GET['Query']) {
+                    $params[] = 'Query=' . urlencode($_GET['Query']);
+                }
+                if ($_GET['date_search_to']) {
+                    $params[] = 'date_search_to=' . $_GET['date_search_to'];
+                }
+                if ($_GET['date_search_from']) {
+                    $params[] = 'date_search_from=' . $_GET['date_search_from'];
+                }
+                if (!empty($params)) {
+                    $pageUrl
+                        .= '?'
+                        . implode('&', $params);
+                }
+                header('Location: ' . $pageUrl);
+                exit;
+            }
+            $this->sent = true;
+        } elseif ($this->isSubmitted()) {
+            $output                     = $this->errorMsg;
+            //$GLOBALS['bottomScripts'][] = MEDIA_APP_BASE_URL . 'libjs/textlimit.js';
+            $GLOBALS['bottomScripts'][]
+                = MEDIA_BASE_URL . 'Toolkit/Events/libjs/edit-event.js';
+            $GLOBALS['topScripts'][]
+                = 'http://maps.googleapis.com/maps/api/js?sensor=true';
+            $GLOBALS['bottomScripts'][]
+                = MEDIA_BASE_URL . 'Toolkit/Maps/geoCoder.js';
+            $output .= parent::toHtml();
+        } else {
+            //$GLOBALS['bottomScripts'][] = MEDIA_APP_BASE_URL . 'libjs/textlimit.js';
+            $GLOBALS['topScripts'][]
+                = 'http://maps.googleapis.com/maps/api/js?sensor=true';
+            $GLOBALS['bottomScripts'][]
+                = MEDIA_BASE_URL . 'Toolkit/Maps/geoCoder.js';
+            $GLOBALS['bottomScripts'][]
+                = MEDIA_BASE_URL . 'Toolkit/Events/libjs/edit-event.js';
+            $output                     = parent::toHtml();
+        }
+        return $output;
+    }
+
+    /**
+     * check to see if the address (old on) is different than the one submitted
+     * if the event is not found then return false
+     *
+     * @param array $values The submitted values for the edit event form
+     *
+     * @return boolean
+     */
+    private function _didAddressChange($values)
+    {
+        $didAddressChange = false;
+        if (!$values['id'] && !ctype_digit($values['id'])) {
+            return $didAddressChange;
+        }
+        try {
+            $sql = "
+            SELECT address,city,state,zip
+              FROM event
+             WHERE id = :id";
+            $stmt = $this->dbh->prepare($sql);
+            $stmt->bindParam(':id', $values['id'], PDO::PARAM_INT);
+            $stmt->execute();
+            $address = $stmt->fetch(PDO::FETCH_ASSOC);
+            if (!$address) {
+                return $didAddressChange;
+            } else {
+                if ($address['address'] != $values['address']) {
+                    $didAddressChange = true;
+                }
+                if ($address['city'] != $values['city']) {
+                    $didAddressChange = true;
+                }
+                if ($address['state'] != $values['state']) {
+                    $didAddressChange = true;
+                }
+                if ($address['zip'] != $values['zip']) {
+                    $didAddressChange = true;
+                }
+            }
+        } catch(PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+        return $didAddressChange;
+    }
+    /**
+     * Check to see if the Lat Lon data changed from submittion
+     *
+     * @param array $values Form Values
+     *
+     * @return boolean
+     */
+    private function _didLatLonChange($values)
+    {
+        $didLatLonChange = false;
+        if (!$values['id'] && !ctype_digit($values['id'])) {
+            return $didLatLonChange;
+        }
+        try {
+            $sql = "
+            SELECT lat,lon
+              FROM event
+             WHERE id = :id";
+            $stmt = $this->dbh->prepare($sql);
+            $stmt->bindParam(':id', $values['id'], PDO::PARAM_INT);
+            $stmt->execute();
+            $location = $stmt->fetch(PDO::FETCH_ASSOC);
+            if (!$location) {
+                return $didLatLonChange;
+            } else {
+                if ($location['lat'] != $values['lat']) {
+                    $didLatLonChange = true;
+                }
+                if ($location['lon'] != $values['lon']) {
+                    $didLatLonChange = true;
+                }
+            }
+        } catch(PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+        return $didLatLonChange;
+    }
+
+    /**
+     * Updates contact data in the contact db
+     *
+     * @param array $values submitted values
+     *
+     * @return object    result of db insert query
+     * @access protected
+     */
+    protected function updateData($values)
+    {
+        if (!$values['oldVisable'] && $values['visable']) {
+            $values['approved_date'] = date('m/d/Y');
+        }
+        unset($values['oldVisable']);
+        try {
+            // if the address changes then get the lat lon
+            if (  (!$this->_didLatLonChange($values)
+                && $this->_didAddressChange($values))
+                || (!$values['lat'] && !$values['lon'])
+            ) {
+                $values = $this->_geocode($values);
+            }
+
+            $sql = Toolkit_Common::createSQLUpdate(
+                $this->tableName, array_keys($values), array('id = :id')
+            );
+
+            return Toolkit_Common::processQuery(
+                $this->dbh, $this->tableName, $sql, $values
+            );
+        } catch (PDOException $e) {
+            return Toolkit_Common::handleError($e);
+        }
+    }
+
+    private function _geocode(array $values)
+    {
+        $geocoder = new GeocodeYahoo();
+        if (!$values['address'] && !$values['city'] && !$values['state']) {
+            return $values;
+        }
+        $address = array(
+            'city'  => $values['city'],
+            'state' => $values['state'],
+            'zip'   => $values['zip'],
+        );
+        if (!empty($values['address'])) {
+            $address['street'] = $values['address'];
+        }
+        try {
+            $response = $geocoder->geocodeAddress($address);
+            $responseArray = unserialize($response);
+            if ($responseArray['ResultSet']['Result'][0]['Latitude']) {
+                $values['lat'] = $responseArray['ResultSet']['Result'][0]['Latitude'];
+                $values['lon'] = $responseArray['ResultSet']['Result'][0]['Longitude'];
+            } else {
+                $values['lat'] = $responseArray['ResultSet']['Result']['Latitude'];
+                $values['lon'] = $responseArray['ResultSet']['Result']['Longitude'];
+            }
+
+            return $values;
+        } catch (BadMethodCallException $e) {
+            Toolkit_Logger::logException('Invalid Arg', $e);
+        } catch (Exception $e) {
+            Toolkit_Logger::logException('Yahoo GeoCode', $e);
+        }
+        return $values;
+
+    }
+
+    /**
+     * ordinalDay
+     *
+     * @param int $ord   The ordinal 1st 2nd 3rd
+     * @param int $day   day of the month
+     * @param int $month month of year
+     * @param int $year  year
+     *
+     * @access public
+     * @return int
+     */
+    function ordinalDay($ord, $day, $month, $year)
+    {
+        $firstOfMonth = mktime(0, 0, 30, $month, 1, $year);
+        $lastOfMonth  = $firstOfMonth + date("t", $firstOfMonth) * 86400;
+        $dayOccurs    = 0;
+
+        for ($i = $firstOfMonth; $i < $lastOfMonth; $i += 86400) {
+            if (date("w", $i) == $day) {
+                $dayOccurs++;
+                if ($dayOccurs == $ord) {
+                    $ordDay = $i;
+                }
+            }
+        }
+        return $ordDay;
+    }
+
+    /**
+     * getEventDates
+     *
+     * @param mixed  $starttime Timestamp for start
+     * @param mixed  $endtime   Timestamp for end
+     * @param array  $recur     recur array which has dow,dom,recur_week
+     * @param string $format    date format to use
+     *
+     * @access public
+     * @return void|array
+     */
+
+    function getEventDates($starttime, $endtime, $recur, $format = 'm/d/Y')
+    {
+        if (!is_array($recur)) {
+            return;
+        }
+        if ($starttime == $endtime) {
+            return;
+        }
+        if (is_array($recur['dow'])) {
+            $daysow = array_sum($recur['dow']);
+        } else {
+            $daysow = $recur['dow'];
+        }
+        if ($recur['recur_week'] == 9) {
+            $fWeekNum = date("W", $starttime);
+            if (date('w', $starttime) == 0) {
+                $fWeekNum++;
+            }
+            $lWeekNum = date("W", $endtime);
+            if (date('w', $endtime) == 0) {
+                $lWeekNum++;
+            }
+            for ($fi = $fWeekNum; $fi <= $lWeekNum; $fi = $fi + 2) {
+                $eWeeks[] = $fi;
+            }
+        }
+        for ($i        = $starttime; $i <= $endtime; $i += 86400) {
+            if ($recur['recur_week'] != '') {
+                if ($daysow) {
+                    $ri = 1;
+                    for ($r  = 0; $r < 7; $r++) {
+                        if ($daysow & $ri) {
+                            $ord = $this->ordinalDay($recur['recur_week'], $r, date('n', $i), date('Y', $i));
+                        }
+                        $ri  = $ri << 1;
+                    }
+                } else {
+                    $ord = null;
+                }
+            } else {
+                $ord = null;
+            }
+            if ($recur['dom']) {
+                if (date("j", $i) == $recur['dom']) {
+                    $events[] = date($format, $i);
+                }
+            } elseif ($daysow) {
+                $cur_dow = date("w", $i);
+                switch ($cur_dow) {
+                case 0:
+                    $cur_dow = 1;
+                    break;
+                case 1:
+                    $cur_dow = 2;
+                    break;
+                case 2:
+                    $cur_dow = 4;
+                    break;
+                case 3:
+                    $cur_dow = 8;
+                    break;
+                case 4:
+                    $cur_dow = 16;
+                    break;
+                case 5:
+                    $cur_dow = 32;
+                    break;
+                case 6:
+                    $cur_dow = 64;
+                    break;
+                }
+                if ((int) $cur_dow & $daysow) {
+                    $cDateWeek = date("W", $i);
+                    if (date('w', $i) == 0) {
+                        $cDateWeek++;
+                    }
+                    if ($recur['recur_week'] == 9) {
+                        if (in_array($cDateWeek, $eWeeks)) {
+                            $events[] = date($format, $i);
+                        }
+                    } elseif ($recur['recur_week'] != '') {
+                        if ($recur['recur_week'] && $i && $ord) {
+                            $day1 = date('z', $i);
+                            $day2 = date('z', $ord);
+                            if ($day1 == $day2) {
+                                $events[] = date($format, $i);
+                            }
+                        }
+                    } else {
+                        $events[] = date($format, $i);
+                    }
+                }
+            }
+        }
+        return $events;
+    }
+
+    /**
+     * insertEventRecur
+     *
+     * @param array $bdate    start date
+     * @param array $edate    end date
+     * @param int   $event_id Event's id
+     * @param array $recur    recur array
+     *
+     * @access public
+     * @return void
+     */
+
+    function insertEventRecur($bdate, $edate, $event_id, $recur)
+    {
+        try {
+            $sql = "
+            DELETE
+              FROM event_recur
+             WHERE event_id = :event_id";
+            $del = $this->dbh->prepare($sql);
+            $del->bindParam(":event_id", $event_id, PDO::PARAM_INT);
+            $del->execute();
+        } catch (PDOException $e) {
+            die($e->getMessage());
+        }
+
+        if (isset($recur) && is_array($recur)) {
+            $eventDates = $this->getEventDates(
+                strtotime($bdate),
+                strtotime($edate),
+                $recur
+            );
+            $fields     = array('event_id', 'event_day');
+            $sql = Toolkit_Common::createSQLInsert(
+                'event_recur', $fields
+            );
+            try {
+                if (isset($eventDates) && is_array($eventDates)) {
+                    foreach ($eventDates as $eventDay) {
+                        $eventValues = array(
+                            'event_id'  => $event_id,
+                            'event_day' => $eventDay
+                        );
+                        $stmt       = Toolkit_Common::prepareQuery(
+                            $this->dbh, 'event_recur', $sql, $eventValues
+                        );
+                        $stmt->execute();
+                    }
+                }
+            } catch (PDOException $e) {
+                Toolkit_Common::handleError($e);
+            }
+        }
+    }
+
+}
diff --git a/Toolkit/Events/Forms/Admin/EditTopics.php b/Toolkit/Events/Forms/Admin/EditTopics.php
new file mode 100644 (file)
index 0000000..0957758
--- /dev/null
@@ -0,0 +1,164 @@
+<?php
+/**
+ * Handles editing of topics
+ *
+ * Long description (if any) ...
+ *
+ * PHP version 5
+ *
+ * The license text...
+ *
+ * @category  Toolkit
+ * @package   Events
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2010 Steve Sutton
+ * @license   Gaslight Media
+ * @version   CVS: $Id:$
+ * @link      http://pear.php.net/package/Toolkit_Events
+ * @see       References to other sections (if any)...
+ */
+
+/**
+ * Handles editing of topics
+ *
+ * Long description (if any) ...
+ *
+ * @category  Toolkit
+ * @package   Events
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2010 Steve Sutton
+ * @license   Gaslight Media
+ * @version   Release: @package_version@
+ * @link      http://pear.php.net/package/Toolkit_Events
+ * @see       References to other sections (if any)...
+ */
+class Toolkit_Events_Forms_Admin_EditTopics
+{
+
+    /**
+     * Description of $_flexyOptions
+     * @var    array
+     * @access private
+     */
+    private $_flexyOptions;
+
+    /**
+     * Description of $_templateFile
+     * @var    string
+     * @access private
+     */
+    private $_templateFile = 'editTopics.html';
+
+    /**
+     * Class constructor
+     *
+     * @param PDO $pdo Parameter description (if any) ...
+     *
+     * @access public
+     */
+    public function __construct(PDO $pdo)
+    {
+        $this->dbh = $pdo;
+        $this->_flexyOptions = array(
+            'templateDir' => BASE . 'Toolkit/Events/templates',
+            'compileDir'  => BASE . 'Toolkit/Events/templates/compiled',
+            'flexyIgnore' => true,
+            'allowPHP'    => true
+        );
+    }
+
+    /**
+     * Returns the form itself in html
+     *
+     * Long description (if any) ...
+     *
+     * @return string  Return description (if any) ...
+     * @access public
+     */
+    public function toHtml()
+    {
+        if ($_POST['Action']) {
+            $this->topicAction();
+        }
+        $GLOBALS['bottomScripts'][]
+            = MEDIA_BASE_URL . 'Toolkit/Events/libjs/edit-topic.js';
+        $GLOBALS['styleSheets'][]
+            = MEDIA_BASE_URL . 'Toolkit/Events/css/edit-topic.css';
+        $template                   = new HTML_Template_Flexy($this->_flexyOptions);
+        $page                       = new stdClass;
+        $sql                        = "
+        SELECT id,descr,topiccolor
+          FROM topic
+          ORDER BY descr";
+        $page->data = $this->dbh->query($sql)->fetchAll(PDO::FETCH_ASSOC);
+        $page->topic_action_page = MEDIA_BASE_URL . 'admin/Events/editTopics.php';
+        $template->compile($this->_templateFile);
+        return $template->bufferedOutputObject($page);
+    }
+
+    /**
+     * Takes a selected action on a topic
+     *
+     * Long description (if any) ...
+     *
+     * @return boolean   Return description (if any) ...
+     * @access protected
+     */
+    protected function topicAction()
+    {
+        $args = array(
+            'id'         => FILTER_VALIDATE_INT,
+            'descr'      => FILTER_SANITIZE_STRING,
+            'topiccolor' => FILTER_SANITIZE_STRING,
+            'topic'      => FILTER_SANITIZE_STRING
+        );
+        $myInputs    = filter_input_array(INPUT_POST, $args);
+        extract($myInputs);
+        switch ($_REQUEST['Action']) {
+        case "Add Topic" :
+            $query = "
+            SELECT id
+              FROM topic
+             WHERE descr = :topic";
+            $stmt = $this->dbh->prepare($query);
+            $stmt->bindParam(':topic', $topic);
+
+            if (!$data  = $stmt->fetchAll(PDO::FETCH_ASSOC)) {
+                $query = "
+                INSERT INTO topic
+                (descr)
+                VALUES
+                (:topic);";
+                $insert = $this->dbh->prepare($query);
+                $insert->bindParam(':topic', $topic);
+                $insert->execute();
+            }
+            break;
+
+        case "Delete Category" :
+            $sql = "
+            DELETE
+              FROM topic
+             WHERE id = :id";
+            $delete = $this->dbh->prepare($sql);
+            $delete->bindParam(':id', $id, PDO::PARAM_INT);
+            $delete->execute();
+            break;
+
+        case "Update Topic" :
+            $sql = "
+            UPDATE topic
+              SET topiccolor = :topiccolor,
+                  descr = :descr
+             WHERE id = :id";
+            $update = $this->dbh->prepare($sql);
+            $update->bindParam(':topiccolor', $topiccolor);
+            $update->bindParam(':descr', $descr);
+            $update->bindParam(':id', $id, PDO::PARAM_INT);
+            $update->execute();
+            break;
+        }
+        return true;
+    }
+
+}
diff --git a/Toolkit/Events/HomeEvents.php b/Toolkit/Events/HomeEvents.php
new file mode 100644 (file)
index 0000000..272682a
--- /dev/null
@@ -0,0 +1,115 @@
+<?php
+
+/**
+ * HomeEvents.php
+ *
+ * PHP version 5.2
+ *
+ * @category  Toolkit
+ * @package   Events
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @version   SVN: (0.1)
+ * @link      <>
+ */
+
+/**
+ * Toolkit_Events_HomeEvents
+ *
+ * Generate a list of events for display on home page (featured)
+ *
+ * @category  Toolkit
+ * @package   Events
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @release   Release: (0.1)
+ * @link      <>
+ */
+class Toolkit_Events_HomeEvents
+{
+    /**
+     * Database Connection
+     * @var    PDO
+     * @access protected
+     */
+    private $_dbh;
+
+    /**
+     * Creates object of HomeEvents
+     *
+     * @param PDO $pdo Database Connection
+     *
+     * @return void
+     * @access public
+     */
+    public function __construct(PDO $pdo)
+    {
+        // assign the global dbh to $this->dbh;
+        $this->_dbh = $pdo;
+    }
+
+    /**
+     * grab all events flaged as home = 't'
+     * Home events must have images with them or they don't get picked
+     *
+     * @access public
+     * @return string
+     */
+    public function getHomeEvents($onlyWithImages = true)
+    {
+        $events = array();
+        $sql = "
+          SELECT event.id,event.img,event.header,
+                 CASE
+                 WHEN event.reacur THEN event_recur.event_day
+                 ELSE event.bdate
+                 END as event_starting_date
+            FROM event
+                 LEFT OUTER JOIN event_recur
+                 ON (event.id = event_recur.event_id)
+           WHERE event.visable
+             AND ((event.reacur AND event_recur.event_day >= current_date)
+                 OR
+                 (reacur is not true AND event.edate >= current_date))
+             AND event.home";
+        if ($onlyWithImages) {
+             $sql .= "
+                 AND (img != '' AND img IS NOT NULL)
+                 ";
+        }
+        $sql .= "
+        ORDER BY event_starting_date,header";
+        $result = $this->_dbh->query($sql);
+        Zend_Date::setOptions(array('format_type' => 'php'));
+        while ($row = $result->fetch()) {
+            $eventDateUsed = new Zend_Date(
+                strtotime($row['event_starting_date']),
+                Zend_Date::TIMESTAMP
+            );
+            $hrefFormat
+                = 'events/%d/%d/';
+            $href = MEDIA_BASE_URL . sprintf(
+                $hrefFormat,
+                EVENT_PAGE,
+                $row['id']
+            );
+            // strip out tags and add slashes to $descr
+            $events[] = array(
+                'id'     => $row['id'],
+                'href'   => $href,
+                'bdate'  => $eventDateUsed->toString('M - jS'),
+                'header' => strip_tags($row['header']),
+                'descr'         => substr(strip_tags($row['descr']), 0, 150),
+                's-img'  => FILE_SERVER_URL . IS_OWNER_ID
+                    . '/eventSlider/' . $row['img'],
+                't-img'  => FILE_SERVER_URL . IS_OWNER_ID
+                    . '/eventFeaturedThumb/' . $row['img'],
+                'f-img'  => FILE_SERVER_URL . IS_OWNER_ID
+                    . '/eventSlider/' . $row['img'],
+            );
+        }
+        return $events;
+    }
+}
diff --git a/Toolkit/Events/ListEvents.php b/Toolkit/Events/ListEvents.php
new file mode 100644 (file)
index 0000000..ebf29f6
--- /dev/null
@@ -0,0 +1,287 @@
+<?php
+
+/**
+ * ListEvents.php
+ *
+ * PHP version 5
+ *
+ * @category  Toolkit
+ * @package   Events
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2010 Gaslight Media
+ * @license   Gaslight Media
+ * @version   CVS: $Id: ListEvents.php,v 1.5 2010/08/04 13:04:50 jamie Exp $
+ * @link      <>
+ */
+
+/**
+ * Sub Class for listing out the events with flexy templates
+ *
+ * To use this class build the query string first then set it with
+ * the setQuery function then call toHtml
+ *
+ * @category  Toolkit
+ * @package   Events
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2010 Gaslight Media
+ * @license   Gaslight Media
+ * @version   Release: @package_version@
+ * @link      <>
+ */
+class Toolkit_Events_ListEvents
+    extends Toolkit_Events_Auxiliary
+{
+    private $_searchFor;
+
+    public function setSearchFor(Toolkit_Events_QueryBuilder $queryBuilder)
+    {
+        $this->_searchFor = '';
+        $startDate = $queryBuilder->getStartingDate();
+        $endDate   = $queryBuilder->getEndingDate();
+        $category  = $queryBuilder->getCategory($this->dbh);
+        $range     = $queryBuilder->getT();
+        if ($category) {
+            $this->_searchFor .= $category.' ';
+        } else {
+            $this->_searchFor .= 'All Events ';
+        }
+        if ($startDate && $endDate) {
+            $this->_searchFor .= "from {$startDate} to {$endDate}";
+        } elseif ($startDate && !$endDate) {
+            $this->_searchFor .= "from {$startDate}";
+        } else if ($endDate && !$startDate) {
+            $this->_searchFor .= "to {$endDate}";
+        }
+        if ($range) {
+            $this->_searchFor = $range;
+        }
+    }
+
+    /**
+     * setQuery
+     *
+     * @param mixed $sql Query string for the selection of events
+     *
+     * @access public
+     * @return void
+     */
+    function setQuery($sql)
+    {
+        $this->_query = $sql;
+    }
+
+    /**
+     * Need to sort by the header field if the date matches
+     *
+     * @param string $titleA first event header
+     * @param string $titleB second event header
+     *
+     * @return int
+     */
+    public function customSortSecondLevel($titleA, $titleB)
+    {
+        if ($titleA == $titleB) {
+            return 0;
+        }
+        return
+            ($titleA < $titleB)
+            ? -1
+            : 1;
+    }
+
+    /**
+     * sorting function for the event data array sort by timestamp
+     *
+     * If the timestamp matches (same day) then sort by hoeader field by
+     * calling the
+     * Toolkit_Events_ListEvents::customSortSecondLevel method and returning it
+     *
+     * @param string $eventA event a
+     * @param string $eventB event b
+     *
+     * @return int
+     */
+    public function customSortEvents($eventA, $eventB)
+    {
+        if ($eventA['timestamp'] == $eventB['timestamp']) {
+            return $this->customSortSecondLevel(
+                $eventA['header'],
+                $eventB['header']
+            );
+        }
+        return
+            ($eventA['timestamp'] < $eventB['timestamp'])
+            ? -1
+            : 1;
+    }
+
+    /**
+     * toHtml
+     *
+     * @param Toolkit_Template_KeywordReplacement $keywordReplacement Reference
+     * @param string                              $tmplFile           tmpl file
+     *
+     * @access public
+     * @return string
+     */
+    function toHtml(
+        Toolkit_Template_KeywordReplacement $keywordReplacement,
+        $tmplFile = 'events.html'
+    ) {
+        $template      = new HTML_Template_Flexy($this->flexyOptions);
+        $page          = new stdClass;
+        $page->events  = $this->_getEvents($keywordReplacement);
+        $page->search  = $this->_searchFor;
+        $page->uploads = UPLOADED_FILES . 'original/';
+        $page->imgPathDetail = FILE_SERVER_URL . IS_OWNER_ID . '/eventDetail/';
+        $page->imgPathList   = FILE_SERVER_URL . IS_OWNER_ID . '/eventThumb/';
+        $template->compile($tmplFile);
+        return $template->bufferedOutputObject($page);
+    }
+
+    public function getEvents($sql)
+    {
+        $this->setQuery($sql);
+        $keywordReplacement = new Toolkit_Template_KeywordReplacement(
+            new Toolkit_Toolbox_PageGatewayPublish(
+                Toolkit_Database::getInstance()
+            )
+        );
+        return $this->_getEvents($keywordReplacement);
+    }
+
+    /**
+     * Return the Events per Query
+     *
+     * @param Toolkit_Template_KeywordReplacement $keywordReplacement Reference
+     *
+     * @return array events
+     */
+    private function _getEvents(Toolkit_Template_KeywordReplacement $keywordReplacement)
+    {
+        $data = array();
+        // days array
+        $days[1]  = "Sun";
+        $days[2]  = "Mon";
+        $days[4]  = "Tue";
+        $days[8]  = "Wed";
+        $days[16] = "Thu";
+        $days[32] = "Fri";
+        $days[64] = "Sat";
+        if ($this->_query) {
+            try {
+                $data = $this->dbh->query($this->_query)
+                    ->fetchAll(PDO::FETCH_ASSOC);
+                if (is_array($data)) {
+                    foreach ($data as &$row) {
+                        $row['href'] = MEDIA_BASE_URL . "events/".EVENT_PAGE."/{$row['id']}/";
+                        if ($row['url'] && !preg_match('/^http:\/\/|^https:\/\//i', $row['url'])) {
+                            $row['url'] = 'http://' . $row['url'];
+                        }
+                        if (is_numeric($row['region'])) {
+                            $row['region_name'] = $regions[$row['region']];
+                        }
+                        if ($row['phone']) {
+                            $row['phone'] = Toolkit_Common::filterPhone($row['phone']);
+                        }
+                        if ($row['btime']) {
+                            $timestamp = strtotime($row['bdate'].' '.$row['btime']);
+                            $row['btime'] = date('g:i A', $timestamp);
+                        }
+                        if ($row['etime']) {
+                            $timestamp = strtotime($row['bdate'].' '.$row['etime']);
+                            $row['etime'] = date('g:i A', $timestamp);
+                        }
+                        $sdate         = strtotime($row['bdate']);
+                        $edate         = strtotime($row['edate']);
+                        $row['spans']  = ($sdate != $edate && !$row['reacur']);
+                        $row['normal'] = (!$row['spans'] && !$row['reacur']);
+                        $eventDayTimeStamp
+                            = ($row['event_day'])
+                            ? strtotime($row['event_day'])
+                            : null;
+                        $event_day
+                            = ($row['event_day'])
+                            ? GLM_TEMPLATE::get_event_date(
+                                $eventDayTimeStamp,
+                                $eventDayTimeStamp,
+                                "timestamp"
+                            )
+                            : '';
+                        if ($row['all_day']) {
+                            $row['timestamp']
+                                = ($row['event_day'])
+                                ? strtotime($row['event_day'].' 11:59 PM')
+                                : strtotime($row['bdate'].' 11:59 PM');
+                        } else if ($row['btime']) {
+                            $row['timestamp']
+                                = ($row['event_day'])
+                                ? strtotime($row['event_day'].' '.$row['btime'])
+                                : strtotime($row['bdate'].' '.$row['btime']);
+                        } else if ($row['etime']) {
+                            $row['timestamp']
+                                = ($row['event_day'])
+                                ? strtotime($row['event_day'].' '.$row['etime'])
+                                : strtotime($row['bdate'].' '.$row['etime']);
+                        } else if ($eventDayTimeStamp) {
+                            $row['timestamp']
+                                = ($eventDayTimeStamp)
+                                ? $eventDayTimeStamp
+                                : $sdate;
+                        } else {
+                            $row['timestamp'] = strtotime($row['bdate']);
+                        }
+
+                        if ($row['descr']) {
+                            if (!$toolbox) {
+                                $toolbox = $GLOBALS['toolbox'];
+                            }
+                            $row['descr']
+                                = $keywordReplacement->findAndReplace($row['descr']);
+                        }
+                        $row['dates']
+                            = ($event_day && !$_REQUEST['eventid'])
+                            ? $event_day
+                            : GLM_TEMPLATE::get_event_date(
+                                $sdate,
+                                $edate,
+                                'timestamp'
+                            );
+                        $dayRecur = array();
+                        if ($row['daysow']) {
+                            $ri = 1;
+                            for ($r=1;$r<8;$r++) {
+                                if ($row["daysow"]&$ri)
+                                    $dayRecur[] = $days[$ri];
+                                $ri = $ri << 1;
+                            }
+                        }
+                        if (!empty($dayRecur)) {
+                            $row['dates'] .= ' ('.implode(', ', $dayRecur).')';
+                        }
+                        $row['filename']
+                            = ($row['filename'])
+                            ? $row['filename']
+                            : $row['file'];
+                        $row['hasLocation']
+                            = ($row['loc']
+                            || ($row['lat'] && $row['lon'])
+                            || $row['address']);
+                        $row['hasLatLon'] = ($row['lat'] && $row['lon']);
+                        $row['hasContactInfo']
+                            = ($row['file']
+                            || $row['url']
+                            || $row['contact']
+                            || $row['email']
+                            || $row['phone']);
+                    }
+                    usort($data, array($this, 'customSortEvents'));
+                }
+            } catch(PDOException $e) {
+                Toolkit_Common::handleError($e);
+            }
+        }
+        return $data;
+    }
+
+}
diff --git a/Toolkit/Events/QueryBuilder.php b/Toolkit/Events/QueryBuilder.php
new file mode 100644 (file)
index 0000000..04f61bb
--- /dev/null
@@ -0,0 +1,220 @@
+<?php
+
+/**
+ * QueryBuilder.php
+ *
+ * PHP version 5.3
+ *
+ * @category  Toolkit
+ * @package   Events
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @version   SVN: (0.1)
+ * @link      <>
+ */
+
+/**
+ * Toolkit_Events_QueryBuilder
+ *
+ * Generate the Query from filtered POST and GET arrays
+ *
+ * @category  Toolkit
+ * @package   Events
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @release   Release: (0.1)
+ * @link      <>
+ */
+class Toolkit_Events_QueryBuilder
+{
+
+    private $_startingDate;
+    private $_endingDate;
+    private $_category;
+    private $_t;
+
+    public function getStartingDate()
+    {
+        return $this->_startingDate;
+    }
+
+    public function getEndingDate()
+    {
+        return $this->_endingDate;
+    }
+
+    public function getT()
+    {
+        return $this->_t;
+    }
+
+    public function getCategory(PDO $dbh)
+    {
+        if (!$this->_category) {
+            return false;
+        }
+        try {
+            $sql = "
+            SELECT descr
+              FROM events.topic
+             WHERE id = :id";
+            $stmt = $dbh->prepare($sql);
+            $stmt->bindParam(':id', $this->_category, PDO::PARAM_INT);
+            $stmt->execute();
+            return $stmt->fetchColumn();
+        } catch (PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+    }
+
+    /**
+     * buildQueryNew()
+     *
+     * get the events by search criteria and show results
+     *
+     * @return mixed  Return description (if any) ...
+     * @access public
+     */
+    public function buildQuery()
+    {
+        $where = $this->_buildWherePart();
+        // query
+        $sql = "
+        SELECT event.*,topic.descr as topicname,
+               event_recur.event_day
+          FROM event
+               LEFT OUTER JOIN topic
+               ON (topic.id = event.topicid)
+               LEFT OUTER JOIN event_recur
+               ON (event_recur.event_id = event.id)";
+        if ($this->_startingDate && $this->_endingDate) {
+            $where[] = "((event.reacur
+                AND event_recur.event_day
+            BETWEEN DATE '{$this->_startingDate}'
+                AND DATE '{$this->_endingDate}')
+                OR event.reacur is not true)";
+        } else if ($this->_startingDate) {
+            $where[] = "((event.reacur
+                AND event_recur.event_day >= DATE '{$this->_startingDate}')
+                OR event.reacur is not true)";
+        }
+        if (is_array($where)) {
+            $sql .= "\nWHERE ".implode("\nAND ", $where);
+        }
+        $sql .= "
+        ORDER BY event.bdate,event.edate";
+        $eventId = filter_var($_REQUEST['eventid'], FILTER_VALIDATE_INT);
+        if ($eventId) {
+            $sql .= " LIMIT 1 OFFSET 0";
+        }
+        return $sql;
+    }
+
+    /**
+     * Create the where part of the query
+     *
+     * @return array
+     */
+    private function _buildWherePart()
+    {
+        $where = array();
+        // where clauses
+        $where[] = "event.visable";
+        // eventid
+        $eventId  = filter_var($_REQUEST['eventid'], FILTER_VALIDATE_INT);
+        $category = filter_var($_REQUEST['category'], FILTER_VALIDATE_INT);
+        if ($eventId) {
+            $where[] = "event.id = {$eventId}";
+        } else if ($category) {
+            $this->_category = $category;
+            $where[] = "event.topicid = {$category}";
+        }
+        $currentTime = time();
+        // month
+        if ($_REQUEST['startMonth'] || $_REQUEST['endMonth']) {
+            $dateFormat = '/([0-9]{2})\/([0-9]{2})\/([0-9]{4})/';
+            if (preg_match($dateFormat, $_REQUEST['startMonth'], $startingMatches)) {
+                $isValidStart = checkdate(
+                    $startingMatches[1],
+                    $startingMatches[2],
+                    $startingMatches[3]
+                );
+            } else {
+                $isValidStart = false;
+            }
+
+            if (preg_match($dateFormat, $_REQUEST['endMonth'], $endingMatches)) {
+                $isValidEnd = checkdate(
+                    $endingMatches[1],
+                    $endingMatches[2],
+                    $endingMatches[3]
+                );
+            } else {
+                $isValidEnd = false;
+            }
+
+            // if they both are good
+            if ( $isValidStart && $isValidEnd) {
+                $this->_startingDate = $_REQUEST['startMonth'];
+                $this->_endingDate   = $_REQUEST['endMonth'];
+                $where[] = "
+                event.edate >= DATE '{$_REQUEST['startMonth']}'
+                ";
+                $where[] = "
+                event.bdate <= DATE '{$_REQUEST['endMonth']}'
+                ";
+            } else if ($isValidStart && !$isValidEnd) {
+                $this->_startingDate = $_REQUEST['startMonth'];
+                $where[] = "
+                event.edate >= DATE '{$_REQUEST['startMonth']}'
+                ";
+            } else if (!$isValidStart && $isValidEnd) {
+                $this->_endingDate   = $_REQUEST['endMonth'];
+                $where[] = "
+                event.bdate >= DATE '{$_REQUEST['endMonth']}'
+                ";
+            }
+        } else if ($_REQUEST['t'] == 'today') {
+            $this->_t = 'Today';
+            $this->_startingDate = date('m/d/Y', $currentTime);
+            $this->_endingDate   = $this->_startingDate;
+            $where[] = "current_date
+                BETWEEN event.bdate
+                    AND event.edate";
+        } else if ($_REQUEST['t'] == 'tomorrow') {
+            $this->_t = 'Tomorrow';
+            $this->_startingDate = date(
+                'm/d/Y',
+                strtotime('+1day')
+            );
+            $this->_endingDate = $this->_startingDate;
+            $where[] = "current_date + INTERVAL '1 day'
+                BETWEEN event.bdate
+                    AND event.edate";
+        } else if ($_REQUEST['t'] == 'next') {
+            $this->_t = 'Next 7 Days';
+            // calculate dates for this week
+            $this->_startingDate = date('m/d/Y');
+            $this->_endingDate   = date(
+                'm/d/Y',
+                strtotime('+7 days') // 7 days out
+            );
+            $where[] = "
+            (DATE '{$this->_startingDate}', DATE '{$this->_endingDate}')
+            OVERLAPS
+            (event.bdate, event.edate)";
+        } else {
+            $startDate    = date("m/d/Y", $currentTime);
+            $where[]      = "event.edate >= DATE '$startDate'";
+            $this->_startingDate = $startDate;
+        }
+        // topicid
+        if ($_REQUEST['topicid'] && ctype_digit($_REQUEST['topicid'])) {
+            $where[] = "event.topicid = {$_REQUEST['topicid']}";
+        }
+
+        return $where;
+    }
+}
diff --git a/Toolkit/Events/SmallCal.php b/Toolkit/Events/SmallCal.php
new file mode 100644 (file)
index 0000000..279c7c1
--- /dev/null
@@ -0,0 +1,453 @@
+<?php
+
+/**
+ * Calendar.php
+ *
+ * PHP version 5
+ *
+ * @category  Toolkit
+ * @package   Events
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2009 Gaslight Media
+ * @license   Gaslight Media
+ * @version   CVS: $Id: Calendar.php,v 1.7 2009/10/23 16:43:44 matrix Exp $
+ * @link      <>
+ */
+
+/**
+ * Display calendar for events
+ *
+ * Using the PEAR::Calendar class this class gets all the events
+ * for a given month/year (one month only) and creates an array
+ * using the PEAR::Calendar_Day class
+ * this is added to the Calendar_Day_Week class in it's constructor
+ * so the days are selected
+ *
+ * @category  Toolkit
+ * @package   Events
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2009 Gaslight Media
+ * @license   Gaslight Media
+ * @version   Release: @package_version@
+ * @link      <>
+ */
+class Toolkit_Events_SmallCal
+{
+    // {{{ Class Properties
+    /**
+     * Class PDO object
+     * @var    object
+     * @access public
+     */
+    public $dbh;
+
+    /**
+     * month of calendar
+     * @var    number
+     * @access private
+     */
+    private $_month;
+
+    /**
+     * yeaor of calendar
+     * @var    unknown
+     * @access private
+     */
+    private $_year;
+
+    /**
+     * selected days array for calendar
+     * @var    unknown
+     * @access private
+     */
+    private $_selectedDays;
+
+    /**
+     * show the calendars prev next links
+     * @var    boolean
+     * @access public
+     */
+    public $showPrevNextLinks = true;
+    // }}}
+    // {{{ __construct()
+    /**
+     * Class constructor
+     *
+     * @param PDO $pdo   PDO can be any type
+     * @param int $month month for the calendar
+     * @param int $year  year for the calendar
+     *
+     * @return void
+     * @access public
+     */
+    public function __construct(PDO $pdo, $month, $year)
+    {
+        $this->dbh    = $pdo;
+        $this->_month = ctype_digit($month) ? $month: date('n');
+        $this->_year  = ctype_digit($year) ? $year: date('Y');
+    }
+    // }}}
+    // {{{ getEventsForMonth()
+    /**
+     * Calculate which dates the events for this month actually occur on
+     * and set them up as an array of $this->_selectedDays
+     *
+     * @return void
+     * @access protected
+     */
+    protected function getEventsForMonth()
+    {
+        // create a starting timestamp
+        $beginTimeStamp = mktime(
+            0,
+            0,
+            1,
+            $this->_month,
+            1,
+            $this->_year
+        );
+        // create start date for query
+        $begin = date(
+            "n/d/Y",
+            mktime(
+                0,
+                0,
+                1,
+                $this->_month,
+                1,
+                $this->_year
+            )
+        );
+        // create end date for query
+        $end   = date(
+            "n/d/Y",
+            mktime(
+                0,
+                0,
+                1,
+                $this->_month,
+                date('t', $beginTimeStamp),
+                $this->_year
+            )
+        );
+        // $sDates will be our $this->_selectedDays array;
+        $sDates = array();
+        // get all event for this month only
+        $sql   = "
+          SELECT id,bdate,edate,daysow,dayom,reacur,weekom
+            FROM event
+           WHERE bdate <= :end
+             AND edate >= :begin
+             AND visable = 't'
+        ORDER BY bdate,edate";
+
+        try {
+            $stmt = $this->dbh->prepare($sql);
+            $stmt->bindParam(":begin", $begin, PDO::PARAM_STR);
+            $stmt->bindParam(":end", $end, PDO::PARAM_STR);
+            $stmt->execute();
+            while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                // timestamp of events starting date
+                $bdate = strtotime($row['bdate']);
+                // timestamp of events ending date
+                $edate = strtotime($row['edate']);
+                // if the dates are the same then add it to the
+                // selected dates array
+                if ($bdate == $edate) {
+                    $sDates[] = new Calendar_Day(
+                        date('Y', $bdate),
+                        date('n', $bdate),
+                        date('d', $bdate)
+                    );
+                } else if ($edate > $bdate) {
+                    // if the end date is larger than the starting one
+                    // only work in this month
+                    // timestamp for start of month
+                    $startOfMonth = mktime(
+                        0,
+                        0,
+                        1,
+                        $this->_month,
+                        1,
+                        $this->_year
+                    );
+                    // timestamp for end of month
+                    $endOfMonth   = mktime(
+                        0,
+                        0,
+                        1,
+                        $this->_month,
+                        date('t', $startOfMonth),
+                        $this->_year
+                    );
+                    // update the start date if needed
+                    if ($startOfMonth >= $bdate) {
+                        $bdate = $startOfMonth;
+                    }
+                    // update the end date if needed
+                    if ($endOfMonth <= $edate) {
+                        $edate = $endOfMonth;
+                    }
+                    // find events ord
+                    if ($row['weekom']) {
+                        if ($row['daysow']) {
+                            $ri = 1;
+                            for ($r = 0; $r < 7; $r++) {
+                                if ($row['daysow'] & $ri) {
+                                    $ord[] = $this->ordinalDay(
+                                        $row['weekom'],
+                                        $r,
+                                        $this->_month,
+                                        $this->_year
+                                    );
+                                }
+                                $ri = $ri << 1;
+                            }
+                        } else {
+                            $ord = null;
+                        }
+                    } else {
+                        $ord = null;
+                    }
+                    // seconds in one day = 86400
+                    // loop through the days in this month
+                    for ($i = $bdate; $i <= $edate; $i = $i + 86400) {
+                        $cur_dow = date("w", $i);
+                        switch($cur_dow) {
+                        case 0:
+                            $cur_dow = 1;
+                            break;
+                        case 1:
+                            $cur_dow = 2;
+                            break;
+                        case 2:
+                            $cur_dow = 4;
+                            break;
+                        case 3:
+                            $cur_dow = 8;
+                            break;
+                        case 4:
+                            $cur_dow = 16;
+                            break;
+                        case 5:
+                            $cur_dow = 32;
+                            break;
+                        case 6:
+                            $cur_dow = 64;
+                            break;
+                        }
+                        // if the event is not a recurring one then add it as
+                        // selectedDay
+                        if (!$row['reacur']) {
+                            $sDates[] = new Calendar_Day(
+                                date('Y', $i),
+                                date('n', $i),
+                                date('d', $i)
+                            );
+                        } else if ($row['reacur']
+                            && !$row['dayom']
+                            && $row['daysow']
+                            && (int)$cur_dow & $row['daysow']
+                        ) {
+                            // if the event is recurring and the daysow are set
+                            if ($row['weekom'] == 9) {
+                                // every other week
+                                unset($eWeeks);
+                                $fWeekNum = date("W", $bdate);
+                                if (date('w', $bdate) == 0) {
+                                    $fWeekNum++;
+                                }
+                                $lWeekNum = date("W", $edate);
+                                if (date('w', $edate) == 0) {
+                                    $lWeekNum++;
+                                }
+                                for ($fi = $fWeekNum;
+                                    $fi <= $lWeekNum;
+                                    $fi = $fi + 2
+                                ) {
+                                    $eWeeks[] = $fi;
+                                }
+                                $cDateWeek = date("W", $i);
+                                if (date('w', $i) == 0) {
+                                    $cDateWeek++;
+                                }
+                                if (in_array($cDateWeek, $eWeeks)) {
+                                    $sDates[] = new Calendar_Day(
+                                        date('Y', $i),
+                                        date('n', $i),
+                                        date('d', $i)
+                                    );
+                                }
+                            } else if ($row['weekom'] && is_array($ord)) {
+                                // if the ord is set (every 1st 2nd 3rd etc.)
+                                foreach ($ord as $ordinal) {
+                                    if (date('m-d-Y', $i) == date('m-d-Y', $ordinal)) {
+                                        $sDates[] = new Calendar_Day(
+                                            date('Y', $i),
+                                            date('n', $i),
+                                            date('d', $i)
+                                        );
+                                    }
+                                }
+                            } else if (!$row['weekom']
+                                && (int)$cur_dow & $row['daysow']
+                            ) {
+                                // if daysow is set but not weekom
+                                $sDates[] = new Calendar_Day(
+                                    date('Y', $i),
+                                    date('n', $i),
+                                    date('d', $i)
+                                );
+                            }
+                        } else if ($row['reacur'] && $row['dayom']
+                            && (int)$row['dayom'] == (int)date('j', $i)
+                        ) {
+                            // if the dayom is set
+                            $sDates[] = new Calendar_Day(
+                                date('Y', $i),
+                                date('n', $i),
+                                date('d', $i)
+                            );
+                        }
+                    }
+                }
+            }
+            $this->_selectedDays = $sDates;
+        } catch(PDOException $e) {
+            die($e->getMessage());
+        }
+    }
+    // }}}
+    // {{{ ordinalDay(
+    /**
+     * ordinalDay
+     * get ordinal (th) day for given timestamp
+     *
+     * @param int $ord   1-5 1st 2nd 3rd (etc.)
+     * @param int $day   day
+     * @param int $month month
+     * @param int $year  year
+     *
+     * @access public
+     * @return int
+     */
+    function ordinalDay($ord, $day, $month, $year)
+    {
+        $firstOfMonth = mktime(
+            0,
+            0,
+            1,
+            $month,
+            $day,
+            $year
+        );
+        $lastOfMonth  = mktime(
+            0,
+            0,
+            1,
+            $month,
+            date("t", $firstOfMonth),
+            $year
+        );
+        $dayOccurs    = 0;
+
+        for ($i = $firstOfMonth; $i < $lastOfMonth ; $i += 86400) {
+            if (date("w", $i) == $day) {
+                $dayOccurs++;
+                if ($dayOccurs == $ord) {
+                    $ordDay = $i;
+                }
+            }
+        }
+        return $ordDay;
+    }
+    // }}}
+    // {{{ toHtml()
+    /**
+     * create the calendar (small) with link to the day if an event
+     * accures on that day
+     *
+     * @return string
+     * @access public
+     */
+    public function toHtml()
+    {
+        $GLOBALS['bottomScripts'][]
+            = $this->baseURL . 'Toolkit/Events/libjs/calendar.js';
+        $linkPage = MEDIA_BASE_URL . 'index.php?';
+        $page     = $linkPage;
+        $page    .= 'catid=' . EVENT_PAGE . '&flat=1&';
+        // create the class array with events
+        $this->getEventsForMonth();
+        // create the calendar_month_weekdays object
+        $Month = new Calendar_Month_Weekdays(
+            $this->_year, // year
+            $this->_month, // month
+            0 // starting day is sunday
+        );
+
+        // build the Calendar_Month_Weekdays with selected days from event table
+        $Month->build($this->_selectedDays);
+
+        // Calendar_Decorator_Textual for month name
+        $monthText = new Calendar_Decorator_Textual($Month);
+
+        // Calendar_Decorator_Uri for links
+        $monthUri = new Calendar_Decorator_Uri($Month);
+        $monthUri->setFragments('year', 'month');
+
+        $out = "<div id=\"eventCalendar\"><table>\n";
+        if ($this->showPrevNextLinks) {
+            $prevLink = '<a class="calLink" rel="'.$monthUri->prev('month').'" href="' . $linkPage
+            . $monthUri->prev('month')
+            . '">&lt;&lt;</a>';
+            $nextLink = '<a class="calLink" rel="'.$monthUri->next('month').'" href="' . $linkPage
+            . $monthUri->next('month')
+            . '">&gt;&gt;</a>';
+        } else {
+            $prevLink = '&nbsp;';
+            $nextLink = '&nbsp;';
+        }
+        $out .= '<tr>
+            <th>' . $prevLink . '</th>
+                <th colspan="5">'
+            . $monthText->thisMonthName()
+            . ' '
+            . $Month->thisYear()
+            . '</th>
+            <th>' . $nextLink . '</th>
+            </tr>';
+        while ($Day = $Month->fetch()) {
+            if ($Day->isFirst()) {
+                $out .= "<tr>\n";
+            }
+
+            if ($Day->isEmpty()) {
+                $out .= "<td>&nbsp;</td>\n";
+            } else {
+                $out .= '<td>';
+                // if the day isSelected then create a link
+                if ($Day->isSelected()) {
+                    // Calendar_Decorator_Uri for links
+                    $dayUri = new Calendar_Decorator_Uri($Day);
+                    $dayUri->setFragments('year', 'month', 'day');
+                    $out .= '<a href="' . $page
+                        . $dayUri->this('day')
+                        . '">' . $Day->thisDay(). '</a>';
+                } else {
+                    $out .= $Day->thisDay();
+                }
+                $out .= "</td>\n";
+            }
+
+            if ($Day->isLast()) {
+                $out .= "</tr>\n";
+            }
+        }
+
+        $out .= "</table></div>\n";
+        return $out;
+    }
+    // }}}
+}
+?>
diff --git a/Toolkit/Events/UserSearchForm.php b/Toolkit/Events/UserSearchForm.php
new file mode 100644 (file)
index 0000000..3e71220
--- /dev/null
@@ -0,0 +1,113 @@
+<?php
+
+/**
+ * UserSearchForm.php
+ *
+ * PHP version 5.3
+ *
+ * @category  Toolkit
+ * @package   Events
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @version   SVN: (0.1)
+ * @link      <>
+ */
+
+/**
+ * Description for require_once
+ */
+require_once 'HTML/Template/Flexy/Element.php';
+
+/**
+ * Create the User Search Form for the Event Page
+ *
+ * Description of UserSearchForm
+ *
+ * @category  Toolkit
+ * @package   Events
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @release   Release: (0.1)
+ * @link      <>
+ */
+class Toolkit_Events_UserSearchForm
+    extends Toolkit_Events_Auxiliary
+{
+    const MAX_LOOP_FOR_MONTHS = 20;
+
+    /**
+     * getEventSearchForm()
+     *
+     * create the search form
+     *
+     * @return string
+     * @access public
+     */
+    public function toHtml()
+    {
+        $template = new HTML_Template_Flexy($this->flexyOptions);
+        $page     = new stdClass;
+        $template->compile('eventSearchForm.html');
+        $elements['event-search-form'] = new HTML_Template_Flexy_Element;
+        // set the action for the form
+        $elements['event-search-form']->attributes['action']
+            = MEDIA_BASE_URL . 'index.php';
+        // set the catid for the form
+        $elements['catid'] = new HTML_Template_Flexy_Element;
+        $elements['catid']->setValue($_REQUEST['catid']);
+        // set the topicid for the form
+        $elements['category'] = new HTML_Template_Flexy_Element;
+        $elements['category']->setOptions($this->_getCategories());
+        $elements['category']->setValue($_REQUEST['category']);
+        // setup starting month
+        $elements['startMonth'] = new HTML_Template_Flexy_Element;
+        if ($_REQUEST['startMonth']) {
+            $elements['startMonth']->setValue($_REQUEST['startMonth']);
+        } else {
+            $elements['startMonth']->setValue(date('m/d/Y', strtotime('now')));
+        }
+        // setup starting month
+        $elements['endMonth'] = new HTML_Template_Flexy_Element;
+        if ($_REQUEST['endMonth']) {
+            $elements['endMonth']->setValue($_REQUEST['endMonth']);
+        } else {
+            $elements['endMonth']->setValue(date('m/d/Y', strtotime('+7days')));
+        }
+        return $template->bufferedOutputObject($page, $elements);
+    }
+
+    /**
+     * getTopics()
+     *
+     * get topics array for search form
+     *
+     * @return array  Return description (if any) ...
+     * @access public
+     */
+    private function _getCategories()
+    {
+        $topics[''] = 'All Categories';
+        $sql        = "
+        SELECT *
+          FROM topic
+         WHERE id IN (
+            SELECT topicid
+              FROM event
+             WHERE visable = 't'
+               AND edate >= current_date)
+      ORDER BY descr";
+        try {
+            $data = $this->dbh->query($sql)->fetchAll();
+            if (is_array($data)) {
+                foreach ($data as $row) {
+                    $topics[$row['id']] = $row['descr'];
+                }
+                return $topics;
+            }
+        } catch(PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+    }
+}
diff --git a/Toolkit/Events/assets/.keepme b/Toolkit/Events/assets/.keepme
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/Toolkit/Events/assets/arrowDown.png b/Toolkit/Events/assets/arrowDown.png
new file mode 100755 (executable)
index 0000000..e665436
Binary files /dev/null and b/Toolkit/Events/assets/arrowDown.png differ
diff --git a/Toolkit/Events/assets/arrowUp.png b/Toolkit/Events/assets/arrowUp.png
new file mode 100755 (executable)
index 0000000..1183076
Binary files /dev/null and b/Toolkit/Events/assets/arrowUp.png differ
diff --git a/Toolkit/Events/assets/go.png b/Toolkit/Events/assets/go.png
new file mode 100755 (executable)
index 0000000..5f8232e
Binary files /dev/null and b/Toolkit/Events/assets/go.png differ
diff --git a/Toolkit/Events/config.ini b/Toolkit/Events/config.ini
new file mode 100644 (file)
index 0000000..f37d5e3
--- /dev/null
@@ -0,0 +1,7 @@
+; Event Database configuration file
+[conf]
+; set the display of the events to show a 
+; calendar at a glance
+; turning this off will display the events 
+; as a list starting with the current month
+calendarAtAGlance = Off
diff --git a/Toolkit/Events/css/edit-topic.css b/Toolkit/Events/css/edit-topic.css
new file mode 100644 (file)
index 0000000..d2f8b93
--- /dev/null
@@ -0,0 +1,59 @@
+/* MAIN PAGE PROPERTIES */
+#topic-list {font-family: arial, helvetica, sans-serif; padding: 20px;font-size: 12px; }
+#topic-list {width: 400px;}
+/*The Row*/
+.topic-row {clear: left;margin: 3px 0; padding: 3px; background-color: #eee; border: 1px solid #ddd; font-size: 12px;}
+.topic-row table {width: 100%;}
+
+/*The Color Picker Square*/
+.colorsquare {height: 18px; width: 18px; border: 1px solid #333; cursor: pointer;}
+/*Name, Edit, Delete*/
+.topic-name { margin: 3px 0 0 6px; font-size: 12px;}
+.topic-name input {width: 200px; }
+.topic-save { margin-left: 5px;}
+.topic-delete { margin-left: 5px;}
+/*Palette ID\'s*/
+/* The Color Palette*/
+.colorbox {top: -21px; left:-100px; clear: left;}
+.colorbox table {width: auto;}
+.colorbox table{
+       border: 1px solid #333; 
+       border-collapse: collapse;
+}
+.colorbox table td {
+       border: 1px solid #333;
+       border-collapse: collapse;
+       height: 19px;
+       width: 19px;
+       cursor: pointer;
+       padding: 0;
+}
+.color1 {background-color: #FFCCCC;}
+.color2 {background-color: #CC9999;}
+.color3 {background-color: #FF9999;}
+.color4 {background-color: #FFCCFF;}
+.color5 {background-color: #CC99CC;}
+
+.color6 {background-color: #FF99FF;}
+.color7 {background-color: #CCCCFF;}
+.color8 {background-color: #9999CC;}
+.color9 {background-color: #9999FF;}
+.color10 {background-color: #CCFFFF;}
+
+.color11 {background-color: #99CCCC;}
+.color12 {background-color: #99FFFF;}
+.color13 {background-color: #CCFFCC;}
+.color14 {background-color: #99CC99;}
+.color15 {background-color: #99FF99;}
+
+.color16 {background-color: #FFFFCC;}
+.color17 {background-color: #CCCC99;}
+.color18 {background-color: #FFFF99;}
+.color19 {background-color: #FFCC99;}
+.color20 {background-color: #FF99CC;}
+
+.color21 {background-color: #CC99FF;}
+.color22 {background-color: #99CCFF;}
+.color23 {background-color: #99FFCC;}
+.color24 {background-color: #CCFF99;}
+.color25 {background-color: #CCCCCC;}
\ No newline at end of file
diff --git a/Toolkit/Events/css/event.css b/Toolkit/Events/css/event.css
new file mode 100755 (executable)
index 0000000..b9a894c
--- /dev/null
@@ -0,0 +1,414 @@
+/* Main Columns on all even pages  */
+#event-main-column {
+       width: 520px;
+       float: left;
+       margin: 0 0 20px 0;
+       }
+#event-second-column {
+       float: right;
+       /*border: 1px solid #c4d2da;*/
+       margin: 0 0 20px 0;
+       }
+
+/* Search Column */
+#event-second-column h3 {
+       margin: 0;
+       /*padding: 15px 5px 5px;*/
+       padding: 0 5px 5px 5px;
+       color: #0578B2;
+       /*border-top: 1px solid #999;*/
+       }
+/* Event Shortcuts */
+#event-shortcuts ul {
+       /*background: #c4d2da;*/
+       padding-bottom: 20px;
+       background: #114C75;
+       -webkit-border-radius: 6px;
+       -moz-border-radius: 6px;
+       border-radius: 6px;
+       -webkit-box-shadow: 1px 1px 4px rgba(0, 0, 0, 0.4);
+       -moz-box-shadow:    1px 1px 4px rgba(0, 0, 0, 0.4);
+       box-shadow:         1px 1px 4px rgba(0, 0, 0, 0.4);
+       }
+#event-shortcuts li {
+       padding: 5px 5px 0;
+       }
+#event-shortcuts a {
+       display: block;
+       padding: 5px;
+       text-align: center;
+       border: 1px solid;
+       border-top-color: #ccc;
+       border-right-color: #aaa;
+       border-bottom-color: #aaa;
+       border-left-color: #ccc;
+       background: #fff;
+       text-decoration: none;
+       color: black;
+       }
+#event-shortcuts a:hover {
+       border-top-color: #aaa;
+       border-right-color: #ccc;
+       border-bottom-color: #ccc;
+       border-left-color: #aaa;
+       background: #eee;
+       }
+#event-shortcuts h3 {
+       margin-top: 20px;
+       }
+/* Event Search Form Side */
+#event-search form {
+       /*background: #c4d2da;
+       background: #eee;*/
+       padding: 5px 5px 15px 5px;
+       background: #114C75;
+       -webkit-border-radius: 6px;
+       -moz-border-radius: 6px;
+       border-radius: 6px;
+       -webkit-box-shadow: 1px 1px 4px rgba(0, 0, 0, 0.4);
+       -moz-box-shadow:    1px 1px 4px rgba(0, 0, 0, 0.4);
+       box-shadow:         1px 1px 4px rgba(0, 0, 0, 0.4);
+       width: 180px;
+       }
+#event-search #startMonth,
+#event-search #endMonth {
+       border: 1px solid #aaa;
+       padding: 2px 4px;
+       display: block;
+       width: 168px;
+       /*background: #fff url(http://app.gaslightmedia.com/assets/icons/calendar.png) no-repeat 156px center;*/
+       background: #fff;
+       margin: 6px 0;
+       }
+#startMonthWrapper, #endMonthWrapper {
+       position: relative;
+       z-index: 5;
+       }
+.ui-datepicker-trigger {
+       position: absolute;
+       z-index: 6;
+       right: 5px;
+       top: 3px;
+       cursor: pointer;
+       }
+#event-search select {
+       border: 1px solid #aaa;
+       padding: 2px 4px;
+       margin: 2px 0;
+       width: 140px;
+       display: block;
+       }
+#event-search input[type="image"] {
+       margin-left: 44px;
+       margin-top: 12px;
+       }
+#event-search span {
+       font-weight: bold;
+       }
+/* Featured Events Side */
+#event-featured div {
+       background-repeat: no-repeat; 
+       padding-bottom: 20px;
+       height: 80px;
+       position: relative;
+       border-top: 1px solid #c4d2da;
+       border-bottom: 1px solid #c4d2da;
+       margin: 0 0 10px 0px;
+       }
+#event-featured h3 {
+       margin-top: 20px;
+       }
+#event-featured p {
+       position: absolute;
+       right: 0;
+       bottom: 5px;
+       display: inline-block;
+       /*background: #ddd;
+       background: rgba(255, 255, 255, .8);*/
+       margin: 0;
+       padding: 3px 6px;
+       font-size: 11px;
+       font-size: 1.1rem;
+       background: #114C75;
+       color: #FFF;
+       }
+#event-featured a {
+       position: absolute;
+       left: 0;
+       top: 0;
+       width: 140px;
+       background: #ddd;
+       background: rgba(255, 255, 255, .9);
+       padding: 3px 5px;
+       font-size: 12px;
+       font-size: 1.2rem;
+       font-weight: bold;
+       background: #114C75;
+       color: #FFF;
+       }
+/* MAIN EVENT PAGE */
+.eventrow {
+       overflow: hidden;
+       height: 1%;
+       clear: left;
+       }
+.event-category-block {
+       /*background: #eee;*/
+       border: 1px solid #114C75;
+       margin: 20px 20px 0 0;
+       float: left;
+       width: 240px;
+       -webkit-border-radius: 6px;
+       -moz-border-radius: 6px;
+       border-radius: 6px;
+       -webkit-box-shadow: 1px 1px 4px rgba(0, 0, 0, 0.4);
+       -moz-box-shadow:    1px 1px 4px rgba(0, 0, 0, 0.4);
+       box-shadow:         1px 1px 4px rgba(0, 0, 0, 0.4);
+       }
+.event-category-2 {
+       margin-right: 0;
+       }
+#event-main-column .event-category-block h2 {
+       font-size: 15px;
+       font-size: 1.5rem;
+       border-bottom: 1px solid #999;
+       margin: 0;
+       padding: 5px 10px;
+       background: #ddd;
+       -webkit-border-top-left-radius: 6px;
+       -webkit-border-top-right-radius: 6px;
+       -moz-border-radius-topleft: 6px;
+       -moz-border-radius-topright: 6px;
+       border-top-left-radius: 6px;
+       border-top-right-radius: 6px;
+       }
+.event-category-block div {
+       /*border-bottom: 1px solid #ccc;*/
+       padding: 5px 10px;
+       font-weight: bold;
+       }
+.event-category-block b {
+       margin-right: 10px;
+       font-weight: bold;
+       display: block;
+       }
+.event-category-block a, .event-list a, .eventDetail a, #eventDetailContact a {
+       color: #114C75;
+       }
+.event-more {
+       display: block;
+       text-align: right;
+       padding: 5px;
+       font-size: 12px;
+       font-size: 1.2rem;
+       display: none;
+       }
+.event-more:hover {
+       background: #fefefe;
+       }
+/* Event Detail page */
+.gmnoprint { /* removes map ui */
+       display: none !important;
+       }
+#eventDetail {
+       margin-top: 1em;
+       }
+#eventDetail h2 {
+       position: relative;
+       margin: 0;
+       padding: 0;
+       border: 1px solid #ccc;
+       }
+#eventDetail h2 p {
+       display: inline-block;
+       background: white;
+       background: rgba(255, 255, 255, .9);
+       padding: 10px;
+       margin: 0;
+       position: absolute;
+       bottom: 20px;
+       left: 20px;
+       font-size: 18px;
+       font-size: 1.8rem;
+       }
+#eventDetailTop {
+       background: #eee;
+       height: 1%;
+       overflow: hidden;
+       border: 1px solid #ccc;
+       border-top: 1px solid #eee;
+       }
+#eventDetailMap {
+       float: right;
+       width: 160px;
+       border: 1px solid;
+       border-top-color: #ccc;
+       border-right-color: #aaa;
+       border-bottom-color: #aaa;
+       border-left-color: #ccc;
+       margin: 10px;
+       }
+#eventDetailMap:hover {
+       border-top-color: #aaa;
+       border-right-color: #ccc;
+       border-bottom-color: #ccc;
+       border-left-color: #aaa;
+       }
+#eventDetail-map {
+       cursor: pointer;
+       height:160px; 
+       }
+#EventDrivingDirectionsContainer {
+       float: right;
+       clear: right;
+       margin: 0 10px 10px 10px;
+       }
+#EventDrivingDirectionSubmit {
+       width: 162px;
+       height: 28px;
+       padding: 5px;
+       border: 1px solid;
+       border-top-color: #ccc;
+       border-right-color: #aaa;
+       border-bottom-color: #aaa;
+       border-left-color: #ccc;
+       background: white;
+       font-size: 13px;
+       font-size: 1.3rem;
+       color: #000;
+       cursor: pointer;
+       }
+#EventDrivingDirectionSubmit:hover {
+       border-top-color: #aaa;
+       border-right-color: #ccc;
+       border-bottom-color: #ccc;
+       border-left-color: #aaa;
+       background: #eee;
+       }
+#EventDrivingDirectionSubmit:hover {
+       background-position: 0px -28px;
+       }
+#eventDetailInfo {
+       padding: 10px;
+       font-size: 12px;
+       font-size: 1.2rem;
+       height: 1%;
+       overflow: hidden;
+       }
+#eventDetailInfo > div {
+       height: 1%;
+       overflow: hidden;
+       padding: 5px 5px 5px 30px;
+       margin: 0 5px 5px 0;
+       border-top: 1px solid #ededed;
+       border-left: 1px solid #ededed;
+       border-right: 1px solid #ddd;
+       border-bottom: 1px solid #ddd;
+       background-color: #fefefe;
+       background-repeat: no-repeat;
+       background-position: 6px 6px;
+       }
+
+#eventDetailTime {
+       background-image: url(http://app.gaslightmedia.com/assets/icons/clock.png);
+       }
+#eventDetailDate {
+       font-weight: bold;
+       float: left;
+       margin-right: 20px;
+       }
+#eventDetailHour {
+       float: left;
+       }
+#eventDetailPlace {
+       background-image: url(http://app.gaslightmedia.com/assets/icons/map.png);
+       background-position: 7px 7px;
+       }
+#eventDetailLocation {
+       font-weight: bold;
+       }
+#eventCost {
+       background-image: url(http://app.gaslightmedia.com/assets/icons/coins.png);
+       }
+#eventDetailContact {
+       background-image: url(http://app.gaslightmedia.com/assets/icons/vcard.png);
+       }
+#eventDetailContact h3 {
+       margin: 0;
+       font-size: 12px;
+       font-size: 1.2rem;
+       }
+#eventDetailContact p {
+       margin: 0;
+       }
+#eventDetailAddress,
+#eventDetailCity {
+       display: block;
+       }
+#eventDetailDesc {
+       margin: 0;
+       padding: 20px;
+       border: 1px solid #ccc;
+       border-top: 0;
+       }
+#eventDetailDesc p {
+       margin-top: 0;
+       }
+/*  Event Search Result */
+.event-search {
+       font-size: 12px;
+       font-size: 1.2rem;
+       background: #eee;
+       border: 1px dashed #ccc;
+       padding: 3px 6px;
+       }
+.event-list {
+       border-bottom: 1px solid #ccc;
+       height: 1%;
+       overflow: hidden;
+       padding: 6px 0;
+       }
+.event-list-date {
+       width: 130px;
+       float: left;
+       padding: 3px 6px;
+       display: block;
+       margin: 0;
+       font-size: 12px;
+       font-size: 1.2rem;
+       font-weight: bold;
+       }
+.event-list h3 {
+       font-size: 12px;
+       font-size: 1.2rem;
+       float: left;
+       padding: 3px;
+       width: 280px;
+       background-image: none !important;
+       margin: 0;
+       line-height: 1.5em;
+       }
+.event-list h3 a {
+       font-size: 13px;
+       font-size: 1.3rem;
+       }
+.event-list-Frequency {
+       float: right;
+       text-align: right;
+       width: 80px;
+       font-size: 11px;
+       font-size: 1.1rem;
+       margin: 0;
+       }
+.ulCr, .ulCr li, .ulCr ul {
+       list-style-type: none;
+       margin: 0;
+       padding: 0;
+       display: block;
+       }
+.nivo-caption {
+       background: #114C75 !important;
+       }
+.nivoSlider, .sliderWrapper {
+       width: 504px;
+       }
diff --git a/Toolkit/Events/getEvents.php b/Toolkit/Events/getEvents.php
new file mode 100644 (file)
index 0000000..01cc11f
--- /dev/null
@@ -0,0 +1,34 @@
+<?php
+
+/**
+ * getEvents.php
+ *
+ * PHP version 5.3
+ *
+ * @category  Toolkit
+ * @package   Events
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @version   SVN: (0.1)
+ * @link      <>
+ */
+require '../../setup.phtml';
+$catid             = EVENT_PAGE;
+$_REQUEST['catid'] = $_POST['catid'] = $_GET['catid'] = $catid;
+//    Create a new registry so we don't pollute the global namespace
+$registry = new Toolkit_Registry();
+$dbh = Toolkit_Database::getInstance();
+$registry->cacheOptions = $GLOBALS['cacheOptions'];
+$registry->flexyOptions = $GLOBALS['flexyOptions'];
+$registry->catid        = $catid;
+$registry->dbh          = $dbh;
+$registry->logger       = Toolkit_Logger::getLogger();
+// create Event Display
+$events = new Toolkit_Events_Display($registry->dbh);
+$keywordReplacement = new Toolkit_Template_KeywordReplacement(
+    new Toolkit_Toolbox_PageGatewayPublish(
+        $registry->dbh
+    )
+);
+echo $events->toHTML($keywordReplacement);
diff --git a/Toolkit/Events/libjs/AddCommonEventForm.php b/Toolkit/Events/libjs/AddCommonEventForm.php
new file mode 100644 (file)
index 0000000..4fc56f0
--- /dev/null
@@ -0,0 +1,1061 @@
+<?php
+
+//     vim:set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker syntax=php:
+
+/**
+ * New Event Form
+ *
+ * PHP version 5
+ *
+ * @category Toolkit
+ * @package  Events
+ * @author   Steve Sutton <steve@gaslightmedia.com>
+ * @license  http://www.gaslightmedia.com Gaslightmedia
+ * @version  CVS: $Id: AddEventForm.php,v 1.20 2010/07/04 23:58:22 jamie Exp $
+ * @link     http://demo.gaslightmedia.com
+ */
+
+/**
+ * New Event Form
+ *
+ * @category  Toolkit
+ * @package   Events
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @link      http://demo.gaslightmedia.com
+ */
+class Toolkit_Events_AddCommonEventForm
+    extends Toolkit_FormBuilder
+    implements Toolkit_Form
+{
+    // {{{     properties
+
+    /**
+     * Table in Database which holds the contact data
+     *
+     * @var    string
+     * @access public
+     */
+    public $tableName = 'events.events';
+
+    /**
+     * Table meta data
+     *
+     * This is used when inserting/updating data for the records
+     * so the PDO's can use explicit data types for the parameters.
+     *
+     * @var    array
+     * @access public
+     */
+    public $tableMetaData;
+
+    /**
+     * Who to send the email to when the contact form is submitted
+     *
+     * If you leave this blank, its value will get set to the OWNER_EMAIL
+     * in the constructor.
+     *
+     * If you ***DO NOT*** want any emails to go out when the form is submitted
+     * then set the value to false. Do not set it to 0 for false, because the
+     * check uses a strict type check to determine if the value is actually
+     * false. This is what allows for the empty value as an option, which sets
+     * the value to OWNER_EMAIL and won't override the $email property if
+     * this class gets subclassed and the value for this property gets set in
+     * the properties of the subclass and not in the constructor after this
+     * constructor function is called.
+     *
+     * tongue twister...I know.
+     * <code>
+     * protected $email = false;
+     * </code>
+     *
+     * @var    unknown
+     * @access protected
+     */
+    protected $email;
+
+    /**
+     * From header in the owner email
+     *
+     * This just sets the From header in the owner email
+     * SITENAME <from@email.com>
+     *
+     * It gets set to the constant SITENAME in the constructor if you leave
+     * empty here, but you can set it to something different here to override
+     * that if you desire.
+     *
+     * @var    unknown
+     * @access protected
+     */
+    protected $siteName;
+
+    /**
+     * Email subject and <h1> header in email
+     *
+     * It gets set in the constructor if you leave empty here, but you
+     * can set it to something different here to override that if you desire.
+     *
+     * @var    string
+     * @access protected
+     */
+    protected $subject = 'New Event Submission';
+    public $formTemplate = 'form.html';
+
+    /**
+     * Message to display if the form is successfully submitted
+     *
+     * @var    string
+     * @access protected
+     */
+    protected $successMsg = '
+    <style type="text/css">
+        #category {display:none};
+        .listings {display:none};
+    </style>
+               <div id="form-sucess-top">
+            Your event has been successfully added to the events calendar,
+            however will not be visible until it has been approved by
+            the Web site administrator. Thank You.
+               </div>';
+
+    /**
+     * Extra rules for processesing
+     *
+     * This registers the Zip validation rules (and any others listed) for
+     * QuickForm.
+     *
+     * Zip validation checks both US and Canadian Zip codes
+     *
+     * @var    array
+     * @access protected
+     */
+    protected $registeredRules = array(
+        'phone',
+        array(
+            'checkEmail',
+            'callback',
+            'email',
+            'Validate'
+        ),
+        array(
+            'checkURI',
+            'callback',
+            'uri',
+            'Validate'
+        )
+    );
+
+    /**
+     * Options for flexy templating engine
+     *
+     * Pulls the preset options from the setup.phtml file
+     * overwrites the templateDir and compileDir to match this classes needs
+     *
+     * @var    array
+     * @access protected
+     */
+    protected $flexyOptions;
+
+    protected $eventMapper;
+
+    // }}}
+    // {{{     __construct()
+
+    /**
+     * Class constructor
+     *
+     * @param PDO    $pdo         PHP Data Object
+     * @param string $formName    Form's name.
+     * @param string $method      (optional)Form's method defaults to 'POST'
+     * @param string $action      (optional)Form's action
+     * @param string $target      (optional)Form's target defaults to '_self'
+     * @param mixed  $attributes  (optional)Extra attributes for <form> tag
+     * @param bool   $trackSubmit (optional)Whether to track if the form was
+     *                                                           submitted by adding a special hidden field
+     *
+     * @access public
+     */
+    public function __construct(
+        PDO $pdo,
+        $formName,
+        $method = 'post',
+        $action = '',
+        $target = '',
+        $attributes = null,
+        $trackSubmit = false,
+        EventMapper $eventMapper
+    ) {
+        parent::__construct(
+            $formName, $method, $action, $target, $attributes, $trackSubmit
+        );
+
+        $this->dbh = $pdo;
+        $this->eventMapper = $eventMapper;
+
+        if ($this->email !== false && empty($this->email)) {
+            // Set to false to turn off email function.
+            $this->email = ADD_EVENT_EMAIL;
+        }
+        if (empty($this->siteName)) {
+            $this->siteName = SITENAME;
+        }
+        if (empty($this->subject)) {
+            $this->subject = 'Contact Request from website ' . SITENAME;
+        }
+
+        $this->flexyOptions = $GLOBALS['flexyOptions'];
+        $this->flexyOptions['templateDir'] = dirname(__FILE__) . "/templates/";
+        $this->flexyOptions['compileDir'] = dirname(__FILE__) . "/templates/compiled/";
+
+        $var = basename(__FILE__, '.php');
+
+        $callbackUrl = MEDIA_BASE_URL;
+
+        $this->captchaOptions = array(
+            'width'        => 100,
+            'height'       => 50,
+            'callback'     => "{$callbackUrl}Toolkit/qfcaptcha.php?var=$var",
+            'sessionVar'   => $var,
+            'imageOptions' => array(
+                'font_size'        => 16,
+                'font_path'        => GLM_APP_BASE . 'glmPEAR/Image/Canvas/Fonts/',
+                'font_file'        => 'times.ttf',
+                'background_color' => '#cccccc',
+                'obfuscation'      => false,
+                'angle'            => true,
+            ),
+        );
+    }
+
+    // }}}
+    //  {{{ checkDate()
+
+    /**
+     * Validate date input
+     *
+     * allows for empty dates to be valid
+     *
+     * @param array $date date group from form
+     *
+     * @return boolean true if valid, false if not
+     * @access public
+     */
+    public function checkDate($date)
+    {
+        if (!$date) {
+            return true;
+        } else {
+            return Validate::date($date, array('format' => '%m/%d/%Y'));
+        }
+    }
+
+    //  }}}
+    //  {{{ checkDateRange()
+
+    /**
+     * Validate date input
+     *
+     * allows for empty end date to be valid
+     *
+     * @param array $d date group from form
+     *
+     * @return boolean true if valid, false if not
+     * @access public
+     */
+    public function checkDateRange(array $d)
+    {
+        if (!$this->hasEndDate($d[1])) {
+            //  no end date is a valid date range
+            return true;
+        }
+        $pattern = '/([0-9]{2})\/([0-9]{2})\/([0-9]{4})/';
+        if (preg_match($pattern, $d[0], $m)) {
+            $t1 = mktime(0, 0, 0, (int) $m[1], (int) $m[2], (int) $m[3]);
+            $bdate = new Date($t1);
+        }
+        if (preg_match($pattern, $d[1], $m)) {
+            $t2    = mktime(0, 0, 0, (int) $m[1], (int) $m[2], (int) $m[3]);
+            $edate = new Date($t2);
+        }
+        if ($bdate && $edate) {
+            //  0 if the dates are equal - valid
+            // -1 if $bdate is before $edate - valid
+            //  1 if $bdate is after $edate - invalid
+            $res = Date::compare($bdate, $edate);
+            return ($res !== 1);
+        }
+        return true;
+    }
+
+    //  }}}
+    // {{{     configureElements()
+
+    /**
+     * Form element definitions
+     *
+     * @return void
+     * @access public
+     */
+    public function configureElements()
+    {
+        $e = array();
+
+        //     All Elements are created here.  This includes group element definitions.
+        $e[] = array(
+            'type'    => 'header',
+            'name'    => 'eventNameHdr',
+            'display' => 'Event Name'
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => true,
+            'name'    => 'header',
+            'display' => 'Event Name'
+        );
+        $e[] = array(
+            'type'    => 'header',
+            'name'    => 'eventDateHdr',
+            'display' => 'Event Date / Time'
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => true,
+            'name'    => 'starting',
+            'display' => 'Start Date',
+            'opts'    => array('id' => 'sdate')
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => true,
+            'name'    => 'ending',
+            'display' => 'End Date',
+            'opts'    => array('id' => 'edate')
+        );
+        $e[] = array(
+            'type'    => 'date',
+            'req'     => false,
+            'name'    => 'btime',
+            'display' => 'Start Time',
+            'opts'    => array(
+                'format'           => 'h : i A',
+                'addEmptyOption'   => true,
+                'emptyOptionValue' => '',
+                'emptyOptionText'  => array(
+                    'h' => 'hh',
+                    'i' => 'mm',
+                    'A' => 'am/pm'
+                ),
+                'optionIncrement' => array(
+                    'i' => 15,
+                ),
+            ),
+            'error' => 'ERROR: You must select a start time!',
+        );
+        $e[] = array(
+            'type'    => 'date',
+            'req'     => false,
+            'name'    => 'etime',
+            'display' => 'End Time',
+            'opts'    => array(
+                'format'           => 'h : i A',
+                'addEmptyOption'   => true,
+                'emptyOptionValue' => '',
+                'emptyOptionText'  => array(
+                    'h' => 'hh',
+                    'i' => 'mm',
+                    'A' => 'am/pm'
+                ),
+                'optionIncrement' => array(
+                    'i'  => 15,
+                ),
+            ),
+        );
+        $e[] = array(
+            'type'    => 'header',
+            'name'    => 'eventInfoHdr',
+            'display' => 'Event Information'
+        );
+        $e[] = array(
+            'type'    => 'select',
+            'req'     => true,
+            'name'    => 'category',
+            'display' => 'Category',
+            'opts'    => $this->getTopicFields(),
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => 'website',
+            'display' => 'Event Website'
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => 'facebook',
+            'display' => 'Facebook',
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => 'twitter',
+            'display' => 'Twitter',
+        );
+
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => 'cost',
+            'display' => 'Cost',
+        );
+
+        $e[] = array(
+            'type'    => 'textarea',
+            'req'     => false,
+            'name'    => 'description',
+            'display' => 'Event Description',
+            'opts'    => array('id' => 'description')
+        );
+        $e[] = array(
+            'type'    => 'header',
+            'name'    => 'eventLocationInfoHeader_rmv',
+            'display' => 'Event Location Information
+                <div id="map-dialog">
+                    <div id="map_canvas" style="width:500px; height:400px"></div>
+                </div>
+                <a id="map-it" href="#">Map It</a>'
+        );
+        $e[] = array(
+            'type' => 'hidden',
+            'req'  => false,
+            'name' => 'lat',
+            'opts' => array('id' => 'lat')
+        );
+        $e[] = array(
+            'type' => 'hidden',
+            'req'  => false,
+            'name' => 'lon',
+            'opts' => array('id' => 'lon')
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => true,
+            'name'    => 'place',
+            'display' => 'Place'
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => 'address',
+            'display' => 'Address',
+            'opts'    => array('id' => 'address')
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => true,
+            'name'    => 'city',
+            'display' => 'City',
+            'opts'    => array('id' => 'city')
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => 'state',
+            'display' => 'State',
+            'opts'    => array('id' => 'state')
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => 'zip',
+            'display' => 'ZIP',
+            'opts'    => array('id' => 'zip')
+        );
+        $e[] = array(
+            'type'    => 'header',
+            'name'    => 'eventContactHeader_rmv',
+            'display' => 'Event Contact Information'
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => 'contact_name',
+            'display' => 'Event Contact Person<br>(published on Web site)'
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => 'contact_email',
+            'display' => 'Contact Email<br>(published on Web site)'
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => true,
+            'name'    => 'contact_phone',
+            'display' => 'Contact Phone<br>(published on Web site)'
+        );
+
+        $e[] = array(
+            'type'    => 'header',
+            'name'    => 'adminInfoHdr',
+            'display' => 'Event Admin Information'
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => true,
+            'name'    => 'admin_contact',
+            'display' => 'Contact Name Submitting Event'
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => true,
+            'name'    => 'admin_org',
+            'display' => 'Organization Name Submitting Event'
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => true,
+            'name'    => 'admin_phone',
+            'display' => 'Phone'
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => true,
+            'name'    => 'admin_email',
+            'display' => 'Email Address'
+        );
+
+        if ($this->useCaptcha) {
+            $e[] = array(
+                'type'    => 'CAPTCHA_Image',
+                'req'     => false,
+                'name'    => 'captcha_question',
+                'display' => 'Verification code',
+                'opts'    => $this->captchaOptions
+            );
+            $e[] = array(
+                'type'    => 'text',
+                'req'     => true,
+                'name'    => 'captcha_rmv',
+                'display' => 'Enter verification code',
+            );
+        }
+        $e[] = array(
+            'type'    => 'submit',
+            'req'     => false,
+            'name'    => 'submit_rmv',
+            'display' => 'Submit',
+            'opts'    => array('class' => 'button')
+        );
+
+        $this->setupElements($e);
+    }
+
+    // }}}
+    // {{{     configureFilters()
+
+    /**
+     * Form filter definitions
+     *
+     * Applies a data filter for the given fields when the form is submitted
+     *
+     * @return void
+     * @access public
+     */
+    public function configureFilters()
+    {
+        $f = array();
+
+        $f[] = array(
+            'element' => '__ALL__',
+            'filter'  => 'trim'
+        );
+        $f[] = array(
+            'element' => 'website',
+            'filter'  => array('Toolkit_Common', 'filterURI')
+        );
+
+        $this->setupFilters($f);
+    }
+
+    // }}}
+    //  {{{ configureForm()
+
+    /**
+     * Helper function to handle setting up the form
+     *
+     * @return void
+     * @access public
+     */
+    public function configureForm()
+    {
+        $this->configureElements();
+        $this->configureFilters();
+        $this->configureRules();
+    }
+
+    //  }}}
+    // {{{     configureRules()
+
+    /**
+     * Form rule definitions
+     *
+     * Adds validation rules for the given fields
+     *
+     * @return void
+     * @access public
+     */
+    public function configureRules()
+    {
+        //     Form Rules
+        $r[] = array(
+            'element'    => 'topicid',
+            'message'    => 'ERROR: Invalid Category!',
+            'type'       => 'numeric',
+            'format'     => null,
+            'validation' => $this->validationType,
+            'reset'      => true,
+            'force'      => false
+        );
+        $r[]         = array(
+            'element' => 'email',
+            'message' => 'ERROR: Invalid Email Format!',
+            'type'    => 'checkEmail',
+            'format'  => array('use_rfc822' => true),
+            'validation' => $this->validationType,
+            'reset'      => true,
+            'force'      => false
+        );
+        $r[]         = array(
+            'element' => array('starting', 'ending'),
+            'message' => 'ERROR: Starting Date must be before Ending Date',
+            'type'    => 'callback',
+            'format'  => array(&$this, 'checkDateRange'),
+            'validation' => $this->validationType,
+            'reset'      => false,
+            'force'      => false
+        );
+        $r[]         = array(
+            'element' => 'starting',
+            'message' => 'ERROR: Invalid date!',
+            'type'    => 'callback',
+            'format'  => array(&$this, 'checkDate'),
+            'validation' => $this->validationType,
+            'reset'      => false,
+            'force'      => false
+        );
+        $r[]         = array(
+            'element' => 'ending',
+            'message' => 'ERROR: Invalid date!',
+            'type'    => 'callback',
+            'format'  => array(&$this, 'checkDate'),
+            'validation' => $this->validationType,
+            'reset'      => false,
+            'force'      => false
+        );
+        $r[]         = array(
+            'element' => 'website',
+            'message' => 'ERROR: Invalid URL format',
+            'type'    => 'checkURI',
+            'format'  => array(
+                'allowed_schemes' => array('http', 'https'),
+                'strict'     => false
+            ),
+            'validation' => $this->validationType,
+            'reset'      => false,
+            'force'      => false
+        );
+        $r[]         = array(
+            'element'    => 'contact_phone',
+            'message'    => 'ERROR: Invalid Phone Format (xxx) xxx - xxxx!',
+            'type'       => 'phone',
+            'format'     => null,
+            'validation' => $this->validationType,
+            'reset'      => true,
+            'force'      => false
+        );
+        if ($this->useCaptcha) {
+            $r[] = array(
+                'element'    => 'captcha_rmv',
+                'message'    => 'ERROR: What you entered didn\'t match!',
+                'type'       => 'CAPTCHA',
+                'format'     => $this->captchaQuestion,
+                'validation' => $this->validationType,
+                'reset'      => true,
+                'force'      => false
+            );
+        }
+
+        $this->setupRules($r);
+    }
+
+    // }}}
+    // {{{     getTopicFields()
+
+    /**
+     * get event topics
+     *
+     * @return array topics
+     * @access protected
+     */
+    protected function getTopicFields()
+    {
+        $categories = array('' => '- select -');
+        $catData = $this->eventMapper->fetchAllCategories();
+        foreach ($catData as $category) {
+            $categories[$category->getId()] = $category->getName();
+        }
+        return $categories;
+    }
+
+    // }}}
+    //  {{{ hasEndDate()
+
+    /**
+     * verifies if we have a valid end date to work with
+     *
+     * @param array $d end date
+     *
+     * @return boolean if the end date is
+     */
+    protected function hasEndDate($d)
+    {
+        $pattern = '/([0-9]{2})\/([0-9]{2})\/([0-9]{4})/';
+        if (preg_match($pattern, $d, $m)) {
+            return checkdate((int) $m[1], (int) $m[2], (int) $m[3]);
+        } else {
+            return false;
+        }
+    }
+
+    //  }}}
+    // {{{     emailOwner()
+
+    /**
+     * Emails the owner the submitted data from the submitted form
+     *
+     * Uses a flexy template to render a nice looking html email.
+     * Fills in the supplied data from the form and doesn't add the
+     * empty fields the user didn't fill in.
+     *
+     * @return boolean   result of the mailing
+     * @access protected
+     */
+    protected function emailOwner()
+    {
+        if (!$this->email) {
+            return;
+        }
+
+        $template = new HTML_Template_Flexy($this->flexyOptions);
+        $page     = new stdClass();
+        // for comments textarea need to replace newlines with br
+        $this->formData['comments']['element'] = nl2br($this->getSubmitValue('comments'));
+        unset($this->formData['lat'], $this->formData['lon']);
+
+        //  these values are required, therefor will always be part of
+        //  the formData array
+        $bdate = explode('/', $this->formData['bdate']['element']);
+        $foo   = array_map('trim', $bdate);
+        $this->formatValue($foo, '%02d/%02d/%d');
+        $this->formData['bdate']['element'] = $foo;
+
+        $btime = explode('/', $this->formData['btime']['element']);
+        $foo   = array_map('trim', $btime);
+        $this->formatValue($foo, '%02d:%02d %s');
+        $this->formData['btime']['element'] = $foo;
+
+        //  not required, so check to make sure it exists before trying
+        //  to format the value
+        if (isset($this->formData['edate'])) {
+            $edate = explode('/', $this->formData['edate']['element']);
+            $foo   = array_map('trim', $edate);
+            $this->formatValue($foo, '%02d/%02d/%d');
+            $this->formData['edate']['element'] = $foo;
+        }
+        if (isset($this->formData['etime'])) {
+            $etime = explode('/', $this->formData['etime']['element']);
+            $foo   = array_map('trim', $etime);
+            $this->formatValue($foo, '%02d:%02d %s');
+            $this->formData['etime']['element'] = $foo;
+        }
+
+        $category = $this->eventMapper->fetchCategory(
+            $this->formData['category']['element']
+        );
+        if ($category) {
+            $this->formData['category']['element'] = $category->getName();
+        }
+
+        $page->email_from = FROM_NEWS_EMAIL;
+        $page->subject = $this->subject;
+        $page->formData = $this->formData;
+        $page->eventAdminURL = MEDIA_BASE_URL . 'admin/CommonEvents/index.php?pending=1';
+
+        $template->compile('emailOwner.tpl');
+        $htmlMsg = $template->bufferedOutputObject($page);
+
+        //     Text version can't have HTML in it
+        $msg = "{$page->subject}\n\n";
+        $msg .= "From {$page->fname} {$page->lname}\n\n";
+        $msg .= "Information\n\n";
+        foreach ($page->formData as $i) {
+            $msg .= "{$i['label']}: {$i['element']}\n";
+        }
+
+        $crlf     = "\n";
+        $mimeMail = new Mail_mime($crlf);
+        $mimeMail->setFrom("{$this->siteName} <{$page->email_from}>");
+        $mimeMail->setSubject($this->subject);
+        $mimeMail->setHTMLBody($htmlMsg);
+        $mimeMail->setTXTBody($msg);
+
+        $mail    = Mail::factory('mail');
+        $body    = $mimeMail->get();
+        $headers = $mimeMail->headers();
+
+        $res = $mail->send($this->email, $headers, $body);
+        if (PEAR::isError($res)) {
+            return Toolkit_Common::handleError($res);
+        } else {
+            return $res;
+        }
+    }
+
+    // }}}
+    //  {{{ formatValue()
+
+    /**
+     * Format an array into an acceptable string
+     *
+     * @param mixed  &$i     array values to format or null value for
+     *                       element that was not filled in
+     * @param string $format string to format values into
+     *
+     * @return string formatted string
+     * @access public
+     */
+    public function formatValue(&$i, $format)
+    {
+        //  Allow for 0 to be not empty.  This allows for minutes in the
+        //  time arrays to be valid if they are on the hour ie. (1:00 pm)
+        $notEmpty = create_function('$v', 'return strlen($v) > 0;');
+        if (is_array($i) && count(array_filter($i, $notEmpty)) == 3) {
+            list($x, $y, $z) = array_values($i);
+            eval("\$i = sprintf('$format', $x, $y, $z);");
+        } else {
+            $i = null;
+        }
+    }
+
+    //  }}}
+    // {{{     insertData()
+
+    /**
+     * Inserts contact data into the contact db
+     *
+     * @param array $values submitted values
+     *
+     * @return object result of db insert query
+     * @access protected
+     */
+    protected function insertData($values)
+    {
+        $values = $this->_geocode($values);
+
+        try {
+            // need to set the dates up first
+            $this->formatValue($values['btime'], '%d:%02d %s');
+            $this->formatValue($values['etime'], '%d:%02d %s');
+            $values['description'] = nl2br($values['description']);
+            $values['website']   = preg_replace("/^(http:\/\/)/", "", $values['website']);
+            if ($values['btime']) {
+                $values['starthour'] = $values['btime'];
+                unset($values['btime']);
+            }
+            if ($values['etime']) {
+                $values['endhour'] = $values['etime'];
+                unset($values['etime']);
+            }
+            if ($values['category']) {
+                $values['category'] = $this->eventMapper->fetchCategory(
+                    $values['category']
+                );
+            }
+            if (defined('MEMBERS_DB') && MEMBERS_DB) {
+                $event = MemberEvent::createByValues($values);
+            } else {
+                $event = Event::createByValues($values);
+            }
+            return $this->eventMapper->saveEvent($event);
+
+            return false;
+        } catch (PDOException $e) {
+            return Toolkit_Common::handleError($e);
+        }
+    }
+
+    // }}}
+    private function _geocode(array $values)
+    {
+        $geocoder = new GeocodeYahoo();
+        if (!$values['address'] && !$values['city'] && !$values['state']) {
+            return $values;
+        }
+        $address = array(
+                       'city'  => $values['city'],
+                       'state' => $values['state'],
+                       'zip'   => $values['zip'],
+               );
+               if (!empty($values['address'])) {
+                       $address['street'] = $values['address'];
+               }
+        try {
+            $response = $geocoder->geocodeAddress($address);
+            $responseArray = unserialize($response);
+            if ($responseArray['ResultSet']['Result'][0]['Latitude']) {
+                $values['lat'] = $responseArray['ResultSet']['Result'][0]['Latitude'];
+                $values['lon'] = $responseArray['ResultSet']['Result'][0]['Longitude'];
+            } else {
+                $values['lat'] = $responseArray['ResultSet']['Result']['Latitude'];
+                $values['lon'] = $responseArray['ResultSet']['Result']['Longitude'];
+            }
+
+            return $values;
+        } catch (BadMethodCallException $e) {
+            Toolkit_Logger::logException('Invalid Arg', $e);
+        } catch (Exception $e) {
+            Toolkit_Logger::logException('Yahoo GeoCode', $e);
+        }
+
+    }
+    // {{{     processData()
+
+    /**
+     * Handles how to process the form when submitted
+     *
+     * @param array $values Form submitted values
+     *
+     * @return array Result of Insert / Update function
+     * @access protected
+     */
+    public function processData($values)
+    {
+        //     Form data used for the insert/update sql queries and
+        //     the form email.
+        $e = array();
+        $this->setFormData($e);
+
+        //     Get rid of any defined un-needed elements.
+        //     un-needed elements after the form is submitted are defined
+        //     by the ending _rmv name.
+        foreach ($values as $k => &$v) {
+            if (!is_array($v)) {
+                $values[$k] = preg_replace("/\r/", "\n", $v);
+            }
+            if (preg_match('/^.+_rmv$/', $k)) {
+                unset($values[$k]);
+            }
+        }
+        $values['create_date'] = date('m/d/Y');
+
+        return $this->insertData($values);
+    }
+
+    // }}}
+    // {{{     setupRenderers()
+
+    /**
+     * Custom rendering templates for special fields on the form
+     *
+     * @return void
+     * @access protected
+     */
+    protected function setupRenderers()
+    {
+        $formConfig = new Zend_Config_Ini(
+            BASE . 'Toolkit/Events/application.ini',
+            strtolower($_ENV['GLM_HOST_ID'])
+        );
+
+        $renderer = new HTML_QuickForm_Renderer_Object(true);
+        $this->accept($renderer);
+        $this->template = new HTML_Template_Flexy(
+            $formConfig->flexyOptions->toArray()
+        );
+
+        $this->view = $this;
+        $this->view->form = $renderer->toObject();
+        $this->template->compile($this->formTemplate);
+    }
+
+    // }}}
+    // {{{     toHtml()
+
+    /**
+     * Handles how to display the current step the user is at in the form
+     *
+     * destroying and resetting the captcha value dis-allows someone from
+     * re-sending a form on a previous captcha.
+     *
+     * @return string form HTML state
+     * @access public
+     */
+    public function toHtml()
+    {
+        if ($this->validate()) {
+            $this->captchaQuestion->destroy();
+            $this->cleanForm();
+
+            if ($this->process(array(&$this, 'processData'), $this->mergeFiles)) {
+                $this->freeze();
+                $this->emailOwner();
+                $output = $this->successMsg;
+            }
+            $this->sent = true;
+        } elseif ($this->isSubmitted()) {
+            if ($this->useCaptcha) {
+                $this->captchaQuestion->destroy();
+                $this->captchaAnswer->setValue('');
+            }
+            $output                     = $this->errorMsg;
+            $GLOBALS['topScripts'][]
+                = '//code.jquery.com/ui/1.11.1/jquery-ui.min.js';
+            $GLOBALS['styleSheets'][]
+                = '//code.jquery.com/ui/1.11.1/themes/smoothness/jquery-ui.css';
+            $GLOBALS['bottomScripts'][] = MEDIA_APP_BASE_URL . 'libjs/textlimit.js';
+            $GLOBALS['topScripts'][]
+                = 'http://maps.googleapis.com/maps/api/js?sensor=true';
+            $GLOBALS['bottomScripts'][]
+                = MEDIA_BASE_URL . 'Toolkit/Maps/geoCoder.js';
+            $GLOBALS['bottomScripts'][]
+                = MEDIA_BASE_URL . 'Toolkit/Events/libjs/addEvent.js';
+            $output  = $this->errorMsg;
+            $this->setupRenderers();
+            $output .= $this->template->bufferedOutputObject($this->view);
+        } else {
+            if ($this->useCaptcha) {
+                $this->captchaQuestion->destroy();
+                $this->captchaAnswer->setValue('');
+            }
+            $GLOBALS['topScripts'][]
+                = '//code.jquery.com/ui/1.11.1/jquery-ui.min.js';
+            $GLOBALS['styleSheets'][]
+                = '//code.jquery.com/ui/1.11.1/themes/smoothness/jquery-ui.css';
+            $GLOBALS['bottomScripts'][] = MEDIA_APP_BASE_URL . 'libjs/textlimit.js';
+            $GLOBALS['topScripts'][]
+                = 'http://maps.googleapis.com/maps/api/js?sensor=true';
+            $GLOBALS['bottomScripts'][]
+                = MEDIA_BASE_URL . 'Toolkit/Maps/geoCoder.js';
+            $GLOBALS['bottomScripts'][]
+                = MEDIA_BASE_URL . 'Toolkit/Events/libjs/addEvent.js';
+            $this->setupRenderers();
+            $output .= $this->template->bufferedOutputObject($this->view);
+        }
+        return $output;
+    }
+
+    // }}}
+}
+
+?>
diff --git a/Toolkit/Events/libjs/addEvent.js b/Toolkit/Events/libjs/addEvent.js
new file mode 100644 (file)
index 0000000..b912912
--- /dev/null
@@ -0,0 +1,28 @@
+var AddEvent =
+{
+       init: function()
+       {
+        $('#description')
+            .after('You have <span class="counter">1000</span>')
+            .textlimit('span.counter', 1000);
+        $('#sdate').datepicker({
+            altField: "#edate"
+        });
+        $('#edate').datepicker();
+        // For the Event Map
+        // Create a jquery dialog with #map-dialog
+        $('#map-dialog').dialog({
+            height: 480,
+            width: 520,
+            modal: true,
+            autoOpen: false
+        });
+        $("#map-it").click(function(e){
+            e.preventDefault();
+            $("#map-dialog").dialog('open');
+            GLM_GeoMap.initialize();
+        });
+       }
+}
+
+$(document).ready(AddEvent.init);
\ No newline at end of file
diff --git a/Toolkit/Events/libjs/calendar.js b/Toolkit/Events/libjs/calendar.js
new file mode 100644 (file)
index 0000000..bfd0a26
--- /dev/null
@@ -0,0 +1,26 @@
+var Calendar =
+{
+    baseUrl: null,
+    init: function()
+    {
+        Calendar.setLinks();
+    },
+    setLinks: function()
+    {
+        baseUrl = '';
+        $(".calLink").click(function(){
+            $.ajax({
+                type: "GET",
+                url: baseUrl + "Toolkit/Events/Ajax.php",
+                data: $(this).attr('rel'),
+                success: function(msg){
+                    $("#eventCalendar").html( msg);
+                    Calendar.setLinks();
+                }
+            });
+            return false;
+        });
+    }
+};
+
+$(document).ready(Calendar.init);
diff --git a/Toolkit/Events/libjs/edit-event.js b/Toolkit/Events/libjs/edit-event.js
new file mode 100644 (file)
index 0000000..16be645
--- /dev/null
@@ -0,0 +1,123 @@
+var Events =
+{
+       calendar: '//app.gaslightmedia.com/assets/icons/calendar.png',
+
+    init: function()
+    {
+        if ($("#descr").length) {
+            //  Only try to replace the textarea if the
+            //  CKEditor is compatible w/ the browser.
+            if (CKEDITOR.env.isCompatible) {
+                // test to see if this is in the Member only section
+                var hostPattern = new RegExp('/members-only-area/');
+                if (hostPattern.test(window.location.pathname)) {
+                    CKEDITOR.replace("descr",
+                    {
+                        toolbar : "Default",
+                        width : 600,
+                        height : 200,
+                        filebrowserImageBrowseUrl : "../Toolkit/CKImages/browser.php?folder=1",
+                        filebrowserImageUploadUrl : "../Toolkit/CKImages/controller.php?command=Upload",
+                        filebrowserImageWindowWidth : "760",
+                        filebrowserImageWindowHeight : "500"
+                    });
+                } else {
+                        CKEDITOR.replace("descr",
+                    {
+                        toolbar : "Default",
+                        width : 600,
+                        height : 200,
+                        filebrowserImageBrowseUrl : "../../Toolkit/CKImages/browser.php?folder=1",
+                        filebrowserImageUploadUrl : "../../Toolkit/CKImages/controller.php?command=Upload",
+                        filebrowserImageWindowWidth : "760",
+                        filebrowserImageWindowHeight : "500"
+                    });
+                }
+
+            }
+        }
+        if ($("#intro").length > 0) {
+            $("#intro").textlimit("#charleft", 350);
+        }
+        if ($("#sdate").length > 0) {
+            $('#sdate').datepicker({
+                altField: "#edate"
+            });
+            $('#edate').datepicker();
+        }
+        $(":checkbox[name='all_day']").change(function (){
+               Events.show_hide_time();
+        });
+        $("select[name='dayom']").change(function (){
+               Events.act_day_om($(this).val());
+        });
+        if ($(":checkbox[name='recurr']").length > 0) {
+            $(":checkbox[name='recurr']").change(function (){
+                Events.show_hide_recurr();
+            });
+            Events.show_hide_recurr();
+        }
+        if ($(":checkbox[name='reacur']").length > 0) {
+            $(":checkbox[name='reacur']").change(function (){
+                Events.show_hide_reacur();
+            });
+            Events.show_hide_reacur();
+        }
+        Events.show_hide_time();
+
+        Events.act_day_om($("select[name='dayom']").val());
+
+        // For the Event Map
+        // Create a jquery dialog with #map-dialog
+        $('#map-dialog').dialog({
+            height: 480,
+            width: 520,
+            modal: true,
+            autoOpen: false
+        });
+        $("#map-it").click(function(e){
+            e.preventDefault();
+            $("#map-dialog").dialog('open');
+            GLM_GeoMap.initialize();
+        });
+    },
+    show_hide_time: function() {
+       if ($(":checkbox[name='all_day']").attr('checked')) {
+               $("select[name^='btime']").val('');
+               $("select[name^='btime']").attr('disabled', 'disabled');
+               $("select[name^='etime']").val('');
+               $("select[name^='etime']").attr('disabled', 'disabled');
+       } else {
+               $("select[name^='btime']").removeAttr('disabled');
+               $("select[name^='etime']").removeAttr('disabled');
+       }
+    },
+    show_hide_reacur: function() {
+       if ($(":checkbox[name='reacur']").attr('checked')) {
+               $(".recur-event").show();
+       } else {
+               $(".recur-event").hide();
+       }
+    },
+    show_hide_recurr: function() {
+       if ($(":checkbox[name='recurr']").attr('checked')) {
+               $(".recur-event").show();
+       } else {
+               $(".recur-event").hide();
+       }
+    },
+    act_day_om: function(val)
+    {
+       if (val) {
+               $("input[name^='daysow']").attr('disabled', 'disabled');
+               $("input[name^='daysow']").removeAttr('checked');
+               $("select[name='weekom']").attr('disabled', 'disabled');
+               $("select[name='weekom']").val('');
+       } else {
+               $("input[name^='daysow']").removeAttr('disabled');
+               $("select[name='weekom']").removeAttr('disabled');
+       }
+    }
+};
+
+$(document).ready(Events.init);
diff --git a/Toolkit/Events/libjs/edit-topic.js b/Toolkit/Events/libjs/edit-topic.js
new file mode 100644 (file)
index 0000000..5e3b091
--- /dev/null
@@ -0,0 +1,11 @@
+function grabcolor( letter, newcolor )
+{
+       document.getElementById('square'+letter).style.backgroundColor= newcolor;
+       document.getElementById('colorbox-'+letter).style.display = 'none';
+       document.getElementById('topiccolor'+letter).value = newcolor;
+}
+function myDelete( myForm )
+{
+       myForm.Action.value = 'Delete Category';
+       myForm.submit();
+}
diff --git a/Toolkit/Events/libjs/editEvent.js b/Toolkit/Events/libjs/editEvent.js
new file mode 100644 (file)
index 0000000..1ceae55
--- /dev/null
@@ -0,0 +1,105 @@
+var Event = 
+{
+       calendar: '//app.gaslightmedia.com/assets/icons/calendar.png',
+       
+       init: function()
+       {
+               $('.delete').click(Event.confirmDelete);
+               
+        if ($("#description").length) {
+            if (CKEDITOR.env.isCompatible) {
+                CKEDITOR.replace("descr",
+                {
+                    toolbar : "Basic",
+                    width : 570,
+                    height : 200
+                });
+            }
+        }
+               if ($('#bdate1').is('select')) {
+                       Event.setupSDateCalendar();
+                       var fromCal = new Zapatec.Calendar.setup({
+                               weekNumbers             : false,
+                               ifFormat                : '%m/%d/%Y',
+                               button                  : 'startcal',
+                               onUpdate                : Event.updateFrom,
+                               showsTime               : false
+                       });
+               }
+
+               if ($('#edate1').is('select')) {
+                       Event.setupEDateCalendar();
+                       var toCal = new Zapatec.Calendar.setup({
+                               weekNumbers             : false,
+                               ifFormat                : '%m/%d/%Y',
+                               button                  : 'endcal',
+                               onUpdate                : Event.updateTo,
+                               showsTime               : false
+                       });
+               }
+
+        $("textarea[name='descr']").textlimit('span.counter',1000);
+
+        $("#bdate1").change(function(){
+                   $("#edate1 option[value='"+$(this).val()+"']").attr('selected', 'selected');
+        });
+        $("#bdate2").change(function(){
+                   $("#edate2 option[value='"+$(this).val()+"']").attr('selected', 'selected');
+        });
+        $("#bdate3").change(function(){
+                   $("#edate3 option[value='"+$(this).val()+"']").attr('selected', 'selected');
+        });
+       },
+
+       updateFrom: function(cal)
+       {
+               var date = cal.date;
+               var month = date.getMonth() + 1;
+               var day = date.getDate();
+               var year = date.getFullYear();
+
+               $("#bdate1 option[value='"+month+"']").attr('selected', 'selected');
+               $("#bdate2 option[value='"+day+"']").attr('selected', 'selected');
+               $("#bdate3 option[value='"+year+"']").attr('selected', 'selected');
+        Event.updateTo(cal);
+       },
+
+       updateTo: function(cal)
+       {
+               var date = cal.date;
+               var month = date.getMonth() + 1;
+               var day = date.getDate();
+               var year = date.getFullYear();
+
+               $("#edate1 option[value='"+month+"']").attr('selected', 'selected');
+               $("#edate2 option[value='"+day+"']").attr('selected', 'selected');
+               $("#edate3 option[value='"+year+"']").attr('selected', 'selected');
+       },
+
+       setupSDateCalendar: function()
+       {
+               var img = '&nbsp;<img id="startcal" height="16" width="16" ' + 
+                                               'style="vertical-align: middle" ' +
+                                               'src="' + Event.calendar + '">';
+               $('#bdate3').after(img);
+        $("#startcal").css('display', 'inline');
+       },
+
+       setupEDateCalendar: function()
+       {
+               var img = '&nbsp;<img id="endcal" height="16" width="16" ' + 
+                                               'style="vertical-align: middle" ' +
+                                               'src="' + Event.calendar + '">';
+               $('#edate3').after(img);
+        $("#endcal").css('display', 'inline');
+       },
+
+       confirmDelete: function(event)
+       {
+               if (!confirm('Are you sure you wish to delete this banner?\n\nThis action is not reversible!')) {
+                       event.preventDefault();
+               }
+       }
+};
+
+$(document).ready(Event.init);
diff --git a/Toolkit/Events/libjs/eventLoader.js b/Toolkit/Events/libjs/eventLoader.js
new file mode 100644 (file)
index 0000000..b8e89f9
--- /dev/null
@@ -0,0 +1,92 @@
+$(document).ready(function(){
+    if ($('#glm-events').length > 0) {
+        var dataParams = "catid=5&month=" + glm_month
+            + "&year=" + glm_year
+            + "&event_name=" + glm_event_name
+            + "&day=" + glm_day
+            + "&topicid=" + glm_topicid
+            + "&cityid=" + glm_cityid
+            + "&flat" + glm_flat
+            + "&eventid=" + glm_eventid;
+        $.ajax({
+            url: glm_base_url + "Toolkit/Events/getEvents.php",
+            data: dataParams,
+            success: function(eventData){
+                $("#glm-events").html(eventData);
+            }
+        });
+        $(document).delegate('a.event-link',
+            'click',
+            function(){
+                $("#glm-events").load($(this).attr('href'));
+                var new_position = $('#glm-events').offset();
+                window.scrollTo(new_position.left,new_position.top);
+                return false;
+            }
+        );
+        $(document).delegate('.glm-event-link',
+            'click',
+            function(){
+                $("#glm-events").load($(this).attr('href'));
+                var new_position = $('#glm-events').offset();
+                window.scrollTo(new_position.left,new_position.top);
+                return false;
+            }
+        );
+        $(document).delegate('#glm-advance-link',
+            'click',
+            function(){
+                $("#glm-events").load($(this).attr('href'), function(){
+                    $('#EventDateFrom').datepicker();
+                    $('#EventDateTo').datepicker();
+                });
+                var new_position = $('#glm-events').offset();
+                window.scrollTo(new_position.left,new_position.top);
+                return false;
+            }
+        );
+        $(document).delegate('#glm-cal-search',
+            'submit',
+            function(){
+                var dataParams = "catid=5"
+                    + "&month=" + encodeURIComponent($("#event-form-month").val())
+                    + "&topicid=" + encodeURIComponent($("#event-form-topicid").val());
+                $.ajax({
+                    type: "post",
+                    url: glm_base_url + "Toolkit/Events/getEvents.php",
+                    data: dataParams,
+                    success: function(eventData){
+                        $("#glm-events").html(eventData);
+                        var new_position = $('#toolbox').offset();
+                        window.scrollTo(new_position.left,new_position.top);
+                    }
+                });
+                return false;
+            }
+        );
+        $(document).delegate('#advance-event-search',
+            'submit',
+            function(){
+                var dataParams = "EventName=" + encodeURIComponent($("#EventName").val())
+                    + "&EventDateFrom=" + encodeURIComponent($("#EventDateFrom").val())
+                    + "&EventDateTo=" + encodeURIComponent($("#EventDateTo").val());
+                $('input[name="topics_id[]"]').each(function(){
+                    if ($(this).is(':checked')) {
+                        dataParams += "&topics_id[]=" + $(this).val();
+                    }
+                });
+                $.ajax({
+                    type: "post",
+                    url: glm_base_url + "Toolkit/Events/getEvents.php",
+                    data: dataParams,
+                    success: function(eventData){
+                        $("#glm-events").html(eventData);
+                        var new_position = $('#toolbox').offset();
+                        window.scrollTo(new_position.left,new_position.top);
+                    }
+                });
+                return false;
+            }
+        );
+    }
+});
\ No newline at end of file
diff --git a/Toolkit/Events/libjs/eventRotate.js b/Toolkit/Events/libjs/eventRotate.js
new file mode 100644 (file)
index 0000000..8ab4812
--- /dev/null
@@ -0,0 +1,10 @@
+$(document).ready(function(){
+    $('.events-rotator').cycle({
+        fx: 'scrollVert',
+        pause: 1,
+        timeout: 7000,
+        next: '#next-event',
+        prev: '#prev-event',
+        rev: 1
+    });
+});
diff --git a/Toolkit/Events/libjs/events.js b/Toolkit/Events/libjs/events.js
new file mode 100644 (file)
index 0000000..03001e0
--- /dev/null
@@ -0,0 +1,41 @@
+var EventFront =
+{
+    init: function()
+    {
+        $('#startMonth').datepicker({
+            showOn: "button",
+            buttonImage: "//app.gaslightmedia.com/assets/icons/calendar.png",
+            buttonImageOnly: true
+        });
+        $('#endMonth').datepicker({
+            showOn: "button",
+            buttonImage: "//app.gaslightmedia.com/assets/icons/calendar.png",
+            buttonImageOnly: true
+        });
+        if ($('#eventDetail-map').length) {
+            GLM_GeoMap.initialize();
+            $('#map-dialog').dialog({
+                height: 480,
+                width: 520,
+                modal: true,
+                autoOpen: false
+            });
+            $("#eventDetail-map").click(function(e){
+                e.preventDefault();
+                $("#map-dialog").dialog('open');
+                GLM_GeoMap.initializeLargeMap();
+            });
+        }
+        $('#EventDrivingDirectionsForm').submit(function() {
+            var place = $("#EventLocation").val().replace(new RegExp(" ", "g"), "+");
+            var lat = $("#EventLat").val();
+            var lon = $("#EventLon").val();
+            //alert(title + "<br>" + lat + "<br>" + lon);
+            var url = "https://maps.google.com/maps?daddr=" + place + "%40" + lat + "," + lon;
+            window.open(url, '_blank');
+            return false;
+        }); 
+    }
+};
+
+$(document).ready(EventFront.init);
diff --git a/Toolkit/Events/libjs/geoCoder.js b/Toolkit/Events/libjs/geoCoder.js
new file mode 100644 (file)
index 0000000..f7a194b
--- /dev/null
@@ -0,0 +1,92 @@
+// Option would be to make this configurable so it can be used
+// with one field (loc) like event
+// or multiple fields like member (street, city, state, zip)
+var GLM_GeoMap = {
+    defaultLat: 45.3748385,
+    defaultLon: -84.9592251,
+    geocoder: null,
+    map: null,
+    mapType: 'small',
+    defaultZoom: 12,
+    setDefaultLat: function(lat) {
+      GLM_GeoMap.defaultLat = lat;
+    },
+    setDefaultLon: function(lon) {
+        GLM_GeoMap.defaultLon = lon;
+    },
+    initialize: function() {
+        GLM_GeoMap.createMap();
+        var lat = $("#event-lat").attr('rel');
+        var lon = $("#event-lon").attr('rel');
+        if (lat && lon) {
+            GLM_GeoMap.createWithLatLon(lat, lon);
+        }
+    },
+    initializeLargeMap: function(){
+        GLM_GeoMap.mapType = 'large';
+        GLM_GeoMap.createMap();
+        var lat = $("#event-lat").attr('rel');
+        var lon = $("#event-lon").attr('rel');
+        if (lat && lon) {
+            GLM_GeoMap.createWithLatLon(lat, lon);
+        }
+    },
+    createMap: function() {
+        if (GLM_GeoMap.mapType == 'small') {
+            var myOptions = {
+                draggable: false,
+                zoom: GLM_GeoMap.defaultZoom,
+                mapTypeId: google.maps.MapTypeId.ROADMAP,
+                disableDefaultUI: true,
+                panControl: false,
+                zoomControl: false,
+                mapTypeControl: false,
+                scaleControl: false,
+                streetViewControl: false,
+                overviewMapControl: false,
+                scrollwheel: false,
+                disableDoubleClickZoom: true
+            };
+            GLM_GeoMap.map = new google.maps.Map(
+                document.getElementById("eventDetail-map"),
+                myOptions
+            );
+        }
+        if (GLM_GeoMap.mapType == 'large') {
+            GLM_GeoMap.defaultZoom = 15;
+            var myOptions = {
+                draggable: true,
+                zoom: GLM_GeoMap.defaultZoom,
+                mapTypeId: google.maps.MapTypeId.ROADMAP,
+                disableDefaultUI: true,
+                panControl: true,
+                zoomControl: true,
+                mapTypeControl: true,
+                scaleControl: true,
+                streetViewControl: true,
+                overviewMapControl: true,
+                scrollwheel: false,
+                disableDoubleClickZoom: true
+            };
+            GLM_GeoMap.map = new google.maps.Map(
+                document.getElementById("map_canvas"),
+                myOptions
+            );
+        }
+    },
+    addMarkerToMap: function(LatLng) {
+        var marker = new google.maps.Marker({
+            map: GLM_GeoMap.map,
+            position: LatLng,
+            draggable: false
+        });
+        GLM_GeoMap.map.setCenter(LatLng);
+        GLM_GeoMap.map.setZoom(GLM_GeoMap.defaultZoom);
+    },
+    createWithLatLon: function(lat, lon) {
+        GLM_GeoMap.addMarkerToMap(new google.maps.LatLng(
+            lat,
+            lon
+        ));
+    }
+}
\ No newline at end of file
diff --git a/Toolkit/Events/templates/currentTables/Element.tpl b/Toolkit/Events/templates/currentTables/Element.tpl
new file mode 100755 (executable)
index 0000000..595457b
--- /dev/null
@@ -0,0 +1,14 @@
+<tr>
+       <td class="labelcell">
+               <!-- BEGIN required -->
+               <span class="req">*</span>
+               <!-- END required -->
+               <label>{label}</label>
+       </td>
+       <td class="fieldcell">
+               <!-- BEGIN error -->
+               <div class="req"> {error} </div>
+               <!-- END error -->
+               {element}
+       </td>
+</tr>
diff --git a/Toolkit/Events/templates/currentTables/Form.tpl b/Toolkit/Events/templates/currentTables/Form.tpl
new file mode 100755 (executable)
index 0000000..f59286a
--- /dev/null
@@ -0,0 +1,7 @@
+<div id="contact">
+       <form{attributes}>
+               <table>
+                       {content}
+               </table>
+       </form>
+</div>
diff --git a/Toolkit/Events/templates/currentTables/Group.tpl b/Toolkit/Events/templates/currentTables/Group.tpl
new file mode 100755 (executable)
index 0000000..cdd24cf
--- /dev/null
@@ -0,0 +1,5 @@
+<table class="group">
+       <tbody>
+               {content}
+       </tbody>
+</table>
diff --git a/Toolkit/Events/templates/currentTables/GroupElement.tpl b/Toolkit/Events/templates/currentTables/GroupElement.tpl
new file mode 100755 (executable)
index 0000000..1a4ba27
--- /dev/null
@@ -0,0 +1,9 @@
+<tr>
+       <td>
+               {element}
+               <!-- BEGIN required -->
+               <span class="req">*</span>
+               <!-- END required -->
+               {label}
+       </td>
+</tr>
diff --git a/Toolkit/Events/templates/currentTables/Header.tpl b/Toolkit/Events/templates/currentTables/Header.tpl
new file mode 100755 (executable)
index 0000000..64ac244
--- /dev/null
@@ -0,0 +1,5 @@
+<tr class="hdr">
+       <td colspan="2">
+               {header}
+       </td>
+</tr>
diff --git a/Toolkit/Events/templates/currentTables/RequiredNote.tpl b/Toolkit/Events/templates/currentTables/RequiredNote.tpl
new file mode 100755 (executable)
index 0000000..dad5d0b
--- /dev/null
@@ -0,0 +1 @@
+<span class="req">* = Required Fields</span>
diff --git a/Toolkit/Events/templates/editTopics.html b/Toolkit/Events/templates/editTopics.html
new file mode 100644 (file)
index 0000000..00b8269
--- /dev/null
@@ -0,0 +1,77 @@
+<div id="topic-list">
+       <h1>Manage Event Categories</h1>
+       <div class="topic-row">
+       <form action="{topic_action_page:h}" method="post">
+       <table>
+               <tr>
+                       <td>Add New Category:</td>
+                       <td><input name="topic" value=""></td>
+                       <td><input type="hidden" name="Action" value="Add Topic"><input type="submit" value="Create Category"></td>
+               </tr>
+       </table>
+       </form>
+       </div>
+<?php
+$colorBoxLetter = ord('a');
+foreach ($t->data as $row) {
+       $letter = chr($colorBoxLetter);
+?>
+       <div class="topic-row">
+       <form name="form<?php echo $letter;?>" action="{topic_action_page:h}" method="post">
+       <input type="hidden" name="Action" value="Update Topic">
+       <input type="hidden" name="id" value="<?php echo $row['id'];?>">
+       <input type="hidden" id="topiccolor<?php echo $letter;?>" name="topiccolor" value="<?php echo $row['topiccolor'];?>">
+               <table>
+                       <tr>
+                               <td><div id="square<?php echo $letter;?>" class="colorsquare" style="background-color: <?php echo $row['topiccolor'];?>;" onclick="document.getElementById('colorbox-<?php echo $letter;?>').style.display = 'block';"></div></td>
+                               <td><div class="topic-name"><input name="descr" value="<?php echo $row['descr'];?>"></div></td>
+                               <td><div class="topic-save"><input type="button" value="Save" onClick="form<?php echo $letter;?>.submit();"></div></td>
+                               <td><div class="topic-delete"><input type="button" value="Delete" onClick="myDelete(this.form);"></div></td>
+                       </tr>
+               </table>
+               <div id="colorbox-<?php echo $letter;?>" class="colorbox" style="display:none;">
+      <table summary="color picker">
+       <tr>
+        <td class="color1" onclick="grabcolor('<?php echo $letter;?>','#FFCCCC')"></td>
+        <td class="color2" onclick="grabcolor('<?php echo $letter;?>','#CC9999')"></td>
+        <td class="color3" onclick="grabcolor('<?php echo $letter;?>','#FF9999')"></td>
+        <td class="color4" onclick="grabcolor('<?php echo $letter;?>','#FFCCFF')"></td>
+        <td class="color5" onclick="grabcolor('<?php echo $letter;?>','#CC99CC')"></td>
+       </tr>
+       <tr>
+        <td class="color6" onclick="grabcolor('<?php echo $letter;?>','#FF99FF')"></td>
+        <td class="color7" onclick="grabcolor('<?php echo $letter;?>','#CCCCFF')"></td>
+        <td class="color8" onclick="grabcolor('<?php echo $letter;?>','#9999CC')"></td>
+        <td class="color9" onclick="grabcolor('<?php echo $letter;?>','#9999FF')"></td>
+        <td class="color10" onclick="grabcolor('<?php echo $letter;?>','#CCFFFF')"></td>
+       </tr>
+       <tr>
+        <td class="color11" onclick="grabcolor('<?php echo $letter;?>','#99CCCC')"></td>
+        <td class="color12" onclick="grabcolor('<?php echo $letter;?>','#99FFFF')"></td>
+        <td class="color13" onclick="grabcolor('<?php echo $letter;?>','#CCFFCC')"></td>
+        <td class="color14" onclick="grabcolor('<?php echo $letter;?>','#99CC99')"></td>
+        <td class="color15" onclick="grabcolor('<?php echo $letter;?>','#99FF99')"></td>
+       </tr>
+       <tr>
+        <td class="color16" onclick="grabcolor('<?php echo $letter;?>','#FFFFCC')"></td>
+        <td class="color17" onclick="grabcolor('<?php echo $letter;?>','#CCCC99')"></td>
+        <td class="color18" onclick="grabcolor('<?php echo $letter;?>','#FFFF99')"></td>
+        <td class="color19" onclick="grabcolor('<?php echo $letter;?>','#FFCC99')"></td>
+        <td class="color20" onclick="grabcolor('<?php echo $letter;?>','#FF99CC')"></td>
+       </tr>
+       <tr>
+        <td class="color21" onclick="grabcolor('<?php echo $letter;?>','#CC99FF')"></td>
+        <td class="color22" onclick="grabcolor('<?php echo $letter;?>','#99CCFF')"></td>
+        <td class="color23" onclick="grabcolor('<?php echo $letter;?>','#99FFCC')"></td>
+        <td class="color24" onclick="grabcolor('<?php echo $letter;?>','#CCFF99')"></td>
+        <td class="color25" onclick="grabcolor('<?php echo $letter;?>','#CCCCCC')"></td>
+       </tr>
+      </table>
+    </div>
+       </form>
+       </div>
+<?php
+       $colorBoxLetter++;
+}
+?>
+</div>
\ No newline at end of file
diff --git a/Toolkit/Events/templates/emailOwner.tpl b/Toolkit/Events/templates/emailOwner.tpl
new file mode 100755 (executable)
index 0000000..944a364
--- /dev/null
@@ -0,0 +1,29 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+       <meta http-equiv="content-type" content="text/html;charset=utf-8">
+       <title>{title:h}</title>
+</head>
+<body>
+       <style>
+               td {line-height:13px;}
+               body {font-size: 14px;}
+       </style>
+       <h2>{subject:h}</h2>
+       <h3>Information</h3>
+       <table cellspacing="0" cellpadding="0" width="500" style="border:1px solid #ccc;border-collapse:collapse;">
+               <tbody>
+                       <tr flexy:foreach="formData,v">
+                               <td flexy:if="v[element]" style="font-weight:bold;width:200px;padding:5px;border:1px solid #ccc;" align="right">{v[label]:h}</td>
+                               {if:v[nowrap]}
+                                       <td flexy:if="v[element]" nowrap style="padding:3px;border:1px solid #ccc;">{v[element]:h}</td>
+                               {else:}
+                                       <td flexy:if="v[element]" style="padding:3px;border:1px solid #ccc;">{v[element]:h}</td>
+                               {end:}
+                       </tr>
+               </tbody>
+       </table>
+       <p>A new Event has been added to your Website from your &quot;Add Your Event&quot; page.</p>
+       <p>To approve it, please go to the <a href="{eventAdminURL}">Pending Events</a> page in your admin.</p>
+</body>
+</html>
diff --git a/Toolkit/Events/templates/eventDetail.html b/Toolkit/Events/templates/eventDetail.html
new file mode 100755 (executable)
index 0000000..667dd91
--- /dev/null
@@ -0,0 +1,58 @@
+<style type="text/css">
+<!--
+#category, .listing {
+       display: none;
+       }
+-->
+</style>
+<div id="eventDetail">
+       <div flexy:foreach="events,event">
+      {if:event[img]}
+               <h2 style="background: url({imgPathDetail}{event[img]}); height: 210px;"><p>{event[header]:h}</p></h2>
+      {else:}
+               <h2>{event[header]:h}</h2>
+      {end:}
+               <div id="eventDetailTop">
+                       <div id="eventDetailMap" flexy:if="event[hasLatLon]">
+                               <div id="eventDetail-map" flexy:if="event[lat]" title="Click to enlarge">map...</div>
+                               <div id="map-dialog" flexy:if="event[lat]"><div id="map_canvas" style="width:500px; height:400px">Loading...</div></div>
+                       </div><!-- /#eventDetailMap -->
+                       <div id="EventDrivingDirectionsContainer" flexy:if="event[lat]">
+                               <form id="EventDrivingDirectionsForm" name="EventDrivingDirectionsForm" flexy:ignore="yes" method="post" action="">
+                                       <input type="hidden" id="EventLocation" name="EventLocation" value="{event[loc]:h}">
+                                       <input type="hidden" id="EventLat" name="EventLat" value="{event[lat]}">
+                                       <input type="hidden" id="EventLon" name="EventLon" value="{event[lon]}">
+                                       <input type="submit" id="EventDrivingDirectionSubmit" name="EventDrivingDirectionSubmit" value="Get Driving Directions">
+                               </form>
+                       </div>
+                       <div id="eventDetailInfo">
+                               <div id="eventDetailTime">
+                                       <div id="eventDetailDate">{event[dates]}</div>
+                                       <div id="eventDetailHour" flexy:if="event[btime]">{event[btime]} {if:event[etime]} to {event[etime]} {end:} </div>
+                               </div><!-- /#eventDetailTime -->
+                               <div id="eventDetailPlace" flexy:if="event[hasLocation]">
+                                       <div id="eventDetailLocation" flexy:if="event[loc]">{event[loc]}</div>
+                    {if:!event[hide_address]}
+                                       <span id="eventDetailAddress" flexy:if="event[address]">{event[address]}</span>
+                                       <span id="eventDetailCity" flexy:if="event[city]">{event[city]}, {event[state]} {event[zip]}</span>
+                    {end:}
+                                       <div id="event-lat" rel="{event[lat]}" flexy:if="event[lat]"></div>
+                                       <div id="event-lon" rel="{event[lon]}" flexy:if="event[lon]"></div>
+                               </div><!-- /#eventDetailPlace -->
+                               <div id="eventCost" flexy:if="event[cost]">{event[cost]}</div>
+                               <div id="eventDetailContact" flexy:if="event[hasContactInfo]">
+                                       <h3>Contact &amp; More Info</h3>
+                                       <div class="eventurl" flexy:if="event[url]"><a href="{event[url]}">{event[url]}</a></div>
+                                       <div class="eventurl" flexy:if="event[file]">File: <a href="{uploads}{event[file]}">{event[filename]:h}</a></div>
+                                       <div class="eventcontact" flexy:if="event[contact]">Contact: {event[contact]}</div>
+                                       <div class="eventemail" flexy:if="event[email]">Email: <a href="mailto:{event[email]}">{event[email]}</a></div>
+                                       <div class="eventphone" flexy:if="event[phone]">Phone: {event[phone]}</div>
+                               </div><!-- /#eventDetailContact -->
+                       </div><!-- /#eventDetailInfo -->
+               </div><!-- /#eventDetailTop -->
+               <div id="eventDetailDesc" flexy:if="event[descr]">{event[descr]:h}</div>
+       </div>
+    {if:!events}
+       <p>No events found</p>
+    {end:}
+</div>
diff --git a/Toolkit/Events/templates/eventFeed.html b/Toolkit/Events/templates/eventFeed.html
new file mode 100644 (file)
index 0000000..59effdb
--- /dev/null
@@ -0,0 +1,22 @@
+<div>
+<h2>{event[dates]}</h2>
+<b flexy:if="event[times]">{event[times]}</b>
+</div>
+{if:event[img]}
+  <img src="{imgPathDetail:h}{event[img]}" />
+{end:}
+<div flexy:if="event[loc]"><b>Place:</b> {event[loc]}</div>
+{if:!event[hide_address]}
+<div flexy:if="event[address]">{event[address]}</div>
+<div flexy:if="event[city]">{event[city]}, {event[state]} {event[zip]}</div>
+{end:}
+<div flexy:if="event[cost]"><b>Cost:</b> {event[cost]}</div>
+<a href="{event[url]}" flexy:if="event[url]">{event[url]}</a>
+<div flexy:if="event[hasContactInfo]">
+    <b>Contact &amp; More Info</b>
+    <div flexy:if="event[contact]">Contact: {event[contact]}</div>
+    <div flexy:if="event[email]">Email:
+        <a href="mailto:{event[email]}">{event[email]}</a></div>
+    <div flexy:if="event[phone]">Phone: {event[phone]}</div>
+</div><!-- /#eventDetailContact -->
+<div flexy:if="event[descr]">{event[descr]:h}</div>
\ No newline at end of file
diff --git a/Toolkit/Events/templates/eventHomePage.html b/Toolkit/Events/templates/eventHomePage.html
new file mode 100644 (file)
index 0000000..4f4c34a
--- /dev/null
@@ -0,0 +1,89 @@
+<style>
+.slider-wrapper .nivoSlider {
+       position:relative;
+       height: 250px;
+       }
+.nivoSlider img {
+       position: absolute;
+       top: 0;
+       left: 0;
+       display: none;
+}
+.nivoSlider a {
+       border: 0;
+       display: block;
+}
+.nivo-controlNav {
+       display: none;
+       }
+.nivo-caption span {
+       float: right;
+       }
+</style>
+<div class="slider-wrapper theme-default" flexy:if="hasFeaturedEvents">
+       <div class="ribbon"></div>
+       <div id="slider" class="nivoSlider">
+{foreach:events,e}
+       {if:e[href]}
+               <a href="{e[href]:h}">
+       {end:}
+                       <img src="{e[s-img]:h}" alt="{e[header]}" title="#event-{e[id]}">
+       {if:e[href]}
+               </a>
+       {end:}
+{end:}
+       </div>
+</div>
+<div flexy:foreach="events,e" id="event-{e[id]}" class="nivo-html-caption">
+       <strong>{e[header]:h}</strong>
+       <span>{e[bdate]}</span>
+</div>
+<script type="text/javascript">
+$(window).load(function() {
+       $('#slider').nivoSlider({
+               effect: 'fade',
+               animSpeed: 1000,
+               pauseTime: 5000
+       });
+       if ( $('#slider').children('a').size() <= 1 ) {
+               $('.nivo-prevNav').css('display', 'none');
+               $('.nivo-nextNav').css('display', 'none');
+       }
+});
+</script>
+<?php
+       $index = 0;
+       if ($this->options['strict'] || (is_array($t->blocks)  || is_object($t->blocks))) {
+               foreach($t->blocks as $category => $events) {
+                       if ($index % 2 == 0) { echo '<div class="eventrow">'; } ?>
+                       <div class="event-category-block<?php
+                 if ($index % 2 != 0) {
+                     echo ' event-category-2';
+                 } echo ' topic' . $events['id']; ?>">
+                               <h2><a href="<?php echo $t->topicSearchUrl.$events['id'];?>">
+                  <?php echo htmlspecialchars($category);?></a>
+                </h2>
+                               <?php
+              if ($this->options['strict'] || (is_array($events['data'])  || is_object($events['data']))) {
+                foreach($events['data'] as $event) {?>
+                                       <div>
+                                       <b><?php echo htmlspecialchars($event['dates']);?></b>
+                    <a href="<?php echo $event['href'];?>">
+                    <?php echo htmlspecialchars($event['header']);?></a>
+                                       </div>
+                               <?php }
+                }?>
+                               <a href="<?php echo $t->topicSearchUrl.$events['id'];?>" class="event-more">All
+              <?php echo htmlspecialchars($category);?></a>
+                       </div>
+               <?php
+        if ($index % 2 != 0) {
+            echo '</div><!-- /.eventrow -->';
+        } ?>
+                       <?php
+            if ($index == count($t->blocks)-1 && ($index + 1) % 2 != 0) {
+                echo '</div><!-- /.eventrow -->';
+            } ?>
+               <?php $index++; }?>
+       <?php }?>
+
diff --git a/Toolkit/Events/templates/eventPage.html b/Toolkit/Events/templates/eventPage.html
new file mode 100644 (file)
index 0000000..01ab8f9
--- /dev/null
@@ -0,0 +1,27 @@
+<div id="event-main-column">
+       {eventContent:h}
+</div>
+<div id="event-second-column">
+{eventSearchForm:h}
+       <div id="event-shortcuts">
+               <h3>Shortcuts</h3>
+               <ul class="ulCr">
+                       <li>
+                               <a href="{todayUrl:h}">Today&#39;s Events</a>
+                       </li>
+                       <li>
+                               <a href="{tomorrowUrl:h}">Tomorrow&#39;s Events</a>
+                       </li>
+                       <li>
+                               <a href="{nextUrl:h}">Next 7 Days</a>
+                       </li>
+               </ul>
+       </div>
+       <div id="event-featured" flexy:if="hasHomeEvents">
+               <h3>Featured</h3>
+               <div flexy:foreach="events,event" style="background-image: url({event[t-img]:h})">
+                       <p>{event[bdate]}</p>
+                       <a href="{event[href]:h}">{event[header]}</a>
+               </div>
+       </div>
+</div>
diff --git a/Toolkit/Events/templates/eventSearchForm.html b/Toolkit/Events/templates/eventSearchForm.html
new file mode 100755 (executable)
index 0000000..e0c61c7
--- /dev/null
@@ -0,0 +1,14 @@
+<div id="event-search">
+       <h3>Find Events</h3>
+       <form name="event-search-form">
+               <input type="hidden" name="catid">
+               <input type="hidden" name="search" value="1">
+               <span>From:</span>
+               <div id="startMonthWrapper"><input name="startMonth" id="startMonth" size="10"></div>
+               <span>To:</span>
+               <div id="endMonthWrapper"><input name="endMonth" id="endMonth" size="10"></div>
+               <span>Type:</span>
+               <select name="category"></select>
+               <input type="image" src="baseurl/Toolkit/Events/assets/go.png" alt="Search Events">
+       </form>
+</div>
diff --git a/Toolkit/Events/templates/events.html b/Toolkit/Events/templates/events.html
new file mode 100755 (executable)
index 0000000..eb71f1a
--- /dev/null
@@ -0,0 +1,26 @@
+<style type="text/css">
+<!--
+#category, .listing {
+       display: none;
+       }
+-->
+</style>
+<div id="eventsList">
+<p class="event-search" flexy:if="search">Showing {search}</p>
+{if:events}
+  {foreach:events,event}
+    {if:event[spans]}<div class="event-list ongoing">{end:}
+    {if:event[reacur]}<div class="event-list reacur">{end:}
+    {if:event[normal]}<div class="event-list">{end:}
+    <p class="event-list-date">{event[dates]}</p>
+    <h3>
+      <a href="{event[href]}">{event[header]}</a>
+    </h3>
+    {if:event[spans]}<p class="event-list-Frequency">Ongoing Event</p>{end:}
+    {if:event[reacur]}<p class="event-list-Frequency">Repeating Event</p>{end:}
+    </div>
+  {end:}
+{else:}
+    <p>No events were found for your search. </p>
+{end:}
+</div><!-- /#eventsList -->
diff --git a/Toolkit/Events/templates/form.html b/Toolkit/Events/templates/form.html
new file mode 100644 (file)
index 0000000..34f1676
--- /dev/null
@@ -0,0 +1,104 @@
+
+    {form.javascript:h}
+    {form.outputHeader():h}
+    {form.hidden:h}
+    {hidden}
+
+    {foreach:form.sections,sec}
+    {if:sec.header}
+    <fieldset>
+            <legend>{sec.header:h}</legend>
+    {end:}
+                    <div class="row">
+        {foreach:sec.elements,elem}
+            {if:elem.style}
+              {elem.outputStyle():h}
+            {else:}
+                {if:elem.isButton()}
+                    {if:elem.notFrozen()}
+                        <div class="medium-6 small-12 columns">{elem.html:h}</div>
+                    
+                    {end:}
+                {else:}
+                    {if:elem.error}
+                        <div class="medium-6  small-12 columns error">
+                    {else:}
+                        <div class="medium-6  small-12 columns">
+                        {end:}
+                    {if:elem.isType(#textarea#)}
+                        {if:elem.required}<label class="req">{else:}<label>{end:}
+
+                            {if:elem.required}<span class="req">*</span>{end:}
+                            {elem.label:h}
+                    {else:}
+                        {if:elem.isType(#CAPTCHA_Image#)}
+                                {if:elem.required}<label>{else:}<label class="req">{end:}
+                                {if:elem.required}<span class="req">*</span>{end:}
+                            {elem.label:h}
+
+                        {else:}
+                            {if:elem.isType(#group#)}
+                                {if:element.isName(#interest#)}
+                                {else:}
+                                    {if:elem.required}<label class="req">{else:}<label>{end:}
+                                    {if:elem.required}<span class="req">*</span>{end:}
+                                    {elem.label:h}<br>
+                                {end:}
+                            {else:}
+
+                                    {if:elem.required}<label class="req">{else:}<label>{end:}
+                                    {if:elem.required}<span class="req">*</span>{end:}
+                                    {elem.label:h}
+
+                                {if:elem.isName(#interest#)}
+
+                                {else:}
+
+                                {end:}
+                            {end:}
+                        {end:}
+                    {end:}
+
+                    {if:elem.isType(#group#)}
+
+                        <div class="row collapse">
+
+                            {foreach:elem.elements,gitem}
+                            <div class="small-12 columns">
+                                {gitem.html:h}{if:gitem.required}<div class="req">*</div>{end:}
+                                {if:elem.separator}{elem.separator:h}{end:}
+                            </div>
+                            {end:}
+
+                        </div>
+
+                    {else:}
+                        {elem.html:h}
+                        {if:elem.isName(#captcha_rmv#)}
+                            <span
+                                class="tooltip"
+                                title="Verification Code|To help us distinguish between
+                                information submitted by individuals and those automatically
+                                entered by software robots, please type the letters shown.">What is this?
+                            </span>
+                        {end:}
+                    {end:}
+                                    </label>
+                            {if:elem.error}<small class="error">{elem.error}</small>{end:}
+                                </div> <!-- end for columns -->
+                {end:}
+            {end:}
+        {end:}  <!-- end for foreach:sec.element,elem -->
+                    </div> <!-- end for row -->
+
+    {if:sec.header}
+    </fieldset>
+    {end:}
+    {end:} <!-- end for foreach:form.section,sec -->
+
+
+       </form>
+       {if:form.requirednote}
+    <div>{form.requirednote:h}</div>
+    {end:}
+
diff --git a/Toolkit/FileServer/AdapterAbstract.php b/Toolkit/FileServer/AdapterAbstract.php
new file mode 100644 (file)
index 0000000..36e214a
--- /dev/null
@@ -0,0 +1,416 @@
+<?php
+
+/**
+ * Adapter class for linking to the file server
+ *
+ * Base abstract class used to connect and talk with file server
+ * can be subclassed for Files, Images, Video, Audio, etc...
+ *
+ * PHP version 5
+ *
+ * The license text...
+ *
+ * @category  Toolkit
+ * @package   FileServer
+ * @author    Jamie Kahgee <jamie@gaslightmedia.com>
+ * @copyright 2010 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com/ Gaslightmedia
+ * @version   CVS: $Id: AdapterAbstract.php,v 1.6 2010/06/04 11:33:54 jamie Exp $
+ * @link      <>
+ * @see       References to other sections (if any)...
+ */
+
+
+/**
+ * Validation Secret
+ */
+define('IS_VALIDATE_SECRET', 'Glm0IS1secreT');
+
+/**
+ * URL to file server, this is dynamic based on the server
+ * so can be setup for any sandbox
+ */
+define('IS_SUBMIT_URL', FILE_SERVER_URL . 'submit.phtml');
+
+/**
+ * Adapter class for linking to the file server
+ *
+ * @category  Toolkit
+ * @package   FileServer
+ * @author    Jamie Kahgee <jamie@gaslightmedia.com>
+ * @copyright 2010 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com/ Gaslightmedia
+ * @version   Release: @package_version@
+ * @link      <>
+ * @see       References to other sections (if any)...
+ */
+abstract class Toolkit_FileServer_AdapterAbstract
+{
+    //  {{{ properties
+
+    /**
+     * ID of owner that was setup in the file server
+     *
+     * This is defined via the file server web interface
+     * @var    string
+     * @access protected
+     */
+    protected $ownerID;
+
+    /**
+     * Owners password that was setup for them
+     *
+     * This is defined via the file server web interface
+     * @var    string
+     * @access protected
+     */
+    protected $ownerPW;
+
+    /**
+     * XML to send to the file server
+     * @var    unknown
+     * @access protected
+     */
+    protected $xml;
+
+    //  }}}
+    //  {{{ __construct()
+
+    /**
+     * Constructor
+     *
+     * @param string $owner File Server owner
+     * @param string $pword File Server owner password
+     */
+    public function __construct($owner = IS_OWNER_ID, $pword = IS_OWNER_PW)
+    {
+        $this->ownerID = $owner;
+        $this->ownerPW = $pword;
+    }
+
+    //  }}}
+
+    //  {{{ buildFileServerXML()
+
+    /**
+     * buildFileServerXML
+     *
+     * Create the xml for the FileServerRequest
+     *
+     * @param string $fName File name to upload or delete
+     * @param string $type  Upload, Delete or URL
+     *
+     * @return string xml content
+     * @access protected
+     */
+    protected function buildFileServerXML($fName, $type)
+    {
+        $xml = new DOMDocument('1.0');
+        $xml->formatOutput = true;
+
+        $fileServerRequest = $xml->createElement('FileServerRequest');
+        $fileServerRequest->setAttribute('version', '1.0');
+
+        //  access request
+        $accessRequest = $xml->createElement('AccessRequest');
+        $owner         = $xml->createElement('Owner');
+        $ownerID       = $xml->createElement('OwnerID', $this->ownerID);
+        $ownerPW       = $xml->createElement('OwnerPW', $this->ownerPW);
+
+        $owner->appendChild($ownerID);
+        $owner->appendChild($ownerPW);
+
+        $accessRequest->appendChild($owner);
+
+        $fileServerRequest->appendChild($accessRequest);
+
+        $file = $this->getFileElement($xml, $type, $fName);
+
+        $fileServerRequest->appendChild($file);
+        $validStr = md5($this->ownerID . $this->ownerPW . IS_VALIDATE_SECRET);
+
+        $validation = $xml->createElement('Validation', $validStr);
+        $fileServerRequest->appendChild($validation);
+        $xml->appendChild($fileServerRequest);
+
+        return $xml->saveXML($xml);
+    }
+
+    // }}}
+
+    //  {{{ delete()
+
+    /**
+     * Delete a file from the file server
+     *
+     * @param string $name File name
+     *
+     * @return string file name
+     * @access public
+     * @throws Toolkit_FileServer_Exception
+     */
+    public function delete($name)
+    {
+        // don't do anything if on development server
+        if (defined('DEVELOPMENT') && DEVELOPMENT == true) {
+            return true;
+        }
+        $ch = curl_init();
+
+        $fileData = array(
+            'request' => $this->buildFileServerXML($name, 'Delete')
+        );
+        $curlOptions = array(
+            CURLOPT_URL            => IS_SUBMIT_URL,
+            CURLOPT_HEADER         => 0,
+            CURLOPT_RETURNTRANSFER => 1,
+            CURLOPT_POSTFIELDS     => $fileData
+        );
+        curl_setopt_array($ch, $curlOptions);
+
+        $response = curl_exec($ch);
+        curl_close($ch);
+
+        try {
+            $xmlDoc   = new DOMDocument;
+            $response = str_replace('<?xml version="1.0"?' . '>', '', $response);
+            $xmlDoc->loadXML($response);
+            $successCode = $this->xmlPathContent(
+                $xmlDoc,
+                '/FileServerResponse/ReplyStatus/SuccessCode'
+            );
+            $message     = $this->xmlPathContent(
+                $xmlDoc,
+                '/FileServerResponse/ReplyStatus/Message'
+            );
+
+            if ($successCode != 0) {
+                throw new RangeException(
+                    "Invalid response `$response` - `$message`"
+                );
+            }
+
+            return $message;
+        } catch (RangeException $e) {
+            Toolkit_Logger::logException('Image Server', $e);
+            throw new Toolkit_FileServer_Exception(
+                'Invalid File Server Response'
+            );
+        }
+    }
+
+    //  }}}
+
+    //  {{{ getFileElement()
+
+
+    /**
+     * Gets the file element used in the XML sent to the file server
+     *
+     * @param DOMDocument $xml   DOM object used to build the xml string
+     * @param string      $type  Type of manipulation (Upload, URL, Delete)
+     * @param string      $fName File name
+     *
+     * @return DOMElement $file File xml element
+     * @access protected
+     */
+    abstract protected function getFileElement(
+        DOMDocument $xml,
+        $type,
+        $fName
+    );
+
+    //  }}}
+    //  {{{ getErrorMessage()
+
+    /**
+     * Gets an appropriate error message for file upload errors
+     *
+     * @param integer Error code
+     *
+     * @return string Error message
+     * @access protected
+     */
+    protected function getErrorMessage($code)
+    {
+        switch ($code) {
+        case UPLOAD_ERR_INI_SIZE :
+            $message = 'The Uploaded file exceeds the upload max filesize directive in php.ini';
+            break;
+
+        case UPLOAD_ERR_FORM_SIZE :
+            $message = 'The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form';
+            break;
+
+        case UPLOAD_ERR_PARTIAL :
+            $message = 'The uploaded file was only partially uploaded';
+            break;
+
+        case UPLOAD_ERR_NO_FILE :
+            $message = 'No file was uploaded';
+            break;
+
+        case UPLOAD_ERR_NO_TMP_DIR :
+            $message = 'Missing a temporary folder';
+            break;
+
+        case UPLOAD_ERR_CANT_WRITE :
+            $message = 'Failed to write file to disk';
+            break;
+
+        case UPLOAD_ERR_EXTENSION :
+            $message = 'A PHP extension stopped the file upload';
+            break;
+
+        default :
+            $message = 'Unknown error uploading file';
+            break;
+        }
+
+        return $message;
+    }
+
+    //  }}}
+
+    //  {{{ upload()
+
+    /**
+     * Upload a file to the File Server
+     *
+     * @param string $name $_FILES index key that holds the file data
+     *
+     * @return array Uploaded file data
+     * @access public
+     * @throws Toolkit_FileServer_Exception
+     */
+    public function upload($name)
+    {
+        $fileType = null;
+        if (preg_match('/^http/', $name)) {
+            $fileType = 'URL';
+        } elseif (isset($_FILES[$name]) && is_array($_FILES[$name])) {
+            $fileType = 'Upload';
+        }
+
+        if ($_FILES[$name]['error'] != 0) {
+            $message = $this->getErrorMessage($_FILES[$name]['error']);
+            throw new Toolkit_FileServer_Exception(
+                $message,
+                $_FILES[$name]['error']
+            );
+        }
+
+        if (is_null($fileType)) {
+            return false;
+        } else {
+            $request = $this->buildFileServerXML($name, $fileType);
+            $ch = curl_init();
+            if ($fileType == 'URL') {
+                $fileData = array(
+                    'request' => $request
+                );
+            } else {
+                if (version_compare(PHP_VERSION, '5.5.0', '>=')) {
+                    $fileData = array(
+                        'request' => $request,
+                        'file_upload'    => new CurlFile(
+                            $_FILES[$name]['tmp_name'],
+                            $_FILES[$name]['type'],
+                            $_FILES[$name]['name']),
+                        'file_upload_name' => $_FILES[$name]['name']
+                    );
+                } else {
+                    $fileData = array(
+                        'request'          => $request,
+                        'file_upload'      => "@{$_FILES[$name]['tmp_name']}",
+                        'file_upload_name' => $_FILES[$name]['name']
+                    );
+                }
+            }
+
+            $curlOptions = array(
+                CURLOPT_URL            => IS_SUBMIT_URL,
+                CURLOPT_HEADER         => 0,
+                CURLOPT_RETURNTRANSFER => 1,
+                CURLOPT_POST           => 1,
+                CURLOPT_POSTFIELDS     => $fileData
+            );
+            curl_setopt_array($ch, $curlOptions);
+
+            $response = curl_exec($ch);
+            curl_close($ch);
+
+            try {
+                $xmlDoc = new DOMDocument();
+                $response = str_replace('<?xml version="1.0"?' . '>', '', $response);
+                $xmlDoc->loadXML($response);
+                $successCode = $this->xmlPathContent(
+                    $xmlDoc,
+                    '/FileServerResponse/ReplyStatus/SuccessCode'
+                );
+                $message     = $this->xmlPathContent(
+                    $xmlDoc,
+                    '/FileServerResponse/ReplyStatus/Message'
+                );
+                $storedName  = $this->xmlPathContent(
+                    $xmlDoc,
+                    '/FileServerResponse/File/StoredName'
+                );
+                $storedSize  = $this->xmlPathContent(
+                    $xmlDoc,
+                    '/FileServerResponse/File/StoredSize'
+                );
+                $mediaType   = $this->xmlPathContent(
+                    $xmlDoc,
+                    '/FileServerResponse/File/MediaType'
+                );
+
+                if ($successCode != 0) {
+                    throw new RangeException(
+                        "Invalid response `$response` - `$message`"
+                    );
+                }
+
+                $extension = end(explode('.', $storedName));
+                return array(
+                    'name' => $storedName,
+                    'size' => $storedSize,
+                    'type' => $mediaType,
+                    'extension' => $extension,
+                );
+            } catch (RangeException $e) {
+                Toolkit_Logger::logException('Image Server', $e);
+                throw new Toolkit_FileServer_Exception(
+                    'Invalid File Server Response'
+                );
+            }
+        }
+    }
+
+    //  }}}
+
+    //  {{{ xmlPathContent()
+
+    /**
+     * Extract a XML node value
+     *
+     * @param DOMDocument $dom     DOM object holding the XML
+     * @param string      $content Node name to extract content from
+     *
+     * @return mixed Node value on success, false if can't find the value
+     * @access protected
+     */
+    protected function xmlPathContent(DOMDocument $dom, $content)
+    {
+        $xPath = new DOMXPath($dom);
+        $nodeList = $xPath->query($content);
+        if ($nodeList->length > 0) {
+            $node = $nodeList->item(0);
+            return $node->nodeValue;
+        }
+
+        return false;
+    }
+
+    //  }}}
+}
diff --git a/Toolkit/FileServer/Exception.php b/Toolkit/FileServer/Exception.php
new file mode 100644 (file)
index 0000000..8739c0f
--- /dev/null
@@ -0,0 +1,5 @@
+<?php
+class Toolkit_FileServer_Exception extends RuntimeException
+{
+}
+?>
diff --git a/Toolkit/FileServer/FileAdapter.php b/Toolkit/FileServer/FileAdapter.php
new file mode 100644 (file)
index 0000000..d59601d
--- /dev/null
@@ -0,0 +1,101 @@
+<?php
+
+/**
+ * File Adapter
+ *
+ * Link to the file server and allow file storage and deletion
+ *
+ * PHP version 5
+ *
+ * @category  Toolkit
+ * @package   FileServer
+ * @author    Jamie Kahgee <jamie@gaslightmedia.com>
+ * @copyright 2010 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com/ Gaslightmedia
+ * @version   CVS: $Id: FileAdapter.php,v 1.4 2010/05/25 14:02:28 jamie Exp $
+ * @link      <>
+ * @see       References to other sections (if any)...
+ */
+
+/**
+ * File Adapter
+ *
+ * Used for linking to the file server for file uploading, storage and deletion
+ *
+ * @category  Toolkit
+ * @package   FileServer
+ * @author    Jamie Kahgee <jamie@gaslightmedia.com>
+ * @copyright 2010 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com/ Gaslightmedia
+ * @version   Release: @package_version@
+ * @link      <>
+ * @see       References to other sections (if any)...
+ */
+class Toolkit_FileServer_FileAdapter extends Toolkit_FileServer_AdapterAbstract
+{
+    //  {{{ getFileElement()
+
+    /**
+     * Creates the file element for the XML string
+     *
+     * @param DOMDocument $xml   XML object used to build the string
+     * @param string      $type  Type of action to take on the file (Upload, Delete)
+     * @param string      $fName file name to delete
+     *
+     * @return DOMElement $file File xml element
+     * @access protected
+     * @throws RuntimeException
+     */
+    protected function getFileElement(DOMDocument $xml, $type, $fName)
+    {
+        $file = $xml->createElement('File');
+
+        switch ($type) {
+        case 'Upload' :
+            $action    = $xml->createElement('FileAction', 'Submit');
+            $delivery  = $xml->createElement('DeliveryMethod', 'Submit');
+            $fieldName = $xml->createElement('FieldName', 'file_upload');
+
+            $fileName = filter_var($_FILES[$fName]['name'], FILTER_SANITIZE_STRING);
+            $nameParts = explode('.', $fileName);
+            $extension = end($nameParts);
+            $fieldName = $xml->createElement('Extension', end($nameParts));
+
+            $file->appendChild($action);
+            $file->appendChild($delivery);
+            $file->appendChild($fieldName);
+            break;
+
+        case 'URL':
+            $action   = $xml->createElement('FileAction', 'Submit');
+            $delivery = $xml->createElement('DeliveryMethod', 'URL');
+            $src      = $xml->createElement('Src', $fName);
+
+            $nameParts = explode('.', $fName);
+            $extension = end($nameParts);
+            $fieldName = $xml->createElement('Extension', end($nameParts));
+
+            $file->appendChild($action);
+            $file->appendChild($delivery);
+            $file->appendChild($src);
+            $file->appendChild($fieldName);
+            break;
+
+        case 'Delete' :
+            $action = $xml->createElement('FileAction', 'Delete');
+            $file->appendChild($action);
+
+            $fileName = $xml->createElement('FileName', $fName);
+            $file->appendChild($fileName);
+            break;
+
+        default :
+            throw new RuntimeException("Invalid XML type - `$type`.");
+            break;
+        }
+
+        return $file;
+    }
+
+    //  }}}
+}
diff --git a/Toolkit/FileServer/ImageAdapter.php b/Toolkit/FileServer/ImageAdapter.php
new file mode 100644 (file)
index 0000000..6db761e
--- /dev/null
@@ -0,0 +1,259 @@
+<?php
+
+/**
+ * Image Adapter
+ *
+ * Link to the file server and allow image storage and deletion
+ *
+ * PHP version 5
+ *
+ * @category  Toolkit
+ * @package   FileServer
+ * @author    Jamie Kahgee <jamie@gaslightmedia.com>
+ * @copyright 2010 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com/ Gaslightmedia
+ * @version   CVS: $Id: ImageAdapter.php,v 1.5 2010/06/04 11:34:31 jamie Exp $
+ * @link      <>
+ * @see       References to other sections (if any)...
+ */
+
+/**
+ * Image Adapter
+ *
+ * Used for linking to the file server for image uploading, storage and deletion
+ *
+ * @category  Toolkit
+ * @package   FileServer
+ * @author    Jamie Kahgee <jamie@gaslightmedia.com>
+ * @copyright 2010 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com/ Gaslightmedia
+ * @version   Release: @package_version@
+ * @link      <>
+ * @see       References to other sections (if any)...
+ */
+class Toolkit_FileServer_ImageAdapter extends Toolkit_FileServer_AdapterAbstract
+{
+    //  {{{ getAllowedMimeTypes()
+
+    /**
+     * Get the mime types that are allowed to be uploaded
+     *
+     * @return array allowed mime types
+     */
+    public function getAllowedMimeTypes()
+    {
+        return array(
+            'image/jpe',
+            'image/jpeg',
+            'image/jpg',
+            'image/jfif',
+            'image/pjpeg',
+            'image/pjp',
+            'image/gif',
+            'image/gif',
+            'image/png',
+        );
+    }
+
+    //  }}}
+    //  {{{ getFileElement()
+
+    /**
+     * Creates the file element for the XML string
+     *
+     * @param DOMDocument $xml   XML object used to build the string
+     * @param string      $type  Type of action to take on the file (Upload, Delete)
+     * @param string      $fName file name to delete
+     *
+     * @return DOMElement $file File xml element
+     * @access protected
+     * @throws RuntimeException
+     */
+    protected function getFileElement(DOMDocument $xml, $type, $fName)
+    {
+        $file = $xml->createElement('File');
+
+        switch ($type) {
+        case 'Upload' :
+            $action    = $xml->createElement('FileAction', 'Submit');
+            $delivery  = $xml->createElement('DeliveryMethod', 'Submit');
+            $fieldName = $xml->createElement('FieldName', 'file_upload');
+
+            $file->appendChild($action);
+            $file->appendChild($delivery);
+            $file->appendChild($fieldName);
+            break;
+
+        case 'URL':
+            $action   = $xml->createElement('FileAction', 'Submit');
+            $delivery = $xml->createElement('DeliveryMethod', 'URL');
+            $src      = $xml->createElement('Src', $fName);
+
+            $file->appendChild($action);
+            $file->appendChild($delivery);
+            $file->appendChild($src);
+            break;
+
+        case 'Delete':
+            $action = $xml->createElement('FileAction', 'Delete');
+            $file->appendChild($action);
+
+            $fileName = $xml->createElement('FileName', $fName);
+            $file->appendChild($fileName);
+            break;
+
+        default :
+            throw new RuntimeException("Invalid XML type - `$type`.");
+            break;
+        }
+
+        return $file;
+    }
+
+    //  }}}
+    //  {{{ getImageSize()
+
+    /**
+     * getImageSize
+     *
+     * Return image data on an image from image server
+     *
+     * @param string $image Full URI to image
+     *                      http://is0/userId/imageStyle/imageName
+     *                      Must be a full URI including an authority.
+     *                      No relative URIs, the // are mandatory
+     *
+     * @return array Image data 0 => width, 1 => height, 2 => html
+     * @access public
+     * @throws Exception
+     */
+    public function getImageSize($image)
+    {
+        $options = array('allowed_schemes' => array('http','https'));
+        if (!Validate::uri($image, $options)) {
+            throw new Exception('Invalid URI for the image');
+        }
+
+        $ch          = curl_init();
+        $curlOptions = array(
+            CURLOPT_URL            => "{$image}/info",
+            CURLOPT_HEADER         => 0,
+            CURLOPT_RETURNTRANSFER => 1,
+            CURLOPT_POSTFIELDS     => $fileData
+        );
+        curl_setopt_array($ch, $curlOptions);
+
+        $response = curl_exec($ch);
+        curl_close($ch);
+
+        if (!@simplexml_load_string($response)) {
+            $logger = Toolkit_Logger::getLogger();
+            $logger->warning("$response - $image");
+        }
+
+        $wPattern = '/<width>(.*)<\/width>/';
+        preg_match($wPattern, $response, $matches);
+        $width    = $matches[1];
+        $hPattern = '/<height>(.*)<\/height>/';
+        preg_match($hPattern, $response, $matches);
+        $height = $matches[1];
+        $html   = "width=\"{$width}\" height=\"{$height}\"";
+        return array($width, $height, $html);
+    }
+
+    //  }}}
+    //  {{{ uploadImageFile()
+
+    /**
+     * Upload a file to the File Server
+     *
+     * @param string $fname full path to image file
+     *
+     * @return array Uploaded file data
+     * @access public
+     * @throws Toolkit_FileServer_Exception
+     */
+    public function uploadImageFile($fname)
+    {
+        if (!file_exists($fname)) {
+            throw new RuntimeException("could not find file `$fname`");
+        }
+
+        $request = $this->buildFileServerXML(basename($fname), 'Upload');
+
+        $ch = curl_init();
+        if (version_compare(PHP_VERSION, '5.5.0', '>=')) {
+            $fileData = array(
+                'request'          => $request,
+                'file_upload'      => new CurlFile(
+                    $_FILES[$fname]['tmp_name'],
+                    $_FILES[$fname]['type'],
+                    $_FILES[$fname]['name']),
+                'file_upload_name' => basename($fname)
+            );
+        } else {
+            $fileData = array(
+                'request'          => $request,
+                'file_upload'      => "@$fname",
+                'file_upload_name' => basename($fname)
+            );
+        }
+
+        $curlOptions = array(
+            CURLOPT_URL            => IS_SUBMIT_URL,
+            CURLOPT_HEADER         => 0,
+            CURLOPT_RETURNTRANSFER => 1,
+            CURLOPT_POSTFIELDS     => $fileData
+        );
+        curl_setopt_array($ch, $curlOptions);
+
+        $response = curl_exec($ch);
+        curl_close($ch);
+
+        try {
+            $xmlDoc = new DOMDocument();
+            $response = str_replace('<?xml version="1.0"?'. '>', '', $response);
+            $xmlDoc->loadXML($response);
+            $successCode = $this->xmlPathContent(
+                $xmlDoc,
+                '/FileServerResponse/ReplyStatus/SuccessCode'
+            );
+            $message     = $this->xmlPathContent(
+                $xmlDoc,
+                '/FileServerResponse/ReplyStatus/Message'
+            );
+            $storedName  = $this->xmlPathContent(
+                $xmlDoc,
+                '/FileServerResponse/File/StoredName'
+            );
+            $storedSize  = $this->xmlPathContent(
+                $xmlDoc,
+                '/FileServerResponse/File/StoredSize'
+            );
+            $mediaType   = $this->xmlPathContent(
+                $xmlDoc,
+                '/FileServerResponse/File/MediaType'
+            );
+
+            if ($successCode != 0) {
+                throw new RangeException(
+                    "Invalid response `$response` - `$message`"
+                );
+            }
+
+            return array(
+                'name' => $storedName,
+                'size' => $storedSize,
+                'type' => $mediaType,
+            );
+        } catch (RangeException $e) {
+            Toolkit_Logger::logException('Image Server', $e);
+            throw new Toolkit_FileServer_Exception(
+                'Invalid File Server Response'
+            );
+        }
+    }
+
+    //  }}}
+}
+?>
diff --git a/Toolkit/FileServer/Mock/ImageAdapter.php b/Toolkit/FileServer/Mock/ImageAdapter.php
new file mode 100644 (file)
index 0000000..ed9307d
--- /dev/null
@@ -0,0 +1,125 @@
+<?php
+
+/**
+ * Mock Image Adapter
+ *
+ * Mocks uploading and deleting images from the image server
+ * This script prevents unneeded resources from being uploaded
+ * or deleted from the file server
+ *
+ * PHP version 5
+ *
+ * @category  FileServer
+ * @package   Toolkit_FileServer
+ * @author    Jamie Kahgee <jamie@gaslightmedia.com>
+ * @copyright 2010 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com/ Gaslightmedia
+ * @version   CVS: $Id: ImageAdapter.php,v 1.1 2010/07/29 16:30:02 jamie Exp $
+ * @link      <>
+ * @see       References to other sections (if any)...
+ */
+
+/**
+ * Mock Image Adapter
+ *
+ * Mocks uploading and deleting images from the image server
+ * This script prevents unneeded resources from being uploaded
+ * or deleted from the file server
+ *
+ * @category  FileServer
+ * @package   Toolkit_FileServer
+ * @author    Jamie Kahgee <jamie@gaslightmedia.com>
+ * @copyright 2010 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com/ Gaslightmedia
+ * @version   Release: @package_version@
+ * @link      <>
+ * @see       References to other sections (if any)...
+ */
+class Toolkit_FileServer_Mock_ImageAdapter
+       extends Toolkit_FileServer_ImageAdapter
+{
+    //  {{{ getImageSize()
+
+    /**
+     * getImageSize
+     *
+     * Return image data on an image from image server
+     *
+     * @param string $image Full URI to image
+     *                      http://is0/userId/imageStyle/imageName
+     *                      Must be a full URI including an authority.
+     *                      No relative URIs, the // are mandatory
+     *
+     * @return array Image data 0 => width, 1 => height, 2 => html
+     * @access public
+     * @throws Exception
+     */
+    public function getImageSize($image)
+    {
+               $width = $height = 1;
+        $html   = "width=\"{$width}\" height=\"{$height}\"";
+        return array($width, $height, $html);
+    }
+
+    //  }}}
+       //      {{{     uploadImageFile()
+
+    /**
+     * Upload a file to the File Server
+     *
+     * @param string $fname full path to image file
+     *
+     * @return array Uploaded file data
+     * @access public
+     * @throws Toolkit_FileServer_Exception
+     */
+    public function uploadImageFile($fname)
+    {
+               return array(
+                       'name' => 'is00-mock-image.jpg',
+                       'size' => 100,
+                       'type' => 'image/jpeg',
+               );
+    }
+
+       //      }}}
+       //      {{{     upload()
+
+    /**
+     * Upload a file to the File Server
+     *
+     * @param string $name $_FILES index key that holds the file data
+     *
+     * @return array Uploaded file data
+     * @access public
+     * @throws Toolkit_FileServer_Exception
+     */
+    public function upload($name)
+    {
+               return array(
+                       'name' => 'is00-mock-image.jpg',
+                       'size' => 100,
+                       'type' => 'image/jpeg',
+               );
+    }
+
+       //      }}}
+       //      {{{     delete()
+
+    /**
+     * Delete a file from the file server
+     *
+     * @param string $name File name
+     *
+     * @return string file name
+     * @access public
+        * @throws Toolkit_FileServer_Exception
+     */
+    public function delete($name)
+    {
+               return true;
+    }
+
+       //      }}}
+}
+?>
diff --git a/Toolkit/FlexyDataGridBuilder.php b/Toolkit/FlexyDataGridBuilder.php
new file mode 100644 (file)
index 0000000..8ee6d1a
--- /dev/null
@@ -0,0 +1,223 @@
+<?php
+//    vim:set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker syntax=php:
+
+/**
+ * Create Flexy templating Datagrids for displaying data
+ *
+ * PHP version 5
+ *
+ * @category Toolkit
+ * @package  FlexyDataGridBuilder
+ * @author   Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @license     http://www.gaslightmedia.com Gaslightmedia
+ * @version     CVS: $Id: FlexyDataGridBuilder.php,v 1.14 2010/07/25 17:08:39 jamie Exp $
+ * @link     <>
+ */
+
+/**
+ * Create Flexy templating Datagrids for displaying data
+ *
+ * This abstract class handles all the base functionality of creating
+ * handeling all the details associated w/ a Flexy template datagrid
+ * 1. Creation
+ * 2. Sorting (via column headers or sortform)
+ * 3. Pagenation
+ *
+ * @category  Toolkit
+ * @package      FlexyDataGridBuilder
+ * @author      Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2008 Gaslight Media
+ * @license   http://www.gaslightmedia.com/Gaslightmedia Gaslightmedia
+ * @link      http://pear.php.net/package/Structures_DataGrid/docs
+ * @see          Structures_DataGrid_Renderer_Flexy, HTML_Template_Flexy
+ */
+abstract class Toolkit_FlexyDataGridBuilder extends Toolkit_DataGridBuilder
+{
+    //    {{{ properties
+
+    /**
+     * Name of the table which we will be dealing with
+     *
+     * @var string
+     * @access protected
+     */
+    protected $tableName;
+
+    /**
+     * The name of the template that renders this datagrid
+     *
+     * @var string
+     * @access protected
+     */
+    protected $template;
+
+    /**
+     * Output an object as $t to the template
+     *
+     * converts array keys to objects usable in a template form.
+     *
+     * <code>
+     *   $this->ctrlObj['url'] = MEDIA_BASE_URL;
+     * </code>
+     *
+     * <example>
+     *   <img alt="{row[img]} src="{url}images/member_thumb/{row[img]}">
+     * </example>
+     *
+     * @var array
+     * @access protected
+     */
+    protected $ctrlObj = array();
+
+    /**
+     * Rendering options for DataGrid
+     *
+     * @var    array
+     * @access protected
+     */
+    protected $rendererOptions = array(
+        'sortIconASC' => '&uArr;',
+        'sortIconDESC'    => '&dArr;',
+        'sortingResetsPaging' => false,
+    );
+
+    /**
+     * The HTML id of the DataGrid
+     *
+     * @var    string
+     * @access protected
+     */
+    protected $gridId = 'dataGrid';
+
+    /**
+     * The HTML class of the DataGrid
+     *
+     * @var    string
+     * @access protected
+     */
+    protected $gridClass = 'dataGrid';
+
+    //    }}}
+    //    {{{ __construct()
+
+    /**
+     * Constructor
+     *
+     * Builds the DataGrid class.
+     *
+     * @param PDO     $pdo          PDO object used in the datagrid
+     * @param integer $limit        The number of records to display per page.
+     * @param integer $page         The current page view. In most cases,
+     *                                this is useless. Note: if you specify
+     *                                this, the "page"GET variable will be ignored.
+     * @param string  $rendererType The type of renderer to use. You may
+     *                                prefer to use the $type argument of
+     *                                render, fill or getOutput.
+     *
+     * @return void
+     * @access public
+     */
+    public function __construct(
+        PDO $pdo,
+        $limit = null,
+        $page = null,
+        $rendererType = null
+    ) {
+        $this->dbh = $pdo;
+        if (ctype_digit($_GET['setPerPage'])) {
+            $limit = $_GET['setPerPage'];
+        }
+
+        parent::__construct($pdo, $limit, $page, $rendererType);
+
+        //    If all records show on one page, then hide sort form.
+        if (!is_null($limit)) {
+            $this->sortableAfter = $limit;
+        }
+    }
+
+    //    }}}
+
+    //    {{{ show()
+
+    /**
+     * Displays the DataGrid results
+     *
+     * @param Structures_DataGrid_Renderer_Flexy $renderer Rendering engine used to render DG
+     *
+     * @return void
+     * @access public
+     */
+    public function show(Structures_DataGrid_Renderer_Flexy $renderer)
+    {
+        echo $this->toHtml($renderer);
+    }
+
+    //    }}}
+
+    //    {{{ toHtml()
+
+    /**
+     * Returns the DataGrid
+     *
+     * @param Structures_DataGrid_Renderer_Flexy $renderer Rendering engine used to render DG
+     * @param string                             $template Template to use
+     *
+     * @return string html templated datagrid results
+     * @access public
+     */
+    public function toHtml(
+        Structures_DataGrid_Renderer_Flexy $renderer,
+        $template = null
+    ) {
+        $this->configureColumns();
+
+        try {
+            $bind = $this->bind($this->sql, $this->options, 'PDO');
+        } catch (PDOException $e) {
+            return Toolkit_Common::handleError($e);
+        }
+
+        if (PEAR::isError($bind)) {
+            return Toolkit_Common::handleError($bind);
+        }
+
+        if (method_exists($this, 'setControlObject')) {
+            $this->setControlObject();
+            foreach ($this->ctrlObj as $k => $v) {
+                $renderer->$k = $v;
+            }
+        }
+
+        $renderer->setOptions($this->rendererOptions);
+        $renderer->setOptions(array('pagerOptions' => $this->pagerOptions));
+
+        $this->attachRenderer($renderer);
+        $tEngine = $renderer->getContainer();
+
+        $pathToTemplate = $tEngine->options['templateDir'][0];
+
+        if (   !is_null($template)
+            && file_exists("$pathToTemplate/$template")
+        ) {
+            $tEngine->compile($template);
+        } elseif (isset($this->template)
+                  && file_exists($pathToTemplate . $this->template)
+        ) {
+            $tEngine->compile($this->template);
+        } else {
+            throw new RuntimeException('Template not available');
+        }
+
+        //    Get the entire datagrid body.
+        $gridBody = $this->getOutput();
+        if (PEAR::isError($gridBody)) {
+            return Tolkit_Common::handleError($gridBody);
+        }
+
+        return $gridSorter . $gridBody;
+    }
+
+    //    }}}
+}
+?>
diff --git a/Toolkit/Form.php b/Toolkit/Form.php
new file mode 100644 (file)
index 0000000..ab3d79e
--- /dev/null
@@ -0,0 +1,143 @@
+<?php
+//  vim:set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker syntax=php:
+
+/**
+ * Define how form objects should be built
+ *
+ * PHP version 5
+ *
+ * @category  Toolkit
+ * @package   Form
+ * @author    Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @version   CVS: $Id: Form.php,v 1.6 2009/08/31 17:43:48 jamie Exp $
+ * @link      http://demo.gaslightmedia.com/
+ */
+
+/**
+ * Minimum form object method definitions
+ *
+ * Each form must at least define some elements and setup the rules for
+ * that form.  They must also create a method which handless extracting
+ * the form object into an html string suitable to be displayed onto a
+ * page.
+ *
+ * @category  Toolkit
+ * @package   Form
+ * @author    Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @link      http://demo.gaslightmedia.com/
+ */
+interface Toolkit_Form
+{
+    //  {{{ configureElements()
+
+    /**
+     * Defines all elements to be used in a form
+     *
+     * Creates an array of elements and passes them off to the setupElements
+     * function wich adds each element to the form object.
+     *
+     * <code>
+     * public function configureElements()
+     * {
+     *   $e = array();
+     *
+     *   $e[] = array(
+     *     'type'    => 'text',
+     *     'req'     => false
+     *     'name'    => 'text_field',
+     *     'display' => 'Text Label',
+     *   );
+     *   $e[] = array(
+     *     'type'    => 'checkbox',
+     *     'req'     => false
+     *     'name'    => 'checkbox_field',
+     *     'display' => 'Checkbox Label',
+     *   );
+     *
+     *   // ... More Element Definitions
+     *
+     *   $this->setupElements($e);
+     * }
+     * </code>
+     *
+     * @access public
+     * @return void
+     */
+    //public function configureElements();
+
+    //  }}}
+    //  {{{ configureForm()
+
+    /**
+     * Calls all the configuration methods to configure a form for use
+     *
+     * @access public
+     * @return void
+     */
+    //public function configureForm();
+
+    //  }}}
+    //  {{{ configureRules()
+
+    /**
+     * Defines all element rules to be used for validation in the form
+     *
+     * At the bare minimum, this function needs to be called to setup the
+     * form rules, even if no extra rules are defined.  Because it still
+     * creates all the required rules that are defined w/ each element.
+     *
+     * <code>
+     * public function configureRules()
+     * {
+     *   $r = array();
+     *
+     *   $r[] = array(
+     *     'element'    => 'text_field',
+     *     'message'    => 'ERROR: 10 characters max!',
+     *     'type'       => 'maxlength',
+     *     'format'     => 10,
+     *     'validation' => $this->validationType,
+     *     'reset'      => true,
+     *     'force'      => false,
+     *   );
+     *   $r[] = array(
+     *     'element'    => 'text_field',
+     *     'message'    => 'ERROR: Numric characters only!',
+     *     'type'       => 'numeric',
+     *     'format'     => null,
+     *     'validation' => $this->validationType,
+     *     'reset'      => true,
+     *     'force'      => false,
+     *   );
+     *
+     *   // ... More Rule Definitions
+     *
+     *   $this->setupRules($r);
+     * }
+     * </code>
+     *
+     * @access public
+     * @return void
+     */
+    public function configureRules();
+
+    //  }}}
+    //  {{{ toHtml()
+
+    /**
+     * Get an html string that contains the form
+     *
+     * Check if the form needs to be validated (ie. it was submitted)
+     * Check if submitted data needs to be processed
+     *
+     * @access public
+     * @return string an html string that contains the entire form
+     */
+    public function toHtml();
+
+    //  }}}
+}
diff --git a/Toolkit/FormBuilder.php b/Toolkit/FormBuilder.php
new file mode 100644 (file)
index 0000000..4eb5592
--- /dev/null
@@ -0,0 +1,1264 @@
+<?php
+//    vim:set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker syntax=php:
+
+/**
+ * File Doc Comment
+ *
+ * PHP version 5
+ *
+ * @category Toolkit
+ * @package  FormBuilder
+ * @author   Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @license     http://www.gaslightmedia.com Gaslightmedia
+ * @version  CVS: $Id: FormBuilder.php,v 1.48 2010/08/05 14:19:47 jamie Exp $
+ * @link     http://demo.gaslightmedia.com
+ */
+
+/**
+ * Allow the inclusion of the Element Grid plugin for QuickForms
+ */
+require_once 'HTML/QuickForm/ElementGrid.php';
+
+/**
+ * Handle QuickForm CAPTCHA's
+ */
+require_once 'HTML/QuickForm/CAPTCHA/Image.php';
+
+/**
+ * Require PEAR QuickForm class
+ */
+require_once 'HTML/QuickForm.php';
+
+require_once 'HTML/QuickForm/Rule/Zip.php';
+require_once 'HTML/QuickForm/Rule/Phone.php';
+require_once 'HTML/QuickForm/Rule/Banwords.php';
+require_once 'HTML/QuickForm/Rule/State.php';
+
+/**
+ * Base functionality for creating HTML_Quickforms
+ *
+ * @category  Toolkit
+ * @package      FormBuilder
+ * @author    Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2008 Gaslight Media
+ * @license      http://www.gaslightmedia.com Gaslightmedia
+ * @link      http://demo.gaslightmedia.com
+ */
+abstract class Toolkit_FormBuilder extends HTML_QuickForm
+{
+    //    {{{    properties
+
+    /**
+     * Holds if the form has been validated
+     *
+     * once the form is validated, store the result of the validation
+     * in this variable.  That way, if multiple validations are attempted
+     * we don't have to reprocess all the data
+     *
+     * @var boolean
+     * @access protected
+     */
+    protected $validated = false;
+
+    /**
+     * Global form captcha question element
+     *
+     * @var object
+     * @access protected
+     */
+    protected $captchaQuestion;
+
+    /**
+     * Global form captcha answer from user
+     *
+     * @var object
+     * @access protected
+     */
+    protected $captchaAnswer;
+
+    /**
+     * Form submitted data used when emailing form results
+     *
+     * @var array
+     * @access protected
+     */
+    protected $formData = array();
+
+    /**
+     * Automatically create validation rules for any date elements
+     *
+     * This will only work if the date element is in m-d-Y format
+     *
+     * @var boolean
+     * @access protected
+     */
+    protected $autoValidateDateElements = true;
+
+    /**
+     * How do we want to validate the form
+     *
+     * Possible options are [server, client]
+     *
+     * @var string
+     * @access protected
+     */
+    protected $validationType = 'server';
+
+    /**
+     * Some special forms dont utlize this stylesheet
+     * Allow classes to override this setting so it doesn't
+     * get included
+     *
+     * @var boolean
+     * @access protected
+     */
+    protected $includeContactStyleSheet = true;
+
+    /**
+     * What do you want the error msg to be if the form doesn't validate
+     *
+     * @var string
+     * @access protected
+     */
+    protected $errorMsg
+        = '<div id="form-warning-top">
+                Warning: The form was not sent, please review the errors below.
+           </div>';
+
+    /**
+     * What do you want the success msg to be if the form validates successfully
+     *
+     * @var string
+     * @access protected
+     */
+    protected $successMsg
+        = '<div id="form-success-top">
+                The information below has been successfully submitted.
+           </div>';
+
+    /**
+     * Whether uploaded files should be processed too (if present)
+     *
+     * @var string
+     * @access protected
+     */
+    protected $mergeFiles = true;
+
+    /**
+     * Include a captcha on the form or not
+     *
+     * @var    boolean
+     * @access protected
+     */
+    protected $useCaptcha;
+
+    /**
+     * The default templates to inject into the form renderer
+     *
+     * @var string
+     * @access protected
+     */
+    protected $template;
+
+    /**
+     * The default rules to register for validating
+     *
+     * We have to register these rules, or any others we want, before
+     * we are able to use them in our forms.
+     *
+     * These rules can be removed in subclasses before the rules are configured
+     * if you want to omit any of them from validating input - just remember
+     * to not call them in your configured rules!
+     *
+     * Phone: validates input against US and CA style phone #'s
+     * <code>
+     * $rules[] = array('element'    => 'phone',
+     *                  'message'    => 'ERROR: Invalid Phone Format!',
+     *                  'type'       => 'phone',
+     *                  'format'     => null,
+     *                  'validation' => $this->validationType,
+     *                  'reset'      => true,
+     *                  'force'      => false);
+     * </code>
+     *
+     * Zip: Validates input against US and CA zip codes, if DB check is
+     *      set to true, validate zip codes against all the zip codes in the
+     *      DB.
+     * <code>
+     * $rules[] = array('element'    => 'zip',
+     *                  'message'    => 'ERROR: Invalid Zip!',
+     *                  'type'       => 'zip',
+     *                  'format'     => array('requireDBCheck' => true),
+     *                  'validation' => $this->validationType,
+     *                  'reset'      => true,
+     *                  'force'      => false);
+     * </code>
+     *
+     * Banwords: Make sure each each doesn't contain a banned word. Checking
+     *           against a DB of banned words.
+     *
+     * State: Validate input against US and CA region / province codes.  If DB
+     *        check is set to true, validate region / province against all the
+     *        regions / provinces in the DB.
+     * <code>
+     * $rules[] = array('element'    => 'state_id',
+     *                  'message'    => 'ERROR: Invalid State / Province!',
+     *                  'type'       => 'state',
+     *                  'format'     => array('requireDBCheck' => true),
+     *                  'validation' => $this->validationType,
+     *                  'reset'      => true,
+     *                  'force'      => false);
+     * </code>
+     *
+     * @var array
+     * @access protected
+     * @see app.gaslightmedia.com/glmPEAR/HTML/QuickForm/Rule/Zip.php
+     * @see app.gaslightmedia.com/glmPEAR/HTML/QuickForm/Rule/Phone.php
+     * @see app.gaslightmedia.com/glmPEAR/HTML/QuickForm/Rule/Banwords.php
+     * @see app.gaslightmedia.com/glmPEAR/HTML/QuickForm/Rule/State.php
+     */
+    protected $registeredRules = array('phone', 'zip', 'state');
+
+    //    }}}
+    //    {{{    __construct()
+
+    /**
+     * Class constructor
+     *
+     * @param string $formName    Form's name.
+     * @param string $method      (optional)Form's method defaults to 'POST'
+     * @param string $action      (optional)Form's action
+     * @param string $target      (optional)Form's target defaults to '_self'
+     * @param mixed  $attributes  (optional)Extra attributes for <form> tag
+     * @param bool   $trackSubmit (optional)Whether to track if the form was
+     *                              submitted by adding a special hidden field
+     *
+     * @access public
+     * @link http://pear.php.net/package/HTML_QuickForm/docs/latest/HTML_QuickForm/HTML_QuickForm.html
+     * @see HTML_QuickForm
+     * @todo Remove assigning the dbh the global dbh and setup a PDO
+     *       to be passed in from a parameter - this will allow for easier
+     *       PHPUnit testing
+     */
+    public function __construct(
+        $formName,
+        $method = 'post',
+        $action = '',
+        $target = '',
+        $attributes = null,
+        $trackSubmit = false
+    ) {
+        //    T_VARIABLE error when passing this server
+        //    var in on the constructors params.
+        $action = empty($action) ? $_SERVER['REQUEST_URI'] : $action;
+        parent::__construct(
+            $formName,
+            $method,
+            $action,
+            $target,
+            $attributes,
+            $trackSubmit
+        );
+        $this->template = BASE . 'Toolkit/Forms/templates/tables/';
+        $this->dbh      = Toolkit_Database::getInstance();
+
+        if ($this->includeContactStyleSheet) {
+            $baseURL = MEDIA_BASE_URL;
+            $GLOBALS['styleSheets'][] = $baseURL . 'css/contactform.css';
+        }
+    }
+
+    //    }}}
+
+    //    {{{    addCharLimit()
+
+    /**
+     * adds a maxlength character limitation to an element
+     *
+     * @param string  $element The name of the element to add
+     *                         the char limit to
+     * @param integer $limit   The number of characters allowed
+     *
+     * @return void
+     * @access public
+     */
+    public function addCharLimit($element, $limit)
+    {
+        $this->addRule(
+            $element,
+            "ERROR: $limit characters max!",
+            'maxlength',
+            $limit,
+            'server'
+        );
+        //    Add a maxlength attribute to the field on the form
+        //    which will help prevent users from sending over 100 chars
+        //    to the server in the first place.
+        if ($this->getElementType($element) == 'text') {
+            $e =& $this->getElement($element);
+            $e->setMaxLength($limit);
+        }
+    }
+
+    //    }}}
+    //    {{{    apiVersion()
+
+    /**
+     * Returns the current FormBuilder API version
+     *
+     * @since 1.10
+     * @access public
+     * @return float
+     */
+    public function version()
+    {
+        return 1.1;
+    }
+
+    //    }}}
+
+    //    {{{    checkDateInput()
+
+    /**
+     * Checks the validation of a m-d-Y date
+     *
+     * This function will only be called if the autoValidateDateElements
+     * property is set to true.
+     *
+     * @param array $value Date element from form.
+     *
+     * @return boolean
+     * @access public
+     * @see Toolkit_FormBuilder->autoValidateDateElements
+     */
+    public function checkDateInput($value)
+    {
+        if (   empty($value['m'])
+            || empty($value['d'])
+            || empty($value['Y'])
+        ) {
+            return false;
+        }
+        return checkdate($value['m'], $value['d'], $value['Y']);
+    }
+
+    //    }}}
+    //    {{{    cleanElements()
+
+    /**
+     * Removes elements from form
+     *
+     * Loops through elements and if any names end in '_rmv' they are removed
+     * from the form object.
+     *
+     * Dynamically finds all elements on a form and removes any that
+     * end in '_rmv'.  This can be really usefull if you want to display
+     * from results after the form was successfully submitted and validated,
+     * but don't need to display any extra form elements initially displayed
+     * on the form (like email confirmation fields, or static instruction
+     * elements).
+     *
+     * @param array $elements Form elements.
+     *
+     * @return void
+     * @access protected
+     */
+    protected function cleanElements($elements)
+    {
+        if (is_array($elements)) {
+            foreach ($elements as $e) {
+                if ($e['type'] == 'group') {
+                    $this->cleanGroup($e['name']);
+                } elseif (preg_match('/^.+_rmv$/', $e['name'])) {
+                    $this->removeElement($e['name']);
+                }
+            }
+        }
+    }
+
+    //    }}}
+    //    {{{    cleanForm()
+
+    /**
+     * Removes elements from form
+     *
+     * Dynamically finds all elements on a form and removes any that
+     * end in '_rmv'.  This can be really usefull if you want to display
+     * from results after the form was successfully submitted and validated,
+     * but don't need to display any extra form elements initially displayed
+     * on the form (like email confirmation fields, or static instruction
+     * elements).
+     *
+     * @return void
+     * @access protected
+     */
+    protected function cleanForm()
+    {
+        $formArray = $this->toArray();
+        if (array_key_exists('sections', $formArray)) {
+            foreach ($formArray['sections'] as $k) {
+                if (preg_match('/^.+_rmv$/', $k['name'])) {
+                    $this->removeElement($k['name']);
+                }
+                $this->cleanElements($k['elements']);
+            }
+        } else {
+            $this->cleanElements($formArray['elements']);
+        }
+    }
+
+    //    }}}
+    //    {{{    cleanGroup()
+
+    /**
+     * Removes any elements from a group that have names that end in '_rmv'
+     *
+     * @param string $name The name of the group element
+     *
+     * @return void
+     * @access protected
+     */
+    protected function cleanGroup($name)
+    {
+        $e =& $this->getElement($name);
+        $g =& $e->getElements();
+        $i = 0;
+        while ($name = $e->getElementName($i++)) {
+            if (preg_match('/^.+_rmv/', $name)) {
+                unset($g[($i - 1)]);
+            }
+        }
+    }
+
+    //    }}}
+    //    {{{    createGridElement()
+
+    /**
+     * Creates a grid element for the form
+     *
+     * Written so this can be overridden easily in subclasses later if needed!
+     *
+     * @param string $elementType QuickForm ElementGrid element definition
+     *
+     * @return mixed ElementGrid element.
+     * @access protected
+     */
+    protected function &createGridElement($elementType)
+    {
+        $args = func_get_args();
+        return call_user_func_array(array($this, 'createElement'), $args);
+
+    }
+
+    //    }}}
+
+    //    {{{    getErrorMessage()
+
+    public function getErrorMessage()
+    {
+        return $this->errorMsg;
+    }
+
+    //    }}}
+    //    {{{    prepElement()
+
+    /**
+     * Make sure all the element array indexes are set
+     *
+     * @param array &$e Element to prep
+     *
+     * @return void
+     * @access protected
+     */
+    protected function prepElement(&$e)
+    {
+        if (!isset($e['opts'])) {
+            $e['opts'] = '';
+        }
+        if (!isset($e['att'])) {
+            $e['att'] = '';
+        }
+        if (!isset($e['val'])) {
+            $e['val'] = '';
+        }
+        if (!isset($e['display'])) {
+            $e['display'] = '';
+        }
+        if (!isset($e['label'])) {
+            $e['label'] = '';
+        }
+        if (!isset($e['noCharLimit'])) {
+            $e['noCharLimit'] = false;
+        }
+    }
+
+    //    }}}
+
+    //    {{{    registerRules()
+
+    /**
+     * Registers custom form rules you can use when validating
+     *
+     * If the registeredRule token is an array, any QF rule can be
+     * registered. This is useful if you want to register rules
+     * from outside classes
+     * e.g. (Validate, Validate_US, Validate_CA, etc...).
+     *
+     * This will set up a rule called 'checkEmail' which uses the
+     * Validate::email() function.
+     * you can still pass in parameters to this rule if you pass them in
+     * via the 'format' option when you are defining your rules.
+     * <code>
+     * class exampleClass
+     * {
+     *     $registeredRules = array(
+     *             array(
+     *             'checkEmail',
+     *             'callback',
+     *             'email',
+     *             'Validate'
+     *         )
+     *     );
+     *
+     *     // ... Rest of your class code
+     *
+     *     public function configureRules()
+     *     {
+     *         $r = array();
+     *
+     *         $r[] = array(
+     *                'element'    => 'process_email',
+     *                'message'    => 'ERROR: Invalid email format!',
+     *                'type'       => 'checkEmail',
+     *                'format'     => array('use_rfc822' => true),
+     *                'validation' => $this->validationType,
+     *                'reset'      => false,
+     *                'force'      => false
+     *         );
+     *
+     *         $this->setupRules($r);
+     *      }
+     *
+     *      // ... Rest of your class code
+     * }
+     * </code>
+     *
+     * If the registeredRule is a string, the corresponding rule in the
+     * glmPEAR/HTML/QuickForm/Rule/ directory will be registered with
+     * the form.
+     *
+     * This will set up the 'phone' rule already defined in the
+     * glmPEAR/HTML/QuickForm/Rule directory which validates both
+     * US and Canadian phone numbers
+     * <code>
+     * class exampleClass
+     * {
+     *     $registeredRules = array('phone');
+     *
+     *     // ... Rest of your class code
+     *
+     *     public function configureRules()
+     *     {
+     *         $r = array();
+     *
+     *         $r[] = array(
+     *                'element'    => 'phone',
+     *                'message'    => 'ERROR: Invalid number (xxx) xxx-xxxx!',
+     *                'type'       => 'phone',
+     *                'format'     => null,
+     *                'validation' => $this->validationType,
+     *                'reset'      => false,
+     *                'force'      => false
+     *         );
+     *
+     *         $this->setupRules($r);
+     *      }
+     *
+     *      // ... Rest of your class code
+     * }
+     * </code>
+     *
+     * @return void
+     * @access protected
+     */
+    protected function registerRules()
+    {
+        if (is_array($this->registeredRules)) {
+            foreach ($this->registeredRules as $r) {
+                if (is_array($r)) {
+                    call_user_func_array(array(&$this, 'registerRule'), $r);
+                } else {
+                    //    Don't nedd to register rules more than once
+                    if (!$this->isRuleRegistered($r)) {
+                        $this->registerRule($r, null, "HTML_QuickForm_Rule_$r");
+                    }
+                }
+            }
+        }
+    }
+
+    //    }}}
+
+    //    {{{    setAutoValidateDateElements()
+
+    /**
+     * Set if we need to auto validate the Date Elements
+     *
+     * @param boolean $validate true/false to auto validate date elements
+     *
+     * @return void
+     * @access public
+     */
+    public function setAutoValidateDateElements($validate)
+    {
+        $this->autoValidateDateElements = $validate;
+    }
+
+    //    }}}
+    //    {{{    setFormData()
+
+    /**
+     * Sets the submitted values from the form
+     *
+     * Set these values into an internal variable so they will be accessible
+     * anywhere we need them in the form.
+     *
+     * @param array $exclude (optional) Any element names you don't want
+     *                         included. Since this is primarily used in
+     *                         emailing, this is helpful to exclude any data
+     *                         we don't want before the array is generated.
+     *
+     * @return void
+     * @access protected
+     */
+    protected function setFormData(array $exclude = array())
+    {
+        $values = $this->getSubmitValues();
+        foreach ($values as $k => $v) {
+            if ($this->elementExists($k)) {
+                $e =& $this->getElement($k);
+                if (!in_array($e->getName(), $exclude)) {
+                    switch ($e->getType()) {
+                    case 'date' :
+                        list($m, $d, $y) = array_values($e->getValue());
+                        //    If all the date fields are empty, then don't add
+                        //    the output to the formData.
+                        if (!(empty($m[0]) && empty($d[0]) && empty($y[0]))) {
+                            $this->formData[$e->getName()]['label']
+                                = $e->getLabel();
+
+                            $oldDate = $e->getValue();
+                            $newDate = Toolkit_Common::arrayFlatten(
+                                $oldDate,
+                                0,
+                                $newDate
+                            );
+
+                            $this->formData[$e->getName()]['element']
+                                = implode(' / ', $newDate);
+                            unset($oldDate, $newDate);
+                        }
+                        break;
+
+                    case 'group':
+                        $e->freeze();
+                        $this->formData[$e->getName()]['label']   = $e->getLabel();
+                        $this->formData[$e->getName()]['element'] = $e->toHtml();
+                        break;
+
+                    case 'select' :
+                        $this->formData[$e->getName()]['label'] = $e->getLabel();
+
+                        $values = $e->getValue();
+                        foreach ($values as $v) {
+                            $this->formData[$e->getName()]['element'] .= $v;
+                        }
+                        break;
+
+                    default :
+                        $this->formData[$e->getName()]['label']   = $e->getLabel();
+                        $this->formData[$e->getName()]['element'] = $e->getValue();
+                        break;
+                    }
+                }
+            }
+        }
+    }
+
+    //    }}}
+    //    {{{    setupConstants()
+
+    /**
+     * Sets the form constant values
+     *
+     * @param array $constants Associative array of form constant values.
+     *
+     * @return void
+     * @access protected
+     */
+    protected function setupConstants($constants)
+    {
+        $this->setConstants($constants);
+    }
+
+    //    }}}
+    //    {{{    setupDefaults()
+
+    /**
+     * Sets the form default values
+     *
+     * @param array $defaults Associative array of form default values.
+     *
+     * @return void
+     * @access protected
+     */
+    protected function setupDefaults($defaults)
+    {
+        $this->setDefaults($defaults);
+    }
+
+    //    }}}
+    //    {{{ setupElements()
+
+    /**
+     * Sets up all the elements to the form
+     *
+     * Takes a multi-dimensional array of form elements and uses them
+     * to set up the form objects elements
+     *
+     * @param array $elements Multi-Dimensional array of form elements.
+     *
+     * @return void
+     * @access protected
+     */
+    protected function setupElements($elements)
+    {
+        if (!is_array($elements)) {
+            return;
+        }
+        foreach ($elements as &$e) {
+            $this->prepElement($e);
+            switch ($e['type']) {
+            case 'group' :
+                if (is_array($e['group']) && !empty($e['group'])) {
+                    //    Special rendering for grouped elements.
+                    unset($field);
+                    foreach ($e['group'] as $g) {
+                        $this->prepElement($g);
+                        $field[] =& HTML_QuickForm::createElement(
+                            $g['type'],
+                            $g['name'],
+                            $g['display'],
+                            $g['opts'],
+                            $g['att'],
+                            $g['val']
+                        );
+                    }
+                    $source =& $this->addGroup(
+                        $field,
+                        $e['name'],
+                        $e['label'],
+                        $e['seperator'],
+                        $e['appendName']
+                    );
+                }
+                break;
+
+            case 'elementGrid' :
+                $source =& $this->addElement(
+                    $e['type'],
+                    $e['name'],
+                    $e['display'],
+                    $e['opts'],
+                    $e['att'],
+                    $e['val']
+                );
+                unset($columnNames);
+
+                //    Loop through the rows (r) and columns (c)
+                //    to add each of the elements to our grid.
+                foreach ($e['group'] as $k => $r) {
+                    unset($set, $rowTitle);
+                    foreach ($r as $c) {
+                        $columnNames[$c['display']] = $c['display'];
+
+                        $set[] =& $this->createGridElement(
+                            $c['type'],
+                            $c['name'],
+                            null,
+                            $c['opts'],
+                            $c['att']
+                        );
+                    }
+                    $rowTitle = is_int($k) ? '&nbsp;' : $k;
+                    $source->addRow($set, $rowTitle);
+                }
+
+                $source->setColumnNames($columnNames);
+                break;
+
+            default :
+                //    Render all elements except groups
+                try {
+                    $source =& $this->addElement(
+                        $e['type'],
+                        $e['name'],
+                        $e['display'],
+                        $e['opts'],
+                        $e['att'],
+                        $e['val']
+                    );
+
+                    if (PEAR::isError($source)) {
+                        var_dump($source);
+                        throw new Exception ('PEAR QuickForm Element Error');
+                    }
+                } catch (HTML_QuickForm_Error $e) {
+                    Toolkit_Common::dieGracefully(null, $e);
+                } catch (Exception $e) {
+                    Toolkit_Common::handleError($e);
+                }
+
+                if ($e['type'] == 'advmultiselect') {
+                    $source->setLabel($e['labels']);
+                }
+                if ($e['name'] == 'categories') {
+                    $res = $source->loadArray($this->categories);
+                    if (PEAR::isError($res)) {
+                        Toolkit_Common::dieGracefully(null, $res);
+                    }
+                }
+                if ($e['type'] == 'header') {
+                    $this->formHeaders[$e['display']] = $e;
+                }
+
+                if ($e['name'] == 'captcha_rmv') {
+                    $this->captchaAnswer =& $source;
+                }
+
+                if ($e['name'] == 'captcha_question') {
+                    $this->captchaQuestion =& $source;
+                }
+                break;
+            }
+        }
+        $this->formElements = $elements;
+    }
+
+    //    }}}
+    //    {{{    setupFilters()
+
+    /**
+     * Sets any filters needed for the form elements when submitting
+     *
+     * @param array $filters Element filters.
+     *
+     * @return void
+     * @access protected
+     */
+    protected function setupFilters($filters)
+    {
+        foreach ($filters as $f) {
+            $res = $this->applyFilter($f['element'], $f['filter']);
+
+            if (PEAR::isError($res)) {
+                Toolkit_Common::handleError($res);
+            }
+        }
+    }
+
+    //    }}}
+    //    {{{ setupRules()
+
+    /**
+     * Apply rules to the form
+     *
+     * 100 & 1000 char length limitations are automatically assigned to
+     * text/textarea elements to help reduce load limitations on the server.
+     * -request per Chuck in a conference call on (5/22/2009 @ 12:15pm)
+     *
+     * Applies rules that are defined in child classes to the form elements
+     * group rules can be kind of tricky, since you can't apply a rule
+     * directly to an element inside of a rule you have to define
+     * the rule inside a nest of array's and then add a group rule.
+     * the array will contain all the elements inside the group you wish
+     * to apply rules to.
+     *
+     * You can assign as many rules as you would like to individual elements,
+     * and you aren't required to make the array associative, although it is
+     * easier to see whats going on.
+     *
+     * see: http://pear.activeventure.com/package/package.html.html-quickform.html-quickform.addgrouprule.html
+     * for another example.
+     * <code>
+     * //    Define the rules for each element in the group.
+     * $num_rule = array(
+     *   'ERROR: Must contain a valid decimal number!',
+     *   'numeric'
+     * );
+     * //    Collect together the rules for each element.
+     * $lat_rules = array('lat' => array($num_rule));
+     * $lon_rules = array('lon' => array($num_rule));
+     * $r[] = array(
+     *   'element'    => 'latitude',
+     *   'message'    => 'ERROR:',
+     *   'type'       => 'group',
+     *   'format'     => $lat_rules,
+     *   'validation' => $this->validationType,
+     *   'reset'      => false,
+     *   'force'      => false
+     * );
+     * </code>
+     *
+     * To make a group required but not require every element in the group
+     * you can use the addGroupRule function again
+     * for example:  say you have a group of checkboxes and you only only
+     * require 1 be checked.  a simple group rule such as the following
+     * will handle this.
+     * N.B. notice the extra "howMany" index.
+     * <code>
+     * $r[] = array(
+     *   'element'   => 'element_name',
+     *   'message'   => 'ERROR: Error to display!',
+     *   'type'      => 'required',
+     *   'format'    => null,
+     *   'howMany'   => 1,
+     *   'validation'=> $this->validationType,
+     *   'reset'     => true,
+     *   'force'     => false,
+     * );
+     * </code>
+     *
+     * @param array $rules Multi-Dimensional array of rules for form elements.
+     *
+     * @return void
+     * @access protected
+     */
+    protected function setupRules(array $rules = null)
+    {
+        $this->registerRules();
+        $preRegisteredRules = $this->getRegisteredRules();
+        if (is_array($this->formElements)) {
+            foreach ($this->formElements as $e) {
+                //    Put length limitations on text and textarea fields
+                if ($e['type'] == 'text' && !$e['noCharLimit']) {
+                    $this->addCharLimit($e['name'], 100);
+                } elseif ($e['type'] == 'textarea' && !$e['noCharLimit']) {
+                    $this->addCharLimit($e['name'], 1000);
+                } elseif ($e['type'] == 'group') {
+                    //    We need to apply these same limitations to the
+                    //    text and textarea fields inside of groups
+                    $r = array();
+                    if (is_array($e['group'])) {
+                        foreach ($e['group'] as $i) {
+                            //    Loop through group elements and if they are
+                            //    text/textarea fields then put the field into
+                            //    a rule array that we will assign to the group
+                            if ($i['type'] == 'text' && !$i['noCharLimit']) {
+                                $r[$i['name']][] = array(
+                                    'ERROR: 100 characters max!',
+                                    'maxlength'
+                                );
+                            } elseif ($i['type'] == 'textarea' && !$i['noCharLimit']) {
+                                $r[$i['name']][] = array(
+                                    'ERROR: 1000 characters max!',
+                                    'maxlength'
+                                );
+                            }
+                        }
+                    }
+                    if (!empty($r)) {
+                        $this->addGroupRule($e['name'], $r);
+                    }
+                }
+                if ($this->validationType == 'client') {
+                    $label = $e['display'];
+                }
+                if ($e['req']) {
+                    if ($e['type'] == 'group') {
+                        foreach ($e['group'] as $ge) {
+                            if ($ge['req']) {
+                                $rule[$ge['name']][] = array(
+                                        'ERROR: You must complete this field!',
+                                        'required',
+                                        null,
+                                        $this->validationType);
+                            }
+                        }
+                        $this->addGroupRule($e['name'], $rule);
+                        unset($rule);
+                    } elseif ($e['type'] == 'date') {
+                        if (!empty($e['error'])) {
+                            //  Custom error message for the date element.
+                            $error = $e['error'];
+                        } else {
+                            //  Default error message for the date element.
+                            $error = 'ERROR: You must enter a date!';
+                        }
+                        $this->addGroupRule(
+                            $e['name'],
+                            $error,
+                            'required',
+                            3,
+                            $this->validationType
+                        );
+                        if ($this->autoValidateDateElements) {
+                            $this->addRule(
+                                $e['name'],
+                                'ERROR: Date is invalid!',
+                                'callback',
+                                array(&$this, 'checkDateInput')
+                            );
+                        }
+                    } else {
+                        $this->addRule(
+                            $e['name'],
+                            "$label ERROR: You must complete this field!",
+                            'required',
+                            null,
+                            $this->validationType
+                        );
+                    }
+                }
+            }
+        }
+        if (is_array($rules)) {
+            foreach ($rules as $r) {
+                if (!is_array($r['element'])) {
+                    $group = ($this->getElementType($r['element']) == 'group');
+                }
+                if ($group) {
+                    $this->addGroupRule(
+                        $r['element'],
+                        $r['message'],
+                        $r['type'],
+                        $r['format'],
+                        $r['howMany'],
+                        $r['validation'],
+                        $r['reset']
+                    );
+                } elseif (in_array($r['type'], $preRegisteredRules)) {
+                    $this->addRule(
+                        $r['element'],
+                        $r['message'],
+                        $r['type'],
+                        $r['format'],
+                        $r['validation'],
+                        $r['reset'],
+                        $r['force']
+                    );
+                }
+            }
+        }
+    }
+
+    //    }}}
+    //    {{{    setupRenderers()
+
+    /**
+     * Sets up renderers for form objects
+     *
+     * Uses the default form renderer to allow templates to be injected
+     * into the form object before displaying on a page.  This allows
+     * changing the face of the form w/ out any backend adjustment.
+     *
+     * Files that can be created for templates are
+     *
+     * Examples:
+     * # Element.tpl
+     * <code>
+     * <tr>
+     *   <td class="labelcell">
+     *     <!-- BEGIN required -->
+     *     <span class="required">*</span>
+     *     <!-- END required -->
+     *     <label> {label} </label>
+     *   </td>
+     *   <td class="fieldcell">
+     *     <!-- BEGIN error -->
+     *     <div class="req"> {error} </div>
+     *     <!-- END error -->
+     *     {element}
+     *   </td>
+     *</tr>
+     * </code>
+     *
+     * # Form.tpl
+     * <code>
+     *   <div id="contact">
+     *     <form{attributes}>
+     *       <table>
+     *         {content}
+     *       </table>
+     *     </form>
+     *   </div>
+     * </code>
+     *
+     * # GroupElement.tpl
+     * <code>
+     *   <tr>
+     *     <td>
+     *       {element}
+     *       <!-- BEGIN required -->
+     *       <span class="req">*</span>
+     *       <!-- END required -->
+     *       {label}
+     *     </td>
+     *   </tr>
+     * </code>
+     *
+     * # Group.tpl
+     * <code>
+     *   <table class="group">
+     *     <tbody>
+     *       {content}
+     *     </tbody>
+     *   </table>
+     * </code>
+     *
+     * # Header.tpl
+     * <code>
+     * <tr class="hdr">
+     *   <td colspan="2">
+     *     {header}
+     *   </td>
+     * </tr>
+     * </code>
+     *
+     * # RequiredNote.tpl
+     * <code>
+     *   <span class="req">*</span> Denotes required field
+     * </code>
+     *
+     * @param array $groups any groups that need to be rendered
+     *                      via the groupElementTemplate and groupTemplate
+     *
+     * @return void
+     * @access protected
+     */
+    protected function setupRenderers(array $groups = array())
+    {
+        $renderer =& $this->defaultRenderer();
+        if (is_dir($this->template)) {
+            if ($dh = opendir($this->template)) {
+                while (($file = readdir($dh)) !== false) {
+                    //    Ignore current dir and parent dir.
+                    if ($file != '..' && $file != '.' && $file != 'CVS') {
+                        $baseName = reset(explode('.', $file));
+                        //    Ignores swp files.
+                        if (!empty($baseName)) {
+                            $method   = "set{$baseName}Template";
+                            $template = file_get_contents($this->template . $file);
+                            if (   $method != 'setGroupTemplate'
+                                && $method != 'setGroupElementTemplate'
+                            ) {
+                                if (method_exists($renderer, $method)) {
+                                    $renderer->$method($template);
+                                }
+                            } else {
+                                //    apply the templates to any defined groups
+                                foreach ($groups as $k) {
+                                    $renderer->$method($template, $k);
+                                }
+                            }
+                        }
+                    }
+                }
+                closedir($dh);
+            }
+        }
+
+        //    Set the default how the captcha's should look
+        //    can be overridden in subclasses
+        if ($this->useCaptcha) {
+            $required = '<!-- BEGIN required --><span class="req">*</span><!-- END required -->';
+            $error    = '<!-- BEGIN error --><div class="req">{error}</div><!-- END error -->';
+            $renderer->setElementTemplate(
+                '<tr>
+                    <td class="labelcell"><label>{label}</label></td>
+                    <td class="fieldcell captcha">{element}</td>
+                </tr>',
+                'captcha_question'
+            );
+            $renderer->setElementTemplate(
+                '<tr>
+                    <td class="labelcell">'.$required.'<label>{label}</label></td>
+                    <td class="fieldcell">'.$error.'{element}
+                        <span class="tooltip" title="Verification Code|To help us distinguish between information submitted by individuals and those automatically entered by software robots, please type the letters shown.">What is this?</span>
+                    </td>
+                </tr>',
+                'captcha_rmv'
+            );
+        }
+    }
+
+    //    }}}
+    //    {{{    useCaptcha()
+
+    /**
+     * Set if we need to use a captcha in this form or not
+     *
+     * @param boolean $useCaptcha true/false to use a captcha with the form
+     *
+     * @return void
+     * @access public
+     */
+    public function useCaptcha($useCaptcha)
+    {
+        $this->useClueTip();
+        $this->useCaptcha = $useCaptcha;
+    }
+
+    //    }}}
+    //    {{{    useClueTip()
+
+    /**
+     * Set if we need to use the JS cluetip
+     *
+     * @param boolean $useCaptcha include cluetip resources or not
+     *
+     * @return void
+     * @access public
+     */
+    public function useClueTip()
+    {
+        $GLOBALS['styleSheets'][]
+            = MEDIA_APP_BASE_URL . 'libjs/cluetip/jquery.cluetip.css';
+        $GLOBALS['bottomScripts'][]
+            = MEDIA_APP_BASE_URL . 'libjs/cluetip/jquery.cluetip.js';
+        $GLOBALS['bottomScripts'][]
+            = MEDIA_APP_BASE_URL . 'libjs/cluetip/lib/jquery.hoverIntent.js';
+        $GLOBALS['bottomScripts'][]
+            = MEDIA_APP_BASE_URL . 'libjs/cluetip/lib/jquery.bgiframe.min.js';
+        $GLOBALS['bottomScripts'][]
+            = MEDIA_APP_BASE_URL . 'libjs/contactform.js';
+    }
+
+    //    }}}
+    //    {{{    show()
+
+    /**
+     * Shows the form
+     *
+     * @return void
+     * @access public
+     */
+    public function show()
+    {
+        Toolkit_Common::show();
+    }
+
+    //    }}}
+    //    {{{    validate()
+
+    public function validate()
+    {
+        if (!$this->validated) {
+            $this->validated = parent::validate();
+        }
+
+        return $this->validated;
+    }
+
+    //    }}}
+}
diff --git a/Toolkit/Forms/Rules/Date.php b/Toolkit/Forms/Rules/Date.php
new file mode 100644 (file)
index 0000000..e5a53c8
--- /dev/null
@@ -0,0 +1,93 @@
+<?php
+/**
+ * Date.php
+ * 
+ * PHP version 5.2
+ * 
+ * @category  Toolkit
+ * @package   Forms/Rules
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @version   SVN: $id$
+ * @link      <>
+ */
+
+/**
+ * Toolkit_Forms_Rules_Date
+ * 
+ * Description of Toolkit_Forms_Rules_Date
+ * 
+ * @category  Toolkit
+ * @package   Forms/Rules
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @release   Release: $id$
+ * @link      <>
+ */
+class Toolkit_Forms_Rules_Date extends HTML_QuickForm_Rule
+{
+       /**
+        * Validate if a string is a valid date
+        *
+        * If element is part of a group, then you can pass in the group name
+        * under the options parameter, that way you can find the date values
+        *
+        * You must pass in at least the format of the date in an array.
+        * array('format' => '%m-%d-%Y') is the default
+        *
+        * @param string $value   the date array to validate
+        * @param array  $options options used to dictate validation of the date
+     *
+        * @return bool if the string could correctly be validated as a date.
+        * @access      public
+        * @see         Validate::date()
+        */
+       function validate($value, array $options)
+       {
+               $month  = !isset($options['group'])
+                       ? $value['m']
+                       : $value[$options['group']]['m'];
+               $day    = !isset($options['group'])
+                       ? $value['d']
+                       : $value[$options['group']]['d'];
+               $year   = !isset($options['group'])
+                       ? $value['Y']
+                       : $value[$options['group']]['Y'];
+
+               if (isset($options['allowEmpty'])
+                       && $options['allowEmpty']
+                       && empty($month)
+                       && empty($day)
+                       && empty($year)
+               ) {
+                       return true;
+               }
+
+               unset($options['group'], $options['allowEmpty']);
+
+               $day = str_pad($day, 2, '0', STR_PAD_LEFT);
+               $month = str_pad($month, 2, '0', STR_PAD_LEFT);
+
+               return Validate::date("$month-$day-$year", $options);
+       }
+
+    /**
+    * gets validation script
+    * 
+    * @return array 
+    */
+       function getValidationScript()
+       {
+               return array('', true);
+       }
+}
+
+HTML_QuickForm::registerRule(
+       'Date',
+       'rule',
+       'Toolkit_Forms_Rules_Date',
+       BASE . 'Toolkit/Forms/Rules/Date.php'
+);
+?>
diff --git a/Toolkit/Forms/Rules/Image.php b/Toolkit/Forms/Rules/Image.php
new file mode 100644 (file)
index 0000000..86eec64
--- /dev/null
@@ -0,0 +1,209 @@
+<?php
+/**
+ * Image.php
+ * 
+ * PHP version 5.2
+ * 
+ * @category  Toolkit
+ * @package   Forms/Rules
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @version   SVN: $id$
+ * @link      <>
+ */
+
+/**
+ * Toolkit_Forms_Rules_Image
+ * 
+ * Description of Toolkit_Forms_Rules_Image
+ * 
+ * @category  Toolkit
+ * @package   Forms/Rules
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @release   Release: $id$
+ * @link      <>
+ */
+class Toolkit_Forms_Rules_Image extends HTML_QuickForm_Rule
+{
+       //      {{{     validate()
+
+       /**
+        * Handle image uploading rules
+        *
+        * The options array is required and has specific key values
+        * that must be present.
+        *
+        * form                : (HTML_Quickform) form object - so we can insert
+        *                       the new image name back back into the forms
+        *                       submit values.
+        * fieldName           : (string) name of the upload field - this is
+        *                       generally the same name as the field we're
+        *                       calling the rule on.
+        * imageField          : (string) name of the field that stores the
+        *                       filename of the image that was uploaded
+        * is                  : (Toolkit_FileServer_ImageAdapter)
+        *                       image server instance
+        * deleteExistingImage : (boolean) Whether to delete any old image from
+        *                       the image server before uploading the new image
+        * injectImage         : (array)
+        *    tgtElement : (string) Name of the field that will hold the preview
+        *                 of the image - This is usually a static element
+        *    path       : (optional) Path to the image - defaults to FORM_THUMB
+        *    imgTag     : (optional) Format of the image tag - defaults to
+        *                 <img src="%s"> where %s is the path + image
+        *
+        *
+        * Full Example:
+        *
+        * $r[] = array(
+        *     'element' => 'file_rmv',
+        *     'message' => 'ERROR: Error uploading image!',
+        *     'type' => 'Image',
+        *     'format' => array(
+        *         'form' => $this,
+        *         'fieldName' => 'file_rmv',
+        *         'imageField' => 'image',
+        *         'is' => new Toolkit_FileServer_ImageAdapter(),
+        *         'deleteExistingImage' => true,
+        *         'injectImage' => array(
+        *             'tgtElement' => 'current_image_rmv',
+        *             'imgTag' => '<img src="%s">',
+        *             'path' => FORM_THUMB
+        *         )
+        *      ),
+        *      'validation' => $this->validationType,
+        *      'reset' => false,
+        *      'force' => false
+        * );
+        *
+        * @param string $value   the $_FILES['xxxx'] array
+        * @param array  $options upload options
+        *
+        * @return bool if the image was successfully uploaded or not
+        * @access public
+        */
+       function validate($value, array $options)
+       {
+               //      No image uploaded - return true
+               //      this can be a valid state, if the field is required - then a
+               //      required rule should be used and will catch before this rule
+               //      is even applied.
+               if (empty($value['tmp_name'])) {
+                       return true;
+               }
+
+               if (!isset($options['fieldName'])) {
+                       throw new RuntimeException(
+                               'Missing `fieldName` key in options'
+                       );
+               }
+               if (   isset($options['is'])
+                       && ($options['is'] instanceof Toolkit_FileServer_ImageAdapter)
+               ) {
+                       $is = $options['is'];
+               } else {
+                       throw new RuntimeException(
+                               'Missing `is` key in options for Image Server instance'
+                       );
+               }
+
+               if (   isset($options['form'])
+                       && ($options['form'] instanceof HTML_QuickForm)
+               ) {
+                       $form =& $options['form'];
+               } else {
+                       throw new RuntimeException(
+                               'Missing `form` key in options'
+                       );
+               }
+
+               if (isset($options['imageField'])) {
+                       $imageField = $options['imageField'];
+               } else {
+                       throw new RuntimeException(
+                               'Missing `imageField` key in options'
+                       );
+               }
+
+               if (   isset($options['injectImage'])
+                       && !is_array($options['injectImage'])
+               ) {
+                       if (!is_array($options['injectImage'])) {
+                               throw new RuntimeException(
+                                       '`injectImage` must be an array of options'
+                               );
+                       }
+               } elseif (   isset($options['injectImage'])
+                                 && is_array($options['injectImage'])
+                                 && !isset($options['injectImage']['tgtElement'])
+               ) {
+                       throw new RuntimeException(
+                               'The `injectImage` array needs a `tgtElement` index to inject the new image into'
+                       );
+               }
+
+               if (   isset($options['deleteExistingImage'])
+                       && $options['deleteExistingImage']
+               ) {
+                       $existingImage =& $form->getSubmitValue($imageField);
+                       if (!empty($existingImage)) {
+                               $is->delete($existingImage);
+                       }
+               }
+
+               try {
+                       $uploadedImage = $is->upload($options['fieldName']);
+               } catch (RangeException $e) {
+                       Toolkit_Logger::logException('Image Server', $e);
+                       return false;
+               }
+
+               if (isset($options['injectImage'])) {
+                       $imgTag = isset($options['injectImage']['imgTag'])
+                               ? $options['injectImage']['imgTag']
+                               : '<img src="%s">';
+
+                       $path = isset($options['injectImage']['path'])
+                               ? $options['injectImage']['path']
+                               : FORM_THUMB;
+
+                       $htmlImg = sprintf($imgTag, $path . $uploadedImage['name']);
+                       $tgt = $options['injectImage']['tgtElement'];
+                       $currImg =& $form->getElement($tgt);
+                       $currImg->setValue($htmlImg);
+               }
+
+               $fileName =& $form->getElement($imageField);
+               $fileName->setValue($uploadedImage['name']);
+               $form->_submitValues[$imageField] = $uploadedImage['name'];
+
+               return true;
+       }
+
+       //      }}}
+
+       //      {{{     getValidationScript()
+
+    /**
+    * gets validation script
+    * 
+    * @return array 
+    */
+       function getValidationScript()
+       {
+               return array('', true);
+       }
+
+       //      }}}
+}
+
+HTML_QuickForm::registerRule(
+       'Image',
+       'rule',
+       'Toolkit_Forms_Rules_Image',
+       BASE . 'Toolkit/Forms/Rules/Image.php'
+);
+?>
diff --git a/Toolkit/Forms/Rules/ShortUrl.php b/Toolkit/Forms/Rules/ShortUrl.php
new file mode 100644 (file)
index 0000000..823f14c
--- /dev/null
@@ -0,0 +1,81 @@
+<?php
+/**
+ * ShortUrl.php
+ * 
+ * PHP version 5.2
+ * 
+ * @category  Toolkit
+ * @package   Forms/Rules
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @version   SVN: $id$
+ * @link      <>
+ */
+
+/**
+ * Toolkit_Forms_Rules_ShortUrl
+ * 
+ * Description of Toolkit_Forms_Rules_ShortUrl
+ * 
+ * @category  Toolkit
+ * @package   Forms/Rules
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @release   Release: $id$
+ * @link      <>
+ */
+class Toolkit_Forms_Rules_ShortUrl extends HTML_QuickForm_Rule
+{
+       //      {{{     validate()
+
+       /**
+        * Validate if a string is a valid short url
+        *
+        * Short URLs can only contain alpha numeric characters plus
+        * dash and underscore. They must also not be the same name as a file
+        * on the server.
+        *
+        * @param string $value short url to validate
+     *
+        * @return bool if the string could correctly be validated as a short url
+        * @access public
+        */
+       public function validate($value)
+       {
+               $hasValidCharacters   = !preg_match('/[^-a-zA-Z0-9_]/', $value);
+               //      Short URLs that are the same as an existing file ie: (index.php)
+               //      cause .htaccess to throw errors
+               $fileDoesNotExist     = !file_exists(BASE . $value);
+               //      Short URLs that are the same as an existing file basename
+               //      ie: (index) cause .htaccess to throw errors
+               list($path, $suffix)  = explode('.', $value);
+               $files                = array_filter(glob(BASE . "$path*"), 'is_file');
+               $baseNameDoesNotExist = ($files !== false && empty($files));
+               return $hasValidCharacters && $fileDoesNotExist && $baseNameDoesNotExist;
+       }
+
+       //      }}}
+       //      {{{     getValidationScript()
+
+    /**
+     * gets validation script
+     * 
+     * @return array 
+     */
+       public function getValidationScript()
+       {
+               return array('', true);
+       }
+
+       //      }}}
+}
+
+HTML_QuickForm::registerRule(
+       'ShortUrl',
+       'rule',
+       'Toolkit_Forms_Rules_ShortUrl',
+       BASE . 'Toolkit/Forms/Rules/ShortUrl.php'
+);
+?>
diff --git a/Toolkit/Forms/Rules/Url.php b/Toolkit/Forms/Rules/Url.php
new file mode 100644 (file)
index 0000000..4216c24
--- /dev/null
@@ -0,0 +1,80 @@
+<?php
+
+/**
+ * Url.php
+ * 
+ * PHP version 5.2
+ * 
+ * @category  Toolkit
+ * @package   Forms/Rules
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @version   SVN: $id$
+ * @link      <>
+ */
+
+/**
+ * Toolkit_Forms_Rules_Url
+ * 
+ * Description of Toolkit_Forms_Rules_Url
+ * 
+ * @category  Toolkit
+ * @package   Forms/Rules
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @release   Release: $id$
+ * @link      <>
+ */
+class Toolkit_Forms_Rules_Url extends HTML_QuickForm_Rule
+{
+       //      {{{     validate()
+
+       /**
+        * Validate if a string is a valid url
+        *
+        * @param string $value   url to validate
+     * @param array  $options set to null
+        *
+        * @return bool if the string could correctly be validated as a url
+        * @access public
+        */
+       public function validate($value, array $options = null)
+       {
+               $validateFilter = FILTER_VALIDATE_URL;
+
+               if (is_null($options)) {
+                       return (bool) filter_var($value, $validateFilter);
+               }
+
+               $lambdaFunction = create_function('$a, $b', 'return $a | $b;');
+               $flags = array_reduce($options, $lambdaFunction, 0);
+
+               return (bool) filter_var($value, $validateFilter, $flags);
+
+       }
+
+       //      }}}
+       //      {{{     getValidationScript()
+    
+    /**
+    * gets validation script
+    * 
+    * @return array 
+    */
+       public function getValidationScript()
+       {
+               return array('', true);
+       }
+
+       //      }}}
+}
+
+HTML_QuickForm::registerRule(
+       'Url',
+       'rule',
+       'Toolkit_Forms_Rules_Url',
+       BASE . 'Toolkit/Forms/Rules/Url.php'
+);
+?>
diff --git a/Toolkit/Forms/templates/tables/Element.tpl b/Toolkit/Forms/templates/tables/Element.tpl
new file mode 100644 (file)
index 0000000..595457b
--- /dev/null
@@ -0,0 +1,14 @@
+<tr>
+       <td class="labelcell">
+               <!-- BEGIN required -->
+               <span class="req">*</span>
+               <!-- END required -->
+               <label>{label}</label>
+       </td>
+       <td class="fieldcell">
+               <!-- BEGIN error -->
+               <div class="req"> {error} </div>
+               <!-- END error -->
+               {element}
+       </td>
+</tr>
diff --git a/Toolkit/Forms/templates/tables/Form.tpl b/Toolkit/Forms/templates/tables/Form.tpl
new file mode 100644 (file)
index 0000000..8ed6c2f
--- /dev/null
@@ -0,0 +1,11 @@
+<div class="webform">
+       <form{attributes}>
+               <div class="hiddenElements">
+                       {hidden}
+               </div>
+               {requiredNote}
+               <table>
+                       {content}
+               </table>
+       </form>
+</div>
\ No newline at end of file
diff --git a/Toolkit/Forms/templates/tables/Group.tpl b/Toolkit/Forms/templates/tables/Group.tpl
new file mode 100644 (file)
index 0000000..cdd24cf
--- /dev/null
@@ -0,0 +1,5 @@
+<table class="group">
+       <tbody>
+               {content}
+       </tbody>
+</table>
diff --git a/Toolkit/Forms/templates/tables/GroupElement.tpl b/Toolkit/Forms/templates/tables/GroupElement.tpl
new file mode 100644 (file)
index 0000000..1a4ba27
--- /dev/null
@@ -0,0 +1,9 @@
+<tr>
+       <td>
+               {element}
+               <!-- BEGIN required -->
+               <span class="req">*</span>
+               <!-- END required -->
+               {label}
+       </td>
+</tr>
diff --git a/Toolkit/Forms/templates/tables/Header.tpl b/Toolkit/Forms/templates/tables/Header.tpl
new file mode 100644 (file)
index 0000000..64ac244
--- /dev/null
@@ -0,0 +1,5 @@
+<tr class="hdr">
+       <td colspan="2">
+               {header}
+       </td>
+</tr>
diff --git a/Toolkit/Forms/templates/tables/RequiredNote.tpl b/Toolkit/Forms/templates/tables/RequiredNote.tpl
new file mode 100644 (file)
index 0000000..8d61480
--- /dev/null
@@ -0,0 +1 @@
+<div><span class="req">*</span> Denotes required field</div>
diff --git a/Toolkit/IController.php b/Toolkit/IController.php
new file mode 100644 (file)
index 0000000..534e21b
--- /dev/null
@@ -0,0 +1,36 @@
+<?php
+
+/**
+ * IController.php
+ *
+ * PHP version 5
+ *
+ * @category Toolkit
+ * @package  Controller
+ * @author   Steve Sutton <steve@gaslightmedia.com>
+ * @license  http://www.gaslightmedia.com Gaslightmedia
+ * @link     http://demo.gaslightmedia.com
+ */
+
+/**
+ * Toolkit_IController
+ *
+ * Object Mapper pattern class for the Video PDO Table. Creates
+ * Toolkit_Video_Video objects by values or by id
+ *
+ * @category  Toolkit
+ * @package   Controller
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight media
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @link      http://demo.gaslightmedia.com
+ */
+interface Toolkit_IController
+{
+    /**
+     * default action for the controller
+     *
+     * @return string
+     */
+    public function indexAction();
+}
diff --git a/Toolkit/IMapper.php b/Toolkit/IMapper.php
new file mode 100644 (file)
index 0000000..aa440b2
--- /dev/null
@@ -0,0 +1,36 @@
+<?php
+/**
+ * IMapper.php
+ *
+ * PHP version 5
+ *
+ * @category Toolkit
+ * @package  Mapper
+ * @author   Steve Sutton <steve@gaslightmedia.com>
+ * @license  http://www.gaslightmedia.com Gaslightmedia
+ * @link     <>
+ */
+
+/**
+ * Toolkit_IMapper
+ *
+ * Interface for creation of mapper classes
+ *
+ * @category Toolkit
+ * @package  Mapper
+ * @author   Steve Sutton <steve@gaslightmedia.com>
+ * @license  http://www.gaslightmedia.com Gaslightmedia
+ * @link     <>
+ */
+interface Toolkit_IMapper
+{
+    /**
+     * createByValues
+     *
+     * @param Array $values array of values to set for the new object
+     *
+     * @return object
+     */
+    public function createByValues($values);
+}
+?>
diff --git a/Toolkit/INavigation.php b/Toolkit/INavigation.php
new file mode 100644 (file)
index 0000000..0c85d77
--- /dev/null
@@ -0,0 +1,52 @@
+<?php
+/**
+ * INavigation.php
+ *
+ * PHP version 5
+ *
+ * @category  Toolkit
+ * @package   Toolkit_INavigation
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @release   Release: $id$
+ * @link      <>
+ */
+
+/**
+ * Toolkit_INavigation
+ *
+ * Interface
+ *
+ * @category  Toolkit
+ * @package   Toolkit_INavigation
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @release   Release: $id$
+ * @link      <>
+ */
+interface Toolkit_INavigation
+{
+    /**
+     * Class constructor
+     *
+     * @param HTML_Menu          $menu    Menu
+     * @param HTML_Menu_Renderer $rEngine Menu Renderer
+     */
+    public function __construct(
+        HTML_Menu $menu,
+        HTML_Menu_Renderer $rEngine
+    );
+
+    /**
+     * Render page navigation
+     *
+     * @param array   $navigation Navigation
+     * @param unknown $type       Type
+     *
+     * @return void
+     */
+    public function renderPageNav(array $navigation, $type);
+}
+?>
diff --git a/Toolkit/ITable.php b/Toolkit/ITable.php
new file mode 100644 (file)
index 0000000..fb777b8
--- /dev/null
@@ -0,0 +1,57 @@
+<?php
+/**
+ * iTable.php
+ *
+ * PHP version 5
+ *
+ * @category Toolkit
+ * @package  Table
+ * @author   Steve Sutton <steve@gaslightmedia.com>
+ * @license  http://www.gaslightmedia.com Gaslightmedia
+ * @link     <>
+ */
+
+/**
+ * Interface for Table objects
+ *
+ * @category Toolkit
+ * @package  Table
+ * @author   Steve Sutton <steve@gaslightmedia.com>
+ * @license  http://www.gaslightmedia.com Gaslightmedia
+ * @link     <>
+ */
+interface Toolkit_ITable
+{
+    /**
+     * setId
+     *
+     * @param Int $id Table primary key
+     *
+     * @return Object
+     */
+    public function setId($id);
+    /**
+     * save
+     *
+     * @param PDO $dbh Database Connection
+     *
+     * @return void
+     */
+    public function save(PDO $dbh);
+    /**
+     * insert
+     *
+     * @param PDO $dbh Database Connection
+     *
+     * @return Object
+     */
+    public function insert(PDO $dbh);
+    /**
+     * update
+     *
+     * @param PDO $dbh Database Connection
+     *
+     * @return Object
+     */
+    public function update(PDO $dbh);
+}
diff --git a/Toolkit/Image/Converter.php b/Toolkit/Image/Converter.php
new file mode 100644 (file)
index 0000000..8b4b859
--- /dev/null
@@ -0,0 +1,262 @@
+<?php
+
+/**
+ * Converter.php
+ *
+ * Long description (if any) ...
+ *
+ * PHP version 5
+ *
+ * The license text...
+ *
+ * @category  Toolkit
+ * @package   Image
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2010 Steve Sutton
+ * @license   Gaslight Media
+ * @version   CVS: $Id: Converter.php,v 1.2 2010/07/29 14:56:50 matrix Exp $
+ * @link      http://pear.php.net/package/Toolkit_Image
+ * @see       References to other sections (if any)...
+ */
+
+/**
+ * Toolkit_Image_Converter
+ *
+ * Long description (if any) ...
+ *
+ * @category  Toolkit
+ * @package   Image
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2010 Steve Sutton
+ * @license   Gaslight Media
+ * @version   Release: @package_version@
+ * @link      http://pear.php.net/package/Toolkit_Image
+ * @see       References to other sections (if any)...
+ */
+class Toolkit_Image_Converter extends Toolkit_FormBuilder
+{
+
+    /**
+     * Description for $is
+     * @var    object
+     * @access protected
+     */
+    protected $is;
+
+    /**
+     * Description for $dbh
+     * @var    object
+     * @access protected
+     */
+    protected $dbh;
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @param PDO                  $pdo         Databse Handler
+     * @param Toolkit_Image_Server $is          Parameter description (if any) ...
+     * @param string               $formName    Parameter description (if any) ...
+     * @param string               $method      Parameter description (if any) ...
+     * @param string               $action      Parameter description (if any) ...
+     * @param string               $target      Parameter description (if any) ...
+     * @param unknown              $attributes  Parameter description (if any) ...
+     * @param boolean              $trackSubmit Parameter description (if any) ...
+     *
+     * @return void
+     * @access public
+     */
+    function __construct(
+        PDO $pdo,
+        Toolkit_Image_Server $is,
+        $formName,
+        $method = 'post',
+        $action = '',
+        $target = '',
+        $attributes = null,
+        $trackSubmit = false
+    ) {
+        $this->dbh = $pdo;
+        $this->is  = $is;
+               $action = empty($action) ? $_SERVER['REQUEST_URI'] : $action;
+        parent::__construct(
+            $formName,
+            $method,
+            $action,
+            $target,
+            $attributes,
+            $trackSubmit
+        );
+    }
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @return void
+     * @access public
+     */
+    function configureForm()
+    {
+        $this->setupElements();
+        $this->setupRules();
+        $this->setupDefaults();
+        $this->setupConstants();
+    }
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @param array $values Parameter description (if any) ...
+     *
+     * @return void
+     * @access public
+     */
+    function processData($values)
+    {
+        echo '<pre>'.print_r($values, true).'</pre>';
+        $sql = "
+        UPDATE {$values['table_name']}
+           SET {$values['img_field_name']} = :new_img
+         WHERE {$values['primary_key']} = :id";
+        $updateImg = $this->dbh->prepare($sql);
+        try {
+            $sql = "
+            SELECT *
+              FROM {$values['table_name']}
+             WHERE {$values['img_field_name']} != ''
+               AND {$values['img_field_name']} IS NOT NULL
+               AND {$values['img_field_name']} != 'ERROR'
+               AND {$values['img_field_name']} !~ '^is'
+               AND {$values['img_field_name']} !~ '^fs'
+               ";
+            $stmt = $this->dbh->prepare($sql);
+            $stmt->execute();
+            while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                if ($row[$values['primary_key']]) {
+                    echo '<pre>'
+                        . print_r(
+                            $values['original_url']
+                            . '/' . $row[$values['img_field_name']],
+                            true
+                        ) . '</pre>';
+                    $newImage = $this->is->imageUpload($values['original_url']. '/' . $row[$values['img_field_name']]);
+                    $updateImg->bindParam(":new_img", $newImage, PDO::PARAM_STR);
+                    $updateImg->bindParam(":id", $row[$values['primary_key']], PDO::PARAM_STR);
+                    $updateImg->execute();
+                }
+            }
+        } catch(PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+    }
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @return void
+     * @access public
+     */
+    function setupElements()
+    {
+        $e[] = array(
+            'type'    => 'header',
+            'name'    => 'header_rmv',
+            'display' => 'Converter'
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'name'    => 'original_url',
+            'display' => 'Original Path URL without ending /'
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'name'    => 'table_name',
+            'display' => 'Table Name'
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'name'    => 'img_field_name',
+            'display' => 'Image Field Name'
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'name'    => 'primary_key',
+            'display' => 'Primary Key'
+        );
+        $e[] = array(
+            'type'    => 'submit',
+            'name'    => 'submit',
+            'display' => 'Submit'
+        );
+        parent::setupElements($e);
+    }
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @return void
+     * @access public
+     */
+    function setupConstants()
+    {
+    }
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @return void
+     * @access public
+     */
+    function setupDefaults()
+    {
+    }
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @return void
+     * @access public
+     */
+    function setupRules()
+    {
+    }
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @return string Return description (if any) ...
+     * @access public
+     */
+    function toHtml()
+    {
+               if ($this->validate()) {
+                       if ($this->process(array(&$this, 'processData'), $this->mergeFiles)) {
+                               $this->freeze();
+                               //$output = $this->successMsg;
+                               //$output .= parent::toHTML();
+                       }
+               } else if ($this->isSubmitted()) {
+                       $output = $this->errorMsg;
+                       $output .= parent::toHtml();
+               } else {
+                       $output = parent::toHtml();
+               }
+        return $output;
+    }
+}
+?>
diff --git a/Toolkit/Image/Server.php b/Toolkit/Image/Server.php
new file mode 100755 (executable)
index 0000000..c56daca
--- /dev/null
@@ -0,0 +1,385 @@
+<?php
+
+/**
+ * Server.php
+ *
+ * PHP version 5
+ *
+ * @category  Toolkit
+ * @package   Image
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2008 Steve Sutton
+ * @license   Gaslight Media
+ * @version   CVS: $Id: Server.php,v 1.19 2010/05/25 14:02:45 jamie Exp $
+ * @link      http://demo.gaslightmedia.com
+ */
+
+/**
+ * Description for define
+ */
+define('IS_VALIDATE_SECRET', 'Glm0IS1secreT');
+
+/**
+ * Description for define
+ */
+define('IS_SUBMIT_URL', FILE_SERVER_URL . 'submit.phtml');
+
+/**
+ * Toolkit_Image_Server
+ *
+ * Class for implementation of the image server process Chuck has
+ * setup for is0.gaslightmedia.com
+ * Uses Curl PHP Library to upload images to the server
+ *
+ * @category  Toolkit
+ * @package   Image
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2008 Steve Sutton
+ * @license   Gaslight Media
+ * @link      http://demo.gaslightmedia.com
+ */
+class Toolkit_Image_Server
+{
+    // {{{ properties
+
+    /**
+     * OwnerID for the Image Server User
+     * @var    string
+     * @access protected
+     */
+    protected $OwnerID;
+
+    /**
+     * Owner Password
+     * @var    string
+     * @access protected
+     */
+    protected $OwnerPW;
+
+    /**
+     * DOMDocument Object
+     * @var    unknown
+     * @access protected
+     */
+    protected $xml;
+
+    // }}}
+
+    //  {{{ __construct()
+
+    /**
+     * __construct
+     *
+     * @return void
+     * @access public
+     */
+    function __construct()
+    {
+        $this->OwnerID = IS_OWNER_ID;
+        $this->OwnerPW = IS_OWNER_PW;
+    }
+
+    // }}}
+
+    //  {{{ buildImageServerXML()
+
+    /**
+     * buildImageServerXML
+     *
+     * Create the xml for the FileServerRequest
+     *
+     * @param string  $fileName File to upload or delete
+     * @param unknown $type     Upload or Delete
+     *
+     * @return string xml content
+     * @access public
+     */
+    function buildImageServerXML($fileName, $type)
+    {
+        $xml               = new DOMDocument('1.0');
+        $xml->formatOutput = true;
+        $FileServerRequest = $xml->createElement('FileServerRequest');
+        $FileServerRequest->setAttribute('version', '1.0');
+        // AccessRequest part
+        $AccessRequest = $xml->createElement('AccessRequest');
+        $Owner         = $xml->createElement('Owner');
+        $OwnerID       = $xml->createElement('OwnerID', $this->OwnerID);
+        $OwnerPW       = $xml->createElement('OwnerPW', $this->OwnerPW);
+        $Owner->appendChild($OwnerID);
+        $Owner->appendChild($OwnerPW);
+        $AccessRequest->appendChild($Owner);
+        $FileServerRequest->appendChild($AccessRequest);
+        // file part
+        $File = $xml->createElement('File');
+        switch ($type) {
+        case "Upload":
+            $FileAction     = $xml->createElement('FileAction', 'Submit');
+            $DeliveryMethod = $xml->createElement('DeliveryMethod', 'Submit');
+            $FieldName      = $xml->createElement('FieldName', 'file_upload');
+            $File->appendChild($FileAction);
+            $File->appendChild($DeliveryMethod);
+            $File->appendChild($FieldName);
+            $FileServerRequest->appendChild($File);
+            $size     = filesize($_FILES[$fileName]['tmp_name']);
+            $validStr = md5($this->OwnerID.$this->OwnerPW.IS_VALIDATE_SECRET);
+            break;
+        case "URL":
+            $FileAction     = $xml->createElement('FileAction', 'Submit');
+            $DeliveryMethod = $xml->createElement('DeliveryMethod', 'URL');
+            $Src            = $xml->createElement('Src', $fileName);
+            $File->appendChild($FileAction);
+            $File->appendChild($DeliveryMethod);
+            $File->appendChild($Src);
+            $FileServerRequest->appendChild($File);
+            $size     = strlen($fileName);
+            $validStr = md5($this->OwnerID.$this->OwnerPW.IS_VALIDATE_SECRET);
+            break;
+        case "Delete":
+            $FileAction = $xml->createElement('FileAction', 'Delete');
+            $File->appendChild($FileAction);
+            $FileName = $xml->createElement('FileName', $fileName);
+            $File->appendChild($FileName);
+            $FileServerRequest->appendChild($File);
+            $validStr = md5($this->OwnerID.$this->OwnerPW.IS_VALIDATE_SECRET);
+            break;
+        }
+        // validation part
+        $Validation = $xml->createElement('Validation', $validStr);
+        $FileServerRequest->appendChild($Validation);
+        $xml->appendChild($FileServerRequest);
+        return $xml->saveXML($xml);
+    }
+
+    // }}}
+
+    //  {{{ imageDelete()
+
+    /**
+     * imageDelete
+     *
+     * Delete an image from the image server
+     *
+     * @param unknown $name Image name
+     *
+     * @return unknown image name
+     * @access public
+     */
+    function imageDelete($name)
+    {
+        // don't do anything if on development server
+        if (defined('DEVELOPMENT') && DEVELOPMENT == true) {
+            return "File deleted. File: " . $name;
+        }
+        if ($name) {
+            $request     = $this->buildImageServerXML($name, 'Delete');
+            $ch          = curl_init();
+            $fileData    = array(
+                'request' => $request
+                );
+            $curlOptions = array(
+                    CURLOPT_URL            => IS_SUBMIT_URL,
+                    CURLOPT_HEADER         => 0,
+                    CURLOPT_RETURNTRANSFER => 1,
+                    CURLOPT_POSTFIELDS     => $fileData
+                    );
+            curl_setopt_array($ch, $curlOptions);
+
+            $response = curl_exec($ch);
+            curl_close($ch);
+            $xmlDoc   = new DOMDocument;
+            $response = str_replace('<?xml version="1.0"?' . '>', '', $response);
+            $xmlDoc->loadXML($response);
+            $SuccessCode = $this->xmlPathContent(
+                $xmlDoc,
+                '/FileServerResponse/ReplyStatus/SuccessCode'
+            );
+            $Message     = $this->xmlPathContent(
+                $xmlDoc,
+                '/FileServerResponse/ReplyStatus/Message'
+            );
+            $Owner       = $this->xmlPathContent(
+                $xmlDoc,
+                '/FileServerResponse/File/Owner'
+            );
+            $StoredName  = $this->xmlPathContent(
+                $xmlDoc,
+                '/FileServerResponse/File/StoredName'
+            );
+            $StoredSize  = $this->xmlPathContent(
+                $xmlDoc,
+                '/FileServerResponse/File/StoredSize'
+            );
+            $MediaType   = $this->xmlPathContent(
+                $xmlDoc,
+                '/FileServerResponse/File/MediaType'
+            );
+            return $Message;
+        }
+    }
+
+    //  }}}
+    //  {{{ imageUpload()
+
+    /**
+     * imageUpload
+     *
+     * Upload image to server
+     *
+     * @param string $name Form field name of image
+     *
+     * @return string Image name
+     * @access public
+     */
+    function imageUpload($name)
+    {
+        $fileType = null;
+        if (preg_match("/^http/", $name)) {
+            $fileType = 'URL';
+        } elseif (is_array($_FILES[$name])) {
+            $fileType = 'Upload';
+        }
+        if ($fileType) {
+            $request = $this->buildImageServerXML($name, $fileType);
+            $ch      = curl_init();
+            if ($fileType == "URL") {
+                $fileData = array(
+                    'request' => $request
+                    );
+            } else {
+                if (version_compare(PHP_VERSION, '5.5.0', '>=')) {
+                    $fileData = array(
+                        'request'          => $request,
+                        'file_upload'      => new CurlFile(
+                            $_FILES[$name]['tmp_name'],
+                            $_FILES[$name]['type'],
+                            $_FILES[$name]['name']
+                        ),
+                        'file_upload_name' => $_FILES[$name]['name']
+                    );
+                } else {
+                    $fileData = array(
+                        'request'          => $request,
+                        'file_upload'      => '@'.$_FILES[$name]['tmp_name'],
+                        'file_upload_name' => $_FILES[$name]['name']
+                    );
+                }
+            }
+            $curlOptions = array(
+                    CURLOPT_URL            => IS_SUBMIT_URL,
+                    CURLOPT_HEADER         => 0,
+                    CURLOPT_RETURNTRANSFER => 1,
+                    CURLOPT_POSTFIELDS     => $fileData
+                    );
+            curl_setopt_array($ch, $curlOptions);
+
+            $response = curl_exec($ch);
+            curl_close($ch);
+            $xmlDoc   = new DOMDocument;
+            $response = str_replace('<?xml version="1.0"?' . '>', '', $response);
+            $xmlDoc->loadXML($response);
+            $SuccessCode = $this->xmlPathContent(
+                $xmlDoc,
+                '/FileServerResponse/ReplyStatus/SuccessCode'
+            );
+            $Message     = $this->xmlPathContent(
+                $xmlDoc,
+                '/FileServerResponse/ReplyStatus/Message'
+            );
+            $Owner       = $this->xmlPathContent(
+                $xmlDoc,
+                '/FileServerResponse/File/Owner'
+            );
+            $StoredName  = $this->xmlPathContent(
+                $xmlDoc,
+                '/FileServerResponse/File/StoredName'
+            );
+            $StoredSize  = $this->xmlPathContent(
+                $xmlDoc,
+                '/FileServerResponse/File/StoredSize'
+            );
+            $MediaType   = $this->xmlPathContent(
+                $xmlDoc,
+                '/FileServerResponse/File/MediaType'
+            );
+            if ($SuccessCode != 0) {
+                //throw new Exception('Image Server Error said:'.$response);
+                throw new PEAR_Exception('Image Server Error said:'.$response);
+                exit;
+            }
+            return $StoredName;
+        }
+    }
+
+    // }}}
+
+    //  {{{ getImageSize()
+
+    /**
+     * getImageSize
+     *
+     * Return image data on an image from image server
+     *
+     * @param string $image Full URI to image
+     *                      http://is0/userId/imageStyle/imageName
+     *                      Must be a full URI including an authority.
+     *                      No relative URIs, the // are mandatory
+     *
+     * @return array Image data 0 => width, 1 => height, 2 => html
+     * @access public
+     * @throws PEAR Error
+     */
+    function getImageSize($image)
+    {
+        $options = array('allowed_schemes' => array('http','https'));
+        if (!Validate::uri($image, $options)) {
+            throw new PEAR_Exception('Invalid URI for the image');
+            exit;
+        }
+
+        $ch          = curl_init();
+        $curlOptions = array(
+            CURLOPT_URL            => "{$image}/info",
+            CURLOPT_HEADER         => 0,
+            CURLOPT_RETURNTRANSFER => 1,
+//            CURLOPT_POSTFIELDS     => $fileData
+        );
+        curl_setopt_array($ch, $curlOptions);
+
+        $response = curl_exec($ch);
+        curl_close($ch);
+        $wPattern = "/<width>(.*)<\/width>/";
+        preg_match($wPattern, $response, $matches);
+        $width    = $matches[1];
+        $hPattern = "/<height>(.*)<\/height>/";
+        preg_match($hPattern, $response, $matches);
+        $height = $matches[1];
+        $html   = "width=\"{$width}\" height=\"{$height}\"";
+        return array($width, $height, $html);
+    }
+
+    //  }}}
+
+    // {{{  xmlPathContent()
+
+    /**
+     * xmlPathContent
+     *
+     * Grab the content given XPath Query
+     *
+     * @param unknown $dom     DOMDocument nodelist
+     * @param unknown $content query for XPath
+     *
+     * @return object string of node
+     * @access public
+     */
+    function xmlPathContent($dom, $content)
+    {
+        $xPath    = new DOMXPath($dom);
+        $nodelist = $xPath->query($content);
+        foreach ($nodelist as $entry) {
+            return $entry->nodeValue;
+        }
+    }
+
+    // }}}
+}
diff --git a/Toolkit/LeadManager/Affiliates/ConstantContact.php b/Toolkit/LeadManager/Affiliates/ConstantContact.php
new file mode 100644 (file)
index 0000000..8a859f6
--- /dev/null
@@ -0,0 +1,1095 @@
+<?php
+date_default_timezone_set('America/New_York');
+
+/**
+ * Class that is using REST to communicate with ConstantContact server
+ * This class currently supports actions performed using the contacts, lists, and campaigns APIs
+ * @author ConstantContact Dev Team
+ * @version 2.0.0
+ * @since 30.03.2010
+ */
+class CC_Utility
+{
+
+    // FROM HERE YOU MAY MODIFY YOUR CREDENTIALS
+    var $login    = CONSTANT_CONTACT_LOGIN; //Username for your account
+    var $password = CONSTANT_CONTACT_PASSWORD; //Password for your account
+    var $apikey   = CONSTANT_CONTACT_APIKEY; // API Key for your account.
+    // CONTACT LIST OPTIONS
+    var $contact_lists = array(); // Define which lists will be available for sign-up.
+    var $force_lists        = false; // Set this to true to take away the ability for users to select and de-select lists
+    var $show_contact_lists = true; // Set this to false to hide the list name(s) on the sign-up form.
+    // NOTE - Contact Lists will only be hidden if force_lists is set to true. This is to prevent available checkboxes form being hidden.
+    // FORM OPT IN SOURCE - (Who is performing these actions?)
+    var $actionBy = 'ACTION_BY_CONTACT'; // Values: ACTION_BY_CUSTOMER or ACTION_BY_CONTACT
+    // ACTION_BY_CUSTOMER - Constant Contact Account holder. Used in internal applications.
+    // ACTION_BY_CONTACT - Action by Site visitor. Used in web site sign-up forms.
+    // DEBUGGING
+    var $curl_debug = true; // Set this to true to see the response code returned by cURL
+    // YOUR BASIC CHANGES SHOULD END HERE
+    var $requestLogin; //this contains full authentication string.
+    var $lastError         = ''; // this variable will contain last error message (if any)
+    var $apiPath           = 'https://api.constantcontact.com/ws/customers/'; //is used for server calls.
+    var $doNotIncludeLists = array('Removed', 'Do Not Mail', 'Active'); //define which lists shouldn't be returned.
+
+    public function __construct()
+    {
+        //when the object is getting initialized, the login string must be created as API_KEY%LOGIN:PASSWORD
+        $this->requestLogin = $this->apikey . "%" . $this->login . ":" . $this->password;
+        $this->apiPath = $this->apiPath . $this->login;
+
+        try {
+            if ($this->login == "username" || $this->password == "password"
+                || $this->apikey == "apikey") {
+                throw new Exception("You need to update your credentials in your cc_class.php");
+            }
+        } catch (Exception $e) {
+            echo "<strong>" . $e . "</strong>";
+        }
+    }
+
+    /**
+     * Method that returns a list with all states found in states.txt file
+     * @return array with state codes and state names
+     */
+    public function getStates()
+    {
+        $returnArr = array();
+        $lines = file("states.txt");
+        foreach ($lines as $line) {
+            $tmp = explode(" - ", $line);
+            if (sizeof($tmp) == 2) {
+                $returnArr[trim($tmp[1])] = trim($tmp[0]);
+            }
+        }
+        return $returnArr;
+    }
+
+    /**
+     * Returns a list with all countries found in countries.txt file
+     * @return array with country codes and country names
+     */
+    public function getCountries()
+    {
+        $returnArr = array();
+        $lines = file("countries.txt");
+        foreach ($lines as $line) {
+            $tmp = explode(" - ", $line);
+            if (sizeof($tmp) == 2) {
+                $returnArr[trim($tmp[1])] = trim($tmp[0]);
+            }
+        }
+        return $returnArr;
+    }
+
+    /**
+     * Validate an email address
+     * @return  TRUE if address is valid and FALSE if not.
+     */
+    protected function isValidEmail($email)
+    {
+        return preg_match('/^[_a-z0-9-][^()<>@,;:"[] ]*@([a-z0-9-]+.)+[a-z]{2,4}$/i', $email);
+    }
+
+    /**
+     * Private function used to send requests to ConstantContact server
+     * @param string $request - is the URL where the request will be made
+     * @param string $parameter - if it is not empty then this parameter will be sent using POST method
+     * @param string $type - GET/POST/PUT/DELETE
+     * @return a string containing server output/response
+     */
+    protected function doServerCall($request, $parameter = '', $type = "GET")
+    {
+        $ch      = curl_init();
+        $request = str_replace('http://', 'https://', $request);
+        // Convert id URI to BASIC compliant
+        curl_setopt($ch, CURLOPT_URL, $request);
+        curl_setopt($ch, CURLOPT_USERAGENT, "Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)");
+        curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
+        curl_setopt($ch, CURLOPT_USERPWD, $this->requestLogin);
+        # curl_setopt ($ch, CURLOPT_FOLLOWLOCATION  ,1);
+        curl_setopt($ch, CURLOPT_HEADER, 0);
+        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
+        curl_setopt($ch, CURLOPT_HTTPHEADER, Array("Content-Type:application/atom+xml", 'Content-Length: ' . strlen($parameter)));
+        curl_setopt($ch, CURLOPT_FAILONERROR, 1);
+        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
+        switch ($type) {
+            case 'POST':
+                curl_setopt($ch, CURLOPT_POST, 1);
+                curl_setopt($ch, CURLOPT_POSTFIELDS, $parameter);
+                break;
+            case 'PUT':
+                curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PUT");
+                curl_setopt($ch, CURLOPT_POSTFIELDS, $parameter);
+                break;
+            case 'DELETE':
+                curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "DELETE");
+                break;
+            default:
+                curl_setopt($ch, CURLOPT_HTTPGET, 1);
+                break;
+        }
+
+        $emessage = curl_exec($ch);
+        if ($this->curl_debug) {
+            echo $error = curl_error($ch);
+        }
+        curl_close($ch);
+
+        return $emessage;
+    }
+
+}
+
+/**
+ * Class that is used for retrieving
+ * all the Email Lists from Constant Contact and
+ * all Registered Email Addresses
+ */
+class CC_List
+    extends CC_Utility
+{
+
+    /**
+     * Recursive Method that retrieves all the Email Lists from ConstantContact.
+     * @param string $path [default is empty]
+     * @return array of lists
+     */
+    public function getLists($path = '')
+    {
+        $mailLists = array();
+
+        if (empty($path)) {
+            $call = $this->apiPath . '/lists';
+        } else {
+            $call = $path;
+        }
+
+        $return       = $this->doServerCall($call);
+        $parsedReturn = simplexml_load_string($return);
+        $call2        = '';
+
+        foreach ($parsedReturn->link as $item) {
+            $tmp     = $item->Attributes();
+            $nextUrl = '';
+            if ((string) $tmp->rel == 'next') {
+                $nextUrl = (string) $tmp->href;
+                $arrTmp  = explode($this->login, $nextUrl);
+                $nextUrl = $arrTmp[1];
+                $call2   = $this->apiPath . $nextUrl;
+                break;
+            }
+        }
+
+        foreach ($parsedReturn->entry as $item) {
+            if ($this->contact_lists) {
+                if (in_array((string) $item->title, $this->contact_lists)) {
+                    $tmp = array();
+                    $tmp['id']    = (string) $item->id;
+                    $tmp['title'] = (string) $item->title;
+                    $mailLists[]  = $tmp;
+                }
+            } else if (!in_array((string) $item->title, $this->doNotIncludeLists)) {
+                $tmp = array();
+                $tmp['id']    = (string) $item->id;
+                $tmp['title'] = (string) $item->title;
+                $mailLists[]  = $tmp;
+            }
+        }
+
+        if (empty($call2)) {
+            return $mailLists;
+        } else {
+            return array_merge($mailLists, $this->getLists($call2));
+        }
+    }
+
+    /**
+     * Method that retrieves  all Registered Email Addresses.
+     * @param string $email_id [default is empty]
+     * @return array of lists
+     */
+    public function getAccountLists($email_id = '')
+    {
+        $mailAccountList = array();
+
+        if (empty($email_id)) {
+            $call = $this->apiPath . '/settings/emailaddresses';
+        } else {
+            $call = $this->apiPath . '/settings/emailaddresses/' . $email_id;
+        }
+
+        $return       = $this->doServerCall($call);
+        $parsedReturn = simplexml_load_string($return);
+
+        foreach ($parsedReturn->entry as $item) {
+            $nextStatus      = $item->content->Email->Status;
+            $nextEmail       = (string) $item->title;
+            $nextId          = $item->id;
+            $nextAccountList = array('Email' => $nextEmail, 'Id'    => $nextId);
+            if ($nextStatus == 'Verified') {
+                $mailAccountList[] = $nextAccountList;
+            }
+        }
+        return $mailAccountList;
+    }
+
+}
+
+/**
+ * Class that is used for ConstantConact CRUD management
+ */
+class CC_Contact
+    extends CC_Utility
+{
+
+    /**
+     * Method that checks if a subscriber already exist
+     * @param string $email
+     * @return subscriber`s id if it exists or false if it doesn't
+     */
+    public function subscriberExists($email = '')
+    {
+        $call   = $this->apiPath . '/contacts?email=' . $email;
+        $return = $this->doServerCall($call);
+        $xml    = simplexml_load_string($return);
+        $id     = $xml->entry->id;
+        if ($id) {
+            return $id;
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * Method that retrieves from Constant Contact a collection with all the Subscribers
+     * If email parameter is mentioned then only mentioned contact is retrieved.
+     * @param string $email
+     * @return Bi-Dimenstional array with information about contacts.
+     */
+    public function getSubscribers($email = '', $page = '')
+    {
+        $contacts = array();
+        $contacts['items'] = array();
+
+        if (!empty($email)) {
+            $call = $this->apiPath . '/contacts?email=' . $email;
+        } else {
+            if (!empty($page)) {
+                $call = $this->apiPath . $page;
+            } else {
+                $call = $this->apiPath . '/contacts';
+            }
+        }
+
+        $return       = $this->doServerCall($call);
+        $parsedReturn = simplexml_load_string($return);
+        // We parse here the link array to establish which are the next page and previous page
+        foreach ($parsedReturn->link as $item) {
+            $attributes = $item->Attributes();
+
+            if (!empty($attributes['rel']) && $attributes['rel'] == 'next') {
+                $tmp              = explode($this->login, $attributes['href']);
+                $contacts['next'] = $tmp[1];
+            }
+            if (!empty($attributes['rel']) && $attributes['rel'] == 'first') {
+                $tmp               = explode($this->login, $attributes['href']);
+                $contacts['first'] = $tmp[1];
+            }
+            if (!empty($attributes['rel']) && $attributes['rel'] == 'current') {
+                $tmp                 = explode($this->login, $attributes['href']);
+                $contacts['current'] = $tmp[1];
+            }
+        }
+
+        foreach ($parsedReturn->entry as $item) {
+            $tmp = array();
+            $tmp['id']           = (string) $item->id;
+            $tmp['title']        = (string) $item->title;
+            $tmp['status']       = (string) $item->content->Contact->Status;
+            $tmp['EmailAddress'] = (string) $item->content->Contact->EmailAddress;
+            $tmp['EmailType']    = (string) $item->content->Contact->EmailType;
+            $tmp['Name']         = (string) $item->content->Contact->Name;
+            $contacts['items'][] = $tmp;
+        }
+
+        return $contacts;
+    }
+
+    /**
+     * Retrieves all the details for a specific contact identified by $email.
+     * @param string $email
+     * @return array with all information about the contact.
+     */
+    public function getSubscriberDetails($email)
+    {
+        $contact     = $this->getSubscribers($email);
+        $fullContact = array();
+        $call                           = str_replace('http://', 'https://', $contact['items'][0]['id']);
+        // Convert id URI to BASIC compliant
+        $return                         = $this->doServerCall($call);
+        $parsedReturn                   = simplexml_load_string($return);
+        $fullContact['id']              = $parsedReturn->id;
+        $fullContact['email_address']   = $parsedReturn->content->Contact->EmailAddress;
+        $fullContact['first_name']      = $parsedReturn->content->Contact->FirstName;
+        $fullContact['last_name']       = $parsedReturn->content->Contact->LastName;
+        $fullContact['middle_name']     = $parsedReturn->content->Contact->MiddleName;
+        $fullContact['company_name']    = $parsedReturn->content->Contact->CompanyName;
+        $fullContact['job_title']       = $parsedReturn->content->Contact->JobTitle;
+        $fullContact['home_number']     = $parsedReturn->content->Contact->HomePhone;
+        $fullContact['work_number']     = $parsedReturn->content->Contact->WorkPhone;
+        $fullContact['address_line_1']  = $parsedReturn->content->Contact->Addr1;
+        $fullContact['address_line_2']  = $parsedReturn->content->Contact->Addr2;
+        $fullContact['address_line_3']  = $parsedReturn->content->Contact->Addr3;
+        $fullContact['city_name']       = (string) $parsedReturn->content->Contact->City;
+        $fullContact['state_code']      = (string) $parsedReturn->content->Contact->StateCode;
+        $fullContact['state_name']      = (string) $parsedReturn->content->Contact->StateName;
+        $fullContact['country_code']    = $parsedReturn->content->Contact->CountryCode;
+        $fullContact['zip_code']        = $parsedReturn->content->Contact->PostalCode;
+        $fullContact['sub_zip_code']    = $parsedReturn->content->Contact->SubPostalCode;
+        $fullContact['custom_field_1']  = $parsedReturn->content->Contact->CustomField1;
+        $fullContact['custom_field_2']  = $parsedReturn->content->Contact->CustomField2;
+        $fullContact['custom_field_3']  = $parsedReturn->content->Contact->CustomField3;
+        $fullContact['custom_field_4']  = $parsedReturn->content->Contact->CustomField4;
+        $fullContact['custom_field_5']  = $parsedReturn->content->Contact->CustomField5;
+        $fullContact['custom_field_6']  = $parsedReturn->content->Contact->CustomField6;
+        $fullContact['custom_field_7']  = $parsedReturn->content->Contact->CustomField7;
+        $fullContact['custom_field_8']  = $parsedReturn->content->Contact->CustomField8;
+        $fullContact['custom_field_9']  = $parsedReturn->content->Contact->CustomField9;
+        $fullContact['custom_field_10'] = $parsedReturn->content->Contact->CustomField10;
+        $fullContact['custom_field_11'] = $parsedReturn->content->Contact->CustomField11;
+        $fullContact['custom_field_12'] = $parsedReturn->content->Contact->CustomField12;
+        $fullContact['custom_field_13'] = $parsedReturn->content->Contact->CustomField13;
+        $fullContact['custom_field_14'] = $parsedReturn->content->Contact->CustomField14;
+        $fullContact['custom_field_15'] = $parsedReturn->content->Contact->CustomField15;
+        $fullContact['notes']           = $parsedReturn->content->Contact->Note;
+        $fullContact['mail_type']       = $parsedReturn->content->Contact->EmailType;
+        $fullContact['status']          = $parsedReturn->content->Contact->Status;
+        $fullContact['lists']           = array();
+
+        if ($parsedReturn->content->Contact->ContactLists->ContactList) {
+            foreach ($parsedReturn->content->Contact->ContactLists->ContactList as $item) {
+                $fullContact['lists'][] = trim((string) $item->Attributes());
+            }
+        }
+
+        return $fullContact;
+    }
+
+    /**
+     * Method that modifies a contact State to DO NOT MAIL
+     * @param string $email - contact email address
+     * @return TRUE in case of success or FALSE otherwise
+     */
+    public function deleteSubscriber($email)
+    {
+        if (empty($email)) {
+            return false;
+        }
+        $contact = $this->getSubscribers($email);
+        $id      = $contact['items'][0]['id'];
+        $return  = $this->doServerCall($id, '', 'DELETE');
+        if (!empty($return)) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Method that modifies a contact State to REMOVED
+     * @param string $email - contact email address
+     * @return TRUE in case of success or FALSE otherwise
+     */
+    public function removeSubscriber($email)
+    {
+        $contact          = $this->getSubscriberDetails($email);
+        $contact['lists'] = array();
+        $xml = $this->createContactXML($contact['id'], $contact);
+
+        if ($this->editSubscriber($contact['id'], $xml)) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * Upload a new contact to Constant Contact server
+     * @param strong $contactXML - formatted XML with contact information
+     * @return TRUE in case of success or FALSE otherwise
+     */
+    public function addSubscriber($contactXML)
+    {
+        $call         = $this->apiPath . '/contacts';
+        $return       = $this->doServerCall($call, $contactXML, 'POST');
+        $parsedReturn = simplexml_load_string($return);
+
+        if ($return) {
+            return true;
+        } else {
+            $xml          = simplexml_load_string($contactXML);
+            $emailAddress = $xml->content->Contact->EmailAddress;
+            if ($this->subscriberExists($emailAddress)) {
+                $this->lastError = 'This contact already exists. <a href="edit_contact.php?email=' . $emailAddress . '">Click here</a> to edit the contact details.';
+            } else {
+                $this->lastError = 'An Error Occurred';
+            }
+            return false;
+        }
+    }
+
+    /**
+     * Modifies a contact
+     * @param string $contactUrl - identifies the url for the modified contact
+     * @param string $contactXML - formed XML with contact information
+     * @return TRUE in case of success or FALSE otherwise
+     */
+    public function editSubscriber($contactUrl, $contactXML)
+    {
+        $return = $this->doServerCall($contactUrl, $contactXML, 'PUT');
+
+        if (!empty($return)) {
+            if (strpos($return, '<') !== false) {
+                $parsedReturn = simplexml_load_string($return);
+                if (!empty($parsedReturn->message)) {
+                    $this->lastError = $parsedReturn->message;
+                }
+            } else {
+                $this->lastError = $parsedReturn->message;
+            }
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Method that compose the needed XML format for a contact
+     * @param string $id
+     * @param array $params
+     * @return Formed XML
+     */
+    public function createContactXML($id, $params = array())
+    {
+        if (empty($id)) {
+            $id = "urn:uuid:E8553C09F4xcvxCCC53F481214230867087";
+        }
+
+        $update_date  = date("Y-m-d") . 'T' . date("H:i:s") . '+01:00';
+        $xml_string   = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><entry xmlns='http://www.w3.org/2005/Atom'></entry>";
+        $xml_object   = simplexml_load_string($xml_string);
+        $title_node   = $xml_object->addChild("title", htmlspecialchars(("TitleNode"), ENT_QUOTES, 'UTF-8'));
+        $updated_node = $xml_object->addChild("updated", htmlspecialchars(($update_date), ENT_QUOTES, 'UTF-8'));
+        $author_node  = $xml_object->addChild("author");
+        $author_name  = $author_node->addChild("name", ("CTCT Samples"));
+        $id_node      = $xml_object->addChild("id", htmlspecialchars(($id), ENT_QUOTES, 'UTF-8'));
+        $summary_node = $xml_object->addChild("summary", htmlspecialchars(("Customer document"), ENT_QUOTES, 'UTF-8'));
+        $summary_node->addAttribute("type", "text");
+        $content_node = $xml_object->addChild("content");
+        $content_node->addAttribute("type", "application/vnd.ctct+xml");
+        $contact_node = $content_node->addChild("Contact", htmlspecialchars(("Customer document"), ENT_QUOTES, 'UTF-8'));
+        $contact_node->addAttribute("xmlns", "http://ws.constantcontact.com/ns/1.0/");
+        $email_node   = $contact_node->addChild("EmailAddress", htmlspecialchars(($params['email_address']), ENT_QUOTES, 'UTF-8'));
+        $fname_node   = $contact_node->addChild("FirstName", urldecode(htmlspecialchars(($params['first_name']), ENT_QUOTES, 'UTF-8')));
+        $lname_node   = $contact_node->addChild("LastName", urldecode(htmlspecialchars(($params['last_name']), ENT_QUOTES, 'UTF-8')));
+        $lname_node   = $contact_node->addChild("MiddleName", urldecode(htmlspecialchars(($params['middle_name']), ENT_QUOTES, 'UTF-8')));
+        if (isset($params['company_name'])) {
+            $lname_node = $contact_node->addChild("CompanyName", urldecode(htmlspecialchars(($params['company_name']), ENT_QUOTES, 'UTF-8')));
+        }
+        if (isset($params['JobTitle'])) {
+            $lname_node = $contact_node->addChild("job_title", urldecode(htmlspecialchars(($params['job_title']), ENT_QUOTES, 'UTF-8')));
+        }
+        if (isset($params['status'])) {
+            if ($params['status'] == 'Do Not Mail') {
+                $this->actionBy = 'ACTION_BY_CONTACT';
+            }
+        }
+
+        $optin_node = $contact_node->addChild("OptInSource", htmlspecialchars($this->actionBy));
+        $hn_node    = $contact_node->addChild("HomePhone", htmlspecialchars($params['home_number'], ENT_QUOTES, 'UTF-8'));
+        if (isset($params['work_number'])) {
+            $wn_node     = $contact_node->addChild("WorkPhone", htmlspecialchars($params['work_number'], ENT_QUOTES, 'UTF-8'));
+        }
+        $ad1_node    = $contact_node->addChild("Addr1", htmlspecialchars($params['address_line_1'], ENT_QUOTES, 'UTF-8'));
+        $ad2_node    = $contact_node->addChild("Addr2", htmlspecialchars($params['address_line_2'], ENT_QUOTES, 'UTF-8'));
+        $ad3_node    = $contact_node->addChild("Addr3", htmlspecialchars($params['address_line_3'], ENT_QUOTES, 'UTF-8'));
+        $city_node   = $contact_node->addChild("City", htmlspecialchars($params['city_name'], ENT_QUOTES, 'UTF-8'));
+        $state_node  = $contact_node->addChild("StateCode", htmlspecialchars($params['state_code'], ENT_QUOTES, 'UTF-8'));
+        $state_name  = $contact_node->addChild("StateName", htmlspecialchars($params['state_name'], ENT_QUOTES, 'UTF-8'));
+        $ctry_node   = $contact_node->addChild("CountryCode", htmlspecialchars($params['country_code'], ENT_QUOTES, 'UTF-8'));
+        $zip_node    = $contact_node->addChild("PostalCode", htmlspecialchars($params['zip_code'], ENT_QUOTES, 'UTF-8'));
+        $subzip_node = $contact_node->addChild("SubPostalCode", htmlspecialchars($params['sub_zip_code'], ENT_QUOTES, 'UTF-8'));
+        if (isset($params['notes'])) {
+            $note_node      = $contact_node->addChild("Note", htmlspecialchars($params['notes'], ENT_QUOTES, 'UTF-8'));
+        }
+        $emailtype_node = $contact_node->addChild("EmailType", htmlspecialchars($params['mail_type'], ENT_QUOTES, 'UTF-8'));
+
+        if (!empty($params['custom_fields'])) {
+            foreach ($params['custom_fields'] as $k => $v) {
+                $contact_node->addChild("CustomField" . $k, htmlspecialchars(($v), ENT_QUOTES, 'UTF-8'));
+            }
+        }
+
+        $contactlists_node = $contact_node->addChild("ContactLists");
+        if ($params['lists']) {
+            foreach ($params['lists'] as $tmp) {
+                $contactlist_node = $contactlists_node->addChild("ContactList");
+                $contactlist_node->addAttribute("id", $tmp);
+            }
+        }
+
+        $entry = $xml_object->asXML();
+        return $entry;
+    }
+
+}
+
+/**
+ * Class that is used for ConstantCampaign CRUD management
+ */
+class CC_Campaign
+    extends CC_Utility
+{
+
+    // set this to true to see the xml sent and the output received
+    var $sent_recived_debug = false;
+    var $usStates           = array("AL", "AK", "AZ", "AR", "CA", "CO", "CT", "DE", "DC", "FL", "GA", "HI", "ID", "IL", "IN", "IA", "KS", "KY", "LA", "ME", "MD", "MA", "MI", "MN", "MS", "MO", "MT", "NE", "NV", "NH", "NJ", "NM", "NY", "NC", "ND", "OH", "OK", "OR", "PA", "RI", "SC", "SD", "TN", "TX", "UT", "VT", "VA", "WA", "WV", "WI", "WY");
+    var $caStates = array("AB", "BC", "MB", "NB", "NL", "NT", "NS", "NU", "ON", "PE", "QC", "SK", "YT");
+    var $armedForces = array("AA", "AE", "AP");
+
+    /**
+     * Method that returns a html sample for email campaign
+     * @param string $sample [default is EmailContent]: EmailContent, EmailTextContent or
+     * PermissionReminder
+     * @param string $type [default is html]: html or text
+     * @return a default content for email content or permission reminder
+     */
+    public function getEmailIntro($sample = 'EmailContent', $type = 'html')
+    {
+        switch ($sample) {
+            case 'EmailContent':
+                $file = 'EmailContent.txt';
+                break;
+            case 'EmailTextContent':
+                $file = 'EmailContent.txt';
+                $type = 'text';
+                break;
+            case 'PermissionReminder':
+                $file = 'PermissionReminder.txt';
+                break;
+            default:
+                $file = 'EmailContent.txt';
+        }
+
+        $handle   = fopen("txt/$file", "rb");
+        $contents = '';
+        while (!feof($handle)) {
+            $contents .= fread($handle, 8192);
+        }
+        $contents = ($type == 'html')
+            ? ($contents)
+            : (trim(strip_tags($contents)));
+        fclose($handle);
+        return $contents;
+    }
+
+    /**
+     * Method that retrieves campaingn collections from ConstantCampaign
+     * If campaign_id is mentioned then only mentioned campaign is retrieved.
+     * If campaign_id represents a status [SENT, DRAFT, RUNNING, SCHEDULED]
+     * only the campaigns with that status will be retrieved
+     * @param string $campaign_id [default is empty]
+     * @return Bi-Dimenstional array with information about campaigns.
+     */
+    public function getCampaigns($campaign_id = '', $page = '')
+    {
+        $campaigns = array();
+        $campaigns['items'] = array();
+
+        switch ($campaign_id) {
+            case 'SENT':
+            case 'DRAFT':
+            case 'RUNNING':
+            case 'SCHEDULED':
+                $call = $this->apiPath . '/campaigns?status=' . $campaign_id;
+                break;
+            case 'ALL':
+                $call = (!empty($page))
+                    ? ($this->apiPath . $page)
+                    : ($this->apiPath . '/campaigns');
+                break;
+            default:
+                $call = $this->apiPath . '/campaigns/' . $campaign_id;
+        }
+
+        $return       = $this->doServerCall($call);
+        $parsedReturn = simplexml_load_string($return);
+        //we parse here the link array to establish which are the next page and previous page
+        if ($parsedReturn != false) {
+
+            foreach ($parsedReturn->link as $item) {
+                $attributes = $item->Attributes();
+                if (!empty($attributes['rel']) && $attributes['rel'] == 'next') {
+                    $tmp               = explode($this->login, $attributes['href']);
+                    $campaigns['next'] = $tmp[1];
+                }
+                if (!empty($attributes['rel']) && $attributes['rel'] == 'first') {
+                    $tmp                = explode($this->login, $attributes['href']);
+                    $campaigns['first'] = $tmp[1];
+                }
+                if (!empty($attributes['rel']) && $attributes['rel'] == 'current') {
+                    $tmp                  = explode($this->login, $attributes['href']);
+                    $campaigns['current'] = $tmp[1];
+                }
+            }
+
+            foreach ($parsedReturn->entry as $item) {
+                $tmp = array();
+                $tmp['id']            = (string) $item->id;
+                $tmp['title']         = (string) $item->title;
+                $tmp['name']          = (string) $item->content->Campaign->Name;
+                $tmp['status']        = (string) $item->content->Campaign->Status;
+                $timestamp            = strtotime($item->content->Campaign->Date);
+                $campaig_date         = date("F j, Y, g:i a", $timestamp);
+                $tmp['date']          = (string) $campaig_date;
+                $campaigns['items'][] = $tmp;
+            }
+        }
+        return $campaigns;
+    }
+
+    /**
+     * Retrieves all the details for a specific campaign identified by $id.
+     * @param string $id
+     * @return array with all information about the campaign.
+     */
+    public function getCampaignDetails($id)
+    {
+        if (!empty($id)) {
+            $fullContact = array();
+            $call                       = str_replace('http://', 'https://', $id);
+            // Convert id URI to BASIC compliant
+            $return                     = $this->doServerCall($call);
+            $parsedReturn               = simplexml_load_string($return);
+            $fullCampaign['campaignId'] = $parsedReturn->id;
+            $cmp_vars                   = get_object_vars($parsedReturn->content->Campaign);
+
+            foreach ($cmp_vars as $var_name => $cmp_item) {
+                $fullCampaign[$var_name] = $cmp_item;
+            }
+
+            $cmp_from_email               = $parsedReturn->content->Campaign->FromEmail->EmailAddress;
+            $fullCampaign['FromEmail']    = (string) $cmp_from_email;
+            $cmp_reply_email              = $parsedReturn->content->Campaign->ReplyToEmail->EmailAddress;
+            $fullCampaign['ReplyToEmail'] = (string) $cmp_reply_email;
+            $fullCampaign['lists']        = array();
+
+            if ($parsedReturn->content->Campaign->ContactLists->ContactList) {
+                foreach ($parsedReturn->content->Campaign->ContactLists->ContactList as $item) {
+                    $fullCampaign['lists'][] = trim((string) $item->Attributes());
+                }
+            }
+            return $fullCampaign;
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * Check if a specific campaign exist already
+     * @param string $id
+     * @param string $new_name
+     * @return a boolean value.
+     */
+    public function campaignExists($id = '', $new_name)
+    {
+        if (!empty($id)) {
+            $call   = $this->apiPath . '/campaigns/' . $id;
+            $return = $this->doServerCall($call);
+            $xml    = simplexml_load_string($return);
+            if ($xml !== false) {
+                $id   = $xml->content->Campaign->Attributes();
+                $id   = $id['id'];
+                $name = $xml->content->Campaign->Name;
+            } else {
+                $id            = null;
+                $name          = null;
+            }
+            $all_campaigns = $this->getCampaigns('ALL');
+            $all_campaigns = $all_campaigns['items'];
+            foreach ($all_campaigns as $key => $item) {
+                if ($item['name'] == $new_name) {
+                    return 1;  // 1 - the new campaign has a similar name with an old one
+                    break;
+                }
+            }
+            /**
+             * 2 - this campaign already exist
+             * 0 - this is a new campaign
+             */
+            return ($id != null)
+                ? (2)
+                : (0);
+        }
+    }
+
+    /**
+     * Method that delete a camaign; this will exclude
+     * the removed campaign from overall statistics
+     * @param string $id - campaign id
+     * @return TRUE in case of success or FALSE otherwise
+     */
+    public function deleteCampaign($id)
+    {
+        if (empty($id)) {
+            return false;
+        }
+        $return = $this->doServerCall($id, '', 'DELETE');
+        if (!empty($return) || $return === false) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Upload a new campaign to ConstantContact server
+     * @param string $campaignXML - formatted XML with campaign information
+     * @return TRUE in case of success or FALSE otherwise
+     */
+    public function addCampaign($campaignXML)
+    {
+        $call         = $this->apiPath . '/campaigns';
+        $return       = $this->doServerCall($call, $campaignXML, 'POST');
+        $parsedReturn = simplexml_load_string($return);
+        if ($return) {
+            return true;
+        } else {
+            $xml      = simplexml_load_string($campaignXML);
+            $cmp_id   = $xml->content->Campaign->Attributes();
+            $cmp_id   = $cmp_id['id'];
+            $cmp_name = $xml->content->Campaign->Name;
+            if (!empty($cmp_id)) {
+                $search_status = $this->campaignExists($cmp_id, $cmp_name);
+                switch ($search_status) {
+                    case 0:
+                        $error = 'An Error Occurred. The campaign could not be added.';
+                        break;
+                    case 1:
+                        $error = 'The name of the campaign already exist. Each campaign must have a distinct name.';
+                        break;
+                    case 2:
+                        $error = 'This campaign already exists.';
+                        break;
+                    default:
+                        $error = 'An Error Occurred. The campaign could not be added.';
+                }
+                $this->lastError = $error;
+            } else {
+                $this->lastError = 'An Error Occurred. The campaign could not be added.';
+            }
+            return false;
+        }
+    }
+
+    /**
+     * Modifies a campaign
+     * @param string $campaignId - identifies the id for the modified campaign
+     * @param string $campaignXML - formed XML with campaign information
+     * @return TRUE in case of success or FALSE otherwise
+     */
+    public function editCampaign($campaignId, $campaignXML)
+    {
+        $return = $this->doServerCall($campaignId, $campaignXML, 'PUT');
+        if ($return === false) {
+            $this->lastError = 'An Error Occurred. The campaign could not be edited.';
+            return false;
+        } else {
+            if (!empty($return)) {
+                if (strpos($return, '<') !== false) {
+                    $parsedReturn = simplexml_load_string($return);
+                    if (!empty($parsedReturn->message)) {
+                        $this->lastError = $parsedReturn->message;
+                    }
+                } else {
+                    $this->lastError = $parsedReturn->message;
+                }
+                return false;
+            }
+            return true;
+        }
+    }
+
+    /**
+     * Method that validate the current campaign before sending it to server
+     * @param string $id
+     * @param array $params
+     * @return an error message or true
+     */
+    public function validateCampaign($id, $params = array())
+    {
+        if (trim($params['cmp_name']) == '') {
+            $this->lastError = '<i>Campaign Name</i> is mandatory.';
+            return true;
+        } elseif (trim($params['cmp_subject']) == '') {
+            $this->lastError = '<i>Subject</i> is mandatory.';
+            return true;
+        } elseif (trim($params['cmp_from_name']) == '') {
+            $this->lastError = '<i>From Name</i> is mandatory.';
+            return true;
+        } elseif (trim($params['cmp_from_email']) == '') {
+            $this->lastError = '<i>From Email Address</i> is mandatory.';
+            return true;
+        } elseif (trim($params['cmp_reply_email']) == '') {
+            $this->lastError = '<i>Reply Email Address</i> is mandatory.';
+            return true;
+        } elseif (trim($params['cmp_grt_name']) == '') {
+            $this->lastError = '<i>Greeting Name</i> is mandatory.';
+            return true;
+        } elseif (trim($params['cmp_org_name']) == '') {
+            $this->lastError = '<i>Organization Name</i> is mandatory.';
+            return true;
+        } elseif (trim($params['cmp_org_addr1']) == '') {
+            $this->lastError = '<i>Address 1</i> is mandatory.';
+            return true;
+        } elseif (trim($params['cmp_org_city']) == '') {
+            $this->lastError = '<i>City</i> is mandatory.';
+            return true;
+        } elseif (trim($params['org_zip']) == '') {
+            $this->lastError = '<i>Zip/Postal Code</i> is mandatory.';
+            return true;
+        } elseif (trim($params['org_country']) == '') {
+            $this->lastError = '<i>Country</i> is mandatory.';
+            return true;
+        } elseif (trim($params['cmp_html_body']) == '') {
+            $this->lastError = '<i>HTML Body</i> is mandatory.';
+            return true;
+        } elseif ($params["lists"] == NULL) {
+            $this->lastError = 'Choose at least <i>one Campaign</i> from the list.';
+            return true;
+        } else {
+            if (trim($params['cmp_perm_reminder']) == 'YES') {
+                $reminder_text = $params['cmp_txt_reminder'];
+                if (trim($reminder_text) == '') {
+                    $this->lastError = '<i>Permission Reminder</i> is required.';
+                    return true;
+                }
+            }
+            if (trim($params['org_country']) != '') {
+                if (trim($params['org_country']) == 'us') {
+                    if (trim($params['org_state_us']) == '') {
+                        $this->lastError = '<i>State</i> is mandatory.';
+                        return true;
+                    }
+                    if (in_array($params['org_state_us'], $this->caStates)) {
+                        $this->lastError = '<i>US State</i> is required.';
+                        return true;
+                    }
+                } elseif (trim($params['org_country']) == 'ca') {
+                    if (trim($params['org_state_us']) == '') {
+                        $this->lastError = '<i>State</i> is mandatory.';
+                        return true;
+                    }
+                    if (in_array($params['org_state_us'], $this->usStates)) {
+                        $this->lastError = '<i>CA State</i> is required.';
+                        return true;
+                    }
+                }
+            }
+            if (trim($params['cmp_as_webpage']) == 'YES') {
+                if (trim($params['cmp_as_webtxt']) == '') {
+                    $this->lastError = '<i>Webpage Text</i> is required.';
+                    return true;
+                }
+                if (trim($params['cmp_as_weblink']) == '') {
+                    $this->lastError = '<i>Webpage Link Text</i> is required.';
+                    return true;
+                }
+            }
+            if (trim($params['cmp_forward']) == 'YES') {
+                $fwd_email = $params['cmp_fwd_email'];
+                if (trim($params['cmp_fwd_email']) == '') {
+                    $this->lastError = '<i>Forward email</i> is required.';
+                    return true;
+                }
+            }
+            if (trim($params['cmp_subscribe']) == 'YES') {
+                if (trim($params['cmp_sub_link']) == '') {
+                    $this->lastError = '<i>Subscribe me</i> is required.';
+                    return true;
+                }
+            } else {
+                return false;
+            }
+        }
+    }
+
+    /**
+     * Method that compose the needed XML format for a campaign
+     * @param string $id
+     * @param array $params
+     * @return Formed XML
+     */
+    public function createCampaignXML($id, $params = array())
+    {
+        if (empty($id)) {  // Add a new Campaign
+            $id          = str_replace('https://', 'http://', $this->apiPath . "/campaigns/1100546096289");
+            $standard_id = str_replace('https://', 'http://', $this->apiPath . "/campaigns");
+        } else {
+            $standard_id              = $id;
+        }
+        $href                     = str_replace("http://api.constantcontact.com", "", $id);
+        $standard_href            = str_replace("https://api.constantcontact.com", "", $this->apiPath . "/campaigns");
+        $update_date              = date("Y-m-d") . 'T' . date("H:i:s") . '+01:00';
+        $xml_string               = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><entry xmlns='http://www.w3.org/2005/Atom'></entry>";
+        $xml_object               = simplexml_load_string($xml_string);
+        $link_node                = $xml_object->addChild("link");
+        $link_node->addAttribute("href", $standard_href); //[1st *href]
+        $link_node->addAttribute("rel", "edit");
+        $id_node                  = $xml_object->addChild("id", $standard_id);  //[1st *id]
+        $title_node               = $xml_object->addChild("title", htmlspecialchars($params['cmp_name'], ENT_QUOTES, 'UTF-8'));
+        $title_node->addAttribute("type", "text");
+        $updated_node             = $xml_object->addChild("updated", htmlentities($update_date));
+        $author_node              = $xml_object->addChild("author");
+        $author_name              = $author_node->addChild("name", htmlentities("Constant Contact"));
+        $content_node             = $xml_object->addChild("content");
+        $content_node->addAttribute("type", "application/vnd.ctct+xml");
+        $campaign_node            = $content_node->addChild("Campaign");
+        $campaign_node->addAttribute("xmlns", "http://ws.constantcontact.com/ns/1.0/");
+        $campaign_node->addAttribute("id", $id);  //[2nd *id]
+        $name_node                = $campaign_node->addChild("Name", urldecode(htmlspecialchars($params['cmp_name'], ENT_QUOTES, 'UTF-8')));
+        $campaign_status          = !empty($params['cmp_status'])
+            ? ($params['cmp_status'])
+            : ('Draft');
+        $status_node              = $campaign_node->addChild("Status", urldecode(htmlentities($campaign_status)));
+        $campaign_date            = !empty($params['cmp_date'])
+            ? ($params['cmp_date'])
+            : ($update_date);
+        $date_node                = $campaign_node->addChild("Date", urldecode(htmlentities($campaign_date)));
+        $subj_node                = $campaign_node->addChild("Subject", urldecode(htmlspecialchars($params['cmp_subject'], ENT_QUOTES, 'UTF-8')));
+        $from_name_node           = $campaign_node->addChild("FromName", urldecode(htmlspecialchars($params['cmp_from_name'], ENT_QUOTES, 'UTF-8')));
+        $view_as_webpage          = (!empty($params['cmp_as_webpage']) && $params['cmp_as_webpage'] == 'YES')
+            ? ('YES')
+            : ('NO');
+        $as_webpage_node          = $campaign_node->addChild("ViewAsWebpage", urldecode(htmlentities($view_as_webpage)));
+        #$as_web_lnk_txt = ($view_as_webpage == 'YES') ? ($params['cmp_as_weblink']) : ('');
+        $as_web_lnk_txt           = $params['cmp_as_weblink'];
+        $as_weblink_node          = $campaign_node->addChild("ViewAsWebpageLinkText", urldecode(htmlspecialchars(($as_web_lnk_txt), ENT_QUOTES, 'UTF-8')));
+        #$as_web_txt = ($view_as_webpage == 'YES') ? ($params['cmp_as_webtxt']) : ('');
+        $as_web_txt               = $params['cmp_as_webtxt'];
+        $as_webtxt_node           = $campaign_node->addChild("ViewAsWebpageText", urldecode(htmlspecialchars(($as_web_txt), ENT_QUOTES, 'UTF-8')));
+        $perm_reminder_node       = $campaign_node->addChild("PermissionReminder", urldecode(htmlentities($params['cmp_perm_reminder'])));
+        $permission_reminder_text = ($params['cmp_perm_reminder'] == 'YES')
+            ? ($params['cmp_txt_reminder'])
+            : ('');
+        $text_reminder_node       = $campaign_node->addChild("PermissionReminderText", urldecode(htmlspecialchars(($permission_reminder_text), ENT_QUOTES, 'UTF-8')));
+        $grt_sal_node             = $campaign_node->addChild("GreetingSalutation", htmlspecialchars(($params['cmp_grt_sal']), ENT_QUOTES, 'UTF-8'));
+        $grt_name_node            = $campaign_node->addChild("GreetingName", htmlentities($params['cmp_grt_name']));
+        $grt_str_node             = $campaign_node->addChild("GreetingString", htmlspecialchars($params['cmp_grt_str'], ENT_QUOTES, 'UTF-8'));
+        $org_name_node            = $campaign_node->addChild("OrganizationName", htmlspecialchars($params['cmp_org_name'], ENT_QUOTES, 'UTF-8'));
+        $org_addr1_node           = $campaign_node->addChild("OrganizationAddress1", htmlspecialchars($params['cmp_org_addr1'], ENT_QUOTES, 'UTF-8'));
+        $org_addr2_node           = $campaign_node->addChild("OrganizationAddress2", htmlspecialchars($params['cmp_org_addr2'], ENT_QUOTES, 'UTF-8'));
+        $org_addr3_node           = $campaign_node->addChild("OrganizationAddress3", htmlspecialchars($params['cmp_org_addr3'], ENT_QUOTES, 'UTF-8'));
+        $org_city_node            = $campaign_node->addChild("OrganizationCity", htmlspecialchars($params['cmp_org_city'], ENT_QUOTES, 'UTF-8'));
+        switch ($params['org_country']) {
+            case 'us':
+                $us_state          = $params['org_state_us'];
+                break;
+            case 'ca':
+                $us_state          = $params['org_state_us'];
+                break;
+            default:
+                $us_state          = '';
+        }
+        $org_state_us_node = $campaign_node->addChild("OrganizationState", htmlentities($us_state));
+        switch ($params['org_country']) {
+            case 'us':
+                $international_state = '';
+                break;
+            case 'ca':
+                $international_state = '';
+                break;
+            default:
+                $international_state = htmlspecialchars($params['org_state'], ENT_QUOTES, 'UTF-8');
+        }
+        $org_state_name      = $campaign_node->addChild("OrganizationInternationalState", htmlentities($international_state));
+        $org_country_node    = $campaign_node->addChild("OrganizationCountry", htmlentities($params['org_country']));
+        $org_zip_node        = $campaign_node->addChild("OrganizationPostalCode", htmlspecialchars($params['org_zip'], ENT_QUOTES, 'UTF-8'));
+        $include_fwd_email   = (!empty($params['cmp_forward']) && $params['cmp_forward'] == 'YES')
+            ? ('YES')
+            : ('NO');
+        #$fwd_txt = ($include_fwd_email == 'YES') ? ($params['cmp_fwd_email']) :('');
+        $fwd_txt             = $params['cmp_fwd_email'];
+        $fwd_node            = $campaign_node->addChild("IncludeForwardEmail", htmlentities($include_fwd_email));
+        $fwd_email_node      = $campaign_node->addChild("ForwardEmailLinkText", htmlspecialchars(($fwd_txt), ENT_QUOTES, 'UTF-8'));
+        $include_sub_link    = (!empty($params['cmp_subscribe']) && $params['cmp_subscribe'] == 'YES')
+            ? ('YES')
+            : ('NO');
+        $sub_node            = $campaign_node->addChild("IncludeSubscribeLink", htmlentities($include_sub_link));
+        #$sub_txt = ($include_sub_link == 'YES') ? ($params['cmp_sub_link']) : ('');
+        $sub_txt             = $params['cmp_sub_link'];
+        $sub_link_node       = $campaign_node->addChild("SubscribeLinkText", htmlspecialchars(($sub_txt), ENT_QUOTES, 'UTF-8'));
+        $email_format_node   = $campaign_node->addChild("EmailContentFormat", $params['cmp_mail_type']);
+        if ($params['cmp_type'] != 'STOCK') {
+            $html_body_node       = $campaign_node->addChild("EmailContent", htmlspecialchars($params['cmp_html_body'], ENT_QUOTES, 'UTF-8'));
+            $text_body_node       = $campaign_node->addChild("EmailTextContent", "<Text>" . htmlspecialchars(strip_tags($params['cmp_text_body']), ENT_QUOTES, 'UTF-8') . "</Text>");
+            $campaign_style_sheet = ($params['cmp_mail_type'] == 'XHTML')
+                ? ($params['cmp_style_sheet'])
+                : ('');
+            $style_sheet_node     = $campaign_node->addChild("StyleSheet", htmlspecialchars($campaign_style_sheet, ENT_QUOTES, 'UTF-8'));
+        }
+        $campaignlists_node   = $campaign_node->addChild("ContactLists");
+
+        if ($params['lists']) {
+            foreach ($params['lists'] as $list) {
+                $campaignlist_node = $campaignlists_node->addChild("ContactList");
+                $campaignlist_node->addAttribute("id", $list);
+                $campaignlink_node = $campaignlist_node->addChild("link");
+                $campaignlink_node->addAttribute("xmlns", "http://www.w3.org/2005/Atom");
+                $campaignlink_node->addAttribute("href", str_replace("http://api.constantcontact.com", "", $list));
+                $campaignlink_node->addAttribute("rel", "self");
+            }
+        }
+
+        $cmp_from_email    = explode('|', $params['cmp_from_email']);
+        $fromemail_node    = $campaign_node->addChild("FromEmail");
+        $femail_node       = $fromemail_node->addChild("Email");
+        $femail_node->addAttribute("id", $cmp_from_email[1]);
+        $femail_node_link  = $femail_node->addChild("link");
+        $femail_node_link->addAttribute("xmlns", "http://www.w3.org/2005/Atom");
+        $femail_node_link->addAttribute("href", str_replace("http://api.constantcontact.com", "", $cmp_from_email[1]));
+        $femail_node_link->addAttribute("rel", "self");
+        $femail_addrs_node = $fromemail_node->addChild("EmailAddress", htmlentities($cmp_from_email[0]));
+        $cmp_reply_email   = explode('|', $params['cmp_reply_email']);
+        $replyemail_node   = $campaign_node->addChild("ReplyToEmail");
+        $remail_node       = $replyemail_node->addChild("Email");
+        $remail_node->addAttribute("id", $cmp_reply_email[1]);
+        $remail_node_link  = $remail_node->addChild("link");
+        $remail_node_link->addAttribute("xmlns", "http://www.w3.org/2005/Atom");
+        $remail_node_link->addAttribute("href", str_replace("http://api.constantcontact.com", "", $cmp_reply_email[1]));
+        $remail_node_link->addAttribute("rel", "self");
+        $remail_addrs_node = $replyemail_node->addChild("EmailAddress", htmlentities($cmp_reply_email[0]));
+        $source_node       = $xml_object->addChild("source");
+        $sourceid_node     = $source_node->addChild("id", $standard_id);  //[3th *id]
+        $sourcetitle_node  = $source_node->addChild("title", "Campaigns for customer: " . $this->login);
+        $sourcetitle_node->addAttribute("type", "text");
+        $sourcelink1_node  = $source_node->addChild("link");
+        $sourcelink1_node->addAttribute("href", "campaigns");  //[2nd *href]
+        $sourcelink2_node  = $source_node->addChild("link");
+        $sourcelink2_node->addAttribute("href", "campaigns");  //[3th *href]
+        $sourcelink2_node->addAttribute("rel", "self");
+        $sourceauthor_node = $source_node->addChild("author");
+        $sauthor_name      = $sourceauthor_node->addChild("name", $this->login);
+        $sourceupdate_node = $source_node->addChild("updated", htmlentities($update_date));
+
+        $entry = $xml_object->asXML();
+        // $search  = array('&gt;', '\"', '&#13;', '&#10;&#13;', '"/>', '&', '&amp;lt;', '�', '�');
+        // $replace = array('>', '"', '', '', '" />', '&amp;', '&lt;', '&amp;Yuml;', '&amp;yuml;');
+        // $entry = str_replace($search, $replace, $entry);
+
+        if ($this->sent_recived_debug) {
+            echo "<div><p style=\"color: blue\">We sent the following XML:</p>  $entry  </div><hr/>";
+        }
+
+        return $entry;
+    }
+
+}
diff --git a/Toolkit/LeadManager/Affiliates/StreamSend.php b/Toolkit/LeadManager/Affiliates/StreamSend.php
new file mode 100755 (executable)
index 0000000..2dfc8a2
--- /dev/null
@@ -0,0 +1,212 @@
+<?php
+/**
+ * StreamSend.php
+ *
+ * PHP version 5
+ *
+ * All Right Reserved
+ *
+ * @category  Contacts
+ * @package   LeadManager
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2009 Gaslight Media
+ * @license   Gaslight Media
+ * @version   CVS: $Id: StreamSend.php,v 1.5 2010/04/30 19:02:10 matrix Exp $
+ * @link      <>
+ */
+
+/**
+ * Default parameters for contact create operations.
+ * Note that these are strings for use in XML data not true/false values.
+ * If false, the person will be created with a status of pending
+ */
+define('STREAMSEND_DEFAULT_ACTIVATE', 'false');
+/**
+  * If activate is false, setting this to true will trigger the sending of the built-in
+  * activation notification; if activate is true, this setting has no effect
+  */
+define('STREAMSEND_DEFAULT_DELIVER_ACTIVATION', 'true');
+/**
+  * If activate is true, setting this to true will trigger the sending of the built-in
+  * welcome notification; if activate is false, this setting has no effect
+  */
+define('STREAMSEND_DEFAULT_DELIVER_WELCOME', 'false');
+/**
+  * URI for streamsend API
+  */
+define('STREAMSEND_BASE_URL', "https://app.streamsend.com");
+
+/**
+ * Toolkit_Contacts_StreamSend
+ *
+ * Contact support class for inergration with StreamSend API
+ *
+ * @category  Contacts
+ * @package   LeadManager
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2009 Gaslight Media
+ * @license   Gaslight Media
+ * @link      <>
+ */
+class Toolkit_LeadManager_Affiliates_StreamSend
+{
+    // {{{ Class Properties
+
+
+    /**
+     * Description for public
+     * @var    boolean
+     * @access public
+     */
+    public $debug = false;
+
+    /**
+     * streamSendFields
+     *
+     * array with key values matching the gaslight contact tabel to the
+     * StreamSend field (normal fields)
+     *
+     * @var    array
+     * @access protected
+     */
+    protected $streamSendFields = array(
+        'email'        => 'email-address',
+        'fname'        => 'first-name',
+        'lname'        => 'last-name',
+        'address'      => 'address1',
+        'address2'     => 'address2',
+        'city'         => 'city',
+        'state'        => 'stateprovince',
+        'zip'          => 'postal-code',
+        'phone'        => 'phone-number',
+        'interest'     => 'interests',
+        'contact_type' => 'contact-type'
+    );
+
+    /**
+     * contactInqTypes
+     *
+     * array with key values matching the gaslight contact_inq tabel to the
+     * StreamSend field (Radio fields)
+     * key   = id from contact_inq table
+     * value = name from contact_inq_table
+     * value is same name as the StreamSend fieldname
+     *
+     * @var    array
+     * @access protected
+     */
+    protected $contactInqTypes = array();
+
+    /**
+     * booleanTypes
+     *
+     * StreamSend fields (boolean fields) an array of field names for the
+     * boolean field types
+     *
+     * @var    array
+     * @access protected
+     */
+    protected $booleanTypes = array();
+    // }}}
+    // {{{ __construct()
+
+
+    /**
+     *  __construct(
+     *
+     * @return void
+     * @access public
+     */
+    public function __construct()
+    {
+    }
+
+
+    // }}}
+    // {{{ addContact()
+
+
+    /**
+     * addContact
+     *
+     * Given $values from a Toolkit_Contacts Form add Contact
+     *
+     * @param array $values Parameter description (if any) ...
+     *
+     * @return boolean Return true if successfull
+     * @access public
+     */
+    public function addContact($values)
+    {
+        // check the array $values to make sure it is correct
+        if (is_array($values) && !empty($values)) {
+            include_once GLM_APP_BASE.'StreamSend/class_streamsend_api.inc';
+            // initialize the streamsend object
+            $ss = new streamSend (STREAMSEND_BASE_URL, STREAMSEND_LOGIN_ID,
+                STREAMSEND_KEY);
+            $ss->debug = $this->debug;
+            $ret = $ss->contactSearch($values['email']);
+
+            $contactData = array();
+            $values[$type] = 1;
+            foreach ($this->streamSendFields as $glmName => $ssName) {
+                switch ($glmName) {
+                case "state":
+                    $contactData[$ssName] = ($values[$glmName]) ? $GLOBALS['states'][$values[$glmName]]: '';
+                    break;
+                default:
+                    $contactData[$ssName] = $values[$glmName];
+                    break;
+                }
+            }
+            foreach ($this->contactInqTypes as $contactInqId => $name) {
+                $slug = str_replace(" ", "-", strtolower($this->contactInqTypes[$contactInqId]));
+                if ($values['interest'][$contactInqId]) {
+                    $contactData[$slug] = 'Yes';
+                } else {
+                    $contactData[$slug] = 'No';
+                }
+            }
+            foreach ($this->booleanTypes as $name) {
+                if ($values[$name]) {
+                    $contactData[$name] = 'Yes';
+                } else {
+                    $contactData[$name] = 'No';
+                }
+            }
+            if ($ret->contact) {
+                $contacts = $ss->contactUpdate(
+                    $ret->contact->id,
+                    $contactData
+                );
+            } else {
+                $contacts = $ss->contactCreate(
+                    $contactData,
+                    STREAMSEND_DEFAULT_ACTIVATE,
+                    STREAMSEND_DEFAULT_DELIVER_ACTIVATION,
+                    STREAMSEND_DEFAULT_DELIVER_WELCOME
+                );
+            }
+            if (!$contacts) {
+
+                // show errors if on development server
+                switch (GLM_HOST_ID) {
+                case "DEVELOPMENT":
+                    echo "<p>A total and complete failure occured.";
+                    break;
+                case "PRODUCTION":
+                    break;
+                }
+            }
+            if ($ss->debug == true) {
+                echo '<p><h3>Debug Results</h3>'.$ss->debugBuffer.'</p>';
+            }
+            return true;
+        }
+        return false;
+    }
+
+
+    // }}}
+}
+?>
diff --git a/Toolkit/LeadManager/ConstantContact.php b/Toolkit/LeadManager/ConstantContact.php
new file mode 100644 (file)
index 0000000..5f93b72
--- /dev/null
@@ -0,0 +1,92 @@
+<?php
+
+/**
+ * ConstantContact.php
+ *
+ * PHP version 5.2
+ *
+ * @category  Toolkit
+ * @package   LeadManager
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @version   SVN: $Id$
+ * @link      <>
+ */
+
+require_once BASE . 'Toolkit/LeadManager/Affiliates/ConstantContact.php';
+/**
+ * Require the Constant Contact Classes
+ */
+/**
+ * Toolkit_Package_ConstantContact
+ *
+ * Description of ConstantContact
+ *
+ * @category  Toolkit
+ * @package   LeadManager
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @release   SVN: $Id$
+ * @link      <>
+ */
+class Toolkit_LeadManager_ConstantContact
+    extends Toolkit_LeadManager_Observer
+{
+    /**
+     * Send LeadManager_Lead object (contact or customer) to ConstantContact
+     *
+     * @param type $subject Object Reference for LeadManager_Lead type
+     *
+     * @return void
+     */
+    public function send(Toolkit_LeadManager_Lead $subject)
+    {
+        // have to convert the $subject Object to an array to be used
+        // in the streamsend call to addContact
+        $values = array(
+            'email_address' => $subject->getEmail(),
+            'first_name'    => $subject->getFname(),
+            'last_name'     => $subject->getLname(),
+            'city_name'     => $subject->getCity(),
+            'state_code'    => $subject->getState(),
+            'state_name'    => $GLOBALS['states'][$subject->getState()],
+            'zip_code'      => $subject->getZip(),
+            'home_number'   => $subject->getPhone()
+        );
+        if ($subject instanceof Toolkit_LeadManager_Contact) {
+            $values['address_line_1'] = $subject->getAddress();
+            $values['address_line_2'] = $subject->getAddress2();
+        } else if ($subject instanceof Toolkit_LeadManager_Customer) {
+            $values['address_line_1'] = $subject->getAdd1();
+            $values['address_line_2'] = $subject->getAdd2();
+        }
+        if (defined('CONSTANT_CONTACT_LIST') && CONSTANT_CONTACT_LIST) {
+            $lists = unserialize(CONSTANT_CONTACT_LIST);
+        }
+        $values["lists"] = $lists;
+        $ccContactOBJ    = new CC_Contact();
+        $constantContact = $ccContactOBJ->getSubscribers(
+            urlencode($values['email_address'])
+        );
+        try {
+            if (!empty($constantContact['items'])) {
+                // update contact
+                $contactXML = $ccContactOBJ->createContactXML(
+                    $constantContact['id'],
+                    $values
+                );
+            } else {
+                // add new contact to constant contact
+                $contactXML = $ccContactOBJ->createContactXML(
+                    null,
+                    $values
+                );
+                $ccContactOBJ->addSubscriber($contactXML);
+            }
+        } catch(Exception $e) {
+            Toolkit_Common::handleError($e);
+        }
+    }
+}
diff --git a/Toolkit/LeadManager/Contact.php b/Toolkit/LeadManager/Contact.php
new file mode 100644 (file)
index 0000000..d1d8493
--- /dev/null
@@ -0,0 +1,315 @@
+<?php
+
+/**
+ * Contact.php
+ *
+ * PHP version 5.2
+ *
+ * @category  Toolkit
+ * @package   LeadManager
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @version   SVN: $Id$
+ * @link      <>
+ */
+
+/**
+ * Toolkit_Package_Contact
+ *
+ * Class Contact used for contact integration with Stream Send
+ * Contant Contact and Mail Chimp programs.
+ *
+ * @category  Toolkit
+ * @package   LeadManager
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @release   SVN: $Id$
+ * @link      <>
+ */
+class Toolkit_LeadManager_Contact
+    extends Toolkit_LeadManager_Lead
+{
+    public static $tableName = 'contact';
+    public static $primaryKey = 'id';
+    private $_id;
+    private $_address;
+    private $_address2;
+    private $_member_ok;
+    private $_members;
+    private $_gift_cer;
+    private $_comments;
+    private $_discover;
+    private $_password;
+    private $_verify_password;
+
+    /**
+     * Contructor for class Contact Objects
+     *
+     * @param type $values Array of Values to create object with
+     */
+    public function __construct(Array $values)
+    {
+        parent::__construct($values);
+        extract($values);
+        $this->setAddress($address)
+            ->setAddress2($address2)
+            ->setComments($comments)
+            ->setDiscover($discover)
+            ->setGiftCert($gift_cert)
+            ->setMembers($members)
+            ->setMemberOk($member_ok)
+            ->setPassword($password)
+            ->setVerifyPassword($verify_password);
+        if ($id) {
+            $this->setId($id);
+        }
+    }
+
+    /**
+     * Creates a Contact class object with values given
+     *
+     * @param type $values Array of values for the contact
+     *
+     * @return Toolkit_Contacts_Admin_Contact
+     */
+    public static function createByValues($values)
+    {
+        return new Toolkit_LeadManager_Contact($values);
+    }
+
+    /**
+     * Returns the Contact Id
+     *
+     * @return type
+     */
+    public function getId()
+    {
+        return $this->_id;
+    }
+
+    /**
+     * sets the id of object
+     *
+     * @param Int $id set the id of object if not numeric throw Exception
+     *
+     * @return Toolkit_Table
+     */
+    public function setId($id)
+    {
+        if (!is_numeric($id)) {
+            throw new Exception('id must be an integer');
+        }
+        if (!$this->_id) {
+            $this->_id = $id;
+        }
+        return $this;
+    }
+
+    /**
+     * Returns the Contact Address
+     *
+     * @return type
+     */
+    public function getAddress()
+    {
+        return $this->_address;
+    }
+
+    /**
+     * Sets the Contact Address
+     *
+     * @param type $address Cotnact Address
+     *
+     * @return Toolkit_Contacts_Admin_Contact
+     */
+    public function setAddress($address)
+    {
+        $this->_address = $address;
+        return $this;
+    }
+
+    /**
+     * Returns the Contact Address2
+     *
+     * @return type
+     */
+    public function getAddress2()
+    {
+        return $this->_address2;
+    }
+
+    /**
+     * Sets the Contact Address2
+     *
+     * @param type $address2 Contact Address2
+     *
+     * @return Toolkit_Contacts_Admin_Contact
+     */
+    public function setAddress2($address2)
+    {
+        $this->_address2 = $address2;
+        return $this;
+    }
+
+    /**
+     * Returns the Contact Member OK
+     *
+     * @return type
+     */
+    public function getMemberOk()
+    {
+        return $this->_member_ok;
+    }
+
+    /**
+     * Sets the Contact Member OK
+     *
+     * @param type $member_ok Contacts Member Ok
+     *
+     * @return Toolkit_Contacts_Admin_Contact
+     */
+    public function setMemberOk($member_ok)
+    {
+        $this->_member_ok = $member_ok;
+        return $this;
+    }
+
+    /**
+     * Returns the Contact Member
+     *
+     * @return type
+     */
+    public function getMembers()
+    {
+        return $this->_members;
+    }
+
+    /**
+     * Sets the Contact Member
+     *
+     * @param type $members Contact Member
+     *
+     * @return Toolkit_Contacts_Admin_Contact
+     */
+    public function setMembers($members)
+    {
+        $this->_members = $members;
+        return $this;
+    }
+
+    /**
+     * Returns the Contact Gift Cert
+     *
+     * @return type
+     */
+    public function getGiftCert()
+    {
+        return $this->_gift_cer;
+    }
+
+    /**
+     * Sets the Contacts gift Cert
+     *
+     * @param type $gift_cert Contact gift_cert
+     *
+     * @return Toolkit_Contacts_Admin_Contact
+     */
+    public function setGiftCert($gift_cert)
+    {
+        $this->_gift_cer = $gift_cert;
+        return $this;
+    }
+
+    /**
+     * Returns the Contacts Comments
+     *
+     * @return type
+     */
+    public function getComments()
+    {
+        return $this->_comments;
+    }
+
+    /**
+     * Sets the Contacts Comments
+     *
+     * @param type $comments Contact Comments
+     *
+     * @return Toolkit_Contacts_Admin_Contact
+     */
+    public function setComments($comments)
+    {
+        $this->_comments = $comments;
+        return $this;
+    }
+
+    /**
+     * Returns Contacts Discover Field
+     *
+     * @return type
+     */
+    public function getDiscover()
+    {
+        return $this->_discover;
+    }
+
+    /**
+     * Sets the Contacts Discover field
+     *
+     * @param type $discover Contacts Discover field
+     *
+     * @return Toolkit_Contacts_Admin_Contact
+     */
+    public function setDiscover($discover)
+    {
+        $this->_discover = $discover;
+        return $this;
+    }
+
+    /**
+     * Returns the leads password
+     *
+     * @return type
+     */
+    public function getPassword()
+    {
+        return $this->_password;
+    }
+
+    /**
+     * Sets the leads password
+     *
+     * @param type $password led password
+     *
+     * @return Toolkit_Contacts_Admin_Lead
+     */
+    public function setPassword($password)
+    {
+        $this->_password = $password;
+        return $this;
+    }
+
+    /**
+     * Returns the Verify Password
+     *
+     * @return string
+     */
+    public function getVerifyPassword()
+    {
+        return $this->_verify_password;
+    }
+
+    /**
+     * Sets the verify_password
+     *
+     * @param string $verifyPassword
+     */
+    public function setVerifyPassword($verifyPassword)
+    {
+        $this->_verify_password = $verifyPassword;
+    }
+
+
+}
diff --git a/Toolkit/LeadManager/Customer.php b/Toolkit/LeadManager/Customer.php
new file mode 100644 (file)
index 0000000..d8e28b9
--- /dev/null
@@ -0,0 +1,461 @@
+<?php
+
+/**
+ * Customer.php
+ *
+ * PHP version 5.2
+ *
+ * @category  Toolkit
+ * @package   LeadManager
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @version   SVN: $Id$
+ * @link      <>
+ */
+
+/**
+ * Toolkit_Package_Customer
+ *
+ * Description of Customer
+ *
+ * @category  Toolkit
+ * @package   LeadManager
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @release   SVN: $Id$
+ * @link      <>
+ */
+class Toolkit_LeadManager_Customer
+    extends Toolkit_LeadManager_Lead
+{
+    public static $tableName = 'customer';
+    public static $primaryKey = 'cust_id';
+    private $_cust_id;
+    private $_userid;
+    private $_usernum;
+    private $_usergroup;
+    private $_add1;
+    private $_add2;
+    private $_org;
+    private $_access_date;
+    private $_purch_date;
+    private $_referred_by;
+    private $_promo;
+    private $_ship_type;
+    private $_club;
+    private $_wherefrom;
+    private $_addr_ext;
+    private $_salutation;
+
+    /**
+     * Constructor for class Customer objects
+     *
+     * @param array $values Array of values
+     */
+    public function __construct(array $values)
+    {
+        parent::__construct($values);
+        extract($values);
+        $this->setUserid($user_id)
+            ->setUsernum($usernum)
+            ->setUsergroup($usergroup)
+            ->setAdd1($add1)
+            ->setAdd2($add2)
+            ->setOrg($org)
+            ->setAccessDate($access_date)
+            ->setPurchDate($purch_date)
+            ->setReferredBy($referred_by)
+            ->setPromo($promo)
+            ->setShipType($ship_type)
+            ->setClub($club)
+            ->setWherefrom($wherefrom)
+            ->setAddrExt($addr_ext)
+            ->setSalutation($salutation);
+        if ($cust_id) {
+            $this->setCustId($cust_id);
+        }
+    }
+
+    /**
+     * Creates a Contact class object with values given
+     *
+     * @param type $values Array of values for the contact
+     *
+     * @return Toolkit_Contacts_Admin_Contact
+     */
+    public static function createByValues($values)
+    {
+        return new Toolkit_LeadManager_Customer($values);
+    }
+
+    /**
+     * Returns cust_id
+     *
+     * @return type
+     */
+    public function getCustId()
+    {
+        return $this->_cust_id;
+    }
+
+    /**
+     * Sets the customer cust_id
+     *
+     * @param type $_cust_id Customer cust_id
+     *
+     * @return Toolkit_LeadManager_Customer
+     */
+    public function setCustId($_cust_id)
+    {
+        $this->_cust_id = $_cust_id;
+        return $this;
+    }
+
+    /**
+     * Returns Customer userid
+     *
+     * @return type
+     */
+    public function getUserid()
+    {
+        return $this->_userid;
+    }
+
+    /**
+     * Sets Customer userid
+     *
+     * @param type $_userid Customer Userid
+     *
+     * @return Toolkit_LeadManager_Customer
+     */
+    public function setUserid($_userid)
+    {
+        $this->_userid = $_userid;
+        return $this;
+    }
+
+    /**
+     * Returns Customer usernum
+     *
+     * @return type
+     */
+    public function getUsernum()
+    {
+        return $this->_usernum;
+    }
+
+    /**
+     * Sets Customer usernum
+     *
+     * @param type $_usernum Customer usernum
+     *
+     * @return Toolkit_LeadManager_Customer
+     */
+    public function setUsernum($_usernum)
+    {
+        $this->_usernum = $_usernum;
+        return $this;
+    }
+
+    /**
+     * Returns Customer Usergroup
+     *
+     * @return type
+     */
+    public function getUsergroup()
+    {
+        return $this->_usergroup;
+    }
+
+    /**
+     * Sets Customer usergroup
+     *
+     * @param type $_usergroup Customer usergroup
+     *
+     * @return Toolkit_LeadManager_Customer
+     */
+    public function setUsergroup($_usergroup)
+    {
+        $this->_usergroup = $_usergroup;
+        return $this;
+    }
+
+    /**
+     * Returns Customer add1
+     *
+     * @return type
+     */
+    public function getAdd1()
+    {
+        return $this->_add1;
+    }
+
+    /**
+     * Sets Customer add1
+     *
+     * @param type $_add1 Customer add1
+     *
+     * @return Toolkit_LeadManager_Customer
+     */
+    public function setAdd1($_add1)
+    {
+        $this->_add1 = $_add1;
+        return $this;
+    }
+
+    /**
+     * Returns Customer add2
+     *
+     * @return type
+     */
+    public function getAdd2()
+    {
+        return $this->_add2;
+    }
+
+    /**
+     * Sets Customer addr2
+     *
+     * @param type $_add2 Customer add2
+     *
+     * @return Toolkit_LeadManager_Customer
+     */
+    public function setAdd2($_add2)
+    {
+        $this->_add2 = $_add2;
+        return $this;
+    }
+
+    /**
+     * Returns Customer org
+     *
+     * @return type
+     */
+    public function getOrg()
+    {
+        return $this->_org;
+    }
+
+    /**
+     * Sets Customer org
+     *
+     * @param type $_org Customer org
+     *
+     * @return Toolkit_LeadManager_Customer
+     */
+    public function setOrg($_org)
+    {
+        $this->_org = $_org;
+        return $this;
+    }
+
+    /**
+     * Returns Customer access_date
+     *
+     * @return type
+     */
+    public function getAccessDate()
+    {
+        return $this->_access_date;
+    }
+
+    /**
+     * Sets Customer acces_date
+     *
+     * @param type $_access_date Customer access_date
+     *
+     * @return Toolkit_LeadManager_Customer
+     */
+    public function setAccessDate($_access_date)
+    {
+        $this->_access_date = $_access_date;
+        return $this;
+    }
+
+    /**
+     * Returns Customer purch_date
+     *
+     * @return type
+     */
+    public function getPurchDate()
+    {
+        return $this->_purch_date;
+    }
+
+    /**
+     * Sets Customer purch_date
+     *
+     * @param type $_purch_date Customer purch_date
+     *
+     * @return Toolkit_LeadManager_Customer
+     */
+    public function setPurchDate($_purch_date)
+    {
+        $this->_purch_date = $_purch_date;
+        return $this;
+    }
+
+    /**
+     * Returns Customer Referred_by
+     *
+     * @return type
+     */
+    public function getReferredBy()
+    {
+        return $this->_referred_by;
+    }
+
+    /**
+     * Sets Customer referred_by
+     *
+     * @param type $_referred_by Customer referred_by
+     *
+     * @return Toolkit_LeadManager_Customer
+     */
+    public function setReferredBy($_referred_by)
+    {
+        $this->_referred_by = $_referred_by;
+        return $this;
+    }
+
+    /**
+     * Returns Customer promo
+     *
+     * @return type
+     */
+    public function getPromo()
+    {
+        return $this->_promo;
+    }
+
+    /**
+     * Sets Customer promo
+     *
+     * @param type $_promo Customer promo
+     *
+     * @return Toolkit_LeadManager_Customer
+     */
+    public function setPromo($_promo)
+    {
+        $this->_promo = $_promo;
+        return $this;
+    }
+
+    /**
+     * Returns Customer ship_type
+     *
+     * @return type
+     */
+    public function getShipType()
+    {
+        return $this->_ship_type;
+    }
+
+    /**
+     * Sets Customer ship_type
+     *
+     * @param type $_ship_type Customer ship_type
+     *
+     * @return Toolkit_LeadManager_Customer
+     */
+    public function setShipType($_ship_type)
+    {
+        $this->_ship_type = $_ship_type;
+        return $this;
+    }
+
+    /**
+     * Returns Customer club
+     *
+     * @return type
+     */
+    public function getClub()
+    {
+        return $this->_club;
+    }
+
+    /**
+     * Sets Customer club
+     *
+     * @param type $_club Customer club
+     *
+     * @return Toolkit_LeadManager_Customer
+     */
+    public function setClub($_club)
+    {
+        $this->_club = $_club;
+        return $this;
+    }
+
+    /**
+     * Returns Customer wherefrom
+     *
+     * @return type
+     */
+    public function getWherefrom()
+    {
+        return $this->_wherefrom;
+    }
+
+    /**
+     * Sets Customer wherefrom
+     *
+     * @param type $_wherefrom Customer wherefrom
+     *
+     * @return Toolkit_LeadManager_Customer
+     */
+    public function setWherefrom($_wherefrom)
+    {
+        $this->_wherefrom = $_wherefrom;
+        return $this;
+    }
+
+    /**
+     * Returns Customer Addr_ext
+     *
+     * @return type
+     */
+    public function getAddrExt()
+    {
+        return $this->_addr_ext;
+    }
+
+    /**
+     * Sets Customer addr_ext
+     *
+     * @param type $_addr_ext Customer addr_ext
+     *
+     * @return Toolkit_LeadManager_Customer
+     */
+    public function setAddrExt($_addr_ext)
+    {
+        $this->_addr_ext = $_addr_ext;
+        return $this;
+    }
+
+    /**
+     * Returns Customer Salutation
+     *
+     * @return type
+     */
+    public function getSalutation()
+    {
+        return $this->_salutation;
+    }
+
+    /**
+     * Sets Customer salutation
+     *
+     * @param type $_salutation Customer Salutation
+     *
+     * @return Toolkit_LeadManager_Customer
+     */
+    public function setSalutation($_salutation)
+    {
+        $this->_salutation = $_salutation;
+        return $this;
+    }
+
+}
diff --git a/Toolkit/LeadManager/Lead.php b/Toolkit/LeadManager/Lead.php
new file mode 100644 (file)
index 0000000..2063551
--- /dev/null
@@ -0,0 +1,665 @@
+<?php
+
+/**
+ * Lead.php
+ *
+ * PHP version 5.2
+ *
+ * @category  Toolkit
+ * @package   LeadManager
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @version   SVN: $Id$
+ * @link      <>
+ */
+
+/**
+ * Toolkit_Package_Lead
+ *
+ * Abstract class Lead used for contact integration with Stream Send
+ * Contant Contact and Mail Chimp programs.
+ *
+ * @category  Toolkit
+ * @package   LeadManager
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @release   SVN: $Id$
+ * @link      <>
+ */
+abstract class Toolkit_LeadManager_Lead
+    extends Toolkit_LeadManager_Subject
+{
+    private $_create_date;
+    private $_email;
+    private $_fname;
+    private $_lname;
+    private $_city;
+    private $_state;
+    private $_zip;
+    private $_country;
+    private $_phone;
+    private $_fax;
+    private $_user_agent;
+    private $_remote_addr;
+    private $_mail_ok;
+    private $_interest;
+    private $_contact_type;
+
+    /**
+     * Constructor for Lead class objects
+     *
+     * @param type $values Array of Values for Lead
+     */
+    public function __construct(Array $values)
+    {
+        extract($values);
+        $this->setCreateDate($create_date)
+            ->setEmail($email)
+            ->setFname($fname)
+            ->setLname($lname)
+            ->setCity($city)
+            ->setState($state)
+            ->setZip($zip)
+            ->setCountry($country)
+            ->setPhone($phone)
+            ->setFax($fax)
+            ->setUserAgent($user_agent)
+            ->setRemoteAddr($remote_addr)
+            ->setMailOk($mail_ok)
+            ->setInterest($interest)
+            ->setContactType($contact_type);
+    }
+
+    /**
+     * Creates a Lead class object by passing in $values array
+     * into the Leads constructor method. Is static method
+     *
+     * @param type $values Array of values to create Lead with
+     *
+     * @return Toolkit_Contacts_Admin_Lead
+     */
+    abstract static public function createByValues($values);
+
+    /**
+     * Returns the Leads Create Date
+     *
+     * @return type
+     */
+    public function getCreateDate()
+    {
+        return $this->_create_date;
+    }
+
+    /**
+     * Sets the Leads Create Date
+     *
+     * @param type $create_date Leads Create Date
+     *
+     * @return Toolkit_Contacts_Admin_Lead
+     */
+    public function setCreateDate($create_date)
+    {
+        $this->_create_date = $create_date;
+        return $this;
+    }
+
+    /**
+     * Returs the Leads Email Address
+     *
+     * @return type
+     */
+    public function getEmail()
+    {
+        return $this->_email;
+    }
+
+    /**
+     * Sets the Leads Email Address
+     *
+     * @param type $email Leads email address
+     *
+     * @return Toolkit_Contacts_Admin_Lead
+     */
+    public function setEmail($email)
+    {
+        $this->_email = $email;
+        return $this;
+    }
+
+    /**
+     * Returns the Leads First Name
+     *
+     * @return type
+     */
+    public function getFname()
+    {
+        return $this->_fname;
+    }
+
+    /**
+     * Sets the Leads First Name
+     *
+     * @param type $fname Leads First Name
+     *
+     * @return Toolkit_Contacts_Admin_Lead
+     */
+    public function setFname($fname)
+    {
+        $this->_fname = $fname;
+        return $this;
+    }
+
+    /**
+     * Returns the Leads Last Name
+     *
+     * @return type
+     */
+    public function getLname()
+    {
+        return $this->_lname;
+    }
+
+    /**
+     * Sets the Leads Last Name
+     *
+     * @param type $lname Leads Last Name
+     *
+     * @return Toolkit_Contacts_Admin_Lead
+     */
+    public function setLname($lname)
+    {
+        $this->_lname = $lname;
+        return $this;
+    }
+
+    /**
+     * Returns the Leads city
+     *
+     * @return type
+     */
+    public function getCity()
+    {
+        return $this->_city;
+    }
+
+    /**
+     * Sets the Leads city
+     *
+     * @param type $city Leads city
+     *
+     * @return Toolkit_Contacts_Admin_Lead
+     */
+    public function setCity($city)
+    {
+        $this->_city = $city;
+        return $this;
+    }
+
+    /**
+     * Returns the Leads state
+     *
+     * @return type
+     */
+    public function getState()
+    {
+        return $this->_state;
+    }
+
+    /**
+     * Sets the Leads state
+     *
+     * @param type $state Leads state
+     *
+     * @return Toolkit_Contacts_Admin_Lead
+     */
+    public function setState($state)
+    {
+        $this->_state = $state;
+        return $this;
+    }
+
+    /**
+     * Returns the Leads zip code
+     *
+     * @return type
+     */
+    public function getZip()
+    {
+        return $this->_zip;
+    }
+
+    /**
+     * Sets the Leads zip code
+     *
+     * @param type $zip Leads Zip code
+     *
+     * @return Toolkit_Contacts_Admin_Lead
+     */
+    public function setZip($zip)
+    {
+        $this->_zip = $zip;
+        return $this;
+    }
+
+    /**
+     * Returns the Leads country
+     *
+     * @return type
+     */
+    public function getCountry()
+    {
+        return $this->_country;
+    }
+
+    /**
+     * Sets the Leads country
+     *
+     * @param type $country Leads country
+     *
+     * @return Toolkit_Contacts_Admin_Lead
+     */
+    public function setCountry($country)
+    {
+        $this->_country = $country;
+        return $this;
+    }
+
+    /**
+     * Returns the Leads phone number
+     *
+     * @return type
+     */
+    public function getPhone()
+    {
+        return $this->_phone;
+    }
+
+    /**
+     * Sets the Leads phone number
+     *
+     * @param type $phone Leads phone number
+     *
+     * @return Toolkit_Contacts_Admin_Lead
+     */
+    public function setPhone($phone)
+    {
+        $this->_phone = $phone;
+        return $this;
+    }
+
+    /**
+     * Returns the Leads fax number
+     *
+     * @return type
+     */
+    public function getFax()
+    {
+        return $this->_fax;
+    }
+
+    /**
+     * Sets the Leads fax number
+     *
+     * @param type $fax Leads fax number
+     *
+     * @return Toolkit_Contacts_Admin_Lead
+     */
+    public function setFax($fax)
+    {
+        $this->_fax = $fax;
+        return $this;
+    }
+
+    /**
+     * Returns the Leads user agent
+     *
+     * @return type
+     */
+    public function getUserAgent()
+    {
+        return $this->_user_agent;
+    }
+
+    /**
+     * Sets the Leads user agent
+     *
+     * @param type $user_agent Leads user agent
+     *
+     * @return Toolkit_Contacts_Admin_Lead
+     */
+    public function setUserAgent($user_agent)
+    {
+        $this->_user_agent = $user_agent;
+        return $this;
+    }
+
+    /**
+     * Returns the Leads remote address
+     *
+     * @return type
+     */
+    public function getRemoteAddr()
+    {
+        return $this->_remote_addr;
+    }
+
+    /**
+     * Sets the Leads remote address
+     *
+     * @param type $remote_addr Leads remote address
+     *
+     * @return Toolkit_Contacts_Admin_Lead
+     */
+    public function setRemoteAddr($remote_addr)
+    {
+        $this->_remote_addr = $remote_addr;
+        return $this;
+    }
+
+    /**
+     * Returns the Leads mail_ok
+     *
+     * @return type
+     */
+    public function getMailOk()
+    {
+        return $this->_mail_ok;
+    }
+
+    /**
+     * Sets the Leads mail_ok
+     *
+     * @param type $mail_ok Leads mail ok
+     *
+     * @return Toolkit_Contacts_Admin_Lead
+     */
+    public function setMailOk($mail_ok)
+    {
+        $this->_mail_ok = $mail_ok;
+        return $this;
+    }
+
+    /**
+     * Returns the Leads interests
+     *
+     * @return type
+     */
+    public function getInterest()
+    {
+        return $this->_interest;
+    }
+
+    /**
+     * Sets the leads interests
+     *
+     * @param type $interest Leads interest
+     *
+     * @return Toolkit_Contacts_Admin_Lead
+     */
+    public function setInterest($interest)
+    {
+        $this->_interest = $interest;
+        return $this;
+    }
+
+    /**
+     * Returns the leads contact type
+     *
+     * @return type
+     */
+    public function getContactType()
+    {
+        return $this->_contact_type;
+    }
+
+    /**
+     * Sets the leads contact type
+     *
+     * @param type $contact_type Leads contact type
+     *
+     * @return Toolkit_Contacts_Admin_Lead
+     */
+    public function setContactType($contact_type)
+    {
+        $this->_contact_type = $contact_type;
+        return $this;
+    }
+
+    /**
+     * Return primary key field value of table
+     *
+     * @return type
+     */
+    public function getPrimaryKey()
+    {
+        $defaultVars   = get_class_vars(get_class($this));
+        $primaryKey
+            = ($defaultVars['primaryKey'] == 'cust_id')
+            ? 'CustId'
+            : ucfirst($defaultVars['primaryKey']);
+        $getPrimaryKey = "get$primaryKey";
+        return $this->$getPrimaryKey();
+    }
+
+    /**
+     * Parse out the integer references for the interest into their
+     * string names
+     *
+     * @param PDO $dbh Database connection
+     *
+     * @return type
+     */
+    public function getInterestsAsArray(PDO $dbh)
+    {
+        $interestArray = array();
+        $interest = preg_replace('/^:|:$/', '', $this->_interest);
+        $intArray
+            = (is_array($interest))
+            ? explode(':', $interest)
+            : array();
+        if (is_array($intArray)) {
+            array_filter($intArray);
+        }
+        if ($this->_interest && !empty($intArray)) {
+            try {
+                $sql = "
+                  SELECT id,header
+                    FROM contact_inq
+                   WHERE id IN (".implode(',', $intArray).")
+                ORDER BY header";
+                $stmt = $dbh->query($sql);
+                while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                    $interestArray[$row['id']] = $row['header'];
+                }
+            } catch (PDOException $e) {
+                Toolkit_Common::handleError($e);
+            }
+        }
+        return $interestArray;
+    }
+
+    /**
+     * Parse out the contact types and return array with their names
+     *
+     * @return type
+     */
+    public function getContactTypesAsArray()
+    {
+        $contactTypesArray = array();
+        $types = preg_replace('/^:|:$/', '', $this->_contact_type);
+        $typeArray = explode(':', $types);
+        if ($this->_contact_type && !empty($typeArray)) {
+            $conf = new Config();
+            $root =& $conf->parseConfig(
+                BASE . 'Toolkit/Contacts/config.ini',
+                'IniFile'
+            );
+            // check for PEAR error
+            if (PEAR::isError($root)) {
+                Toolkit_Common::handleError($root);
+            }
+            $cTypes
+                = $root->getItem('section', 'contact_types')
+                    ->toArray();
+            foreach ($typeArray as $type) {
+                $contactTypesArray[$type] = $cTypes['contact_types'][$type];
+            }
+        }
+        return $contactTypesArray;
+    }
+
+    /**
+     * Checks the id of the object if it is set then calls update othervise
+     * calls insert function
+     *
+     * @param PDO     $dbh            Database connection
+     * @param boolean $saveNullFields To save nul fields or not
+     *
+     * @return viod
+     */
+    public function save(PDO $dbh, $saveNullFields = true)
+    {
+        $id = $this->getPrimaryKey();
+        if ($id) {
+            $this->update($dbh, $saveNullFields);
+        } else {
+            $this->insert($dbh);
+        }
+        $this->notify();
+    }
+
+    /**
+     * insert the object
+     *
+     * @param PDO $dbh Database Connection
+     *
+     * @return Toolkit_Table
+     */
+    public function insert(PDO $dbh)
+    {
+        try {
+            $methods = get_class_methods(get_class($this));
+            $values = array();
+            if ($methods) {
+                $pattern = '/get(.*)/';
+                foreach ($methods as $mName) {
+                    if (   preg_match($pattern, $mName, $matches)
+                        && !in_array(
+                            $mName,
+                            array(
+                                'getInterestsAsArray',
+                                'getContactTypesAsArray',
+                                'getPrimaryKey'
+                            )
+                        )
+                    ) {
+                        $func = create_function(
+                            '$c',
+                            'return "_" . strtolower($c[1]);'
+                        );
+                        $fieldName = preg_replace_callback(
+                            '/([A-Z])/',
+                            $func,
+                            $matches[1]
+                        );
+                        $fieldName = preg_replace('/^_/', '', $fieldName);
+                        $values[$fieldName] = $this->$matches[0]();
+                    }
+                }
+            }
+            $defaultVars = get_class_vars(get_class($this));
+            $primaryKey  = $defaultVars['primaryKey'];
+            $tableName   = $defaultVars['tableName'];
+            unset($values[$primaryKey]);
+            $sql = Toolkit_Common::createSQLInsert(
+                $tableName,
+                array_keys($values)
+            );
+            $sql .= " RETURNING $primaryKey";
+            $stmt = Toolkit_Common::prepareQuery(
+                $dbh,
+                $tableName,
+                $sql,
+                $values
+            );
+            $stmt->execute();
+            $primaryKey
+                = ($primaryKey == 'cust_id')
+                ? 'custId'
+                : $primaryKey;
+            $setter = 'set'.ucfirst($primaryKey);
+            $this->$setter($stmt->fetchColumn());
+            return $this;
+        } catch(PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+    }
+
+    /**
+     * update the object
+     *
+     * @param PDO     $dbh            Database connection
+     * @param boolean $saveNullFields To save null fields or not
+     *
+     * @return Toolkit_Table
+     */
+    public function update(PDO $dbh, $saveNullFields = true)
+    {
+        try {
+            $methods = get_class_methods(get_class($this));
+            $values = array();
+            if ($methods) {
+                $pattern = '/get(.*)/';
+                foreach ($methods as $mName) {
+                    if (   preg_match($pattern, $mName, $matches)
+                        && !in_array(
+                            $mName,
+                            array(
+                                'getInterestsAsArray',
+                                'getContactTypesAsArray',
+                                'getPrimaryKey'
+                            )
+                        )
+                    ) {
+                        $func = create_function(
+                            '$c',
+                            'return "_" . strtolower($c[1]);'
+                        );
+                        $fieldName = preg_replace_callback(
+                            '/([A-Z])/',
+                            $func,
+                            $matches[1]
+                        );
+                        $fieldName = preg_replace('/^_/', '', $fieldName);
+                        $fieldValue = $this->$matches[0]();
+                        if (( $fieldValue === null && $saveNullFields)
+                            || $fieldValue !== null
+                        ) {
+                            $values[$fieldName] = $fieldValue;
+                        }
+                    }
+                }
+            }
+            $defaultVars = get_class_vars(get_class($this));
+            $primaryKey  = $defaultVars['primaryKey'];
+            $tableName   = $defaultVars['tableName'];
+            $sql = Toolkit_Common::createSQLUpdate(
+                $tableName,
+                array_keys($values),
+                array("$primaryKey = :$primaryKey")
+            );
+            $stmt = Toolkit_Common::prepareQuery(
+                $dbh,
+                $tableName,
+                $sql,
+                $values
+            );
+            $stmt->execute();
+            return $this;
+        } catch(PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+    }
+}
diff --git a/Toolkit/LeadManager/Observer.php b/Toolkit/LeadManager/Observer.php
new file mode 100644 (file)
index 0000000..6bdcb40
--- /dev/null
@@ -0,0 +1,57 @@
+<?php
+
+/**
+ * Observer.php
+ *
+ * PHP version 5.2
+ *
+ * @category  Toolkit
+ * @package   LeadManager
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @version   SVN: $Id$
+ * @link      <>
+ */
+
+/**
+ * Toolkit_Package_Observer
+ *
+ * Description of Observer
+ *
+ * @category  Toolkit
+ * @package   LeadManager
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @release   SVN: $Id$
+ * @link      <>
+ */
+abstract class Toolkit_LeadManager_Observer
+{
+    /**
+     * abstract method send to be used to send the contact
+     * which is a Toolkit_LeadManager_Lead to appropiate wrapper
+     *
+     * @param Toolkit_LeadManager_Lead $subject Object Lead
+     *
+     * @return void
+     */
+   abstract public function send(Toolkit_LeadManager_Lead $subject);
+
+    /**
+     * Update method for the Observer Calls the send method.
+     *
+     * @param type $subject Observer Object Reference
+     *
+     * @return void
+     */
+    public function update($subject)
+    {
+        // looks for an observer method with the send name
+        if (method_exists($this, 'send')) {
+            call_user_func_array(array($this, 'send'), array($subject));
+        }
+    }
+
+}
diff --git a/Toolkit/LeadManager/StreamSend.php b/Toolkit/LeadManager/StreamSend.php
new file mode 100644 (file)
index 0000000..4929af6
--- /dev/null
@@ -0,0 +1,74 @@
+<?php
+
+/**
+ * StreamSend.php
+ *
+ * PHP version 5.2
+ *
+ * @category  Toolkit
+ * @package   LeadManager
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @version   SVN: $Id$
+ * @link      <>
+ */
+
+/**
+ * Toolkit_Package_StreamSend
+ *
+ * StreamSend Observer pattern for LeadManager Contact class
+ *
+ * @category  Toolkit
+ * @package   LeadManager
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @release   SVN: $Id$
+ * @link      <>
+ */
+class Toolkit_LeadManager_StreamSend
+    extends Toolkit_LeadManager_Observer
+{
+    /**
+     * Send LeadManager_Lead object (contact or customer) to StreamSend
+     *
+     * @param type $subject Object Reference for LeadManager_Lead type
+     *
+     * @return void
+     */
+    public function send(Toolkit_LeadManager_Lead $subject)
+    {
+        // have to convert the $subject Object to an array to be used
+        // in the streamsend call to addContact
+        $values = array(
+            'email'    => $subject->getEmail(),
+            'fname'    => $subject->getFname(),
+            'lname'    => $subject->getLname(),
+            'city'     => $subject->getCity(),
+            'state'    => $subject->getState(),
+            'zip'      => $subject->getZip(),
+            'phone'    => $subject->getPhone()
+        );
+        if ($subject instanceof Toolkit_LeadManager_Contact) {
+            $values['address'] = $subject->getAddress();
+            $values['address2'] = $subject->getAddress2();
+        } else if ($subject instanceof Toolkit_LeadManager_Customer) {
+            $values['address'] = $subject->getAdd1();
+            $values['address2'] = $subject->getAdd2();
+        }
+
+        $contactTypes = $subject->getContactTypesAsArray();
+        if ($contactTypes) {
+            $values['contact_type'] = implode('|', $contactTypes);
+        }
+        $interests
+            = $subject->getInterestsAsArray(Toolkit_Database::getInstance());
+        if ($interests) {
+            $values['interest'] = implode('|', $interests);
+        }
+        // call the streamsends API to send contact to stream send
+        $streamSend = new Toolkit_LeadManager_Affiliates_StreamSend();
+        $streamSend->addContact($values);
+    }
+}
diff --git a/Toolkit/LeadManager/Subject.php b/Toolkit/LeadManager/Subject.php
new file mode 100644 (file)
index 0000000..7995f4b
--- /dev/null
@@ -0,0 +1,80 @@
+<?php
+
+/**
+ * Subject.php
+ *
+ * PHP version 5.2
+ *
+ * @category  Toolkit
+ * @package   LeadManager
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @version   SVN: $Id$
+ * @link      <>
+ */
+
+/**
+ * Toolkit_Package_Subject
+ *
+ * Description of Subject
+ *
+ * @category  Toolkit
+ * @package   LeadManager
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @release   SVN: $Id$
+ * @link      <>
+ */
+abstract class Toolkit_LeadManager_Subject
+{
+    protected $observers = array();
+
+    /**
+     * Sets the Observer into the observers array
+     *
+     * @param Toolkit_LeadManager_Observer $observer Observer object to add
+     *
+     * @return void
+     */
+    public function attach(Toolkit_LeadManager_Observer $observer)
+    {
+        $i = array_search($observer, $this->observers);
+        if ($i === false) {
+            $this->observers[] = $observer;
+        }
+    }
+
+    /**
+     * Unsets the Observer from the observers array
+     *
+     * @param Toolkit_LeadManager_Observer $observer Observer to delete
+     *
+     * @return void
+     */
+    public function detach(Toolkit_LeadManager_Observer $observer)
+    {
+        if (!empty($this->observers)) {
+            $i = array_search($observer, $this->observers);
+            if ($i !== false) {
+                unset($this->observers[$i]);
+            }
+        }
+    }
+
+    /**
+     * Call each Observers update method
+     *
+     * @return void
+     */
+    public function notify()
+    {
+        if (!empty($this->observers)) {
+            foreach ($this->observers as $observer) {
+                $observer->update($this);
+            }
+        }
+    }
+
+}
diff --git a/Toolkit/Logger.php b/Toolkit/Logger.php
new file mode 100644 (file)
index 0000000..17993c2
--- /dev/null
@@ -0,0 +1,168 @@
+<?php
+
+/**
+ * Error handling logger for system
+ *
+ * PHP version 5
+ *
+ * @category  Logger
+ * @package   Toolkit_Logger
+ * @author    Jamie Kahgee <jamie@gaslightmedia.com>
+ * @copyright 2010 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com/ Gaslightmedia
+ * @version   CVS: $Id: Logger.php,v 1.6 2010/05/25 14:09:12 jamie Exp $
+ * @link      <>
+ * @see       Log
+ */
+
+/**
+ * Logging class used to handle system logs
+ */
+require_once 'Log.php';
+
+/**
+ * Error handling logger for system
+ *
+ * @category  Logger
+ * @package   Toolkit_Logger
+ * @author    Jamie Kahgee <jamie@gaslightmedia.com>
+ * @copyright 2010 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com/ Gaslightmedia
+ * @version   Release: @package_version@
+ * @link      <>
+ * @see       Log
+ */
+class Toolkit_Logger
+{
+    //  {{{ &getLogger()
+
+    /**
+     * Gets a concrete log subclass based on constant parameters set for server
+     *
+     * Define parameters in server setup block of bootstrap file
+     *
+     * @return Log concrete Log subclass
+     * @access public
+     * @static
+     */
+    public static function &getLogger()
+    {
+        if (defined('ERROR_LOG_NAME') && constant('ERROR_LOG_NAME') != '') {
+            $errorLogName = constant('ERROR_LOG_NAME');
+        } else {
+            $errorLogName = '';
+        }
+
+        $logger =& Log::singleton(
+            ERROR_LOG_TYPE,
+            $errorLogName,
+            constant('ERROR_LOG_IDENT'),
+            $GLOBALS['ERROR_LOG_CONF'],
+            constant('ERROR_LOG_LEVEL')
+        );
+
+        return $logger;
+    }
+
+    //  }}}
+    //  {{{ errorHandler()
+
+    /**
+     * User-defined error handler function
+     *
+     * handles errors in script.  E_ERROR, E_WARNING, E_NOTICE are automatically
+     * logged when they occur.  Fatal errors (E_ERROR) have a 404 page shown to
+     * user so script doesn't halt.
+     *
+     * @param int    $errno   Level of the error rasied
+     * @param string $errstr  Error message
+     * @param string $errfile Filename that the error was raised in
+     * @param int    $errline Line number the error was raised at
+     * @param Log    $logger  Logger to user for error loggin
+     *
+     * @return boolean false, allows the normal error handler to continue
+     * @access public
+     * @static
+     */
+    public static function errorHandler(
+        $errno,
+        $errstr,
+        $errfile,
+        $errline,
+        $logger = null
+    ) {
+        if (!($logger instanceof Log)) {
+            $logger =& self::getLogger();
+        }
+
+        $msg = "$errstr in $errfile at line $errline";
+        switch ($errno) {
+        case E_ERROR :
+        case E_USER_ERROR :
+            $logger->emerg($msg, PEAR_LOG_EMERG);
+            header("HTTP/1.0 404 Not Found");
+            exit(1);
+            break;
+
+        case E_WARNING :
+        CASE E_USER_WARNING :
+            $logger->warning($msg, PEAR_LOG_WARNING);
+            break;
+
+        case E_NOTICE :
+        case E_USER_NOTICE :
+            $logger->notice($msg, PEAR_LOG_NOTICE);
+            break;
+
+        default :
+            $logger->info($msg, PEAR_LOG_INFO);
+            break;
+        }
+
+        return false; // let the normal error handler continue
+    }
+
+    //  }}}
+    //  {{{ logException()
+
+    /**
+     * Create a log for an exception
+     *
+     * @param string    $type Type of error encountered that caused exception
+     * @param Exception $e    Exception that was thrown
+     *
+     * @return void
+     * @static
+     */
+    public static function logException($type, Exception $e)
+    {
+        $msg  = $e->getMessage();
+        $file = $e->getFile();
+        $line = $e->getLine();
+
+        $lcType = strtolower($type); // uniformity in type when looking at log
+        $log = "[$lcType] $msg in $file at line $line";
+
+        $logger =& self::getLogger();
+
+        switch ($lcType) {
+        case 'db error' :
+        case 'invalid arg' :
+        case 'runtime error' :
+            $logger->emerg($log);
+            break;
+
+        case 'image server' :
+        case 'file server' :
+            $logger->warning($log);
+            break;
+
+        default :
+            $logger->err($log);
+            break;
+        }
+    }
+
+    //  }}}
+}
+?>
diff --git a/Toolkit/Maps/geoCoder.js b/Toolkit/Maps/geoCoder.js
new file mode 100644 (file)
index 0000000..302b839
--- /dev/null
@@ -0,0 +1,164 @@
+// Option would be to make this configurable so it can be used
+// with one field (loc) like event
+// or multiple fields like member (street, city, state, zip)
+var GLM_GeoMap = {
+    defaultLat: 45.3748385,
+    defaultLon: -84.9592251,
+    geocoder: null,
+    map: null,
+    setDefaultLat: function(lat) {
+      GLM_GeoMap.defaultLat = lat;
+    },
+    setDefaultLon: function(lon) {
+        GLM_GeoMap.defaultLon = lon;
+    },
+    initialize: function() {
+        GLM_GeoMap.createMap();
+        var lat = $("#lat").val();
+        var lon = $("#lon").val();
+        if ($("#loc").length > 0) {
+            var location = $("#loc").val();
+        } else {
+            var location = null;
+        }
+        if ($("#address").length > 0) {
+            var street = $("#address").val();
+        }
+        if ($("#city").length > 0) {
+            var city = $("#city").val();
+        }
+        if ($("#state").length > 0) {
+            var state = $("#state").val();
+        }
+        if ($("#zip").length > 0) {
+            var zip = $("#zip").val();
+        }
+
+        if (lat && lon) {
+            GLM_GeoMap.createWithLatLon();
+        } else if (location) {
+            GLM_GeoMap.createWithLocation(location);
+        } else if (street || city || state || zip) {
+            GLM_GeoMap.createWithAddress(street, city, state, zip);
+        } else {
+            GLM_GeoMap.createWithClientLocation();
+        }
+    },
+    initializeWithNewLocation: function(location) {
+        GLM_GeoMap.createMap();
+        GLM_GeoMap.createWithLocation(location);
+    },
+    createMap: function() {
+        var myOptions = {
+            zoom: 15,
+            mapTypeId: google.maps.MapTypeId.ROADMAP
+        };
+        GLM_GeoMap.map = new google.maps.Map(
+            document.getElementById("map_canvas"),
+            myOptions
+        );
+    },
+    addMarkerToMap: function(LatLng) {
+        var marker = new google.maps.Marker({
+            map: GLM_GeoMap.map,
+            position: LatLng,
+            draggable: true
+        });
+        var location = marker.getPosition();
+        GLM_GeoMap.updateLatLonDiv(location);
+        google.maps.event.addListener(marker, 'dragend', function(){
+            var location = marker.getPosition();
+            GLM_GeoMap.updateLatLonDiv(location);
+        });
+        GLM_GeoMap.map.setCenter(LatLng);
+        GLM_GeoMap.map.setZoom(15);
+    },
+    updateLatLonDiv: function(point) {
+        $("#lat").val(point.lat());
+        $("#lon").val(point.lng());
+    },
+    createWithClientLocation: function() {
+        // Try HTML5 geolocation
+        if (navigator.geolocation) {
+            navigator.geolocation.getCurrentPosition(function(position) {
+                var pos = new google.maps.LatLng(
+                    position.coords.latitude,
+                    position.coords.longitude
+                );
+                GLM_GeoMap.addMarkerToMap(pos);
+            }, function() {
+                GLM_GeoMap.handleNoGeolocation(true);
+            });
+        }
+    },
+    createWithLatLon: function() {
+        GLM_GeoMap.addMarkerToMap(new google.maps.LatLng(
+            $("#lat").val(),
+            $("#lon").val()
+        ));
+    },
+    createWithDefaultLatLon: function() {
+
+        GLM_GeoMap.addMarkerToMap(new google.maps.LatLng(
+            GLM_GeoMap.defaultLat,
+            GLM_GeoMap.defaultLon
+        ));
+    },
+    createWithLocation: function(address) {
+        GLM_GeoMap.geocoder = new google.maps.Geocoder();
+        GLM_GeoMap.geocoder.geocode(
+            {'address': address},
+            function(results, status) {
+                if (status == google.maps.GeocoderStatus.OK) {
+                    GLM_GeoMap.map.setCenter(results[0].geometry.location);
+                    GLM_GeoMap.addMarkerToMap(results[0].geometry.location);
+                } else {
+                    GLM_GeoMap.createWithClientLocation();
+                }
+            }
+        );
+    },
+    createWithAddress: function(street, city, state, zip) {
+        var location = "";
+        if (street) {
+            location += street + ",";
+        }
+        if (city) {
+            location += city + ",";
+        }
+        if (state) {
+            location += state + ",";
+        }
+        if (zip) {
+            location += zip + ",";
+        }
+        GLM_GeoMap.geocoder = new google.maps.Geocoder();
+        GLM_GeoMap.geocoder.geocode(
+            {'address': location},
+            function(results, status) {
+                if (status == google.maps.GeocoderStatus.OK) {
+                    GLM_GeoMap.map.setCenter(results[0].geometry.location);
+                    GLM_GeoMap.addMarkerToMap(results[0].geometry.location);
+                } else {
+                    GLM_GeoMap.createWithClientLocation();
+                }
+            }
+        );
+    },
+    handleNoGeolocation: function(errorFlag) {
+        if (errorFlag) {
+            var content = 'Error: The Geolocation service failed.';
+        } else {
+            var content = 'Error: Your browser doesn\'t support geolocation.';
+        }
+
+        var options = {
+            map: GLM_GeoMap.map,
+            position: new google.maps.LatLng(60, 105),
+            content: content
+        };
+
+        var infowindow = new google.maps.InfoWindow(options);
+        GLM_GeoMap.map.setCenter(options.position);
+    }
+}
\ No newline at end of file
diff --git a/Toolkit/Maps/marker.php b/Toolkit/Maps/marker.php
new file mode 100755 (executable)
index 0000000..0a5e5f5
--- /dev/null
@@ -0,0 +1,100 @@
+<?php
+require_once '../../setup.phtml';
+// filter any input
+$color = filter_input(INPUT_GET, 'color', FILTER_SANITIZE_STRING);
+$text  = filter_input(INPUT_GET, 'text', FILTER_SANITIZE_STRING);
+// check to see if this png already exists (cache)
+if (!strstr($color, ".") && !is_dir("./iconCache/".$color)) {
+    $oldUmask = umask(0);
+    mkdir("./iconCache/".$color, 0777, true);
+    umask($oldUmask);
+}
+$fileName = "./iconCache/".$color."/map-".$text.".png";
+if (is_file($fileName)) {
+    header("Content-type: image/png");
+    $fn = fopen($fileName, "r");
+    fpassthru($fn);
+    fclose($fn);
+    exit;
+}
+if (!$color) {
+    $color = "ff776b";  //default google map color
+}
+$color = str_replace("#", "", $color);
+
+$font = GLM_APP_BASE . 'glmPEAR/Image/Canvas/Fonts/Arial_Bold.ttf';
+
+//unfortunately we still must do some offsetting
+switch (ord(substr($text,0,1))) {
+    case 49: //1
+        $offset = -2;
+        break;
+    case 55: //7
+        $offset = -1;
+        break;
+    case 65: //A
+        $offset = 1;
+        break;
+    case 74: //J
+        $offset = -1;
+        break;
+    case 84: //T
+        $offset = 1;
+        break;
+    case 99: //c
+        $offset = -1;
+        break;
+    case 106: //j
+        $offset = 1;
+        break;
+    default:
+       $offset = -2;
+       break;
+}
+if ((int)$text > 99) {
+   $offset = 2;
+} else if ((int)$text > 9) {
+   $offset = 0;
+} else {
+   $offset = -3;
+}
+//$offset = 0; //reset offset
+if (strlen($text) == 1) {
+    $fontsize = 12.5;
+} else if (strlen($text) == 2) {
+    $fontsize = 11;
+} else if (strlen($text) == 3) {
+    $fontsize = 10;
+} else {
+    $fontsize = 10.5;
+    $offset = 0; //reset offset
+    $text = null;//chr(149);
+}
+
+$bbox = imagettfbbox($fontsize, 0, $font, $text);
+$width = $bbox[2] - $bbox[0] + 1;
+$height = $bbox[1] - $bbox[7] + 1;
+
+$image_name = "../../assets/blue.png";
+$im = imagecreatefrompng($image_name);
+imageAlphaBlending($im, true);
+imageSaveAlpha($im, true);
+$black = imagecolorallocate($im, 0, 0, 0);
+
+if ($text) {
+    imagettftext(
+        $im,            // image resource
+        $fontsize,      // font size
+        0,              // angle
+        8 - $offset,    // x
+        15 + $height/2, // y
+        $black,         // color
+        $font,          // font file
+        $text         // text
+    );
+}
+
+header("Content-type: image/png");
+imagepng($im);
+imagepng($im, $fileName);
+imagedestroy($im);
diff --git a/Toolkit/Maps/marker2.php b/Toolkit/Maps/marker2.php
new file mode 100644 (file)
index 0000000..7b58058
--- /dev/null
@@ -0,0 +1,38 @@
+<?php
+require_once '../../setup.phtml';
+$color = filter_input(INPUT_GET, 'color', FILTER_SANITIZE_STRING);
+if (!strstr($color, ".") && !is_dir("./iconCache/".$color)) {
+    $oldUmask = umask(0);
+    mkdir("./iconCache/".$color, 0777, true);
+    umask($oldUmask);
+}
+$fileName = "./iconCache/{$color}/map.png";
+if (is_file($fileName)) {
+    header("Content-type: image/png");
+    $fn = fopen($fileName, "r");
+    fpassthru($fn);
+    fclose($fn);
+    exit;
+}
+$image_name = "http://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=%20|{$color}|000000&ext=.png";
+
+//var_dump($image_name);exit;
+$ch = curl_init();
+curl_setopt ($ch, CURLOPT_URL, $image_name);
+curl_setopt ($ch, CURLOPT_CONNECTTIMEOUT, 0);
+
+// Getting binary data
+curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
+curl_setopt($ch, CURLOPT_BINARYTRANSFER, 1);
+
+$image_string = curl_exec($ch);
+//var_dump($image_string);exit;
+//curl_close($ch);
+
+$im = imagecreatefromstring ($image_string);
+//imagecolortransparent($im, '000000');
+imagesavealpha($im, true);
+header("Content-type: image/png");
+imagepng($im);
+imagepng($im, $fileName);
+imagedestroy($im);
\ No newline at end of file
diff --git a/Toolkit/Members.php b/Toolkit/Members.php
new file mode 100644 (file)
index 0000000..066ef01
--- /dev/null
@@ -0,0 +1,429 @@
+<?php
+//    vim:set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker syntax=php:
+
+/**
+ * Driver for admin side processing of member data
+ *
+ * PHP version 5
+ *
+ * @category Toolkit
+ * @package  Members
+ * @author   Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @license     http://www.gaslightmedia.com Gaslightmedia
+ * @version  CVS: $Id: Members.php,v 1.38 2010/07/16 20:51:05 jamie Exp $
+ * @link     http://demo.gaslightmedia.com
+ */
+
+/**
+ * Base class for the memberdb
+ *
+ * @category  Toolkit
+ * @package   Members
+ * @author    Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2008 Gaslight Media
+ * @license      http://www.gaslightmedia.com Gaslightmedia
+ * @link      http://demo.gaslightmedia.com
+ */
+class Toolkit_Members
+{
+    //    {{{    __construct()
+
+    /**
+     * Constructor
+     *
+     * @access    public
+     */
+    public function __construct()
+    {
+        HTTP_Session2::useCookies(false);
+        HTTP_Session2::start('MemberDB');
+        if (HTTP_Session2::get('newSearch')) {
+            HTTP_Session2::set('newSearch', false);
+            HTTP_Session2::set(
+                'searchResults',
+                urldecode($_SERVER['HTTP_REFERER'])
+            );
+        }
+    }
+
+    //    }}}
+
+    //    {{{ toHtml()
+
+    /**
+     * Determine which form to show to the user
+     *
+     * When editing a member the (a)ction in the URL controls
+     * which form is displayed to the user. Member Id's from
+     * the database should be passed along as the ID in the $_GET array.
+     *
+     * @return void
+     * @access public
+     */
+    public function toHtml()
+    {
+        $pdo = Toolkit_Database::getInstance();
+        //  application configuration
+        $conf = new Config;
+        $root =& $conf->parseConfig(
+            BASE . 'Toolkit/Members/config.ini',
+            'IniFile'
+        );
+
+        $nav = new Toolkit_Members_RecordNavigation($root);
+        $nav->setupAdminNavStructure();
+
+        $tplOpts =  Toolkit_Members::getFlexyOptions();
+        $tEngine = new HTML_Template_Flexy($tplOpts);
+
+        switch ($_GET['tab']) {
+        case 'invoices' :
+            $GLOBALS['styleSheets'][]
+                = MEDIA_BASE_URL . 'Toolkit/Members/Billing/billing.css';
+            if ($_REQUEST['returnPdf']) {
+                $statement = new Toolkit_Members_Billing_Invoices();
+                $statement->pdfToBrowser(
+                    $pdo,
+                    $_REQUEST['invoice_id']
+                );
+                exit;
+            }
+            if ($_REQUEST['editBilling'] == '1') {
+                $billingForm = new Toolkit_Members_Billing_EditBillingForm(
+                    $pdo,
+                    'edit-billing-form',
+                    'post',
+                    MEDIA_BASE_URL . "admin/members.php?id={$_REQUEST['id']}"
+                    . "&rt=Members&ac=editMember&tab=invoices&editBilling=1"
+                );
+                $billingForm->configureForm();
+                $out .= $billingForm->toHtml();
+            } else if ($_REQUEST['full']) {
+                $nav = new Toolkit_Members_RecordNavigation($root);
+                $nav->setupAdminNavStructure();
+                $out  = '<div id="nav-detail">'.$nav->getPageNav().'</div>';
+                $statement = new Toolkit_Members_Billing_Statement();
+                $out .= $statement->createMemberStatements(
+                    $pdo,
+                    $_REQUEST['id'],
+                    true
+                );
+            } else {
+                $mc = new Toolkit_Members_Billing_EditMemberPayment(
+                    $pdo,
+                    'edit_billing',
+                    'post',
+                    null,
+                    null,
+                    null,
+                    true
+                );
+                $mc->configureForm();
+                $out = '';
+                if ($_REQUEST['formSubmitGood']) {
+                    $out = '<div id="form-success-top">
+                        The information below has been successfully submitted.
+                    </div>';
+                }
+                $out .= $mc->toHtml();
+            }
+            break;
+
+        case 'billingInfo' :
+            $mc = new Toolkit_Members_Billing_EditMemberAccount(
+                $pdo,
+                'edit_billing',
+                'post',
+                null,
+                null,
+                null,
+                true
+            );
+            $sql = "
+              SELECT id
+                FROM member_account
+               WHERE member_id = :member_id
+            ORDER BY id";
+            try {
+                $stmt = $pdo->prepare($sql);
+                $stmt->bindParam(
+                    ':member_id',
+                    $_GET['id'],
+                    PDO::PARAM_INT
+                );
+                $stmt->execute();
+                $data = $stmt->fetchAll(PDO::FETCH_ASSOC);
+                if (is_numeric($data[0]['id'])) {
+                    $mc->setPrimaryAccountId($data[0]['id']);
+                }
+                if (is_numeric($data[1]['id'])) {
+                    $mc->setSecondaryAccountId($data[1]['id']);
+                }
+            } catch (PDOException $e) {
+                Toolkit_Common::handleError($e);
+            }
+            $mc->configureForm();
+            $out = '';
+            if ($_REQUEST['formSubmitGood']) {
+                $out = '<div id="form-success-top">
+                    The information below has been successfully submitted.
+                </div>';
+            }
+            $out .= $mc->toHtml();
+            break;
+        case 'contacts' :
+            $GLOBALS['bottomScripts'][]
+                = MEDIA_BASE_URL . 'Toolkit/Members/libjs/edit-member-contacts.js';
+
+            if ($_GET['d'] == 't' && ctype_digit($_GET['cid'])) {
+                Toolkit_Members_Contact::delete($pdo, $_GET['cid']);
+                $tgt = MEDIA_BASE_URL . "admin/members.php?rt=Members&ac=editMember&tab=contacts&id={$_GET['id']}";
+                header("Location: $tgt");
+            }
+
+            $nav = new Toolkit_Members_RecordNavigation($root);
+            $nav->setupAdminNavStructure();
+            $out  = '<div id="nav-detail">'.$nav->getPageNav().'</div>';
+            $out .= '<div id="member-info">';
+
+            $tEngine = new HTML_Template_Flexy(self::getFlexyOptions());
+            $editContactForm = new Toolkit_Members_EditContactForm(
+                'edit_contacts',
+                'post',
+                MEDIA_BASE_URL . "admin/members.php?rt=Members&ac=editMember&id={$_GET['id']}&tab=contacts"
+            );
+            $editContactForm->configureForm($pdo);
+            $out .= $editContactForm->toHtml(
+                $pdo,
+                new HTML_QuickForm_Renderer_Object(true),
+                $tEngine
+            );
+
+            $rEngine = new Structures_DataGrid_Renderer_Flexy();
+            $rEngine->setContainer($tEngine);
+            $contactsList = new Toolkit_Members_ContactsDataGrid($pdo);
+            $contactsList->setQuery();
+            $contactsList->setDefaultSort(array('fname' => 'ASC', 'lname' => 'ASC'));
+            $out .= $contactsList->toHtml($rEngine);
+            $out .= '</div>';
+            break;
+
+        case 'files' :
+            $GLOBALS['bottomScripts'][]
+                = MEDIA_BASE_URL . 'Toolkit/Members/libjs/edit-member-files.js';
+
+            $mf = new Toolkit_Members_EditMemberFile($pdo, 'edit_files');
+            $mf->setConfig($root);
+            $mf->configureForm();
+            $out = $mf->toHtml();
+            break;
+
+        case 'amenities' :
+            $GLOBALS['bottomScripts'][]
+                = MEDIA_BASE_URL . 'Toolkit/Members/libjs/edit-member-amenities.js';
+
+            $ma = new Toolkit_Members_EditMemberAmenities(
+                $pdo,
+                'edit_amenities',
+                'post',
+                null,
+                null,
+                null,
+                true
+            );
+            $ma->setConfig($root);
+            $ma->configureForm();
+            $out = $ma->toHtml();
+            break;
+
+        case 'packages' :
+            $GLOBALS['bottomScripts'][]
+                = CKEDITOR_JS . '';
+            $GLOBALS['bottomScripts'][]
+                = MEDIA_BASE_URL . 'Toolkit/Members/libjs/edit-member-packages.js';
+
+            $mp = new Toolkit_Members_Admin_EditPackages($pdo, $tEngine);
+            $mp->setupPage();
+            $out = $mp->getPage($nav);
+            break;
+
+        case 'photos' :
+            $GLOBALS['bottomScripts'][]
+                = MEDIA_APP_BASE_URL . 'libjs/jqueryui/1.8.13/js/jquery-ui-1.8.13.custom.min.js';
+            $GLOBALS['bottomScripts'][]
+                = MEDIA_BASE_URL . 'Toolkit/Members/libjs/edit-member-photos.js';
+
+            //  Object to use when rendering the page template
+            $page = new stdClass();
+            //  Page navigation
+            $page->nav = $nav->getPageNav();
+
+            //  Linked List of member photos
+            $linkedList = new Toolkit_Members_Photos(null, $_GET['id']);
+            $linkedList->createMemberList(
+                $pdo,
+                $root
+            );
+            //  Member Object to get calculate attributes about a member
+            $member = new Toolkit_Members_Member(
+                $pdo,
+                $root
+            );
+            //  Server side caching
+            $cache = new Cache_Lite(Toolkit_Members::getCacheOptions());
+            //  Image server for processing uploaded images
+            $is = new Toolkit_Image_Server();
+
+            if ($member->canAddPhotos($linkedList, $root)) {
+                $aForm = new Toolkit_Members_Admin_AddPhoto(
+                    $pdo,
+                    'new_member_photo',
+                    'post',
+                    MEDIA_BASE_URL . "admin/members.php?rt=Members&ac=editMember&tab=photos&id={$_GET['id']}",
+                    '',
+                    null,
+                    true
+                );
+                $aForm->configureForm($root);
+                $page->uploadForm = $aForm->toHtml(
+                    $tEngine,
+                    $cache,
+                    $root,
+                    $is,
+                    $linkedList
+                );
+            }
+
+            if ($member->hasUploadedPhotos($linkedList)) {
+                $editForms = array();
+                $linkedList->rewind();
+
+                foreach ($linkedList as $i) {
+                    if (!$i->getPending()) {
+                        $id = $i->getId();
+                        $eForm = new Toolkit_Members_Admin_EditPhoto(
+                            $pdo,
+                            $linkedList,
+                            "edit_member_photo_$id",
+                            'post',
+                            MEDIA_BASE_URL . "admin/members.php?rt=Members&ac=editMember&tab=photos&id={$_GET['id']}",
+                            '',
+                            array('id' => $id),
+                            true
+                        );
+                        $eForm->configureForm($root);
+                        $page->editForms[$id] = $eForm->toHtml(
+                            $tEngine,
+                            $cache,
+                            $is,
+                            $root
+                        );
+                    }
+                }
+            }
+
+            //  get reference to [photos] section of config file
+            $config =& $root->getItem('section', 'photos');
+            $template =& $config->getItem('directive', 'editGalleryTemplate');
+
+            $tEngine->compile($template->getContent());
+            $out = $tEngine->bufferedOutputObject($page);
+            break;
+
+        case 'info' :
+        default :
+            $GLOBALS['bottomScripts'][]
+                = CKEDITOR_JS . '';
+            $GLOBALS['bottomScripts'][]
+                = MEDIA_BASE_URL . 'Toolkit/Members/libjs/edit-member.js';
+            $GLOBALS['bottomScripts'][]
+                = MEDIA_APP_BASE_URL . 'gallery/thickbox-3.1.1.js';
+            $GLOBALS['bottomScripts'][]
+                = MEDIA_APP_BASE_URL . 'libjs/cluetip/jquery.cluetip.js';
+            $GLOBALS['bottomScripts'][]
+                = MEDIA_APP_BASE_URL . 'libjs/cluetip/lib/jquery.hoverIntent.js';
+            $GLOBALS['bottomScripts'][]
+                = MEDIA_APP_BASE_URL . 'libjs/cluetip/lib/jquery.bgiframe.min.js';
+            $GLOBALS['styleSheets'][]
+                = MEDIA_APP_BASE_URL . 'gallery/thickbox.css';
+            $GLOBALS['styleSheets'][]
+                = MEDIA_APP_BASE_URL . 'libjs/cluetip/jquery.cluetip.css';
+
+            $mr = new Toolkit_Members_EditMemberInfo(
+                $pdo,
+                'edit_member',
+                'post',
+                null,
+                null,
+                null,
+                true
+            );
+
+            $mr->setStates(
+                Toolkit_Common::getStates($pdo)
+            );
+            $mr->setCities(
+                Toolkit_Common::getCities($pdo)
+            );
+            $mr->setConfig($root);
+            $mr->configureForm();
+            $out = $mr->toHtml();
+            break;
+        }
+
+        return $out;
+    }
+
+    //    }}}
+
+    //    {{{    getCacheOptions()
+
+    /**
+     * Get the cache options defined for the memberdb app
+     *
+     * @return array Cache_Lite options defined for the memberdb app
+     * @access public
+     * @static
+     */
+    public static function getCacheOptions()
+    {
+        $options = $GLOBALS['cacheOptions'];
+        $options['lifeTime'] = (DEVELOPMENT) ? 0: 28800;
+        return $options;
+    }
+
+    //    }}}
+    //    {{{    getFlexyOptions()
+
+    /**
+     * Get the flexy options defined for the memberdb app
+     *
+     * @return array Flexy template options defined for the memberdb app
+     * @access public
+     * @static
+     */
+    public static function getFlexyOptions()
+    {
+        $options                = $GLOBALS['flexyOptions'];
+        $options['templateDir'] = BASE . 'Toolkit/Members/templates/';
+        $options['compileDir']  = BASE . 'Toolkit/Members/templates/compiled/';
+        return $options;
+    }
+
+    //    }}}
+
+    //    {{{    show()
+
+    /**
+     * Show the page
+     *
+     * @return void
+     * @access public
+     */
+    public function show()
+    {
+        echo $this->toHtml();
+    }
+
+    //    }}}
+}
diff --git a/Toolkit/Members/AddPhoto.php b/Toolkit/Members/AddPhoto.php
new file mode 100644 (file)
index 0000000..d3d28cb
--- /dev/null
@@ -0,0 +1,203 @@
+<?php
+//     vim:set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker syntax=php:
+
+/**
+ * Handles the photos tab in the member record
+ * 
+ * Controls setting up the add photo form if applicable, and rendering
+ * each uploaded photo edit form to edit/delete the photo.
+ * 
+ * PHP version 5
+ * 
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @version   CVS: $Id: AddPhoto.php,v 1.3 2009/10/27 14:43:41 jamie Exp $
+ * @link      http://demo.gaslightmedia.com
+ * @see       Toolkit/Image/Server.php
+ */
+
+
+/**
+ * The image server processing class
+ */
+require_once BASE . 'Toolkit/Image/Server.php';
+
+/**
+ * Form to handle creating a new photo in the members only area
+ * 
+ * Handles inserting new photo into db as a pending photo and creating a
+ * tuple in the member_updates table which will allow the admin to
+ * approve/deny the new photo request.
+ * 
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @link      http://demo.gaslightmedia.com
+ */
+class Toolkit_Members_AddPhoto extends Toolkit_Members_Admin_AddPhoto
+{
+       //      {{{ createPendingPhoto()
+
+       /**
+        * Adds photo to member and makes a pending row in the updates table
+        *
+        * Adds a photo to the member_photos table but sets the pending field
+        * to true and then inserts a row into the updates table to make the field
+        * pending.  If the photo gets rejected we will have to delete from both
+        * the updates table and the member_photos table.
+        *
+        * @param array &$values submitted values from the form.
+        *
+        * @return boolean Result of creating a pending photo
+        * @access protected
+        */
+       protected function createPendingPhoto(&$values)
+       {
+               try {
+                       //      Get the last photo inserted.
+                       $sql = "
+                SELECT *
+                  FROM {$this->tableName}
+                 WHERE pending   = true
+                   AND member_id = :member_id
+                 ORDER BY id DESC";
+
+                       $stmt = $this->dbh->prepare($sql);
+                       $stmt->bindParam(
+                ':member_id',
+                $GLOBALS['memberAuth']->getAuthData('member_id'),
+                PDO::PARAM_INT
+            );
+                       $stmt->execute();
+                       $photo = $stmt->fetch();
+
+                       //      Insert the picture into the updates table for approval.
+                       $label    = '<img class="thumb" alt="'.$values['image'].'"
+                                                       src="'.MEMBER_PHOTOS.$values['image'].'">';
+                       $update   = false;
+                       $dataType = 'boolean';
+                       $field    = 'pending';
+
+                       $sql =  "
+                               INSERT INTO " . Toolkit_Membersonly::PENDING_TABLE . "
+                               (member_id, field, update , db_table,
+                                data_type, label, foreign_key)
+                VALUES (:member_id, :field, :update, :db_table,
+                                               :data_type, :label, :foreign_key)";
+
+                       $stmt = $this->dbh->prepare($sql);
+                       $stmt->bindParam(
+                ':member_id',
+                $GLOBALS['memberAuth']->getAuthData('member_id'),
+                PDO::PARAM_INT
+            );
+                       $stmt->bindParam(':field', $field, PDO::PARAM_STR);
+                       $stmt->bindParam(':update', $update, PDO::PARAM_BOOL);
+                       $stmt->bindParam(':db_table', $this->tableName, PDO::PARAM_STR);
+                       $stmt->bindParam(':data_type', $dataType, PDO::PARAM_STR);
+                       $stmt->bindParam(':label', $label, PDO::PARAM_STR);
+                       $stmt->bindParam(':foreign_key', $photo['id'], PDO::PARAM_STR);
+                       return $stmt->execute();
+               } catch (PDOException $e) {
+                       return Toolkit_Common::handleError($e);
+               }
+       }
+
+        //     }}}
+       //      {{{ processData()
+
+       /**
+        * Handles setting up the from processing and which function to get it done
+        *
+        * @param array $values Submitted values from the form.
+        *
+        * @return void
+        * @access protected
+        */
+       protected function processData($values)
+       {
+               try {
+            $this->dbh->beginTransaction();
+            $sql = Toolkit_Common::createSQLInsert(
+                $this->tableName,
+                array_keys($values)
+            );
+
+            $res = Toolkit_Common::processQuery(
+                $this->dbh,
+                $this->tableName,
+                $sql,
+                $values
+            );
+
+
+            if ($values['pending']) {
+                $this->createPendingPhoto($values);
+            }
+            $this->dbh->commit();
+               } catch (PDOException $e) {
+                       $this->dbh->rollBack();
+                       return Toolkit_Common::handleError($e);
+               }
+
+        header('Location:' . $this->getAttribute('action').'&notify=1');
+       }
+
+       //      }}}
+
+       //      {{{ toHtml()
+
+       /**
+        * Renders the form
+        *
+        * sets the page the form should be redirected to instead of coming back
+        * around to itself.
+     *
+     * @param HTML_Template_Flexy    $tEngine Templating Engine
+     * @param Cache_Lite             $cache   Caching Engine
+     * @param Config_Container       $c       Application configuration
+     * @param Toolkit_Image_Server   $is      Image Server
+     * @param Toolkit_Members_Photos $ll      Linked List of member photos
+        *
+        * @return string The rendered form
+        * @access public
+        */
+       public function toHtml(
+        HTML_Template_Flexy $tEngine,
+        Cache_Lite $cache,
+        Config_Container $c,
+        Toolkit_Image_Server $is,
+        Toolkit_Members_Photos $ll
+    ) {
+               if ($this->validate()) {
+            $config = $c->getItem('section', 'conf');
+            $strictPending = $config->getItem('directive', 'strictPending');
+
+            $id = $GLOBALS['memberAuth']->getAuthData('member_id');
+            $cache->remove("Member-$id", 'Profile');
+
+            //  Inject some data into the submitted values
+            $this->_submitValues['image'] = $is->imageUpload('file');
+            $this->_submitValues['pos'] = $ll->getListSize() + 1;
+            $this->_submitValues['member_id'] = $id;
+            $this->_submitValues['pending'] = (int) $strictPending->getContent();
+
+            unset($this->_submitValues['MAX_FILE_SIZE'],
+                  $this->_submitValues['submit']);
+
+            $this->process(array(&$this, 'processData'), false);
+               }
+
+               $this->setupRenderers($tEngine);
+
+               return $tEngine->bufferedOutputObject($this->view);
+       }
+
+       //      }}}
+}
+?>
diff --git a/Toolkit/Members/AddYourBusiness.php b/Toolkit/Members/AddYourBusiness.php
new file mode 100755 (executable)
index 0000000..993c51d
--- /dev/null
@@ -0,0 +1,1603 @@
+<?php
+//    vim:set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker syntax=php:
+
+/**
+ * AddYourBusiness.php
+ *
+ * PHP version 5
+ *
+ * @category  Toolkit
+ * @package   Members_Admin
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2008 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @version   CVS: $Id: AddYourBusiness.php,v 1.40 2010/07/14 23:31:14 jamie Exp $
+ * @link      http://demo.gaslightmedia.com
+ */
+
+require_once BASE . 'Toolkit/Members/Rules/DuplicateMember.php';
+require_once BASE . 'Toolkit/Members/Rules/MemberLogin.php';
+
+/**
+ * Creates, Renders, Processes form for request to be added to Business DB
+ *
+ * This Form just sends the primary advisee an HTML email of the data
+ * that was filled in and requires them to add the users to the Business DB
+ * themselves.
+ *
+ * @category  Toolkit
+ * @package   Members_Admin
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2008 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @link      http://demo.gaslightmedia.com
+ */
+class Toolkit_Members_AddYourBusiness
+    extends Toolkit_FormBuilder
+{
+    //    {{{ properties
+
+    /**
+     * PDO
+     * @var pdo
+     * @access private
+     */
+    private $_dbh;
+
+    /**
+     * Application configuration
+     * @var Config_Container
+     */
+    private $_config;
+
+    /**
+     * What do you want the success msg to be if the form validates successfully
+     *
+     * @var        string
+     * @access    protected
+     */
+    protected $successMsg = '
+        <div id="form-success-top">
+            Your business Information has been successfully submitted.
+            A representative will be contacting you shortly to follow up.
+        </div>';
+
+    /**
+     * Primary email address to send notification a record has been updated
+     *
+     * Set this value to false and it will turn off the email
+     * update notifications.
+     *
+     * @var string
+     * @access protected
+     */
+    protected $primaryAdvisee;
+
+    /**
+     * Email address of people that might also want to be advised of updates
+     *
+     * A list of all email address that might also want to be advised
+     * updates have been made on a business record. You can add as many
+     * as you would like and they will be CC in the email.
+     *
+     * If you use associative key values then they will be appended
+     * to the email string. i.e.
+     * Jamie Kahgee <jamie.kahgee+secondaryAdvisorTest1@gmail.com>
+     * Other wise the address will just be added.
+     *
+     * N.B. You cannot make this array bigger than 5 people or you will need to
+     * rewrite the function to handle the server spam issue.
+     *
+     * @var array
+     * @access private
+     */
+     protected $secondaryAdvisees = array();
+
+    /**
+     * The default rules to register for validating
+     *
+     * @var array
+     * @access protected
+     */
+    protected $registeredRules = array(
+        'zip',
+        'phone',
+        array(
+            'checkEmail',
+            'callback',
+            'email',
+            'Validate'
+        ),
+        array(
+            'checkURI',
+            'callback',
+            'uri',
+            'Validate'
+        )
+    );
+
+    //    }}}
+    //    {{{ __construct()
+
+    /**
+     * Constructor
+     *
+     * @param PDO              $pdo         PHP Data Object to use for DB calls
+     * @param Config_Container $config      Application Configuration
+     * @param string           $formName    Form's name.
+     * @param string           $method      (optional) Form's method defaults to 'POST'
+     * @param string           $action      (optional) Form's action.
+     * @param string           $target      (optional) Form's target defaults to '_self'
+     * @param mixed            $attributes  (optional) Extra attributes for <form> tag.
+     * @param bool             $trackSubmit (optional) Whether to track if the form was
+     *                                          submitted by adding a special hidden
+     *                                          field.
+     *
+     * @access public
+     * @see    Toolkit_FormBuilder, HTML_QuickForm
+     */
+    public function __construct(
+        PDO $pdo,
+        Config_Container $config,
+        $formName,
+        $method = 'post',
+        $action = '',
+        $target = '',
+        $attributes = null,
+        $trackSubmit = false
+    ) {
+        parent::__construct(
+            $formName,
+            $method,
+            $action,
+            $target,
+            $attributes,
+            $trackSubmit
+        );
+
+        $this->_dbh    = $pdo;
+        $this->_config = $config;
+
+        if ($this->primaryAdvisee !== false && empty($this->primaryAdvisee)) {
+            //    Set to false to turn off email function.
+            $this->primaryAdvisee = FROM_MEMBER_NEWS_EMAIL;
+        }
+
+        $var = basename(__FILE__, '.php');
+
+        $callbackUrl = MEDIA_BASE_URL;
+
+        $this->captchaOptions = array(
+            'width' => 100,
+            'height' => 50,
+            'callback' => "{$callbackUrl}Toolkit/qfcaptcha.php?var=$var",
+            'sessionVar' => $var,
+            'imageOptions' => array(
+                'font_size' => 16,
+                'font_path' => GLM_APP_BASE . 'glmPEAR/Image/Canvas/Fonts/',
+                'font_file' => 'times.ttf',
+                'background_color' => '#cccccc',
+                'obfuscation' => false,
+                'angle' => true,
+            ),
+        );
+    }
+
+    //    }}}
+
+    //    {{{    checkCvv()
+
+    /**
+     * Check if the credit card CVV is properly formatted
+     *
+     * @param array $values credit card num and cvv number from the form
+     *
+     * @return boolean whether the cvv is properly formatted
+     * @access public
+     */
+    public function checkCvv($values)
+    {
+        return Validate_Finance_CreditCard::cvv($values[0], $values[1]);
+    }
+
+    //    }}}
+    //    {{{    checkDateInput()
+
+    /**
+     * Checks the validation of a m-d-Y date
+     *
+     * This function will only be called if the autoValidateDateElements
+     * property is set to true
+     *
+     * @param array $value Date element from form.
+     *
+     * @return boolean
+     * @access public
+     * @see    Toolkit_FormBuilder::autoValidateDateElements
+     */
+    public function checkDateInput($value)
+    {
+        $res1 = is_numeric($value['m']);
+        $res2 = is_numeric($value['Y']);
+
+        if (!($res1 && $res2)) {
+            return false;
+        }
+
+        $date = implode('-', $value);
+
+        return Validate::date($date, array('format' => '%j-%Y'));
+    }
+
+    //    }}}
+    //    {{{    checkNumber()
+
+    /**
+     * Check if the credit card number is properly formatted
+     *
+     * Credit card numbers have a specific formatting based on the credit card
+     * types.
+     * i.e. (visa, discover, etc...) This call verifies if the # was properly
+     * formatted against the card type selected
+     *
+     * @param array $values credit card type and number from the form
+     *
+     * @return boolean if cc number is properly formatted based on the card type
+     * @access public
+     */
+    public function checkNumber($values)
+    {
+        if (empty($values[1])) {
+            $values[1] = null;
+        }
+
+        return Validate_Finance_CreditCard::number($values[0], $values[1]);
+    }
+
+    //    }}}
+    //    {{{ configureConstants()
+
+    /**
+     * Sets the constants for the form
+     *
+     * @return void
+     * @access public
+     */
+    public function configureConstants()
+    {
+        $approvalNeeded = $this->_config
+                               ->getItem('section', 'add your business')
+                               ->getItem('directive', 'approvalNeeded')
+                               ->getContent();
+        $hasAuthNetAcc  = $this->_config
+                               ->getItem('section', 'authorize net')
+                               ->getItem('directive', 'useAuthNet')
+                               ->getContent();
+        $newMember = ($approvalNeeded && !$hasAuthNetAcc) ? 1 : 0;
+
+        $c = array(
+            'new_member' => $newMember,
+        );
+
+        $this->setupConstants($c);
+    }
+
+    //    }}}
+    //    {{{ configureElements()
+
+    /**
+     * Setup the elements to use on the form.
+     *
+     * @return void
+     * @access public
+     */
+    public function configureElements()
+    {
+        $e = array();
+        $singularType  = $this->_config
+                              ->getItem('section', 'listing type')
+                              ->getItem('directive', 'singular')
+                              ->getContent();
+        $pluralType    = $this->_config
+                              ->getItem('section', 'listing type')
+                              ->getItem('directive', 'plural')
+                              ->getContent();
+        $useCtrlCities = $this->_config
+                              ->getItem('section', 'conf')
+                              ->getItem('directive', 'controlledCities')
+                              ->getContent();
+        $hasAuthNetAcc = $this->_config
+                              ->getItem('section', 'authorize net')
+                              ->getItem('directive', 'useAuthNet')
+                              ->getContent();
+
+        $ccTypes = array(
+            '' => '-- Select --',
+            'amex' => 'American Express',
+            'discover' => 'Discover Card',
+            'mastercard' => 'Master Card',
+            'visa' => 'Visa'
+        );
+
+        $states = Toolkit_Common::getStates($this->_dbh);
+        $this->setCategories();
+        //    All Grouped Elements are created here.
+
+        //    All Elements are created here.  This includes group element definitions.
+        //    Member Information
+        $e[] = array(
+            'type' => 'hidden',
+            'req'  => false,
+            'name' => 'new_member'
+        );
+        $e[] = array(
+            'type'    => 'header',
+            'req'     => false,
+            'name'    => 'businessInfoHdr',
+            'display' => "$singularType Information",
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => true,
+            'name'    => 'member_name',
+            'display' => "$singularType Name",
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => true,
+            'name'    => 'primary_contact_fname',
+            'display' => 'Primary Contact First Name'
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => true,
+            'name'    => 'primary_contact_lname',
+            'display' => 'Primary Contact Last Name'
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => true,
+            'name'    => 'process_email',
+            'display' => 'Primary Contact Email'
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => true,
+            'name'    => 'process_email_rmv',
+            'display' => 'Confirm Email'
+        );
+        $e[] = array(
+            'type'    => 'select3',
+            'req'     => true,
+            'name'    => 'category_id',
+            'display' => "$singularType Category",
+            'opts'    => $this->categories,
+        );
+        $e[] = array(
+            'type' => 'header',
+            'req' => false,
+            'name' => 'physicalAddressHdr',
+            'display' => 'Physical Address',
+        );
+        $e[] = array(
+            'type' => 'text',
+            'req' => true,
+            'name' => 'street',
+            'display' => 'Street Address'
+        );
+        if ($useCtrlCities) {
+            $e[] = array(
+                'type'    => 'select',
+                'req'     => true,
+                'name'    => 'city_id',
+                'display' => 'City',
+                'opts'    => $this->getCities(),
+            );
+        } else {
+            $e[] = array(
+                'type'    => 'text',
+                'req'     => true,
+                'name'    => 'city',
+                'display' => 'City',
+            );
+        }
+        $e[] = array(
+            'type' => 'select',
+            'req' => true,
+            'name' => 'state_id',
+            'display' => 'State / Provice',
+            'opts' => array('' => '-- Select --') + $states
+        );
+        $e[] = array(
+            'type' => 'text',
+            'req' => true,
+            'name' => 'zip',
+            'display' => 'Zip / Postal Code',
+        );
+        $e[] = array(
+            'type' => 'header',
+            'req' => false,
+            'name' => 'mailingAddressHdr',
+            'display' => 'Mailing Address - <em>If different from physical address</em>',
+        );
+        $e[] = array(
+            'type' => 'text',
+            'req' => false,
+            'name' => 'mailing_address',
+            'display' => 'Street Address'
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => 'mailing_city',
+            'display' => 'City',
+        );
+        $e[] = array(
+            'type' => 'select',
+            'req' => false,
+            'name' => 'mailing_state_id',
+            'display' => 'State / Provice',
+            'opts' => array('' => '-- Select --') + $states
+        );
+        $e[] = array(
+            'type' => 'text',
+            'req' => false,
+            'name' => 'mailing_zip',
+            'display' => 'Zip / Postal Code'
+        );
+        $e[] = array(
+            'type' => 'header',
+            'req' => false,
+            'name' => 'contactInfoHdr',
+            'display' => 'Public Contact Information'
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => 'phone',
+            'display' => 'Phone'
+        );
+        $e[] = array(
+            'type' => 'text',
+            'req' => false,
+            'name' => 'toll_free',
+            'display' => 'Phone 2'
+        );
+        $e[] = array(
+            'type' => 'text',
+            'req' => false,
+            'name' => 'fax',
+            'display' => 'Fax'
+        );
+        $e[] = array(
+            'type' => 'text',
+            'req' => false,
+            'name' => 'member_contact_email',
+            'display' => 'Email on Website'
+        );
+        $e[] = array(
+            'type' => 'text',
+            'req' => false,
+            'name' => 'url',
+            'display' => 'Website Address'
+        );
+        $e[] = array(
+            'type' => 'textarea',
+            'req' => false,
+            'name' => 'description',
+            'display' => 'Description of Business - <em id="charcount">1,000 characters max</em>'
+        );
+        $e[] = array(
+            'type' => 'header',
+            'req' => false,
+            'name' => 'accountInfoHdr',
+            'display' => 'Account Information'
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => true,
+            'name'    => 'member_login',
+            'display' => 'Username'
+        );
+        $e[] = array(
+            'type'    => 'password',
+            'req'     => true,
+            'name'    => 'member_passwd',
+            'display' => 'Password'
+        );
+        $e[] = array(
+            'type'    => 'password',
+            'req'     => true,
+            'name'    => 'member_passwd_rmv',
+            'display' => 'Confirm Password'
+        );
+        if ($hasAuthNetAcc) {
+            $e[] = array(
+                'type' => 'header',
+                'req' => false,
+                'name' => 'creditCardInfoHdr',
+                'display' => 'CreditCard Information'
+            );
+            if (DEVELOPMENT) {
+                $e[] = array(
+                    'type' => 'static',
+                    'req' => false,
+                    'name' => 'development_notice',
+                    'opts' => 'Test Mode: Credit Card transactions will not be processed'
+                );
+            }
+            $e[] = array(
+                'type' => 'select',
+                'req' => true,
+                'name' => 'cc_type',
+                'display' => 'Type',
+                'opts' => $ccTypes
+            );
+            $e[] = array(
+                'type' => 'text',
+                'req' => true,
+                'name' => 'cc_num',
+                'display' => 'Credit Card Number',
+            );
+            $e[] = array(
+                'type' => 'date',
+                'req' => true,
+                'name' => 'cc_exp',
+                'display' => 'Expiration Date',
+                'opts' => array(
+                    'format' => 'm / Y',
+                    'minYear' => date('Y'),
+                    'maxYear' => date('Y') + 10,
+                    'addEmptyOption' => true,
+                    'emptyOptionText' => array(
+                        'm' => 'mm',
+                        'Y' => 'yyyy'
+                    )
+                )
+            );
+            $e[] = array(
+                'type' => 'text',
+                'req' => true,
+                'name' => 'cc_cvv',
+                'display' => 'CVV',
+            );
+        }
+        if ($this->useCaptcha) {
+            $e[] = array(
+                'type' => 'header',
+                'req' => false,
+                'name' => 'captchaHdr',
+                'display' => 'Captcha'
+            );
+            $e[] = array(
+                'type'    => 'CAPTCHA_Image',
+                'req'     => false,
+                'name'    => 'captcha_question',
+                'display' => 'Verification code',
+                'opts'    => $this->captchaOptions
+            );
+            $e[] = array(
+                'type'    => 'text',
+                'req'     => true,
+                'name'    => 'captcha_rmv',
+                'display' => 'Enter verification code'
+            );
+        }
+        $e[] = array(
+            'type'     => 'submit',
+            'req'      => false,
+            'name'     => 'submit_rmv',
+            'display'  => 'Submit'
+        );
+
+        $this->setupElements($e);
+    }
+
+    //    }}}
+    //    {{{ configureFilters()
+
+    /**
+     * Setup the filters to apply to the elements before we are
+     * handed the values submitted
+     *
+     * @return void
+     * @access public
+     */
+    public function configureFilters()
+    {
+        $f = array();
+        $f[] = array(
+            'element' => '__ALL__',
+            'filter'  => 'trim'
+        );
+        $f[] = array(
+            'element' => 'url',
+            'filter' => array('Toolkit_Common', 'filterURI')
+        );
+        $f[] = array(
+            'element' => 'phone',
+            'filter' => array('Toolkit_Common', 'filterPhone')
+        );
+        $f[] = array(
+            'element' => 'toll_free',
+            'filter' => array('Toolkit_Common', 'filterPhone')
+        );
+        $f[] = array(
+            'element' => 'fax',
+            'filter' => array('Toolkit_Common', 'filterPhone')
+        );
+
+        $this->setupFilters($f);
+    }
+
+    //    }}}
+    //  {{{ configureForm()
+
+    /**
+     * Helper method to setup the entire form
+     *
+     * @return void
+     * @access public
+     */
+    public function configureForm()
+    {
+        $this->configureElements();
+        $this->configureFilters();
+        $this->configureRules();
+        $this->configureConstants();
+    }
+
+    //  }}}
+    //    {{{ configureRules()
+
+    /**
+     * Sets up all the rules to be used when the form is validated.
+     *
+     * @return void
+     * @access public
+     */
+    public function configureRules()
+    {
+        $r = array();
+
+        $useCtrlCities
+            = $this->_config
+                   ->getItem('section', 'conf')
+                   ->getItem('directive', 'controlledCities')
+                   ->getContent();
+        $allowDuplicateMemberNames
+            = $this->_config
+                   ->getItem('section', 'conf')
+                   ->getItem('directive', 'duplicateMembers')
+                   ->getContent();
+
+
+        $r[] = array(
+            'element'    => 'category_id',
+            'message'    => 'ERROR: Invalid Category!',
+            'type'       => 'numeric',
+            'format'     => null,
+            'validation' => $this->validationType,
+            'reset'      => false,
+            'force'      => false
+        );
+        //    If we're using controlled cities, then we only accept numeric
+        //    ids for cities.
+        if ($useCtrlCities) {
+            $r[] = array(
+                'element'    => 'city_id',
+                'message'    => 'ERROR: Invalid City!',
+                'type'       => 'numeric',
+                'format'     => null,
+                'validation' => $this->validationType,
+                'reset'      => false,
+                'force'      => false
+            );
+            $r[] = array(
+                'element'    => 'mailing_city_id',
+                'message'    => 'ERROR: Invalid City!',
+                'type'       => 'numeric',
+                'format'     => null,
+                'validation' => $this->validationType,
+                'reset'      => false,
+                'force'      => false
+            );
+        }
+        $r[] = array(
+            'element'    => 'member_contact_email',
+            'message'    => 'ERROR: Invalid email format!',
+            'type'       => 'checkEmail',
+            'format'     => array('use_rfc822' => true),
+            'validation' => $this->validationType,
+            'reset'      => false,
+            'force'      => false
+        );
+        $r[] = array(
+            'element'    => 'process_email',
+            'message'    => 'ERROR: Invalid email format!',
+            'type'       => 'checkEmail',
+            'format'     => array('use_rfc822' => true),
+            'validation' => $this->validationType,
+            'reset'      => false,
+            'force'      => false
+        );
+        $r[] = array(
+            'element'    => array(
+                'process_email',
+                'process_email_rmv'
+            ),
+            'message'    => 'ERROR: Your email addresses do not match!',
+            'type'       => 'compare',
+            'format'     => null,
+            'validation' => $this->validationType,
+            'reset'      => false,
+            'force'      => false
+        );
+        $r[] = array(
+            'element'   => array(
+                'member_passwd',
+                'member_passwd_rmv'
+            ),
+            'message'    => 'ERROR: Your passwords do not match!',
+            'type'       => 'compare',
+            'format'     => null,
+            'validation' => $this->validationType,
+            'reset'      => false,
+            'force'      => false
+        );
+        $r[] = array(
+            'element'    => 'url',
+            'message'    => 'ERROR: Invalid URL format (http, https only)',
+            'type'       => 'checkURI',
+            'format'     => array(
+                'allowed_schemes' => array('http', 'https'),
+                'strict' => true
+            ),
+            'validation' => $this->validationType,
+            'reset'      => false,
+            'force'      => false
+        );
+        $r[] = array(
+            'element'    => 'state_id',
+            'message'    => 'ERROR: Invalid State!',
+            'type'       => 'numeric',
+            'format'     => null,
+            'validation' => $this->validationType,
+            'reset'      => false,
+            'force'      => false
+        );
+        $r[] = array(
+            'element'    => 'mailing_state_id',
+            'message'    => 'ERROR: Invalid State!',
+            'type'       => 'numeric',
+            'format'     => null,
+            'validation' => $this->validationType,
+            'reset'      => false,
+            'force'      => false
+        );
+        $r[] = array(
+            'element'    => 'zip',
+            'message'    => 'ERROR: Invalid Zip Code!',
+            'type'       => 'zip',
+            'format'     => array('requireDBCheck' => false),
+            'validation' => $this->validationType,
+            'reset'      => false,
+            'force'      => false
+        );
+        $r[] = array(
+            'element'    => 'mailing_zip',
+            'message'    => 'ERROR: Invalid Zip Code!',
+            'type'       => 'zip',
+            'format'     => array('requireDBCheck' => false),
+            'validation' => $this->validationType,
+            'reset'      => false,
+            'force'      => false
+        );
+        $r[] = array(
+            'element'    => 'phone',
+            'message'    => 'ERROR: Invalid Phone Number! (xxx) xxx-xxxx',
+            'type'       => 'phone',
+            'format'     => null,
+            'validation' => $this->validationType,
+            'reset'      => false,
+            'force'      => false
+        );
+        $r[] = array(
+            'element'    => 'fax',
+            'message'    => 'ERROR: Invalid Phone Number! (xxx) xxx-xxxx',
+            'type'       => 'phone',
+            'format'     => null,
+            'validation' => $this->validationType,
+            'reset'      => false,
+            'force'      => false
+        );
+        $r[] = array(
+            'element'    => 'toll_free',
+            'message'    => 'ERROR: Invalid Phone Number! (xxx) xxx-xxxx',
+            'type'       => 'phone',
+            'format'     => null,
+            'validation' => $this->validationType,
+            'reset'      => false,
+            'force'      => false
+        );
+        if ($allowDuplicateMemberNames) {
+            $r[] = array(
+                'element' => 'member_name',
+                'message' => 'ERROR: This name already exists!',
+                'type' => 'DuplicateMember',
+                'format' => $this->_dbh,
+                'validation' => $this->validationType,
+                'reset' => false,
+                'force' => false
+            );
+        }
+        $r[] = array(
+            'element'    => 'member_login',
+            'message'    => 'ERROR: This username is taken already!',
+            'type'       => 'MemberLogin',
+            'format'     => $this->_dbh,
+            'validation' => $this->validationType,
+            'reset'      => false,
+            'force'      => false
+        );
+        $hasAuthNetAcc = $this->_config
+                              ->getItem('section', 'authorize net')
+                              ->getItem('directive', 'useAuthNet')
+                              ->getContent();
+        if ($hasAuthNetAcc) {
+            $r[] = array(
+                'element'    => array('cc_num', 'cc_type'),
+                'message'    => 'ERROR: Number does not match card type!',
+                'type'       => 'callback',
+                'format'     => array(&$this, 'checkNumber'),
+                'validation' => $this->validationType,
+                'reset'      => false,
+                'force'      => false
+            );
+            $r[] = array(
+                'element'    => array('cc_cvv', 'cc_type'),
+                'message'    => 'ERROR: CVV does not match card type!',
+                'type'       => 'callback',
+                'format'     => array(&$this, 'checkCvv'),
+                'validation' => $this->validationType,
+                'reset'      => false,
+                'force'      => false
+            );
+        }
+        if ($this->useCaptcha) {
+            $r[] = array(
+                'element'    => 'captcha_rmv',
+                'message'    => 'ERROR: What you entered didn\'t match!',
+                'type'       => 'CAPTCHA',
+                'format'     => $this->captchaQuestion,
+                'validation' => $this->validationType,
+                'reset'      => false,
+                'force'      => false
+            );
+        }
+
+        $this->setupRules($r);
+    }
+
+    //    }}}
+    //    {{{ createCategories()
+
+    /**
+     * Creates an array from the array of category tree objects
+     *
+     * @param array   $tree  linear category tree array
+     * @param integer $depth what level we are on
+     *
+     * @return array The array list of categories that
+     *                 that can be loaded into a select element
+     * @access protected
+     */
+    protected function createCategories($tree, $depth = 0)
+    {
+        $this->records[] = $tree->category;
+        if ($depth == 0) {
+            $this->categories[$tree->catid] = "<span>{$tree->category}</span>";
+        } else {
+            $this->categories[$tree->catid] = $tree->category;
+        }
+        if (empty($tree->children)) {
+            return;
+        } else {
+            ++$depth;
+            foreach ($tree->children as $miniTrees) {
+                $this->createCategories($miniTrees, $depth);
+            }
+        }
+    }
+
+    //    }}}
+
+    //    {{{ getCategories()
+
+    /**
+     * Creates array structure of the category tree for use in a select element.
+     *
+     * This method will also instantiate a class property called tree for your
+     * class. This object will hold the tree structure of the categories list
+     * from the Database.
+     *
+     * @return array The array list of categories that can be loaded
+     *                 into a select element
+     *
+     * @access protected
+     */
+    protected function getCategories()
+    {
+        try {
+            $sql = "
+                SELECT *
+                  FROM category
+                 WHERE parent_id = 0
+                 ORDER BY name";
+            foreach ($this->_dbh->query($sql) as $row) {
+                $this->tree[] = new Toolkit_Members_CategoryTree(
+                    $row['category_id'],
+                    $row['name'],
+                    $this->_dbh
+                );
+            }
+            if (!empty($this->tree)) {
+                foreach ($this->tree as $t) {
+                    $this->createCategories($t);
+                }
+            }
+
+            if (empty($this->categories)) {
+                $this->categories = array('' => '-- No Categories Created Yet --');
+            } else {
+                $this->categories
+                    = array('' => '-- Select Category --') + $this->categories;
+            }
+        } catch (PDOException $e) {
+            return Toolkit_Common::handleError($e);
+        }
+    }
+
+    //    }}}
+    //    {{{ getCities()
+
+    /**
+     * Configure the cities for member stored in the database into an array
+     *
+     * @return array the cities
+     * @access protected
+     */
+    protected function getCities()
+    {
+        $cities = array();
+        try {
+            $sql = "
+                SELECT *
+                  FROM city
+                 ORDER BY city_name";
+            foreach ($this->_dbh->query($sql) as $row) {
+                $cities[$row['city_id']] = $row['city_name'];
+            }
+            if (!empty($cities)) {
+                $cities = array('' => '-- Select --') + $cities;
+            } else {
+                $cities = array('' => '-- No Cities Created Yet -- ');
+            }
+            return $cities;
+        } catch (PDOException $e) {
+            return Toolkit_Common::handleError($e);
+        }
+    }
+
+    //    }}}
+    //    {{{    getMemberCategories()
+
+    /**
+     * Get the array of member categories used for the select list
+     *
+     * @return array $pages pages used to populate a select list element
+     * @access protected
+     */
+    protected function getMemberCategories()
+    {
+        $categories = Toolkit_Common::getHierarchicalTreeStructure(
+            $this->_dbh,
+            'category',
+            'category_id',
+            'parent_id'
+        );
+        //  Get only the active categories from
+        //  the nav structure for our select list.
+        $sql = "
+            SELECT category_id, name
+              FROM category
+             WHERE category_id = :id";
+
+        $stmt = $this->_dbh->prepare($sql);
+
+        foreach ($categories as $k => $v) {
+            unset($row, $category);
+            $stmt->bindParam(':id', $k, PDO::PARAM_INT);
+            $stmt->execute();
+
+            //  If we actually retrieved a row, add it to the select list
+            //  after we clean it up.
+            if ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                $pages[$k] = array(
+                    'level' => $v - 1,
+                    'name' => $row['name']
+                );
+            }
+        }
+
+        return $pages;
+    }
+
+    //    }}}
+    //    {{{    insertData()
+
+    /**
+     * Inserts contact data into the contact db
+     *
+     * @param array $values submitted values
+     *
+     * @return object    result of db insert query
+     * @access protected
+     */
+    protected function insertData($values)
+    {
+        $hasAuthNetAcc = $this->_config
+            ->getItem('section', 'authorize net')
+            ->getItem('directive', 'useAuthNet')
+            ->getContent();
+        try {
+            $this->setFormData();
+            $values['description']
+                = nl2br(str_replace("\r", "\n", $values['description']));
+
+            $this->setLatLngCoords($values);
+
+            $this->_dbh->beginTransaction();
+            $memberCategory = $values['category_id'];
+            $authValues     = $values;
+            unset(
+                $values['category_id'],
+                $values['cc_type'],
+                $values['cc_num'],
+                $values['cc_exp'],
+                $values['cc_cvv']
+            );
+            if ($hasAuthNetAcc) {
+                $values['active'] = true;
+            }
+            $sql = Toolkit_Common::createSQLInsert('member', array_keys($values));
+            $stmt = Toolkit_Common::prepareQuery(
+                $this->_dbh,
+                'member',
+                $sql,
+                $values
+            );
+            $stmt->execute();
+
+            $sql = "
+                SELECT member_id
+                  FROM member
+                 ORDER BY member_id DESC LIMIT 1";
+
+            $row = $this->_dbh->query($sql)->fetch();
+
+            $sql = "
+                INSERT INTO member_category (member_id, category_id)
+                VALUES (:mid, :cid)";
+            $stmt = $this->_dbh->prepare($sql);
+            $stmt->bindParam(':mid', $row['member_id']);
+            $stmt->bindParam(':cid', $memberCategory, PDO::PARAM_INT);
+            $stmt->execute();
+
+            //    If they are paying via auth.net and their account
+            //    doesn't go through, then don't actually insert the member.
+
+            if ($hasAuthNetAcc) {
+                $res = $this->processAuthNet($authValues);
+                if (is_array($res) && !empty($res)) {
+                    if ($res[0] == 1) {
+                        //    CC was Accepted.
+                        $this->_dbh->commit();
+                        return true;
+                    } elseif ($res[0] == 2) {
+                        //    CC was Declined.
+                        $this->_dbh->rollback();
+                        return 2;
+                    } else {
+                        //    There was a CC processing error.
+                        $this->_dbh->rollback();
+                        return $res[0];
+                    }
+                } else {
+                    //    There was a CC processing error.
+                    $this->_dbh->rollback();
+                    return false;
+                }
+            } else {
+                //    Not paying by CC so just commit the insert.
+                return $this->_dbh->commit();
+            }
+        } catch (PDOException $e) {
+            $this->_dbh->rollback();
+            return Toolkit_Common::handleError($e);
+        }
+    }
+
+    //    }}}
+
+    //    {{{    processAuthNet()
+
+    /**
+     * Send payment to auth.net
+     *
+     * Test Cards
+     * Card Number      Card Type
+     * ===============  ===============
+     * 370000000000002  American Express
+     * 6011000000000012 Discover Card
+     * 5424000000000015 MasterCard
+     * 4000000000000000 Visa
+     *
+     * @param array $values Submitted values from the form
+     *
+     * @return array Auth.net response
+     * @access protected
+     */
+    protected function processAuthNet($values)
+    {
+        $authNetLoginId = $this->_config
+                               ->getItem('section', 'add your business')
+                               ->getItem('directive', 'authNetLoginId')
+                               ->getContent();
+        $authNetTranKey = $this->_config
+                               ->getItem('section', 'add your business')
+                               ->getItem('directive', 'authNetTranKey')
+                               ->getContent();
+        $authNetUrl     = "https://secure.authorize.net/gateway/transact.dll";
+
+        $authNetValues = array(
+            'x_login'          => $authNetLoginId,
+            'x_version'        => '3.1',
+            'x_delim_char'     => '|',
+            'x_delim_data'     => 'TRUE',
+            'x_type'           => 'AUTH_CAPTURE',
+            'x_method'         => 'CC',
+            "x_test_request"   => DEVELOPMENT ? "TRUE" : "FALSE",
+            'x_tran_key'       => $authNetTranKey,
+            'x_relay_response' => 'FALSE',
+            'x_card_num'       => $values['cc_num'],
+            'x_exp_date'       => implode('', $values['cc_exp']),
+            'x_amount'         => $values[$values['member_type']],
+            'x_first_name'     => $values['cc_fname'],
+            'x_last_name'      => $values['cc_lname'],
+        );
+
+        $fields = '';
+        foreach ($authNetValues as $i => $j) {
+            $fields .= "$i=" . urlencode($j) . '&';
+        }
+
+        $ch = curl_init($authNetUrl);
+        //    set to 0 to elimindate header info from response
+        curl_setopt($ch, CURLOPT_HEADER, 0);
+        //    Returns response data instead of TRUE(1)
+        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
+        //    Use HTTP POST to send form data
+            curl_setopt($ch, CURLOPT_POSTFIELDS, rtrim($fields, "& "));
+        //    Uncomment this line if you get no gateway response. ###
+        //    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
+        //    Execute post and get results
+        $resp = curl_exec($ch);
+        curl_close($ch);
+
+        $res = explode('|', $resp);
+        return $res;
+    }
+
+    //    }}}
+    //    {{{    processData()
+
+    /**
+     * Handles how to process the form when submitted
+     *
+     * @param array $values Form submitted values
+     *
+     * @return array     Result of Insert / Update function
+     * @access protected
+     */
+    public function processData($values)
+    {
+        //    Form data used for the insert/update sql queries and
+        //    the form email.
+        $e = array();
+        $this->setFormData($e);
+
+        //    Get rid of any defined un-needed elements.
+        //    un-needed elements after the form is submitted are defined
+        //    by the ending _rmv name.
+        foreach ($values as $k => &$v) {
+            if (preg_match('/^.+_rmv$/', $k)) {
+                unset($values[$k]);
+            }
+        }
+
+        $this->tableMetaData = Toolkit_Common::getTableMetaData(
+            $this->_dbh,
+            'member'
+        );
+
+        return $this->insertData($values);
+    }
+
+    //    }}}
+
+    //    {{{    sendAdminEmail()
+
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @param boolean $test Parameter description (if any) ...
+     *
+     * @return boolean Return description (if any) ...
+     * @access public
+     */
+    public function sendAdminEmail($test = false)
+    {
+        if ($this->primaryAdvisee === false) {
+            return true;
+        } else {
+            $template = new HTML_Template_Flexy(Toolkit_Members::getFlexyOptions());
+            $page     = new stdClass();
+
+            $page->base_url        = MEDIA_BASE_URL;
+            $page->approval_needed = $this->approvalNeeded;
+            $page->member_name     = $this->formData['member_name']['element'];
+            $page->client_name     = SITENAME;
+            $page->email_from      = OWNER_EMAIL;
+
+            $template->compile('addYourBusinessAdminEmail.tpl');
+            //    Merge the compiled template with the $page object.
+            $htmlMsg = $template->bufferedOutputObject($page);
+
+            $msg = '';
+
+            $crlf     = "\n";
+            $mimeMail = new Mail_mime($crlf);
+            $mimeMail->setFrom(SITENAME . " <{$page->email_from}>");
+            $mimeMail->setSubject('Add Your Business Request From ' . SITENAME);
+            if (!empty($this->secondaryAdvisees)) {
+                //    Spam will choke if we CC: more than 5 addresses.
+                //    so if the user puts more than 5 in, we'll only
+                //    accept the first 5.  This is stated in the property
+                //    description for secondaryAdvisees
+                if (count($this->secondaryAdvisees) > 5) {
+                    $this->secondaryAdvisees = array_slice($this->secondaryAdvisees, 0, 5);
+                }
+                foreach ($this->secondaryAdvisees as $k => $v) {
+                    if (is_string($k) && !is_numeric($k)) {
+                        $email = "$k <$v>";
+                    } else {
+                        $email = $v;
+                    }
+                    $mimeMail->addCC($email);
+                }
+            }
+            $mimeMail->setHTMLBody($htmlMsg);
+            $mimeMail->setTXTBody($msg);
+
+            //    If we're testing the interface then don't actually send out the mail
+            $interface = $test ? 'mock' : 'mail';
+            $mail      = Mail::factory($interface);
+            $body      = $mimeMail->get();
+            $headers   = $mimeMail->headers($hdrs);
+
+            $res = $mail->send($this->primaryAdvisee, $headers, $body);
+
+            return PEAR::isError($res) ? Toolkit_Common::handleError($res) : $res;
+        }
+    }
+
+    //    }}}
+    //    {{{    sendMemberEmail()
+
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @param boolean $test Parameter description (if any) ...
+     *
+     * @return unknown Return description (if any) ...
+     * @access public
+     */
+    public function sendMemberEmail($test = false)
+    {
+        $template = new HTML_Template_Flexy(Toolkit_Members::getFlexyOptions());
+        $page     = new stdClass();
+
+        $page->base_url        = ($_SERVER['HTTPS'] == 'on') ? BASE_SECURE_URL :
+                                                               MEDIA_BASE_URL;
+        $page->member_category = MEMBERS_CATEGORY;
+        $page->approval_needed = $this->approvalNeeded;
+        $page->first_name      = $this->formData['primary_contact_fname']['element'];
+        $page->member_name     = $this->formData['member_name']['element'];
+        $page->username        = $this->formData['member_login']['element'];
+        $page->password        = $this->formData['member_passwd']['element'];
+        $page->client_name     = SITENAME;
+
+        $template->compile('addYourBusinessMemberEmail.tpl');
+        //    Merge the compiled template with the $page object.
+        $htmlMsg = $template->bufferedOutputObject($page);
+
+        $msg = '';
+
+        $crlf     = "\n";
+        $mimeMail = new Mail_mime($crlf);
+        $mimeMail->setFrom(SITENAME . ' <'.OWNER_EMAIL.'>');
+        $mimeMail->setSubject('Add Your Business Request From ' . SITENAME);
+        $mimeMail->setHTMLBody($htmlMsg);
+        $mimeMail->setTXTBody($msg);
+
+        //    If we're testing the interface then don't actually send out the mail
+        $interface = $test ? 'mock' : 'mail';
+        $mail      = Mail::factory($interface);
+        $body      = $mimeMail->get();
+        $headers   = $mimeMail->headers($hdrs);
+
+        $res = $mail->send($this->formData['process_email']['element'], $headers, $body);
+
+        return PEAR::isError($res) ? Toolkit_Common::handleError($res) : $res;
+    }
+
+    //    }}}
+    //    {{{ setCategories()
+
+    /**
+     * Creates array structure of the category tree for use in a select element.
+     *
+     * This method will also instantiate a class property called tree for your
+     * class. This object will hold the tree structure of the categories list
+     * from the Database.
+     *
+     * @return array The array list of categories that can be loaded
+     *                 into a select element
+     *
+     * @access protected
+     */
+    protected function setCategories()
+    {
+        try {
+            $sql = "
+                SELECT *
+                  FROM category
+                 WHERE parent_id = 0
+                 ORDER BY name";
+            foreach ($this->_dbh->query($sql) as $row) {
+                $this->tree[] = new Toolkit_Members_CategoryTree(
+                    $row['category_id'],
+                    $row['name'],
+                    $this->_dbh
+                );
+            }
+            if (!empty($this->tree)) {
+                foreach ($this->tree as $t) {
+                    $this->createCategories($t);
+                }
+            }
+
+            if (empty($this->categories)) {
+                $this->categories = array('' => '-- No Categories Created Yet --');
+            } else {
+                $this->categories
+                    = array('' => '-- Select --') + $this->categories;
+            }
+        } catch (PDOException $e) {
+            return Toolkit_Common::handleError($e);
+        }
+    }
+
+    //    }}}
+    //    {{{    setLatLngCoords()
+
+    /**
+     * Description for setLatLngCoords
+     *
+     * @param array &values
+     * @return mixed From Toolkit_Common
+     * @access protected
+     */
+    protected function setLatLngCoords(&$values)
+    {
+        $geocoder = new GeocodeYahoo();
+
+        $sql = "
+            SELECT c.city_name, s.state_abb
+              FROM city c, state s
+             WHERE c.city_id  = :cityId
+               AND s.state_id = :stateId";
+
+        try {
+            $stmt = $this->_dbh->prepare($sql);
+            $stmt->bindParam(':cityId', $values['city_id'], PDO::PARAM_INT);
+            $stmt->bindParam(':stateId', $values['state_id'], PDO::PARAM_INT);
+            $stmt->execute();
+            $stmt->bindColumn('city_name', $cityName);
+            $stmt->bindColumn('state_abb', $stateName);
+            $stmt->fetch(PDO::FETCH_ASSOC);
+        } catch (PDOException $e) {
+            return Toolkit_Common::handleError($e);
+        }
+
+        $address = array(
+            'city' => $cityName,
+            'state' => $stateName,
+            'zip' => $values['zip'],
+        );
+        if (!empty($values['street'])) {
+            $address['street'] = $values['street'];
+        }
+
+        $response = $geocoder->geocodeAddress($address);
+        $responseArray = unserialize($response);
+        $values['lat'] = $responseArray['ResultSet']['Result']['Latitude'];
+        $values['lon'] = $responseArray['ResultSet']['Result']['Longitude'];
+    }
+
+    //    }}}
+    //    {{{    setupRenderers()
+
+    /**
+     * Setup how the form should render
+     *
+     * We also need to attach a couple specific rendering templates to
+     * a few special elements.
+     *
+     * @return void
+     * @access protected
+     */
+    protected function setupRenderers()
+    {
+        parent::setupRenderers();
+        $renderer = $this->defaultRenderer();
+        $required = '<!-- BEGIN required --><span class="req">*</span><!-- END required -->';
+        $error    = '<!-- BEGIN error --><div class="req">{error}</div><!-- END error -->';
+
+        $renderer->setElementTemplate(
+            '<tr align="center"><td colspan="2">{element}</td></tr>',
+            'submit_rmv'
+        );
+        $renderer->setElementTemplate(
+            '<tr>
+                <td colspan="2">'.$required.'{label}'.$error.'{element}</td>
+            </tr>',
+            'description'
+        );
+        if (DEVELOPMENT) {
+        $renderer->setElementTemplate(
+            '<tr>
+                <td colspan="2"><em id="cc-dev-mode-notice">{element}</em></td>
+            </tr>',
+            'development_notice'
+        );
+        }
+    }
+
+    //    }}}
+
+    //    {{{    toHtml()
+
+    /**
+     * Call the rendering function to get the form in a string
+     *
+     * destroying and resetting the captcha value dis-allows someone from
+     * re-sending a form on a previous captcha.
+     *
+     * @return string The Form to be rendered or success msg.
+     * @access public
+     */
+    public function toHtml()
+    {
+        $this->setupRenderers();
+        if ($this->validate()) {
+            if ($this->useCaptcha) {
+                $this->captchaQuestion->destroy();
+            }
+
+            $res = $this->process(
+                array(&$this, 'processData'),
+                $this->mergeFiles
+            );
+
+//return '<h1>validated</h1>' . parent::toHtml();
+            if ($res === true) {
+                $this->cleanForm();
+                $this->sendAdminEmail();
+                $this->sendMemberEmail();
+                $this->freeze();
+                $output = $this->successMsg;
+                $hasAuthNetAcc = $this->_config
+                    ->getItem('section', 'authorize net')
+                    ->getItem('directive', 'useAuthNet')
+                    ->getContent();
+                if ($hasAuthNetAcc) {
+                    $rem = array(
+                        'cc_cvv',
+                        'cc_type',
+                        'cc_fname',
+                        'cc_lname',
+                        'member_login',
+                        'member_passwd',
+                        'captcha_question',
+                    );
+                    foreach ($rem as $i) {
+                        if ($this->elementExists($i)) {
+                            $this->removeElement($i);
+                        }
+                    }
+
+                    if ($this->elementExists('cc_num')) {
+                        //    Obscure th CC Num so its not displayed.
+                        $e = $this->getElement('cc_num');
+                        $ccNum = $e->getValue();
+                        $newCCNum = preg_replace(
+                            '/\d/',
+                            '*',
+                            $ccNum,
+                            strlen($ccNum) - 4
+                        );
+                        $e->setValue($newCCNum);
+                    }
+                }
+                $output = $this->successMsg;
+            } elseif ($res == 2) {
+                $this->_errors['cc_num'] = 'ERROR: Your credit card has been declined!';
+                if ($this->useCaptcha) {
+                    $this->captchaQuestion->destroy();
+                    $this->captchaAnswer->setValue('');
+                }
+                $output  = $this->errorMsg;
+                $output .= parent::toHtml();
+            } else {
+                $this->_errors['cc_num'] = 'ERROR: There was an error processing your credit card!';
+                if ($this->useCaptcha) {
+                    $this->captchaQuestion->destroy();
+                    $this->captchaAnswer->setValue('');
+                }
+                $output  = $this->errorMsg;
+                $output .= parent::toHtml();
+            }
+        } elseif ($this->isSubmitted()) {
+            if ($this->useCaptcha) {
+                $this->captchaQuestion->destroy();
+                $this->captchaAnswer->setValue('');
+            }
+            $GLOBALS['bottomScripts'][] = MEDIA_APP_BASE_URL . 'libjs/textlimit.js';
+            $GLOBALS['bottomScripts'][]
+                = MEDIA_BASE_URL . 'Toolkit/Members/libjs/new-member-sign-up.js';
+            $output = $this->errorMsg;
+            $output .= parent::toHtml();
+        } else {
+            if ($this->useCaptcha) {
+                $this->captchaQuestion->destroy();
+                $this->captchaAnswer->setValue('');
+            }
+            $GLOBALS['bottomScripts'][] = MEDIA_APP_BASE_URL . 'libjs/textlimit.js';
+            $GLOBALS['bottomScripts'][]
+                = MEDIA_BASE_URL . 'Toolkit/Members/libjs/new-member-sign-up.js';
+            $output = parent::toHtml();
+        }
+        return $output;
+    }
+
+    //    }}}
+}
+?>
diff --git a/Toolkit/Members/Admin/AddPhoto.php b/Toolkit/Members/Admin/AddPhoto.php
new file mode 100644 (file)
index 0000000..7d8be8a
--- /dev/null
@@ -0,0 +1,359 @@
+<?php
+//     vim:set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker syntax=php:
+
+/**
+ * Handles the photos tab in the member record
+ *
+ * Controls setting up the add photo form if applicable, and rendering
+ * each uploaded photo edit form to edit/delete the photo.
+ *
+ * PHP version 5
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @version   CVS: $Id: AddPhoto.php,v 1.5 2010/07/16 20:53:08 jamie Exp $
+ * @link      http://demo.gaslightmedia.com
+ * @see       Toolkit/Image/Server.php
+ */
+
+
+/**
+ * The image server processing class
+ */
+require_once BASE . 'Toolkit/Image/Server.php';
+
+/**
+ * Form to handle creating a new photo in the members only area
+ *
+ * Handles inserting new photo into db as a pending photo and creating a
+ * tuple in the member_updates table which will allow the admin to
+ * approve/deny the new photo request.
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @link      http://demo.gaslightmedia.com
+ */
+class Toolkit_Members_Admin_AddPhoto extends Toolkit_FormBuilder
+{
+       //      {{{ properties
+
+       /**
+        * The table name in the database used to store the data
+        *
+        * @var string
+        * @access public
+        */
+       public $tableName = 'member_photos';
+
+       /**
+        * The template used to render the form
+        *
+        * @var string
+        * @access protected
+        */
+       protected $formTemplate = 'addPhoto.tpl';
+
+       /**
+        * Some special forms dont utlize this stylesheet
+        * Allow classes to override this setting so it doesn't
+        * get included
+        *
+        * @var boolean
+        * @access protected
+        */
+       protected $includeContactStyleSheet = false;
+
+    /**
+     * Success message when a photo gets successfully uploaded
+     * @var string
+     * @access protected
+     */
+    protected $successMsg = '
+        <div id="form-success-top">
+            You successfully uploaded your photo.
+        </div>';
+
+    /**
+     * Description for protected
+     * @var array
+     * @access protected
+     */
+       protected $registeredRules = array();
+
+       //      }}}
+       //      {{{ __construct()
+
+       /**
+        * Class constructor
+        *
+        * @param PDO    $pdo         PHP Data Object to use for DB calls
+        * @param string $formName    Form's name.
+        * @param string $method      (optional) Form's method defaults to 'POST'
+        * @param string $action      (optional) Form's action.
+        * @param string $target      (optional) Form's target defaults to '_self'
+        * @param mixed  $attributes  (optional) Extra attributes for <form> tag.
+        * @param bool   $trackSubmit (optional) Whether to track if the form was
+        *                                                                               submitted by adding a special hidden
+        *                                                                               field.
+        *
+        * @access public
+        * @see    Toolkit_Members_Admin_EditPhotos
+        */
+       public function __construct(
+        $pdo,
+        $formName,
+        $method = 'post',
+        $action = '',
+        $target = '',
+        $attributes = null,
+        $trackSubmit = false
+    ) {
+               parent::__construct(
+            $formName,
+            $method,
+            $action,
+            $target,
+            $attributes,
+            $trackSubmit
+        );
+        $this->dbh = $pdo;
+       }
+
+       //      }}}
+
+       //      {{{ configureElements()
+
+       /**
+        * Setup the elements to use on the form.
+        *
+     * @param Config_Container $c application configuration
+     *
+        * @return void
+        * @access public
+        */
+       public function configureElements(Config_Container $c)
+       {
+               $e = array();
+
+        $config = $c->getItem('section', 'photos');
+        $maxLength = $config->getItem('directive', 'maxCaptionLength');
+               //      All Grouped Elements are created here.
+
+               //      All Elements are created here.  This includes group element definitions.
+               $e[] = array(
+                       'type' => 'file',
+                       'req'  => true,
+                       'name' => 'file',
+            'display' => 'Upload a Photo',
+                       'opts' => array('class' => 'submit')
+               );
+               $e[] = array(
+                       'type' => 'text',
+                       'req'  => false,
+                       'name' => 'caption',
+            'display' => 'Caption',
+                       'opts' => array(
+                               'class' => 'text',
+                               'maxlength' => $maxLength->getContent(),
+                       ),
+            'noCharLimit' => true
+               );
+               $e[] = array(
+                       'type'    => 'submit',
+                       'req'     => false,
+                       'name'    => 'submit',
+                       'display' => 'Upload new photo',
+                       'opts'    => array('class' => 'submit')
+               );
+
+               $this->setupElements($e);
+       }
+
+       //      }}}
+    //  {{{ configureForm()
+
+    /**
+        * helper function to set up entire form definition
+        *
+     * @param Config_Container $c application configuration
+        *
+        * @return void
+        * @access public
+     */
+    public function configureForm(Config_Container $c)
+    {
+        $this->configureElements($c);
+               $this->configureRules($c);
+    }
+
+    //  }}}
+       //      {{{ configureRules()
+
+    /**
+     * Configure rules for form
+        *
+     * @param Config_Container $c application configuration
+     *
+     * @return void
+     * @access public
+     */
+       public function configureRules(Config_Container $c)
+       {
+        $config = $c->getItem('section', 'photos');
+        $maxLength = $config->getItem('directive', 'maxCaptionLength');
+
+               $mimeTypes = array(
+                       'image/jpe',
+                       'image/jpeg',
+                       'image/jpg',
+                       'image/jfif',
+                       'image/pjpeg',
+                       'image/pjp',
+                       'image/gif',
+                       'image/png',
+               );
+
+               $r = array();
+
+               $r[] = array(
+                       'element'    => 'file',
+                       'message'    => 'ERROR: You must select a file!',
+                       'type'       => 'uploadedfile',
+                       'format'     => null,
+                       'validation' => $this->validationType,
+                       'reset'      => false,
+                       'force'      => false
+               );
+               $r[] = array(
+                       'element'    => 'caption',
+
+                       'message'    => "ERROR: {$maxLength->getContent()} characters max!",
+                       'type'       => 'maxlength',
+                       'format'     => $maxLength->getContent(),
+                       'validation' => $this->validationType,
+                       'reset'      => false,
+                       'force'      => false
+               );
+               $r[] = array(
+                       'element'    => 'file',
+                       'message'    => 'ERROR: Incorrect File Type (.gif, .png, .jpg) only!',
+                       'type'       => 'mimetype',
+                       'format'     => $mimeTypes,
+                       'validation' => $this->validationType,
+                       'reset'      => false,
+                       'force'      => false
+               );
+
+               $this->setupRules($r);
+       }
+
+       //      }}}
+
+       //      {{{ processData()
+
+       /**
+        * Handles setting up the from processing and which function to get it done
+        *
+        * @param array $values Submitted values from the form.
+        *
+        * @return void
+        * @access protected
+        */
+       protected function processData($values)
+       {
+               try {
+            $sql = Toolkit_Common::createSQLInsert(
+                $this->tableName,
+                array_keys($values)
+            );
+
+            $res = Toolkit_Common::processQuery(
+                $this->dbh,
+                $this->tableName,
+                $sql,
+                $values
+            );
+               } catch (PDOException $e) {
+                       return Toolkit_Common::handleError($e);
+               }
+
+        header('Location:' . $this->getAttribute('action'));
+       }
+
+       //      }}}
+
+       //      {{{ setupRenderers()
+
+    /**
+     * Set up the rendering engine we are going to use to display this form
+     *
+     * @param HTML_Template_Flexy &$tEngine Templating Engine
+     *
+     * @return void
+     * @access protected
+     */
+       protected function setupRenderers(HTML_Template_Flexy &$tEngine)
+       {
+               $renderer = new HTML_QuickForm_Renderer_ObjectFlexy($tEngine);
+
+               $this->accept($renderer);
+               $this->view       = new stdClass();
+               $this->view->form = $renderer->toObject();
+               $tEngine->compile($this->formTemplate);
+       }
+
+       //      }}}
+
+       //      {{{ toHtml()
+
+       /**
+        * Renders the form
+        *
+        * sets the page the form should be redirected to instead of coming back
+        * around to itself.
+     *
+     * @param HTML_Template_Flexy    $tEngine Templating Engine
+     * @param Cache_Lite             $cache   Caching Engine
+     * @param Config_Container       $c       Application configuration
+     * @param Toolkit_Image_Server   $is      Image Server
+     * @param Toolkit_Members_Photos $ll      Linked List of member photos
+        *
+        * @return string The rendered form
+        * @access public
+        */
+       public function toHtml(
+        HTML_Template_Flexy $tEngine,
+        Cache_Lite $cache,
+        Config_Container $c,
+        Toolkit_Image_Server $is,
+        Toolkit_Members_Photos $ll
+    ) {
+               if ($this->validate()) {
+            $cache->remove("Member-{$_GET['id']}", 'Profile');
+
+            //  Put the processed photo's new name into the submitted values
+            $this->_submitValues['image'] = $is->imageUpload('file');
+            $this->_submitValues['pos'] = $ll->getListSize() + 1;
+            $this->_submitValues['member_id'] = $_GET['id'];
+            $this->_submitValues['pending'] = 0;
+
+            unset($this->_submitValues['MAX_FILE_SIZE'],
+                  $this->_submitValues['submit']);
+
+                       $this->process(array(&$this, 'processData'), false);
+               }
+
+               $this->setupRenderers($tEngine);
+
+               return $tEngine->bufferedOutputObject($this->view);
+       }
+
+       //      }}}
+}
+?>
diff --git a/Toolkit/Members/Admin/AdvancedSearch.php b/Toolkit/Members/Admin/AdvancedSearch.php
new file mode 100644 (file)
index 0000000..1ce2816
--- /dev/null
@@ -0,0 +1,908 @@
+<?php
+//     vim:set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker syntax=php:
+
+/**
+ * Admin search functionality for memberdb
+ *
+ * PHP version 5
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @version   CVS: $Id: AdvancedSearch.php,v 1.14 2010/08/15 19:35:15 jamie Exp $
+ * @link      http://demo.gaslightmedia.com
+ */
+
+/**
+ * Form to search the members database
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @link      http://demo.gaslightmedia.com
+ */
+class Toolkit_Members_Admin_AdvancedSearch
+    extends Toolkit_FormBuilder
+{
+       //      {{{     properties
+
+    /**
+     * Description of $_members
+     * 
+     * @var unknown 
+     * @access private
+     */
+       private $_members;
+
+       //      }}}
+
+       //      {{{ configureDefaults()
+
+       /**
+        * Sets the defaults for the form
+        *
+        * @param PDO $dbh Database handler
+        *
+        * @access public
+     * @return void
+        */
+       public function configureDefaults(PDO $dbh)
+       {
+               //      earliest create date is the same thing as
+               //      earliest modified date.
+
+               //$earliestDate = $this->_getEarliestCreateDate($dbh);
+               //if they want to default dates to the earliest date
+               //available in the DB, then uncomment this variable
+               //and the default values below.
+
+               $d = array(
+                       'rt' => $_GET['rt'],
+                       'rt' => $_GET['rt'],
+                       'active' => '-1',
+                       /*
+                       'create_date_beg' => $earliestDate,
+                       'create_date_end' => array(
+                               'm' => date('m'),
+                               'd' => date('d'),
+                               'Y' => date('Y')
+                       ),
+                       'last_modified_date_beg' => $earliestDate,
+                       'last_modified_date_end' => array(
+                               'm' => date('m'),
+                               'd' => date('d'),
+                               'Y' => date('Y')
+                       )
+                       */
+               );
+
+               $this->setupDefaults($d);
+       }
+
+       //      }}}
+       //      {{{ configureElements()
+
+    /**
+     * Form element definitions
+     *
+        * @param PDO                      $dbh Databse handler
+     * @param Config_Container $c   Configuration object
+     *
+     * @return void
+     * @access public
+     */
+       public function configureElements(PDO $dbh, Config_Container $c)
+       {
+        $e = array();
+
+               $singularType = $c->getItem('section', 'listing type')
+                       ->getItem('directive', 'singular')
+                       ->getContent();
+               $pluralType = $c->getItem('section', 'listing type')
+                       ->getItem('directive', 'plural')
+                       ->getContent();
+               $useCtrlCities = $c->getItem('section', 'conf')
+                       ->getItem('directive', 'controlledCities')
+                       ->getContent();
+               $dateStartYear = $c->getItem('section', 'conf')
+                       ->getItem('directive', 'dateStartYear')
+                       ->getContent();
+
+               //      All Grouped Elements are created here.
+               $isActive = array();
+               $isActive[] = array(
+                       'type'    => 'radio',
+                       'req'     => false,
+                       'name'    => 'active',
+                       'opts' => 'Yes',
+                       'att' => '1'
+               );
+               $isActive[] = array(
+                       'type'    => 'radio',
+                       'req'     => false,
+                       'name'    => 'active',
+                       'opts' => 'No',
+                       'att' => '0'
+               );
+               $isActive[] = array(
+                       'type'    => 'radio',
+                       'req'     => false,
+                       'name'    => 'active',
+                       'opts' => "Don't Care",
+                       'att' => '-1'
+               );
+               $createDate = array();
+               $createDate[] = array(
+                       'type'    => 'date',
+                       'req'     => false,
+                       'name'    => 'create_date_beg',
+                       'opts'    => array(
+                               'format'           => 'm / d / Y',
+                               'minYear'          => $dateStartYear,
+                               'maxYear'          => date('Y'),
+                               'addEmptyOption'   => true,
+                               'emptyOptionValue' => '',
+                               'emptyOptionText'  => array(
+                                       'm' => 'mm',
+                                       'd' => 'dd',
+                                       'Y' => 'yyyy',
+                               )
+                       )
+               );
+               $createDate[] = array(
+                       'type'    => 'date',
+                       'req'     => false,
+                       'name'    => 'create_date_end',
+                       'opts'    => array(
+                               'format'           => 'm / d / Y',
+                               'minYear'          => $dateStartYear,
+                               'maxYear'          => date('Y'),
+                               'addEmptyOption'   => true,
+                               'emptyOptionValue' => '',
+                               'emptyOptionText'  => array(
+                                       'm' => 'mm',
+                                       'd' => 'dd',
+                                       'Y' => 'yyyy',
+                               )
+                       )
+               );
+               $lastModifiedDate = array();
+               $lastModifiedDate[] = array(
+                       'type'    => 'date',
+                       'req'     => false,
+                       'name'    => 'last_modified_date_beg',
+                       'opts'    => array(
+                               'format'           => 'm / d / Y',
+                               'minYear'          => $dateStartYear,
+                               'maxYear'          => date('Y'),
+                               'addEmptyOption'   => true,
+                               'emptyOptionValue' => '',
+                               'emptyOptionText'  => array(
+                                       'm' => 'mm',
+                                       'd' => 'dd',
+                                       'Y' => 'yyyy',
+                               )
+                       )
+               );
+               $lastModifiedDate[] = array(
+                       'type'    => 'date',
+                       'req'     => false,
+                       'name'    => 'last_modified_date_end',
+                       'opts'    => array(
+                               'format'           => 'm / d / Y',
+                               'minYear'          => $dateStartYear,
+                               'maxYear'          => date('Y'),
+                               'addEmptyOption'   => true,
+                               'emptyOptionValue' => '',
+                               'emptyOptionText'  => array(
+                                       'm' => 'mm',
+                                       'd' => 'dd',
+                                       'Y' => 'yyyy',
+                               )
+                       )
+               );
+               $memberSinceDate = array();
+               $memberSinceDate[] = array(
+                       'type'    => 'date',
+                       'req'     => false,
+                       'name'    => 'join_date_beg',
+                       'opts'    => array(
+                               'format'           => 'm / d / Y',
+                               'minYear'          => $dateStartYear,
+                               'maxYear'          => date('Y'),
+                               'addEmptyOption'   => true,
+                               'emptyOptionValue' => '',
+                               'emptyOptionText'  => array(
+                                       'm' => 'mm',
+                                       'd' => 'dd',
+                                       'Y' => 'yyyy',
+                               )
+                       )
+               );
+               $memberSinceDate[] = array(
+                       'type'    => 'date',
+                       'req'     => false,
+                       'name'    => 'join_date_end',
+                       'opts'    => array(
+                               'format'           => 'm / d / Y',
+                               'minYear'          => $dateStartYear,
+                               'maxYear'          => date('Y'),
+                               'addEmptyOption'   => true,
+                               'emptyOptionValue' => '',
+                               'emptyOptionText'  => array(
+                                       'm' => 'mm',
+                                       'd' => 'dd',
+                                       'Y' => 'yyyy',
+                               )
+                       )
+               );
+
+               //      All Elements are created here.  This includes group element definitions.
+               $e[] = array(
+            'type' => 'hidden',
+            'req' => false,
+            'name' => 'rt'
+        );
+               $e[] = array(
+            'type' => 'hidden',
+            'req' => false,
+            'name' => 'ac'
+        );
+               $e[] = array(
+            'type' => 'header',
+            'req' => false,
+            'name' => 'attributesInfoHdr',
+            'display' => "Record Attributes",
+        );
+               $e[] = array(
+            'type' => 'group',
+            'req' => false,
+            'name' => 'active',
+                       'group' => $isActive,
+                       'seperator' => '<br>',
+            'label' => 'Is Active',
+                       'appendName' => false
+        );
+               $e[] = array(
+                       'type'    => 'group',
+                       'req'     => false,
+                       'name'    => 'create_date',
+                       'group' => $createDate,
+                       'seperator' => '<br>',
+                       'label' => "Created Between",
+                       'appendName' => false
+               );
+               $e[] = array(
+                       'type'    => 'group',
+                       'req'     => false,
+                       'name'    => 'member_since',
+                       'group' => $memberSinceDate,
+                       'seperator' => '<br>',
+                       'label' => "Member Since Between",
+                       'appendName' => false
+               );
+               $e[] = array(
+                       'type'    => 'group',
+                       'req'     => false,
+                       'name'    => 'last_modified',
+                       'group' => $lastModifiedDate,
+                       'seperator' => '<br>',
+                       'label' => "Last Modified Between",
+                       'appendName' => false
+               );
+               $e[] = array(
+            'type' => 'selectglm',
+            'req' => false,
+            'name' => 'category',
+            'display' => 'Has Category',
+            'opts' => $this->_getCategories($dbh),
+                       'att' => array(
+                               'multiple' => 'multiple',
+                               'size' => 6
+                       )
+        );
+               $e[] = array(
+            'type' => 'header',
+            'req' => false,
+            'name' => 'recordInfoHdr',
+            'display' => "Record Information",
+        );
+               $e[] = array(
+            'type' => 'text',
+            'req' => false,
+            'name' => 'member_name',
+            'display' => "$singularType Name"
+        );
+               $e[] = array(
+            'type' => 'text',
+            'req' => false,
+            'name' => 'member_contact_email',
+            'display' => "$singularType Email"
+        );
+        if ($useCtrlCities) {
+            $e[] = array(
+                'type'    => 'select',
+                'req'     => false,
+                'name'    => 'city_id',
+                'display' => 'City',
+                'opts'    => array('' => '-- Select --')
+                                                       + Toolkit_Common::getCities($this->dbh),
+            );
+        } else {
+            $e[] = array(
+                'type'    => 'text',
+                'req'     => false,
+                'name'    => 'city',
+                'display' => 'City',
+                'opts'    => array('class' => 'text')
+            );
+        }
+               $e[] = array(
+                       'type'    => 'select',
+                       'req'     => false,
+                       'name'    => 'state_id',
+                       'display' => 'State / Province',
+                       'opts'    => array('' => '-- Select --')
+                                               + Toolkit_Common::getStates($this->dbh)
+               );
+               $e[] = array(
+                       'type'    => 'text',
+                       'req'     => false,
+                       'name'    => 'zip',
+                       'display' => 'Zip / Postal Code',
+                       'opts'    => array('class' => 'text')
+               );
+               $e[] = array(
+            'type' => 'header',
+            'req' => false,
+            'name' => 'PrimaryContactInfoHdr',
+            'display' => "Record Contact Information"
+        );
+               $e[] = array(
+            'type' => 'text',
+            'req' => false,
+            'name' => 'contact_fname',
+            'display' => 'First Name'
+        );
+               $e[] = array(
+            'type' => 'text',
+            'req' => false,
+            'name' => 'contact_lname',
+            'display' => 'Last Name'
+        );
+               $e[] = array(
+            'type' => 'text',
+            'req' => false,
+            'name' => 'process_email',
+            'display' => 'Email'
+        );
+               $e[] = array(
+            'type' => 'submit',
+            'req' => false,
+            'name' => 'submit',
+            'display' => 'Search'
+        );
+
+               $this->setupElements($e);
+       }
+
+       //      }}}
+       //      {{{ configureFilters()
+
+    /**
+     * Form filter definitions
+     *
+     * @return void
+     * @access public
+     */
+       public function configureFilters()
+       {
+        $f = array();
+               $f[] = array(
+            'element' => '__ALL__',
+            'filter' => 'trim'
+        );
+
+               $this->setupFilters($f);
+       }
+
+       //      }}}
+    //  {{{ configureForm()
+
+    /**
+     * Helper function to configure an entire form
+     *
+        * @param PDO                      $dbh Databse handler
+     * @param Config_Container $c   Configuration object
+     *
+     * @return void
+     * @access public
+     */
+    public function configureForm(PDO $dbh, Config_Container $c)
+    {
+        $this->configureElements($dbh, $c);
+        $this->configureDefaults($dbh);
+        $this->configureRules();
+        $this->configureFilters();
+    }
+
+    //  }}}
+       //      {{{ configureRules()
+
+    /**
+     * Form rule definitions
+     *
+     * @return void
+     * @access public
+     */
+       public function configureRules()
+       {
+        $r = array();
+               $r[] = array(
+            'element' => 'member_contact_email',
+            'message' => 'ERROR: Invalid Email Address!',
+            'type' => 'email',
+        );
+        /*
+               $r[] = array(
+            'element' => 'category',
+            'message' => 'ERROR: Invalid Category!',
+            'type' => 'numeric',
+        );
+        */
+
+               $this->setupRules($r);
+       }
+
+       //      }}}
+
+       //      {{{     getSearchResults()
+
+       /**
+        * Get the results of the search
+        *
+        * @return mixed false if not searched else array of matching members
+        * @access public
+        */
+       public function getSearchResults()
+       {
+               if (!$this->isSubmitted()) {
+                       return false;
+               } else {
+                       return $this->_members;
+               }
+       }
+
+       //      }}}
+       //      {{{     _getCategories()
+
+    /**
+     * get all the categories available in alpha order
+     *
+        * @param PDO $dbh Database handler
+        *
+     * @return array alpha order of categories available in the DB
+     * @access private
+     */
+       private function _getCategories(PDO $dbh)
+       {
+               $categories = Toolkit_Common::getHierarchicalTreeStructure(
+                       $dbh,
+            'category',
+            'category_id',
+            'parent_id',
+                       'name'
+        );
+               //  Get only the active categories from
+               //  the nav structure for our select list.
+               $sql = "
+                       SELECT category_id, name
+                         FROM category
+                        WHERE category_id = :id";
+
+               $stmt = $dbh->prepare($sql);
+
+               foreach ($categories as $k => $v) {
+                       unset($row, $category);
+                       $stmt->bindParam(':id', $k, PDO::PARAM_INT);
+                       $stmt->execute();
+
+                       //  If we actually retrieved a row, add it to the select list
+                       //  after we clean it up.
+                       if ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                               $pages[$k] = array(
+                                       'level' => $v - 1,
+                                       'name' => $row['name']
+                               );
+                       }
+               }
+
+               return $pages;
+       }
+
+       //      }}}
+       //      {{{     _getEarliestCreateDate()
+
+    /**
+     * Description for _getEarliestCreateData()
+     * 
+     * @param PDO $dbh PDO
+     * 
+     * @return array
+     * @access private 
+     */
+       private function _getEarliestCreateDate(PDO $dbh)
+       {
+               try {
+                       $sql = "
+                               SELECT date_part('month', min(create_date)) AS month,
+                                          date_part('day', min(create_date)) AS day,
+                                          date_part('year', min(create_date)) AS year
+                                 FROM member";
+
+                       $minDate = $dbh->query($sql, PDO::FETCH_ASSOC)->fetch();
+                       $date = array(
+                               'm' => $minDate['month'],
+                               'd' => $minDate['day'],
+                               'Y' => $minDate['year']
+                       );
+
+                       return $date;
+               } catch (PDOException $e) {
+                       return Toolkit_Common::handleError($e);
+               }
+       }
+
+       //      }}}
+
+       //      {{{     _getSQLQuery()
+
+       /**
+        * generate the SQL query for the search
+        *
+        * @param PDO              $dbh    Database handler
+     * @param Config_Container $c      Configuration object
+     * @param array            $values Form submit values
+        *
+        * @return string SQL query string
+        * @access private
+        */
+       private function _getSQLQuery(PDO $dbh, Config_Container $c, array $values)
+       {
+               $useCtrlCities = $c->getItem('section', 'conf')
+                       ->getItem('directive', 'controlledCities')
+                       ->getContent();
+
+               $sql = "
+                       SELECT DISTINCT m.member_id
+                         FROM member m LEFT JOIN member_contacts mc USING(member_id),
+                                  member_last_updates mlu,
+                                  exploded_members_name emn";
+
+               $params = array(
+                       'm.member_id = emn.mid',
+                       "m.new_member <> '1'"
+               );
+
+               if ($values['active'] == 1) {
+                       $params[] = 'm.active = ' . $dbh->quote(1);
+               } elseif ($values['active'] == 0) {
+                       $params[] = 'm.active IS NULL';
+               } else {
+                       //      do nothing
+               }
+
+        if ($_REQUEST['page'] == 'newsletterSearch') {
+            $params[] = "(m.process_email is NOT NULL OR m.process_email <> '')";
+        }
+
+               if (isset($values['create_date_beg'])
+                       && is_array($values['create_date_beg'])
+                       && ctype_digit($values['create_date_beg']['m'])
+                       && ctype_digit($values['create_date_beg']['d'])
+                       && ctype_digit($values['create_date_beg']['Y'])
+               ) {
+                       $params[] = 'm.create_date >= ' .
+                               $dbh->quote(implode('-', $values['create_date_beg']));
+               }
+
+               if (isset($values['create_date_end'])
+                       && is_array($values['create_date_end'])
+                       && ctype_digit($values['create_date_end']['m'])
+                       && ctype_digit($values['create_date_end']['d'])
+                       && ctype_digit($values['create_date_end']['Y'])
+               ) {
+                       $params[] = 'm.create_date <= ' .
+                               $dbh->quote(implode('-', $values['create_date_end']));
+               }
+
+               if (isset($values['join_date_beg'])
+                       && is_array($values['join_date_beg'])
+                       && ctype_digit($values['join_date_beg']['m'])
+                       && ctype_digit($values['join_date_beg']['d'])
+                       && ctype_digit($values['join_date_beg']['Y'])
+               ) {
+                       $params[] = 'm.join_date >= ' .
+                               $dbh->quote(implode('-', $values['join_date_beg']));
+               }
+
+               if (isset($values['join_date_end'])
+                       && is_array($values['join_date_end'])
+                       && ctype_digit($values['join_date_end']['m'])
+                       && ctype_digit($values['join_date_end']['d'])
+                       && ctype_digit($values['join_date_end']['Y'])
+               ) {
+                       $params[] = 'm.join_date <= ' .
+                               $dbh->quote(implode('-', $values['join_date_end']));
+               }
+
+               if (isset($values['last_modified_date_beg'])
+                       && is_array($values['last_modified_date_beg'])
+                       && ctype_digit($values['last_modified_date_beg']['m'])
+                       && ctype_digit($values['last_modified_date_beg']['d'])
+                       && ctype_digit($values['last_modified_date_beg']['Y'])
+               ) {
+                       $lastModifiedTimestampBeg
+                               = implode('-', $values['last_modified_date_beg']) . ' 00:00:00';
+                       $params[]
+                               = 'mlu.timestamp >= ' . $dbh->quote($lastModifiedTimestampBeg);
+                       $params[] = 'm.member_id = mlu.member_id';
+               }
+
+               if (isset($values['last_modified_date_end'])
+                       && is_array($values['last_modified_date_end'])
+                       && ctype_digit($values['last_modified_date_end']['m'])
+                       && ctype_digit($values['last_modified_date_end']['d'])
+                       && ctype_digit($values['last_modified_date_end']['Y'])
+               ) {
+                       $lastModifiedTimestampEnd
+                               = implode('-', $values['last_modified_date_end']) . ' 23:59:59';
+                       $params[]
+                               = 'mlu.timestamp <= ' . $dbh->quote($lastModifiedTimestampEnd);
+                       $params[] = 'm.member_id = mlu.member_id';
+               }
+
+               //      Include all/any subcategories beneath selected cats
+               if (isset($values['category']) && is_array($values['category'])) {
+                       $categoryKeys = array();
+                       foreach ($values['category'] as $k => $v) {
+                               if ($category = filter_var($v, FILTER_VALIDATE_INT)) {
+                                       $tree = Toolkit_Common::getHierarchicalTreeStructure(
+                                               $this->dbh,
+                                               'category',
+                                               'category_id',
+                                               'parent_id',
+                                               'pos',
+                                               $category,
+                                               0,
+                                               false
+                                       );
+                                       $categoryKeys = array_merge($categoryKeys, array_keys($tree));
+                               }
+                       }
+
+                       $params[] = "
+                                 m.member_id in (
+                                                SELECT member_id
+                                                  FROM member_category
+                                 WHERE category_id in (" . implode(', ', $categoryKeys) . "))";
+               }
+
+               //      Fuzzy search on member name
+               if (isset($values['member_name']) && !empty($values['member_name'])) {
+                       $lowerName = strtolower($values['member_name']);
+                       $noSpaceMemberName = preg_replace('/[^[:alnum:]]/', '', urldecode($lowerName));
+                       $spaceMemberName = urldecode($lowerName);
+                       $sanitizedNoSpaceMemberName = $this->dbh->quote($noSpaceMemberName);
+                       $sanitizedSpaceMemberName = $this->dbh->quote($spaceMemberName);
+                       //      fuzzy name search
+                       $params[] = "
+                               (   regexp_replace(lower(m.member_name), '[^[:alnum:]]', '', 'g') ~* $sanitizedNoSpaceMemberName
+                                OR metaphone(lower(m.member_name), 4) = metaphone($sanitizedSpaceMemberName, 4)
+                               )
+                        OR (
+                                    m.member_id = emn.mid
+                                        AND m.new_member <> '1'
+                                AND (
+                                                $sanitizedNoSpaceMemberName ~* regexp_replace(lower(emn.part), '[^[:alnum:]]', '', 'g')
+                                         OR (metaphone($sanitizedSpaceMemberName, 4) = metaphone(lower(emn.part), 4))
+                                        )
+                                AND char_length(emn.part) > 1
+                               )";
+               }
+
+               if (   isset($values['member_contact_email'])
+                       && !empty($values['member_contact_email'])
+               ) {
+                       $lowerMemberEmail = strtolower($values['member_contact_email']);
+                       $memberEmail = preg_replace('/[^[:alnum:]]/', '', urldecode($lowerMemberEmail));
+                       $params[] = "regexp_replace(lower(m.member_contact_email), '[^[:alnum:]]', '', 'g') ~* " .
+                               $this->dbh->quote($memberEmail);
+               }
+
+        if ($useCtrlCities) {
+            if (ctype_digit($values['city_id'])) {
+                $params[] = "m.city_id = " . $this->dbh->quote($values['city_id']);
+            }
+        } else {
+            if (isset($values['city']) && !empty($values['city'])) {
+                               $lowerCity = strtolower($values['city']);
+                               $city = preg_replace('/[^[:alnum:]]/', '', urldecode($lowerCity));
+                $params[] = "regexp_replace(lower(m.city), '[^[:alnum:]]', '', 'g') ~* " .
+                                       $this->dbh->quote($city);
+            }
+        }
+
+               if (isset($values['state_id']) && ctype_digit($values['state_id'])) {
+                       $params[] = "m.state_id = " . $this->dbh->quote($values['state_id']);
+               }
+               if (isset($values['zip']) && !empty($values['zip'])) {
+                       $params[] = 'm.zip = ' . $this->dbh->quote($values['zip']);
+               }
+
+               //      Fuzzy search on primary contact first name
+               if (   isset($values['contact_fname'])
+                       && !empty($values['contact_fname'])
+               ) {
+                       $lowerPrimaryContactFname = strtolower($values['contact_fname']);
+                       $noSpaceContactFName = preg_replace('/[^[:alnum:]]/', '', urldecode($lowerPrimaryContactFname));
+                       $spaceContactFName = urldecode($lowerPrimaryContactFname);
+                       $sanitizedNoSpaceContactFName = $this->dbh->quote($noSpaceContactFName);
+                       $sanitizedSpaceContactFName = $this->dbh->quote($spaceContactFName);
+                       $params[] = "
+                               (
+                                       (
+                                               regexp_replace(lower(m.primary_contact_fname), '[^[:alnum:]]', '', 'g') ~* $sanitizedNoSpaceContactFName
+                                               OR
+                                               metaphone(lower(m.primary_contact_fname), 4) = metaphone($sanitizedSpaceContactFName, 4)
+                                       )
+                                       OR
+                                       (
+                                               (
+                                                       regexp_replace(lower(mc.fname), '[^[:alnum:]]', '', 'g') ~* $sanitizedNoSpaceContactFName
+                                                       OR
+                                                       metaphone(lower(mc.fname), 4) = metaphone($sanitizedSpaceContactFName, 4)
+                                               )
+                                               AND mc.member_id = m.member_id
+                                       )
+                               )";
+               }
+
+               //      Fuzzy search on primary contact last name
+               if (   isset($values['contact_lname'])
+                       && !empty($values['contact_lname'])
+               ) {
+                       $lowerPrimaryContactLname = strtolower($values['contact_lname']);
+                       $noSpaceContactLName = preg_replace('/[^[:alnum:]]/', '', urldecode($lowerPrimaryContactLname));
+                       $spaceContactLName = urldecode($lowerPrimaryContactLname);
+                       $sanitizedNoSpaceContactFName = $this->dbh->quote($noSpaceContactLName);
+                       $sanitizedSpaceContactFName = $this->dbh->quote($spaceContactLName);
+                       $params[] = "
+                               (
+                                       (
+                                               regexp_replace(lower(m.primary_contact_lname), '[^[:alnum:]]', '', 'g') ~* $sanitizedNoSpaceContactFName
+                                               OR
+                                               metaphone(lower(m.primary_contact_lname), 4) = metaphone($sanitizedSpaceContactFName, 4)
+                                       )
+                                       OR
+                                       (
+                                               (
+                                                       regexp_replace(lower(mc.lname), '[^[:alnum:]]', '', 'g') ~* $sanitizedNoSpaceContactFName
+                                                       OR
+                                                       metaphone(lower(mc.lname), 4) = metaphone($sanitizedSpaceContactFName, 4)
+                                               )
+                                               AND mc.member_id = m.member_id
+                                       )
+                               )";
+               }
+
+               if (   isset($values['process_email'])
+                       && !empty($values['process_email'])
+               ) {
+                       $lowerPrimaryContactEmail = strtolower($values['process_email']);
+                       $primaryContactEmail = preg_replace('/[^[:alnum:]]/', '', urldecode($lowerPrimaryContactEmail));
+                       $params[] = "regexp_replace(lower(m.process_email), '[^[:alnum:]]', '', 'g') ~* " .
+                               $this->dbh->quote($primaryContactEmail);
+               }
+
+        //  Limit to members whose first letter starts with the letter
+        //  a user clicked in the narrow alphabetically list.
+               if (isset($_GET['alpha']) && $_GET['alpha'] == '0-9') {
+                       $params[] = "substr(m.member_name, 1, 1) ~ '[0-9]'";
+               } elseif (isset($_GET['alpha']) && ctype_alpha($_GET['alpha'])) {
+                       $letter = $_GET['alpha'][0];
+            $params[] = "upper(substr(member_name, 1, 1)) = upper(" .
+                               $this->dbh->quote($letter) . ')';
+        }
+
+               $params = implode(' AND ', $params);
+
+               return "$sql WHERE $params";
+       }
+
+       //      }}}
+       //      {{{     _processData()
+
+       /**
+        * generate the array of member ids that were created from the search
+        *
+        * @param PDO              $dbh    Database handler
+     * @param Config_Container $c      Configuration object
+     * @param array            $values Form submit values
+        *
+        * @return void
+        * @access private
+     * 
+        */
+       private function _processData(PDO $dbh, Config_Container $c, array $values)
+       {
+               $sql = $this->_getSQLQuery($dbh, $c, $values);
+
+               try {
+                       $this->_members = array();
+                       foreach ($dbh->query($sql, PDO::FETCH_ASSOC) as $row) {
+                               $this->_members[] = $row['member_id'];
+                       }
+               } catch (PDOException $e) {
+                       Toolkit_Common::handleError($e);
+               }
+       }
+
+       //      }}}
+
+       //      {{{     setupRenderers()
+    //  @codeCoverageIgnoreStart
+
+    /**
+     * Custom rendering templates for special fields on the form
+     *
+     * @return void
+     * @access protected
+     */
+       protected function setupRenderers()
+       {
+               parent::setupRenderers();
+               $renderer =& $this->defaultRenderer();
+               $tpl = '<tr align="center"><td colspan="2">{element}</td></tr>';
+               $renderer->setElementTemplate($tpl, 'submit');
+       }
+
+    //  @codeCoverageIgnoreEnd
+       //      }}}
+
+       //      {{{     toHtml()
+
+       /**
+        * Call the rendering function to get the form in a string
+        *
+        * @param PDO              $dbh Database handler
+     * @param Config_Container $c   Configuration object
+        *
+        * @access protected
+        * @return string $output The Form to be rendered or success msg.
+        */
+       public function toHtml(PDO $dbh, Config_Container $c)
+       {
+               $jqueryuiPath = MEDIA_APP_BASE_URL . 'libjs/jqueryui/1.8/development-bundle';
+               $GLOBALS['styleSheets'][] = "$jqueryuiPath/themes/base/jquery.ui.all.css";
+               $GLOBALS['bottomScripts'][] = "$jqueryuiPath/ui/minified/jquery.ui.core.min.js";
+               $GLOBALS['bottomScripts'][] = "$jqueryuiPath/ui/minified/jquery.ui.widget.min.js";
+               $GLOBALS['bottomScripts'][] = "$jqueryuiPath/ui/minified/jquery.ui.datepicker.min.js";
+               $GLOBALS['bottomScripts'][] = MEDIA_BASE_URL . 'Toolkit/Members/libjs/advancedsearch.js';
+
+               $this->setupRenderers();
+
+               if ($this->validate()) {
+                       $this->_processData($dbh, $c, $this->getSubmitValues());
+               }
+
+               return parent::toHTML();
+       }
+
+       //      }}}
+}
+?>
diff --git a/Toolkit/Members/Admin/AdvancedSearchController.php b/Toolkit/Members/Admin/AdvancedSearchController.php
new file mode 100644 (file)
index 0000000..5a6ff0b
--- /dev/null
@@ -0,0 +1,96 @@
+<?php
+/**
+ * AdvancedSearchController.php
+ * 
+ * PHP versions 4 and 5
+ * 
+ * @category  Toolkit
+ * @package   Members_Admin
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2009 Gaslight Media
+ * @license   Gaslight Media
+ * @version   CVS: $Id: Newsletters.php,v 1.9 2009/09/16 19:00:58 matrix Exp $
+ * @link      http://demo.gaslightmedia.com
+ */
+
+/**
+ * Toolkit_Members_Admin_AdvancedSearchController
+ * 
+ * Description for Toolkit_Members_Admin_AdvancedSearchController
+ * 
+ * @category  Toolkit
+ * @package   Members_Admin
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2009 Gaslight Media
+ * @license   Gaslight Media
+ * @link      http://demo.gaslightmedia.com
+ */
+
+class Toolkit_Members_Admin_AdvancedSearchController
+       extends Toolkit_BaseControllerAbstract implements Toolkit_IController
+{
+       //      {{{     indexAction()
+    /**
+     * Main action for controller. Runs admin template, list out all regions
+     * 
+     * @return mixed
+     * @access public 
+     */
+       public function indexAction()
+       {
+               $GLOBALS['styleSheets'][] = MEDIA_BASE_URL . 'css/contactform.css';
+
+               HTTP_Session2::set('newSearch', true);
+               $searchForm = new Toolkit_Members_Admin_AdvancedSearch(
+            'advanced-record-search',
+                       'get',
+                       MEDIA_BASE_URL . 'admin/members.php',
+                       '',
+                       null,
+                       true
+               );
+
+        $searchForm->configureForm($this->registry->dbh, $this->registry->config);
+
+               $this->registry->controllerObject->content 
+            = $searchForm->toHtml($this->registry->dbh, $this->registry->config);
+               if ($results = $searchForm->getSearchResults()) {
+                       $GLOBALS['bottomScripts'][]
+                               = MEDIA_BASE_URL . 'Toolkit/Members/libjs/member-list.js';
+
+                       $membersList = new Toolkit_Members_Admin_AdvancedSearchDataGrid(
+                               $this->registry->dbh,
+                               50
+                       );
+                       if ($_GET['d'] == 't' && ctype_digit($_GET['id'])) {
+                               $membersList->removeMember(
+                                       $_GET['id'],
+                                       new Toolkit_Image_Server()
+                               );
+                       }
+                       $membersList->setQuery($results);
+                       $membersList->setDefaultSort(array('sort_field' => 'ASC'));
+
+                       //  rendering engine to use
+                       $rEngine = new Structures_DataGrid_Renderer_Flexy();
+                       $rEngine->setContainer($this->registry->tEngine);
+
+                       $this->registry->controllerObject->content .=
+                               $membersList->toHtml($rEngine, 'listMembers.tpl');
+               }
+
+               $this->registry->controllerObject->topScripts
+                       = Toolkit_Common::getScripts($GLOBALS['topScripts']);
+               $this->registry->controllerObject->bottomScripts
+                       = Toolkit_Common::getScripts($GLOBALS['bottomScripts']);
+               $this->registry->controllerObject->styles
+                       = Toolkit_Common::getStyleSheets($GLOBALS['styleSheets']);
+
+               $this->registry->tEngine->compile('admin.tpl');
+               return $this->registry->tEngine->bufferedOutputObject(
+                       $this->registry->controllerObject
+               );
+       }
+
+       //      }}}
+}
diff --git a/Toolkit/Members/Admin/AdvancedSearchDataGrid.php b/Toolkit/Members/Admin/AdvancedSearchDataGrid.php
new file mode 100644 (file)
index 0000000..2eb86af
--- /dev/null
@@ -0,0 +1,70 @@
+<?php
+//     vim:set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker syntax=php:
+
+/**
+ * AdvancedSearchDataGrid.php
+ *
+ * PHP version 5
+ *
+ * @category MembersDB
+ * @package  Toolkit_Members
+ * @author   Jamie Kahgee <steve@gaslightmedia.com>
+ * @license     http://www.gaslightmedia.com Gaslightmedia
+ * @release  CVS: $Id: AdvancedSearchDataGrid.php,v 1.1 2010/01/11 15:08:46 jamie Exp $
+ * @link        http://demo.gaslightmedia.com
+ */
+
+/**
+ * Toolkit_Members_Admin_AdvancedSearchDataGrid
+ * 
+ * Description of Toolkit_Members_Admin_AdvancedSearchDataGrid
+ * 
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @release   Release: $id$
+ * @link      <>
+ */
+
+class Toolkit_Members_Admin_AdvancedSearchDataGrid
+       extends Toolkit_Members_Admin_ListMembers
+{
+    //  {{{ setControlObject()
+
+    /**
+     * don't call parents method
+     *
+     * @return void
+     * @access protected
+     */
+    protected function setControlObject() 
+    {
+    }
+
+    //  }}}
+    //  {{{ setQuery()
+
+    /**
+     * Sets the query to use to fetch the datagrid results
+     *
+     * @param array $ids IDs array
+     * 
+     * @return void
+     * @access public
+     */
+    public function setQuery(array $ids)
+    {
+               $memberIds = implode(', ', $ids);
+               $sql = "
+                       SELECT *, LOWER(member_name) AS sort_field
+              FROM member m
+                         WHERE member_id IN ($memberIds)";
+
+        Toolkit_FlexyDataGridBuilder::setQuery($sql);
+    }
+
+    //  }}}
+}
+?>
diff --git a/Toolkit/Members/Admin/AmenitiesController.php b/Toolkit/Members/Admin/AmenitiesController.php
new file mode 100644 (file)
index 0000000..9cfc189
--- /dev/null
@@ -0,0 +1,105 @@
+<?php
+/**
+ * AmenitiesController.php
+ * 
+ * PHP version 5.2
+ * 
+ * @category  Toolkit
+ * @package   Members_Admin
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @version   SVN: $id$
+ * @link      <>
+ */
+
+/**
+ * Toolkit_Members_Admin_AmenitiesController
+ * 
+ * Description of Toolkit_Members_Admin_AmenitiesController
+ * 
+ * @category  Toolkit
+ * @package   Members_Admin
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @release   Release: $id$
+ * @link      <>
+ */
+class Toolkit_Members_Admin_AmenitiesController
+       extends Toolkit_BaseControllerAbstract implements Toolkit_IController
+{
+       //      {{{     indexAction()
+    /**
+     * Description of indexAction()
+     * 
+     * @return string
+     * @access public 
+     */
+       public function indexAction()
+       {
+               $amenityList = new Toolkit_Members_Admin_ListAmenities($this->registry->dbh);
+               $this->registry->controllerObject->content = $amenityList->renderAmenities();
+
+               $this->registry->controllerObject->topScripts
+                       = Toolkit_Common::getScripts($GLOBALS['topScripts']);
+               $this->registry->controllerObject->bottomScripts
+                       = Toolkit_Common::getScripts($GLOBALS['bottomScripts']);
+               $this->registry->controllerObject->styles
+                       = Toolkit_Common::getStyleSheets($GLOBALS['styleSheets']);
+
+               $this->registry->tEngine->compile('admin.tpl');
+               return $this->registry->tEngine->bufferedOutputObject(
+                       $this->registry->controllerObject
+               );
+       }
+
+       //      }}}
+       //      {{{     editAmenityAction()
+    /**
+     * Description of editAmnityAction()
+     * 
+     * @return string
+     * @access public
+     */
+       public function editAmenityAction()
+       {
+               $GLOBALS['bottomScripts'][]
+                       = MEDIA_BASE_URL . 'Toolkit/Members/libjs/edit-amenity.js';
+
+               $form = new Toolkit_Members_Admin_EditAmenity(
+            $this->registry->dbh,
+            'edit_amenity'
+        );
+        $form->configureForm();
+               $this->registry->controllerObject->content = $form->toHtml();
+
+               $this->registry->controllerObject->topScripts
+                       = Toolkit_Common::getScripts($GLOBALS['topScripts']);
+               $this->registry->controllerObject->bottomScripts
+                       = Toolkit_Common::getScripts($GLOBALS['bottomScripts']);
+               $this->registry->controllerObject->styles
+                       = Toolkit_Common::getStyleSheets($GLOBALS['styleSheets']);
+
+               $this->registry->tEngine->compile('admin.tpl');
+               return $this->registry->tEngine->bufferedOutputObject(
+                       $this->registry->controllerObject
+               );
+       }
+
+       //      }}}
+       //      {{{     listAmenitiesAction()
+    /**
+     * Description of listAmenitiesAction
+     * 
+     * @return string
+     * @access public
+     */
+       public function listAmenitiesAction()
+       {
+               return $this->indexAction();
+       }
+
+       //      }}}
+}
+?>
diff --git a/Toolkit/Members/Admin/AuthorizeNewMemberForm.php b/Toolkit/Members/Admin/AuthorizeNewMemberForm.php
new file mode 100755 (executable)
index 0000000..2ad7288
--- /dev/null
@@ -0,0 +1,792 @@
+<?php
+//     vim:set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker syntax=php:
+
+/**
+ * Authorizes or denies new member admission into the DB
+ *
+ * PHP version 5
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2008 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @version   CVS: $Id: AuthorizeNewMemberForm.php,v 1.19 2010/07/14 23:27:59 jamie Exp $
+ * @link      http://demo.gaslightmedia.com
+ */
+
+/**
+ * Creates, Renders, Processes form for request to be added to Business DB
+ *
+ * This Form just sends the primary advisee an HTML email of the data
+ * that was filled in and requires them to add the users to the Business DB
+ * themselves.
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2008 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @link      http://demo.gaslightmedia.com
+ */
+class Toolkit_Members_Admin_AuthorizeNewMemberForm extends Toolkit_FormBuilder
+{
+       //      {{{ properties
+
+       /**
+        * The name of the DB table that holds the member records
+        *
+        * @var array
+        * @access protected
+        */
+       public $tableName = 'member';
+
+       /**
+        * What do you want the success msg to be if the form validates successfully
+        *
+        * @var         string
+        * @access      protected
+        */
+       protected $successMsg = '<div id="form-success-top"></div>';
+
+       /**
+        * Flexy options used in the renderer
+        *
+        * @var array
+        * @access protected
+        */
+       protected $flexyOptions;
+
+       /**
+        * The default rules to register for validating
+        *
+        * @var array
+        * @access protected
+        */
+       protected $registeredRules = array('zip', 'phone', 'memberlogo');
+
+       /**
+        * The flexy template object which holds the rendered object
+        *
+        * @var object
+        * @access protected
+        */
+       protected $template;
+
+       //      }}}
+
+       //      {{{ __construct()
+
+       /**
+        * Constructor
+        *
+     * @param PDO    $pdo         PHP Data Object to use for DB calls
+        * @param string $formName    Form's name.
+        * @param string $method      (optional) Form's method defaults to 'POST'
+        * @param string $action      (optional) Form's action.
+        * @param string $target      (optional) Form's target defaults to '_self'
+        * @param mixed  $attributes  (optional) Extra attributes for <form> tag.
+        * @param bool   $trackSubmit (optional) Whether to track if the form was
+        *                                                                               submitted by adding a special hidden
+        *                                                                               field.
+        *
+        * @access public
+        * @see    Toolkit_FormBuilder, HTML_QuickForm
+        */
+       public function __construct(
+        PDO $pdo,
+        $formName,
+        $method = 'post',
+        $action = '',
+        $target = '',
+        $attributes = null,
+        $trackSubmit = false
+    ) {
+               parent::__construct(
+            $formName,
+            $method,
+            $action,
+            $target,
+            $attributes,
+            $trackSubmit
+        );
+               $this->template = dirname(__FILE__) . '/../templates/currentTables/';
+
+               $this->dbh = $pdo;
+
+               $this->flexyOptions = Toolkit_Members::getFlexyOptions();
+       }
+
+       //      }}}
+
+       //      {{{     approveMember()
+
+
+    /**
+     * Set a members "new" flag to false
+     *
+     * @return boolean result of sql query
+     * @access protected
+     */
+       protected function approveMember()
+       {
+               try {
+                       $sql = "
+                UPDATE {$this->tableName}
+                   SET new_member = false
+                 WHERE member_id  = ?";
+
+                       $stmt = $this->dbh->prepare($sql);
+                       return $stmt->execute(array($_GET['id']));
+               } catch (PDOException $e) {
+                       return Toolkit_Common::handleError($e);
+               }
+       }
+
+       //      }}}
+
+       //      {{{ configureConstants()
+
+       /**
+        * Sets the constants for the form
+        *
+        * @return void
+        * @access protected
+        */
+       protected function configureConstants()
+       {
+               $c = array(
+                       'new_member' => 0,
+               );
+
+               $this->setupConstants($c);
+       }
+
+       //      }}}
+       //      {{{ configureDefaults()
+
+       /**
+        * Sets the defaults for the form
+        *
+        * @return array $defaults Returns the array of defaults
+        *                                                 so children who call this function
+        *                                                 can obtain a copy of these values.
+        * @access protected
+        */
+       protected function configureDefaults()
+       {
+               $defaults = array();
+
+               try {
+                       if (ctype_digit($_GET['id'])) {
+                               $sql = "
+                    SELECT m.*
+                      FROM {$this->tableName} m
+                     WHERE m.member_id  = :mid
+                       AND m.new_member = true";
+
+                               $stmt = $this->dbh->prepare($sql);
+                               $stmt->bindParam(':mid', $_GET['id'], PDO::PARAM_INT);
+                               $stmt->execute();
+                if ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                    foreach ($row as $i => $j) {
+                        $defaults[$i] = $j;
+                    }
+                }
+
+                $sql = "
+                       SELECT *
+                         FROM member_category
+                        WHERE member_id = :mid";
+
+                $stmt = $this->dbh->prepare($sql);
+                $stmt->bindParam(':mid', $_GET['id'], PDO::PARAM_INT);
+                $stmt->execute();
+                $row = $stmt->fetch(PDO::FETCH_ASSOC);
+                $defaults['category_id'] = $row['category_id'];
+                               $defaults['description'] = strip_tags($defaults['description']);
+                       }
+               } catch (PDOException $e) {
+                       return Toolkit_Common::handleError($e);
+               }
+
+               $this->setupDefaults($defaults);
+               return $defaults;
+       }
+
+       //      }}}
+       //      {{{ configureElements()
+
+       /**
+        * Setup the elements to use on the form.
+        *
+        * @return void
+        * @access protected
+        */
+       protected function configureElements()
+       {
+        //  get reference to the [listing type] section of config file
+        $config =& $this->config->getItem('section', 'listing type');
+        //  get listing type
+        $singularDirective =& $config->getItem('directive', 'singular');
+        $singularType = $singularDirective->getContent();
+
+        //  get reference to [conf] section of config file
+        $config =& $this->config->getItem('section', 'conf');
+        //  get controlled cities
+        $ctrlCtyDirective =& $config->getItem('directive', 'controlledCities');
+
+        $states = Toolkit_Common::getStates($this->dbh);
+        $this->setCategories();
+
+               $e = array();
+               //      All Grouped Elements are created here.
+
+               //      All Elements are created here.  This includes group element definitions.
+               $submitBtns[] = array(
+                       'type'    => 'submit',
+                       'req'     => false,
+                       'name'    => 'approve',
+                       'display' => 'Approve',
+                       'opts'    => array('id' => 'approve')
+               );
+               $submitBtns[] = array(
+                       'type'    => 'submit',
+                       'req'     => false,
+                       'name'    => 'deny',
+                       'display' => 'Deny',
+                       'opts'    => array('id' => 'deny')
+               );
+
+               //      Member Information
+               $e[] = array(
+                       'type' => 'hidden',
+                       'req'  => false,
+                       'name' => 'new_member'
+               );
+               $e[] = array(
+                       'type'    => 'header',
+                       'req'     => false,
+                       'name'    => 'newMemberHdr',
+                       'display' => "$singularType Information"
+               );
+               $e[] = array(
+                       'type'    => 'text',
+                       'req'     => false,
+                       'name'    => 'member_name',
+                       'display' => $singularType . ' Name'
+               );
+               $e[] = array(
+                       'type'    => 'text',
+                       'req'     => false,
+                       'name'    => 'primary_contact_fname',
+                       'display' => 'Primary Contact First Name'
+               );
+               $e[] = array(
+                       'type'    => 'text',
+                       'req'     => false,
+                       'name'    => 'primary_contact_lname',
+                       'display' => 'Primary Contact Last Name'
+               );
+               $e[] = array(
+                       'type'    => 'text',
+                       'req'     => false,
+                       'name'    => 'process_email',
+                       'display' => 'Primary Contact Email'
+               );
+        $e[] = array(
+            'type'    => 'select3',
+            'req'     => false,
+            'name'    => 'category_id',
+            'display' => "$singularType Category",
+            'opts'    => $this->categories,
+        );
+               $e[] = array(
+                       'type'    => 'header',
+                       'req'     => false,
+                       'name'    => 'physicalAddressHdr',
+                       'display' => 'Physical Address'
+               );
+               $e[] = array(
+                       'type'    => 'text',
+                       'req'     => false,
+                       'name'    => 'street',
+                       'display' => 'Street Address'
+               );
+        if ($ctrlCtyDirective->getContent()) {
+            $e[] = array(
+                'type'    => 'select',
+                'req'     => false,
+                'name'    => 'city_id',
+                'display' => 'City',
+                'opts'    => $this->getCities(),
+            );
+        } else {
+            $e[] = array(
+                'type'    => 'text',
+                'req'     => false,
+                'name'    => 'city',
+                'display' => 'City',
+            );
+        }
+               $e[] = array(
+                       'type' => 'select',
+                       'req' => false,
+                       'name' => 'state_id',
+                       'display' => 'State / Provice',
+                       'opts' => array('' => '-- Select --') + $states
+               );
+               $e[] = array(
+                       'type'    => 'text',
+                       'req'     => false,
+                       'name'    => 'zip',
+                       'display' => 'Zip / Postal Code'
+               );
+               $e[] = array(
+                       'type'    => 'header',
+                       'req'     => false,
+                       'name'    => 'mailingAddressHdr',
+                       'display' => 'Mailing Address'
+               );
+               $e[] = array(
+                       'type'    => 'text',
+                       'req'     => false,
+                       'name'    => 'mailing_address',
+                       'display' => 'Street Address'
+               );
+        if ($ctrlCtyDirective->getContent()) {
+            $e[] = array(
+                'type'    => 'select',
+                'req'     => false,
+                'name'    => 'mailing_city_id',
+                'display' => 'City',
+                'opts'    => $this->getCities(),
+            );
+        } else {
+            $e[] = array(
+                'type'    => 'text',
+                'req'     => false,
+                'name'    => 'mailing_city',
+                'display' => 'City',
+            );
+        }
+               $e[] = array(
+                       'type' => 'select',
+                       'req' => false,
+                       'name' => 'mailing_state_id',
+                       'display' => 'State / Provice',
+                       'opts' => array('' => '-- Select --') + $states
+               );
+               $e[] = array(
+                       'type'    => 'text',
+                       'req'     => false,
+                       'name'    => 'mailing_zip',
+                       'display' => 'Zip / Postal Code'
+               );
+               $e[] = array(
+                       'type'    => 'header',
+                       'req'     => false,
+                       'name'    => 'publicContactInfoHdr',
+                       'display' => 'Public Contact Information'
+               );
+               $e[] = array(
+                       'type'    => 'text',
+                       'req'     => false,
+                       'name'    => 'phone',
+                       'display' => 'Phone'
+               );
+               $e[] = array(
+                       'type'    => 'text',
+                       'req'     => false,
+                       'name'    => 'toll_free',
+                       'display' => 'Phone 2'
+               );
+               $e[] = array(
+                       'type'    => 'text',
+                       'req'     => false,
+                       'name'    => 'fax',
+                       'display' => 'Fax'
+               );
+               $e[] = array(
+                       'type'    => 'text',
+                       'req'     => false,
+                       'name'    => 'member_contact_email',
+                       'display' => 'Email on Website'
+               );
+               $e[] = array(
+                       'type'    => 'text',
+                       'req'     => false,
+                       'name'    => 'url',
+                       'display' => 'Website Address'
+               );
+               $e[] = array(
+                       'type'    => 'textarea',
+                       'req'     => false,
+                       'name'    => 'description',
+                       'display' => "Description of $singularType",
+            'noCharLimit' => true
+               );
+               $e[] = array(
+                       'type'       => 'group',
+                       'req'        => false,
+                       'name'       => 'decision',
+                       'group'      => $submitBtns,
+                       'seperator'  => '',
+                       'appendName' => false
+               );
+
+               $this->setupElements($e);
+       }
+
+       //      }}}
+       //      {{{ configureFilters()
+
+       /**
+        * Setup the filters to apply to the elements before we are
+        * handed the values submitted
+        *
+        * @return void
+        * @access protected
+        */
+       protected function configureFilters()
+       {
+               $f   = array();
+               $f[] = array(
+                       'element' => '__ALL__',
+                       'filter'  => 'trim'
+               );
+
+               $this->setupFilters($f);
+       }
+
+       //      }}}
+    //  {{{ configureForm()
+
+    /**
+     * Wrapper function to handle setting up the form
+     *
+     * @return void
+     * @access public
+     */
+    public function configureForm()
+    {
+        $this->configureElements();
+        $this->configureRules();
+        $this->configureFilters();
+        $this->configureDefaults();
+        $this->configureConstants();
+    }
+
+    //  }}}
+       //      {{{ configureRules()
+
+       /**
+        * Sets up all the rules to be used when the form is validated.
+        *
+        * @return void
+        * @access Protected
+        */
+       protected function configureRules()
+       {
+               $this->setupRules($rules);
+       }
+
+       //      }}}
+       //      {{{ createCategories()
+
+       /**
+        * Creates an array from the array of category tree objects
+        *
+     * @param array   $tree  linear category tree array
+     * @param integer $depth what level we are on
+     *
+        * @return array The array list of categories that
+        *                               that can be loaded into a select element
+        * @access protected
+        */
+       protected function createCategories($tree, $depth = 0)
+       {
+               $this->records[] = $tree->category;
+               if ($depth == 0) {
+                       $this->categories[$tree->catid] = "<span>{$tree->category}</span>";
+               } else {
+                       $this->categories[$tree->catid] = $tree->category;
+               }
+               if (empty($tree->children)) {
+                       return;
+               } else {
+                       ++$depth;
+                       foreach ($tree->children as $miniTrees) {
+                               $this->createCategories($miniTrees, $depth);
+                       }
+               }
+       }
+
+       //      }}}
+
+       //      {{{     denyMember()
+
+
+    /**
+     * Removes member from DB
+     *
+     * @return boolean Result of sql query
+     * @access protected
+     */
+       protected function denyMember()
+       {
+               try {
+                       $sql = "
+                DELETE FROM {$this->tableName}
+                 WHERE member_id  = ?";
+
+                       $stmt = $this->dbh->prepare($sql);
+                       return $stmt->execute(array($_GET['id']));
+               } catch (PDOException $e) {
+                       return Toolkit_Common::handleError($e);
+               }
+       }
+
+       //      }}}
+
+       //      {{{     emailMember()
+
+
+    /**
+     * Emails member who requested to join
+     *
+     * If approved, sends email telling member how to login.
+     *
+     * If denied, sends email explaining they were denied and how to
+     * correct the problem if that was a mistake
+     *
+     * @param boolean $status If the member was approved/denied
+     * @param boolean $test   If this is a test
+     *
+     * @return mixed PEAR Error on mail failure, otherwise true
+     * @access public
+     */
+       public function emailMember($status, $test = false)
+       {
+        //  get reference to the [listing type] section of config file
+        $config =& $this->config->getItem('section', 'listing type');
+        //  get listing type
+        $singularDirective =& $config->getItem('directive', 'singular');
+        $singularType = $singularDirective->getContent();
+
+               $template = new HTML_Template_Flexy($this->flexyOptions);
+               $page     = new stdClass();
+
+               $data = $this->configureDefaults();
+               $page->base_url = ($_SERVER['HTTPS'] == 'on') ? BASE_SECURE_URL :
+                                                                                                               MEDIA_BASE_URL;
+               $page->client_name     = SITENAME;
+               $page->member_login    = $data['member_login'];
+               $page->member_passwd   = $data['member_passwd'];
+               $page->first_name      = $data['primary_contact_fname'];
+               $page->member_category = MEMBERS_CATEGORY;
+
+               $tpl = $status ? 'newMemberApproval.tpl' : 'newMemberDenial.tpl';
+               $template->compile($tpl);
+               //      Merge the compiled template with the $page object.
+               $htmlMsg = $template->bufferedOutputObject($page);
+
+               $crlf     = "\n";
+               $mimeMail = new Mail_mime($crlf);
+               $mimeMail->setFrom(SITENAME . ' <' . OWNER_EMAIL . '>');
+               $mimeMail->setSubject("New $singularType Request");
+               $mimeMail->setHTMLBody($htmlMsg);
+               $mimeMail->setTXTBody($msg);
+
+               $interface = $test ? 'mock' : 'mail';
+               $mail      =& Mail::factory($interface);
+               $body      = $mimeMail->get();
+               $headers   = $mimeMail->headers($hdrs);
+
+               try {
+                       $sql = "
+                               SELECT process_email
+                                 FROM {$this->tableName}
+                                WHERE member_id = ?";
+
+                       $stmt = $this->dbh->prepare($sql);
+                       $stmt->execute(array($_GET['id']));
+                       $stmt->bindColumn('process_email', $email);
+                       $stmt->fetch();
+               } catch (PDOException $e) {
+                       return Toolkit_Common::handleError($e);
+               }
+
+               $res = $mail->send($email, $headers, $body);
+
+               return PEAR::isError($res) ? Toolkit_Common::handleError($res) : $res;
+       }
+
+       //      }}}
+
+       //      {{{ getCities()
+
+       /**
+        * Configure the cities for member stored in the database into an array
+        *
+     * @return array the cities
+        * @access protected
+        */
+       protected function getCities()
+       {
+               $cities = array();
+               try {
+                       $sql = "
+                               SELECT *
+                                 FROM city
+                                ORDER BY city_name";
+                       foreach ($this->dbh->query($sql) as $row) {
+                               $cities[$row['city_id']] = $row['city_name'];
+                       }
+                       if (!empty($cities)) {
+                               $cities = array('' => '-- Select --') + $cities;
+                       } else {
+                               $cities = array('' => '-- No Cities Created Yet -- ');
+                       }
+                       return $cities;
+               } catch (PDOException $e) {
+                       return Toolkit_Common::handleError($e);
+               }
+       }
+
+       //      }}}
+
+       //      {{{     processData()
+
+    /**
+     * Handles how to process the form when submitted
+     *
+     * @param array $values Form submitted values
+        *
+     * @return array Result of Insert / Update function
+     * @access protected
+     */
+       public function processData($values)
+       {
+               if (array_key_exists('deny', $values)) {
+                       $this->emailMember(false);
+                       return $this->denyMember();
+               } elseif (array_key_exists('approve', $values)) {
+                       $this->emailMember(true);
+                       return $this->approveMember();
+               }
+       }
+
+       //      }}}
+
+    //  {{{ setConfig()
+
+    /**
+     * Sets the query to use to fetch the datagrid results
+     *
+     * @param Config_Container $c Configuration object
+     *
+     * @return void
+     * @access public
+     */
+    public function setConfig(Config_Container $c)
+    {
+        $this->config = $c;
+    }
+
+    //  }}}
+       //      {{{ setCategories()
+
+       /**
+        * Creates array structure of the category tree for use in a select element.
+        *
+        * This method will also instantiate a class property called tree for your
+        * class. This object will hold the tree structure of the categories list
+        * from the Database.
+        *
+        * @return array The array list of categories that can be loaded
+        *                               into a select element
+        *
+        * @access protected
+        */
+       protected function setCategories()
+       {
+               try {
+                       $sql = "
+                               SELECT *
+                                 FROM category
+                                WHERE parent_id = 0
+                                ORDER BY name";
+                       foreach ($this->dbh->query($sql) as $row) {
+                               $this->tree[] = new Toolkit_Members_CategoryTree($row['category_id'], $row['name'], $this->dbh);
+                       }
+                       if (!empty($this->tree)) {
+                               foreach ($this->tree as $t) {
+                                       $this->createCategories($t);
+                               }
+                       }
+
+                       if (empty($this->categories)) {
+                               $this->categories = array('' => '-- No Categories Created Yet --');
+                       } else {
+                               $this->categories
+                                       = array('' => '-- Select --') + $this->categories;
+                       }
+               } catch (PDOException $e) {
+                       return Toolkit_Common::handleError($e);
+               }
+       }
+
+       //      }}}
+       //      {{{     setupRenderers()
+
+    /**
+        * Setup how the form should render
+        *
+        * We also need to attach a couple specific rendering templates to
+        * a few special elements.
+        *
+     * @return void
+     * @access protected
+     */
+       protected function setupRenderers()
+       {
+               parent::setupRenderers();
+               $renderer =& $this->defaultRenderer();
+               $required = '<!-- BEGIN required --><span class="req">*</span><!-- END required -->';
+               $error    = '<!-- BEGIN error --><div class="req">{error}</div><!-- END error -->';
+
+               $renderer->setElementTemplate('<tr align="center"><td colspan="2">{element}</td></tr>', 'decision');
+       }
+
+       //      }}}
+
+       //      {{{     toHtml()
+
+       /**
+        * Call the rendering function to get the form in a string
+        *
+        * @access protected
+        * @return string $output The Form to be rendered or success msg.
+        */
+       public function toHtml()
+       {
+               $this->setupRenderers();
+               $this->freeze();
+               if ($this->validate()) {
+                       $this->cleanForm();
+                       if ($this->process(array(&$this, 'processData'))) {
+                               header('Location: ' . MEDIA_BASE_URL . 'admin/members.php?rt=Members&ac=newMemberRequests');
+                       }
+               } else if ($this->isSubmitted()) {
+                       $output  = $this->errorMsg;
+                       $output .= parent::toHTML();
+               } else {
+                       $output = parent::toHTML();
+               }
+               return $output;
+       }
+
+       //      }}}
+}
+?>
diff --git a/Toolkit/Members/Admin/AuthorizeUpdates.php b/Toolkit/Members/Admin/AuthorizeUpdates.php
new file mode 100755 (executable)
index 0000000..ca81e0d
--- /dev/null
@@ -0,0 +1,1770 @@
+<?php
+//  vim:set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker syntax=php:
+
+/**
+ * File Doc Comment
+ *
+ * PHP version 5
+ *
+ * @category MembersDB
+ * @package  Toolkit_Members
+ * @author   Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @license  http://www.gaslightmedia.com Gaslightmedia
+ * @version  CVS: $Id: AuthorizeUpdates.php,v 1.17 2010/07/14 23:27:59 jamie Exp $
+ * @link     http://demo.gaslightmedia.com
+ */
+
+/**
+ * Handle processing all the update requests from members
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2008 Gaslight media
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @link      http://demo.gaslightmedia.com
+ */
+class Toolkit_Members_Admin_AuthorizeUpdates extends Toolkit_FormBuilder
+{
+    //  {{{ properties
+
+    /**
+     * The member we are going to process updates for
+     *
+     * @var integer
+     * @access private
+     */
+    private $_mid;
+
+    /**
+     * The Table name used to store the data of the member record in the database.
+     *
+     * @var string
+     * @access public
+     */
+    public $tableName = 'member_updates';
+
+    /**
+     * Array of data that holds the meta data info on the table
+     *
+     * Contains information on the type of fields in the database.
+     * That way when we run our automated SQL queries with our PDO
+     * we can properly bind data to our SQL queries.  This will
+     * allow for one more layer of protection against any sql
+     * injection attempts.
+     *
+     * @var string
+     * @access public
+     * @see Toolkit_Common::processData()
+     */
+    public $tableMetaData;
+
+    /**
+     * The name of the template used to render the business info form.
+     *
+     * @var string
+     * @access protected
+     */
+    protected $formTemplate = 'authorizeChanges.tpl';
+
+    /**
+     * Form access from inside the template
+     *
+     * @var object
+     * @access protected
+     */
+    protected $view;
+
+    //  }}}
+    //  {{{ __construct()
+
+    /**
+     * Constructor
+     *
+     * @param PDO    $pdo         PHP Data Object to use for DB calls
+     * @param string $formName    Form's name.
+     * @param string $method      (optional)Form's method defaults to 'POST'
+     * @param string $action      (optional)Form's action
+     * @param string $target      (optional)Form's target defaults to '_self'
+     * @param mixed  $attributes  (optional)Extra attributes for <form> tag
+     * @param bool   $trackSubmit (optional)Whether to track if the form was submitted by adding a special hidden field
+     *
+     * @access public
+     */
+    public function __construct(
+        PDO $pdo,
+        $formName,
+        $method = 'post',
+        $action = '',
+        $target = '',
+        $attributes = null,
+        $trackSubmit = false
+    ) {
+        parent::__construct(
+            $formName,
+            $method,
+            $action,
+            $target,
+            $attributes,
+            $trackSubmit
+        );
+
+        $this->dbh = $pdo;
+
+        $this->flexyOptions = Toolkit_Members::getFlexyOptions();
+    }
+
+    //  }}}
+
+    //  {{{ commitUpdates
+
+    /**
+     * Commit the updates requested by the member
+     *
+     * @param string $table   The table to commit any updates for
+     * @param array  $fields  The fields to update
+     * @param array  $updates The updates
+     *
+     * @return boolean true on success, false on error
+     * @access protected
+     */
+    protected function commitUpdates($table, array $fields, array $updates)
+    {
+        foreach ($fields as $k => &$v) {
+            $orig          = $v;
+            list ($id, $v) = explode('_', $v, 2);
+            if (array_key_exists("{$orig}_update", $updates)) {
+                $updates["{$v}_update"] = $updates["{$orig}_update"];
+                unset($updates["{$orig}_update"]);
+            }
+        }
+        try {
+            $this->dbh->beginTransaction();
+            //  This will be the query we will use at the end to update the
+            //  members record.
+            $sql        = $this->getUpdateQuery($table, $fields);
+
+            //  hack to fix states trying to update w/ the
+            //  text ie (North Carolina - go heels!) vs. field id.
+            unset(
+                $updates['state_id_update'],
+                $updates['city_id_update'],
+                $updates['mailing_state_id_update']
+            );
+            $updateStmt = $this->dbh->prepare($sql);
+            $updateStmt->bindParam(':member_id', $this->_mid, PDO::PARAM_INT);
+
+            //  This will be the query we will use to remove the updates
+            //  from the update table.  By removing the field from the updates
+            //  table it takes that field out of a "pending" state, by removing all
+            //  of the fields for a member out of the updates table it removes
+            //  that member from being in a "pending" state.
+            $sql = "
+                DELETE FROM {$this->tableName}
+                 WHERE field        = :field
+                   AND member_id    = :member_id";
+
+            $deleteStmt = $this->dbh->prepare($sql);
+            $deleteStmt->bindParam(':member_id', $this->_mid, PDO::PARAM_INT);
+
+            //  This will be the query we will use to get the updated fields
+            //  information.
+            $sql =  "
+                SELECT *
+                  FROM {$this->tableName}
+                 WHERE id in (
+                        SELECT max(id)
+                          FROM {$this->tableName}
+                         WHERE member_id     = :member_id
+                           AND field   = :field)";
+
+            $fetchStmt = $this->dbh->prepare($sql);
+            $fetchStmt->bindParam(':member_id', $this->_mid, PDO::PARAM_INT);
+            foreach ($fields as $k => &$f) {
+                unset($v);
+                //  Get the newest row for this field for this member.
+                $fetchStmt->bindParam(':field', $f, PDO::PARAM_STR);
+                $fetchStmt->execute();
+                $row = $fetchStmt->fetch(PDO::FETCH_ASSOC);
+
+                //  Get the update to apply.
+                if (array_key_exists("{$f}_update", $updates)) {
+                    $v = $updates["{$f}_update"];
+                } else {
+                    $v = $row['update'];
+                }
+
+                //  Get the dataType for the PDO Statement.
+                $metaData = $row['data_type'];
+                if ($metaData == 'integer') {
+                    $dataType = PDO::PARAM_INT;
+                } else if ($metaData == 'boolean') {
+                    $dataType = PDO::PARAM_BOOL;
+                } else {
+                    $dataType = PDO::PARAM_STR;
+                }
+                //  for empty values that are not actually a zero (0), we
+                //  want to insert null's.
+                //  This will help hold the unique values for member_logins,
+                //  as empty values '', are not considered unique
+                if (empty($v) && $v !== 0) {
+                    $v        = null;
+                    $dataType = PDO::PARAM_NULL;
+                }
+                //  Bind the update for later execution.
+                $updateStmt->bindParam(":$f", $v, $dataType);
+
+                //  Remove this field from the updates
+                $deleteStmt->bindParam(':field', $f, PDO::PARAM_STR);
+                $deleteStmt->execute();
+            }
+            $updateStmt->execute();
+            return $this->dbh->commit();
+        } catch (PDOException $e) {
+            $this->dbh->rollback();
+            return Toolkit_Common::handleError($e);
+        }
+    }
+
+    //  }}}
+    //  {{{ configureDefaults()
+
+    /**
+     * Sets the defaults for the an existing member
+     *
+     * Populates data for the main member form.  Also grabs
+     * data to populate the modules on the form if needed.
+     *
+     * @return array $defaults Returns the array of defaults
+     *                         so children who call this function
+     *                         can obtain a copy of these values.
+     * @access public
+     */
+    public function configureDefaults()
+    {
+        $updatedFieldValues  = $this->getUpdatedFieldValues();
+        $originalFieldValues = $this->getOriginalFieldValues();
+
+        if (is_array($updatedFieldValues)) {
+            $defaults = $updatedFieldValues;
+        }
+
+        if (is_array($originalFieldValues)) {
+            if (is_array($defaults)) {
+                $defaults += $originalFieldValues;
+            } else {
+                $defaults = $originalFieldValues;
+            }
+        }
+
+        $this->setupDefaults($defaults);
+        return $defaults;
+    }
+
+    //  }}}
+    //  {{{ configureElements()
+
+    /**
+     * Setup the elements to use on the form.
+     *
+     * Categories are populated into the protected class property $categories.
+     * These categories are used to populate the select list of categories.
+     * Modules are configured before any elements are setup, that way we will
+     * know if we need to include them in the rendering of the form.
+     *
+     * @return void
+     * @access public
+     */
+    public function configureElements()
+    {
+        //  Get the newest updates in the update table for this member
+        //  for each field that was updated.
+        try {
+            $sql = "
+                SELECT *
+                  FROM {$this->tableName}
+                 WHERE id IN (
+                        SELECT max(id)
+                          FROM {$this->tableName}
+                         WHERE member_id     = :member_id
+                         GROUP BY field, foreign_key)
+                 ORDER BY db_table, foreign_key, id";
+
+            $stmt = $this->dbh->prepare($sql);
+            $stmt->bindParam(':member_id', $this->_mid, PDO::PARAM_INT);
+            $stmt->execute();
+            while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                //  If we are dealing w/ a credit card, this
+                //  will extract the name of the card.
+                //  Otherwise, it will leave the value unchanged.
+                $pattern      = '/creditCards\[(.+)\]/';
+                $replacement  = '$1';
+                $row['field'] = preg_replace($pattern, $replacement, $row['field']);
+                $updates[]    = $row;
+                switch ($row['field_type']) {
+                case 'select' :
+                case 'checkbox' :
+                    $elementsToFreeze[$row['id']] = $row['field'];
+                    break;
+
+                default :
+                    break;
+                }
+            }
+        } catch (PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+
+        $fields = $labels = array();
+        if (is_array($updates)) {
+            //  We're going to need to alter how some of the fields
+            //  are displayed on the page.  This is where we set them up.
+            foreach ($updates as $u) {
+                unset($type, $opts);
+                //  Determine which fields should be a text field so
+                //  the admin can update them w/ thier own text.
+                switch ($u['data_type']) {
+                case 'text' :
+                case 'integer' :
+                    $textField = true;
+                    break;
+
+                default :
+                    $textField = false;
+                    break;
+                }
+                if ($textField) {
+                    switch ($u['label']) {
+                    case 'Description' :
+                    case 'Special Package Description' :
+                    case 'Golf Package Description' :
+                        $type = 'textarea';
+                        $opts = array(
+                            'id' => 'd' . microtime(),
+                            'rows' => 8,
+                            'cols' => 43,
+                        );
+                        break;
+
+                    default :
+                        $type = 'text';
+                        $opts = array('class' => 'text');
+                        break;
+                    }
+                }
+                //  If we are dealing w/ the fields for the restaurant module
+                //  we need to alter the labels of the text fields.
+                //  Otherwise we could potentially have multiple [To] or [From]
+                //  fields and no way to discern which meal they represent.
+                if ($u['db_table'] == 'member_restaurants') {
+                    if ($u['data_type'] == 'text') {
+                        $u['label'] = ucwords(str_replace('_', ' ', $u['field']));
+                    }
+                }
+                if ($u['db_table'] == 'member' && $u['field'] == 'logo') {
+                    $u['label'] = '<div class="pendingLogo">
+                        <img alt="'.$u['update'].'"
+                        src="'.MEMBER_PHOTOS.$u['update'].'">Main Image</div>';
+                }
+                if ($u['db_table'] == 'member_packages' && $u['field'] == 'image') {
+                    unset($type, $opts);
+                }
+                if (!isset($type)) {
+                    $type = 'static';
+                }
+                $labels[$u['id']][$u['field']]['label']    = $u['label'];
+                $labels[$u['id']][$u['field']]['db_table'] = $u['db_table'];
+
+                $fields[$u['id']][$u['field']][] = array('type' => 'static', 'req' => false, 'name' => "{$u['id']}_{$u['field']}_orig");
+                $fields[$u['id']][$u['field']][] = array('type' => $type, 'req' => false, 'name' => "{$u['id']}_{$u['field']}_update", 'opts' => $opts);
+            }
+            $elements[] = array('type' => 'header', 'req' => false, 'name' => 'memberInfoHdr',  'display' => 'Updates', 'col1' => true);
+            foreach ($fields as $rowid => $field) {
+                foreach ($field as $k => $arr) {
+                    if ($table != $labels[$rowid][$k]['db_table']) {
+                        $elements[] = array('type' => 'header', 'req' => false, 'name' => 'table',  'display' => ucwords(str_replace('_', ' ', $labels[$rowid][$k]['db_table'])));
+                        $table      = $labels[$rowid][$k]['db_table'];
+                    }
+                    $elements[] = array('type' => 'group',  'req' => false, 'name' => "{$rowid}_{$k}", 'group' => $field[$k],   'label' => $labels[$rowid][$k]['label'], 'seperator' => '&nbsp;', 'appendName' => false);
+                }
+            }
+        }
+
+        $this->setupElements($elements);
+
+        if (!empty($elementsToFreeze)) {
+            foreach ($elementsToFreeze as $k => $v) {
+                $eName = "{$k}_{$v}";
+                if ($this->elementExists($eName)) {
+                    $e =& $this->getElement($eName);
+                    if (PEAR::isError($e)) {
+                        return Toolkit_Common::handleError($e);
+                    } else {
+                        $e->freeze();
+                    }
+                }
+            }
+        }
+    }
+
+    //  }}}
+    //  {{{ configureFilters()
+
+    /**
+     * Setup the filters to apply to the elements before we are
+     * handed the values submitted
+     *
+     * @return void
+     * @access public
+     */
+    public function configureFilters()
+    {
+        $filters[] = array('element' => '__ALL__', 'filter' => 'trim');
+
+        $this->setupFilters($filters);
+    }
+
+    //  }}}
+    //  {{{ configureForm()
+
+    /**
+     * Wrapper function to handle setting up the form
+     *
+     * @return mixed PEAR error if no member id is set, otherwise true
+     * @access public
+     */
+    public function configureForm()
+    {
+        if (!is_numeric($this->_mid)) {
+            return PEAR::raiseError('Invalid member id');
+        }
+
+        $this->configureElements();
+        $this->configureFilters();
+        //$this->configureRules();
+        $this->configureDefaults();
+
+        return true;
+    }
+
+    //  }}}
+    //  {{{ configureModules()
+
+    /**
+     * Turns on a modules if the member has the appropriate category
+     *
+     * @return void
+     * @access protected
+     */
+    protected function configureModules()
+    {
+        if (is_array($this->memberCategories)) {
+            try {
+                $sql = "
+                    SELECT *
+                      FROM category
+                     WHERE category_id = :cid";
+
+                $stmt = $this->dbh->prepare($sql);
+                foreach ($this->memberCategories as $cid => $v) {
+                    $stmt->bindParam(':cid', $cid, PDO::PARAM_INT);
+                    $stmt->execute();
+                    $row = $stmt->fetch(PDO::FETCH_ASSOC);
+                    if ($row['accommodations'] == 't') {
+                        $this->accommodations = true;
+                    }
+                    if ($row['restaurant'] == 't') {
+                        $this->restaurant = true;
+                    }
+                    if ($row['golf'] == 't') {
+                        $this->golf = true;
+                    }
+                }
+            } catch (PDOException $e) {
+                return Toolkit_Common::handleError($e);
+            }
+        }
+    }
+
+    //  }}}
+    //  {{{ configureRules()
+
+    /**
+     * Sets up all the rules to be used when the form is validated.
+     *
+     * @return void
+     * @access public
+     */
+    public function configureRules()
+    {
+        //  Define the rules for each element in the group.
+        $num_rule = array('ERROR: Must contain a valid positive or negative decimal number!', 'numeric');
+        //  Collect together the rules for each element.
+        $lat_rules = array('lat' => array($num_rule));
+        $lon_rules = array('lon' => array($num_rule));
+
+        $rules[] = array('element' => 'reservation_id', 'message' => 'ERROR: Must be an integer!',  'type' => 'numeric', 'format' => null, 'validation' => $this->validationType, 'reset' => false, 'force' => false);
+        $rules[] = array('element' => 'num_rooms',      'message' => 'ERROR: Must be an integer!',  'type' => 'numeric', 'format' => null, 'validation' => $this->validationType, 'reset' => false, 'force' => false);
+        $rules[] = array('element' => 'latitude',       'message' => 'ERROR:',  'type' => 'group', 'format' => $lat_rules, 'validation' => $this->validationType, 'reset' => false, 'force' => false);
+        $rules[] = array('element' => 'longitude',      'message' => 'ERROR:',  'type' => 'group', 'format' => $lon_rules, 'validation' => $this->validationType, 'reset' => false, 'force' => false);
+        $rules[] = array('element' => 'member_name',    'message' => 'ERROR: Name Already used!',   'type' => 'callback', 'format' => array($this, 'checkMemberName'), 'validation' => $this->validationType, 'reset' => false, 'force' => false);
+        $rules[] = array('element' => 'member_login',   'message' => 'ERROR: Already used, Please choose another!', 'type' => 'callback', 'format' => array($this, 'checkUName'), 'validation' => $this->validationType, 'reset' => false,  'force' => false);
+        $rules[] = array('element' => 'zip',            'message' => 'ERROR: Invalid Zip Code!',    'type' => 'zip', 'format' => array('requireDBCheck' => false), 'validation' => $this->validationType, 'reset' => false, 'force' => false);
+        $rules[] = array('element' => 'phone',          'message' => 'ERROR: Invalid Phone Number!','type' => 'phone', 'format' => null, 'validation' => $this->validationType, 'reset' => false,   'force' => false);
+        $rules[] = array('element' => 'fax',            'message' => 'ERROR: Invalid Phone Number!','type' => 'phone', 'format' => null, 'validation' => $this->validationType, 'reset' => false,   'force' => false);
+
+        $this->setupRules($rules);
+    }
+
+    //  }}}
+
+    //  {{{ getMultipleFieldUpdates()
+
+    /**
+     * Gets the current values for multiple fields of members
+     *
+     * Multiple fields are ones that a member can have many of.  They idealy
+     * need a foreign key, but it is impossible to set up dynamic foreign keys.
+     * examples of these fields are photo captions or file name, or newly added
+     * files.  Since a member can have multiple of these fields pending at the
+     * same time, we need to figure out the original value for the file names,
+     * captions, etc...  of these fields.
+     *
+     * To get these fields, figure out which ones have updates in the updates
+     * table and get corresponding field from the original table. Then use the
+     * psuedo foreign_key to get the corresponding match in the actual table.
+     *
+     * @return array All the original updates made by the member
+     * @access protected
+     * @see    Toolkit_Members_authorizeupdates::getOriginalFieldValues()
+     */
+    protected function getMultipleFieldUpdates()
+    {
+        $tables = array();
+        //  Then we need to get all the tables which DO NOT have DISTINCT
+        //  fields in the updates table.  That means they DO have a
+        //  psuedo foreign_key pointing back to them.  These are going
+        //  to take a little more work to obtain the original values.
+        $sql = "
+            SELECT DISTINCT db_table
+              FROM {$this->tableName}
+             WHERE foreign_key is NOT NULL";
+        foreach ($this->dbh->query($sql) as $row) {
+            $tables[] = $row['db_table'];
+        }
+
+        if (empty($tables)) {
+            return $tables;
+        }
+
+        //  This query will get all the fields that we need to get
+        //  from the original table.
+        $sql = "
+            SELECT *
+              FROM {$this->tableName}
+             WHERE id in (
+                    SELECT max(id)
+                      FROM {$this->tableName}
+                     WHERE db_table  = :db_table
+                       AND member_id = :member_id
+                     GROUP BY field, foreign_key)";
+
+        $getFieldStmt = $this->dbh->prepare($sql);
+        $getFieldStmt->bindParam(':member_id', $this->_mid, PDO::PARAM_INT);
+
+        $defaults = array();
+        foreach ($tables as $t) {
+            $getFieldStmt->bindParam(':db_table', $t, PDO::PARAM_STR);
+            $getFieldStmt->execute();
+
+            //  This query will get the current value that the member
+            //  has stored in the DB for the field we want.
+            $sql = "
+                SELECT %s
+                  FROM $t
+                 WHERE id = %d";
+
+            while ($row = $getFieldStmt->fetch(PDO::FETCH_ASSOC)) {
+                if ($row['data_type'] == 'text') {
+                    $newSql  = sprintf($sql, $row['field'], $row['foreign_key']);
+                    $current
+                        = $this->dbh->query($newSql)->fetch();
+                    if (empty($current[$row['field']])) {
+                        $current[$row['field']] = 'Did not previously exist.';
+                    } elseif ($t == 'member_packages') {
+                        if ($row['field'] == 'image') {
+                            $current[$row['field']]
+                                = '<div class="oldPackageImage">Old Image
+                                <img class="oldPackageImage"
+                                    alt="'.$current[$row['field']].'"
+                                    src="'.MEMBER_PHOTOS.$current[$row['field']].'">
+                                </div>';
+                        }
+                    }
+
+                    if ($t == 'member_packages' && $row['field'] == 'image') {
+                        $sql = "
+                           SELECT *
+                             FROM member_packages
+                            WHERE id = {$row['foreign_key']}";
+                        $package = $this->dbh->query($sql)->fetch(PDO::FETCH_ASSOC);
+
+                        if ($package['pending']) {
+                            $defaults["{$row['id']}_{$row['field']}_orig"] = '';
+                        } else {
+                            $defaults["{$row['id']}_{$row['field']}_orig"]
+                                = $current[$row['field']];
+                        }
+                    } else {
+                       $defaults["{$row['id']}_{$row['field']}_orig"]
+                            = $current[$row['field']];
+                    }
+                }
+            }
+        }
+
+        return $defaults;
+    }
+
+    //  }}}
+    //  {{{ getOriginalFieldValues()
+
+    /**
+     * Gets all the original values for the fields that have pending updates
+     *
+     * @return array of updates for all the fields that the member sent
+     * @access protected
+     */
+    protected function getOriginalFieldValues()
+    {
+        try {
+            $staticUpdates  = $this->getSingleFieldUpdates();
+            $dynamicUpdates = $this->getMultipleFieldUpdates();
+
+            $defaults = array_merge($staticUpdates, $dynamicUpdates);
+
+            return $defaults;
+        } catch (PDOException $e) {
+            return Toolkit_Common::handleError($e);
+        }
+    }
+
+    //  }}}
+    //  {{{ getSingleFieldUpdates()
+
+    /**
+     * Gets the current values for single fields of members
+     *
+     * Single fields are ones that can only show reference one field from
+     * the updates table to a member. Such as, url, description, num_rooms,
+     * etc...
+     * These field will only come from the member table or any member module
+     * table (golf, restaurant, accommodations).
+     *
+     * To get these fields, figure out which ones have updates in the updates
+     * table and get corresponding field from the original table.
+     *
+     * @return array
+     * @access protected
+     * @see    Toolkit_Members_authorizeupdates::getOriginalFieldValues()
+     */
+    protected function getSingleFieldUpdates()
+    {
+        $tables = array();
+        //  We need to get all the tables which have DISTINCT fields
+        //  in the updates table. That means they do not have a
+        //  psuedo foreign_key pointing back to them.  These will be
+        //  easier to get the original values for.
+        $sql = "
+            SELECT DISTINCT db_table
+              FROM {$this->tableName}
+             WHERE foreign_key is null";
+        foreach ($this->dbh->query($sql) as $row) {
+            $tables[] = $row['db_table'];
+        }
+
+        if (empty($tables)) {
+            return $tables;
+        }
+
+        //  This query will get all the fields that we need to get
+        //  from the original table.
+        $sql = "
+            SELECT *
+              FROM {$this->tableName}
+             WHERE id in (
+                    SELECT max(id)
+                      FROM {$this->tableName}
+                     WHERE db_table  = :db_table
+                       AND member_id = :member_id
+                     GROUP BY field)";
+
+        $getFieldStmt = $this->dbh->prepare($sql);
+        $getFieldStmt->bindParam(':member_id', $this->_mid, PDO::PARAM_INT);
+
+        $defaults = array();
+        //  We need to get all the tables which have DISTINCT fields
+        foreach ($tables as &$t) {
+            $getFieldStmt->bindParam(':db_table', $t, PDO::PARAM_STR);
+            $getFieldStmt->execute();
+
+            //  This query will get the current value that the member
+            //  has stored in the DB for the field we want.
+            $sql = "
+                SELECT %s
+                  FROM $t
+                 WHERE member_id = {$this->_mid}";
+
+            while ($row = $getFieldStmt->fetch()) {
+                if ($row['data_type'] == 'text') {
+                    $newSql  = sprintf($sql, $row['field']);
+                    $current
+                        = $this->dbh->query($newSql)->fetch();
+
+                    $defaults["{$row['id']}_{$row['field']}_orig"]
+                        = $current[$row['field']];
+                }
+                if ($row['field'] == 'state_id') {
+                    $locationNames = '
+                         ,(
+                        select state_name
+                          from state
+                         where state.state_id = member.state_id) as state_id';
+
+                    $newSql  = sprintf($sql, $row['field'] . $locationNames);
+                    $current
+                        = $this->dbh->query($newSql)->fetch();
+
+                    $defaults["{$row['id']}_{$row['field']}_orig"]
+                        = $current[$row['field']];
+                }
+            }
+        }
+
+        return $defaults;
+    }
+
+    //  }}}
+    //  {{{ getUpdateQuery()
+
+    /**
+     * Get the update query for a table in the database
+     *
+     * @param string $tname    The table name to update in the database
+     * @param array  $accepted All the accepted updates/changes
+     *
+     * @return string The update query to run against the database
+     * @access protected
+     */
+    protected function getUpdateQuery($tname, array $accepted)
+    {
+        $params = array_values($accepted);
+        $length = count($params);
+        for ($i = 0; $i < $length; ++$i) {
+            $bindParams .= "{$params[$i]} = :{$params[$i]}";
+            if ($i < ($length - 1)) {
+                $bindParams .= ', ';
+            }
+        }
+        $sql = "
+            UPDATE $tname
+               SET $bindParams
+            WHERE member_id = :member_id";
+        return $sql;
+    }
+
+    //  }}}
+    //  {{{ getUpdatedFieldValues()
+
+    /**
+     * Gets all the values for the newest updates
+     *
+     * @return array The updated values
+     * @access protected
+     */
+    protected function getUpdatedFieldValues()
+    {
+        try {
+            //  Get all the latest updates, When we are dealing w/ the state_id
+            //  fields, get the state name vs showing the id number.
+            //  users won't know what the # is.
+            $sql = "
+                SELECT *,
+                   CASE
+                   WHEN field = 'city_id' THEN (
+                    SELECT city_name
+                      FROM city
+                     WHERE city_id   = CAST(UPDATE AS integer) )
+                   WHEN field = 'mailing_state_id' AND update != '' THEN (
+                    SELECT state_name
+                      FROM state
+                     WHERE state_id   = CAST(UPDATE AS integer) )
+                   WHEN field = 'mailing_state_id' AND update = '' THEN NULL
+                   WHEN field = 'state_id' THEN (
+                    SELECT state_name
+                      FROM state
+                     WHERE state_id   = CAST(UPDATE AS integer) )
+                   WHEN field ~* 'creditCards' AND update = '0' THEN 'Removed'
+                   WHEN field ~* 'creditCards' AND update = '1' THEN 'Added'
+                   WHEN db_table = 'member_amenity' AND update = '0' THEN 'Removed'
+                   WHEN db_table = 'member_amenity' AND update = '1' THEN 'Added'
+                   ELSE UPDATE END AS
+                UPDATE
+                  FROM {$this->tableName}
+                 WHERE id IN (
+                        SELECT MAX(id)
+                          FROM {$this->tableName}
+                         WHERE member_id = :member_id
+                         GROUP BY field, foreign_key)
+                ORDER BY db_table, foreign_key, id";
+
+            $stmt = $this->dbh->prepare($sql);
+            $stmt->bindParam(':member_id', $this->_mid, PDO::PARAM_INT);
+            $stmt->execute();
+            while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                //  If we are dealing w/ a credit card, this
+                //  will extract the name of the card.
+                //  Otherwise, it will leave the value unchanged.
+                $pattern      = '/creditCards\[(.+)\]/';
+                $replacement  = '$1';
+                $row['field'] = preg_replace($pattern, $replacement, $row['field']);
+                if (empty($row['update'])) {
+                    if ($row['data_type'] == 'boolean') {
+                        $row['update'] = 'Off';
+                    }
+                } elseif ($row['data_type'] == 'boolean') {
+                    $row['update'] = 'On';
+                }
+
+                //  Handle all the member photos requests
+                if ($row['db_table'] == 'member_photos') {
+                    //  The boolean field will only be for the pending status
+                    //  if the data_type is boolean we know we are dealing
+                    //  with a newly uploaded image
+                    if ($row['data_type'] == 'boolean') {
+                        //  No need to show any instructions for
+                        //  the accept/reject buttons
+                        $row['update'] = 'Added';
+                    }
+                }
+                if ($row['db_table'] == 'member') {
+                    if ($row['field'] == 'logo') {
+                        $row['update'] = 'Updated';
+                    }
+                }
+                if ($row['db_table'] == 'member_files') {
+                    //  if the data_type is boolean we know we are dealing
+                    //  with a newly uploaded file
+                    if ($row['data_type'] == 'boolean') {
+                        //  No need to show any instructions for
+                        //  the accept/reject buttons
+                        $row['update'] = 'Added';
+                    }
+                }
+                if ($row['db_table'] == 'member_packages') {
+                    if ($row['field'] == 'image') {
+                        $row['update'] = ' <div class="newPackageImage">
+                                New Image
+                                <img alt="'.$row['update'].'"
+                                class="newPackageImage"
+                                src="'.MEMBER_PHOTOS.$row['update'].'">
+                                </div>';
+                    }
+                }
+                $defaults["{$row['id']}_{$row['field']}_update"] = $row['update'];
+            }
+
+            return $defaults;
+        } catch (PDOException $e) {
+            return Toolkit_Common::handleError($e);
+        }
+    }
+
+    //  }}}
+    //  {{{ group()
+
+    /**
+     * Gets the group [table name] the field is supposed to be a part of
+     *
+     * @param string $field the name of the field in the db.
+     *
+     * @return string
+     * @access public
+     */
+    public function group($field)
+    {
+        //  The field is going to come in as [id]_[field name]
+        //  so we need to extract the field name from the id.
+        list($id, $field) = explode('_', $field, 2);
+        try {
+            $sql = "
+                SELECT db_table
+                  FROM {$this->tableName}
+                 WHERE id = :id";
+
+            $stmt = $this->dbh->prepare($sql);
+            $stmt->bindParam(':id', $id, PDO::PARAM_INT);
+            $stmt->execute();
+            $row = $stmt->fetch(PDO::FETCH_ASSOC);
+            return $row['db_table'];
+        } catch (PDOException $e) {
+            return Toolkit_Common::handleError($e);
+        }
+    }
+
+    //  }}}
+
+    //  {{{ processData()
+
+    /**
+     * Handles processing the submitted forms data.
+     *
+     * Sets up the table meta data so we can insert into the member table.
+     * removes any unwanted fields from the values array.  these fields
+     * will be denoted by the '_rmv' at the end of thier name in the
+     * configureElements function
+     *
+     * <example>
+     * ... 'name' => 'remove_rmv', ...
+     * </example>
+     *
+     * This function also verifies that the lat/lon values were properly filled
+     * out, if they were left blank then the values will either be looked up
+     * via google maps or through our own zip DB. These values will then be
+     * inserted into the values array AND also injected into the form field
+     * values so they will display when the form comes back around.
+     *
+     * The form then decides if it is handeling a new member or not and calls
+     * the appropriate function to insert or update data.
+     *
+     * @param array $values The array of all submitted form values.
+     *
+     * @return boolean Whether the insert/update succeeded.
+     * @access protected
+     * @see    Toolkit_Members_EditMemberInfo::configureElements()
+     */
+    protected function processData($values)
+    {
+        $this->tableMetaData = Toolkit_Common::getTableMetaData(
+            $this->dbh,
+            $this->tableName
+        );
+
+        foreach ($values as $k => $v) {
+            switch ($k) {
+            default :
+                if (preg_match('/^.+_rmv$/', $k)) {
+                    unset($values[$k]);
+                }
+                break;
+            }
+        }
+
+        $this->updateData($values);
+        $listPage = MEDIA_BASE_URL . "admin/members.php?rt=Members&ac=pendingUpdates";
+        header("Location: $listPage");
+    }
+
+    //  }}}
+
+    //  {{{ setMember()
+
+    /**
+     * Sets the member id to use for updating
+     *
+     * @param integer $mid Target Member Id
+     *
+     * @return object PEAR Error on invalid member id
+     * @access public
+     */
+    public function setMember($mid)
+    {
+        if (!is_numeric($mid)) {
+            return PEAR::raiseError('Invalid member id');
+        } elseif (!ctype_digit($mid) && !is_int($mid)) {
+            return PEAR::raiseError('Invalid member id string');
+        }
+        $this->_mid = $mid;
+    }
+
+    //  }}}
+    //  {{{ setupRenderers()
+
+    /**
+     * Handles compiling and rendering the form
+     *
+     * @return void
+     * @access protected
+     */
+    protected function setupRenderers()
+    {
+        $renderer = new HTML_QuickForm_Renderer_Object(true);
+
+        $this->accept($renderer);
+
+        $this->template = new HTML_Template_Flexy($this->flexyOptions);
+
+        //  Make the view a copy of the $this object
+        //  That way we have access to call functions in
+        //  this class from within the template.
+        $this->view       = $this;
+        $this->view->form = $renderer->toObject();
+        $this->template->compile($this->formTemplate);
+    }
+
+    //  }}}
+
+    //  {{{ toHtml()
+
+    /**
+     * Renders the form
+     *
+     * sets the page the form should be redirected to instead of coming back
+     * around to itself.
+     *
+     * @return string The rendered form
+     * @access public
+     */
+    public function toHtml()
+    {
+        //  We need to validate (and freeze if needed)
+        //  before we render the form. That way the
+        //  template knows about any errors on the form.
+        $this->validated = $this->validate();
+
+        if ($this->validated) {
+            $processed = $this->process(
+                array(&$this, 'processData'),
+                $this->mergeFiles
+            );
+        }
+
+        //  ProcessData handles settingup the lat/lon coordinates if they were
+        //  not entered into the form.  these values ar calculated and then
+        //  inserted into the forms element values. So we need to process the
+        //  data first and then render the form.
+        $this->setupRenderers();
+
+        return $this->template->bufferedOutputObject($this->view);
+    }
+
+    //  }}}
+
+    //  {{{ updateAmenities()
+
+    /**
+     * Handle updating any approvals or rejections on the member amenities
+     *
+     * @param array $amenities The amenity update statuses submitted from the form
+     *
+     * @return bool True on success, false on error.
+     * @access protected
+     */
+    protected function updateAmenities($amenities)
+    {
+        if (empty($amenities)) {
+            return true;
+        }
+        try {
+            $this->dbh->beginTransaction();
+            //  Stmt to find the newest request in the updates table.
+            $sql = "
+                SELECT *
+                  FROM {$this->tableName}
+                 WHERE id = :id";
+
+            $fetchStmt = $this->dbh->prepare($sql);
+            //  Stmt to add amenity to member.
+            $sql = "
+                INSERT INTO member_amenity(member_id, amenity_id)
+                VALUES (:member_id, :aid)";
+
+            $insStmt = $this->dbh->prepare($sql);
+            $insStmt->bindParam(':member_id', $this->_mid, PDO::PARAM_INT);
+            //  Stmt to remove amenity from member.
+            $sql = "
+                DELETE FROM member_amenity
+                 WHERE amenity_id = :aid
+                   AND member_id = :member_id";
+
+            $delStmt = $this->dbh->prepare($sql);
+            //  Remove from updates table.
+            $sql = "
+                DELETE FROM {$this->tableName}
+                 WHERE member_id          = :member_id
+                   AND db_table     = 'member_amenity'
+                   AND field        = :aid";
+
+            $remStmt = $this->dbh->prepare($sql);
+            $remStmt->bindParam(':member_id', $this->_mid, PDO::PARAM_INT);
+            //  Loop through all the amenities and see if the update was
+            //  accepted or not.  If it was accepted, get the update
+            //  row from the updates table and find out if the update
+            //  was to add [update = 1] the card or remove [update = 0].
+            //  Perform the update, and then remove the requests from the
+            //  updates table.
+            foreach ($amenities as $aid => $accepted) {
+                list($id, $aid) = explode('_', $aid, 2);
+                if ($accepted == 'yes') {
+                    $fetchStmt->bindParam(':id', $id, PDO::PARAM_INT);
+                    $fetchStmt->execute();
+                    $row = $fetchStmt->fetch(PDO::FETCH_ASSOC);
+
+                    if ($row['update']) {
+                        //  Remove the the cards from from the db to make
+                        //  sure we don't try to insert the same card to
+                        //  a member twice and conflict w/ the unique
+                        //  constraint on member_id and ccard_id
+                        $delStmt->bindParam(':aid', $aid, PDO::PARAM_STR);
+                        $delStmt->bindParam(':member_id', $this->_mid, PDO::PARAM_INT);
+                        $delStmt->execute();
+                        //  Insert the card as a row into the db.
+                        $insStmt->bindParam(':aid', $aid, PDO::PARAM_STR);
+                        $insStmt->execute();
+                    } else {
+                        //  Remove the the cards from from the db.
+                        $delStmt->bindParam(':aid', $aid, PDO::PARAM_STR);
+                        $delStmt->bindParam(':member_id', $this->_mid, PDO::PARAM_INT);
+                        $delStmt->execute();
+                    }
+
+                    //  Remove field from the member_updates table.
+                    $remStmt->bindParam(':aid', $aid, PDO::PARAM_STR);
+                    $remStmt->execute();
+                } else {
+                    //  Remove field from the member_updates table.
+                    $remStmt->bindParam(':aid', $aid, PDO::PARAM_STR);
+                    $remStmt->execute();
+                }
+            }
+            return $this->dbh->commit();
+        } catch (PDOException $e) {
+            $this->dbh->rollBack();
+            return Toolkit_Common::handleError($e);
+        }
+    }
+
+    //  }}}
+    //  {{{ updateCCards()
+
+    /**
+     * Handle updating any approvals or rejections on the member credit cards
+     *
+     * @param array $cards The cards update status submitted from the form
+     *
+     * @return bool True on success, false on error.
+     * @access protected
+     */
+    protected function updateCCards($cards)
+    {
+        if (empty($cards)) {
+            return true;
+        }
+        foreach ($cards as $k => $v) {
+            $orig         = $k;
+            list($id, $k) = explode('_', $k, 2);
+            $cards[$k]    = $v;
+            unset($cards[$orig]);
+        }
+        try {
+            $this->dbh->beginTransaction();
+            //  Stmt to find the newest request in the updates table.
+            $sql = "
+                SELECT *
+                  FROM {$this->tableName}
+                 WHERE id in (
+                        SELECT max(id)
+                          FROM {$this->tableName}
+                         WHERE member_id     = :member_id
+                   AND field   = :field)";
+
+            $fetchStmt = $this->dbh->prepare($sql);
+            $fetchStmt->bindParam(':member_id', $this->_mid, PDO::PARAM_INT);
+            //  Stmt to add card to member.
+            $sql = "
+                INSERT INTO member_ccard_type (member_id, ccard_type_id)
+                SELECT :member_id, ccard_type_id
+                  FROM ccard_type
+                 WHERE ccard_type_name = :name";
+
+            $insStmt = $this->dbh->prepare($sql);
+            $insStmt->bindParam(':member_id', $this->_mid, PDO::PARAM_INT);
+            //  Stmt to remove card from member.
+            $sql = "
+                DELETE FROM member_ccard_type
+                 WHERE member_id       = :member_id
+                   AND ccard_type_id in (
+                        SELECT ccard_type_id
+                          FROM ccard_type
+                 WHERE ccard_type_name = :name)";
+
+            $delStmt = $this->dbh->prepare($sql);
+            $delStmt->bindParam(':member_id', $this->_mid, PDO::PARAM_INT);
+            //  Remove from updates table.
+            $sql = "
+                DELETE FROM {$this->tableName}
+                 WHERE member_id = :member_id
+                   AND label = :name";
+
+            $remStmt = $this->dbh->prepare($sql);
+            $remStmt->bindParam(':member_id', $this->_mid, PDO::PARAM_INT);
+            //  Loop through all the cards and see if the update was
+            //  accepted or not.  If it was accepted, get the update
+            //  row from the updates table and find out if the update
+            //  was to add [update = 1] the card or remove [update = 0].
+            //  Perform the update, and then remove the requests from the
+            //  updates table.
+            foreach ($cards as $card => $accepted) {
+                if ($accepted == 'yes') {
+                    $field = "creditCards[$card]";
+                    $fetchStmt->bindParam(':field', $field, PDO::PARAM_STR);
+                    $fetchStmt->execute();
+                    $row = $fetchStmt->fetch(PDO::FETCH_ASSOC);
+
+                    if ($row['update']) {
+                        //  Remove the the cards from from the db to make
+                        //  sure we don't try to insert the same card to
+                        //  a member twice and conflict w/ the unique
+                        //  constraint on member_id and ccard_id
+                        $delStmt->bindParam(':name', $card, PDO::PARAM_STR);
+                        $delStmt->execute();
+                        //  Insert the card as a row into the db.
+                        $insStmt->bindParam(':name', $card, PDO::PARAM_STR);
+                        $insStmt->execute();
+                    } else {
+                        //  Remove the the cards from from the db.
+                        $delStmt->bindParam(':name', $card, PDO::PARAM_STR);
+                        $delStmt->execute();
+                    }
+
+                    //  Remove field from the member_updates table.
+                    $remStmt->bindParam(':name', $card, PDO::PARAM_STR);
+                    $remStmt->execute();
+                } else {
+                    //  Remove field from the member_updates table.
+                    $remStmt->bindParam(':name', $card, PDO::PARAM_STR);
+                    $remStmt->execute();
+                }
+            }
+            return $this->dbh->commit();
+        } catch (PDOException $e) {
+            $this->dbh->rollBack();
+            return Toolkit_Common::handleError($e);
+        }
+    }
+
+    //  }}}
+    //  {{{ updateData()
+
+    /**
+     * Handles updating all approvals and rejections of pending updates
+     *
+     * @param array $values All elements submitted from the form
+     *
+     * @return boolean True on success, false on error
+     * @access protected
+     */
+    protected function updateData($values)
+    {
+        //  Credit Cards are a special case and handled seperately
+        $ccardUpdates   = $values['member_ccard_type'];
+        $amenityUpdates = $values['member_amenity'];
+        $photoUpdates   = $values['member_photos'];
+        $fileUpdates    = $values['member_files'];
+        $packageUpdates = $values['member_packages'];
+        unset ($values['member_ccard_type'],
+            $values['member_amenity'],
+            $values['member_files'],
+            $values['member_photos'],
+            $values['member_packages']
+        );
+
+        foreach ($values as $k => $v) {
+            if (!is_array($v)) {
+                $updates[$k] = $v;
+                unset($values[$k]);
+            }
+        }
+        //  The values array should now hold all the submitted updates
+        //  in a grouped format and tell you if the field was approved
+        //  to be updated or not. Fields from each table will be grouped
+        //  together and any text field that "could" be changed by the
+        //  admin to fix typos or errors will be in thier own array.
+        //  These updates will have the same associative array key name
+        //  as thier corresponding field with '_update' appended.
+        //  ie. street => street_update,  url => url_update, etc..
+        //  (values example:)
+        //  Array
+        //  (
+        //      [member] => Array
+        //          (
+        //              [street] => yes
+        //              [url] => no
+        //              [description] => yes
+        //          )
+        //
+        //      [member_accommodations] => Array
+        //          (
+        //              [num_rooms] => yes
+        //              [year_round] => no
+        //          )
+        //
+        //      [member_restaurants] => Array
+        //          (
+        //              [breakfast] => yes
+        //          )
+        //  )
+        //  (updates example:)
+        //  Array
+        //  (
+        //      [street_update] => 139 Dove Cottage Ln
+        //      [url_update] => www.google.com
+        //      [description_update] => test update 8
+        //  )
+
+        try {
+            //  Remove from updates table.
+            $sql = "
+                DELETE FROM {$this->tableName}
+                 WHERE member_id = :member_id
+                   AND field = :field
+                   AND db_table = :db_table";
+
+            $remStmt = $this->dbh->prepare($sql);
+            $remStmt->bindParam(':member_id', $this->_mid, PDO::PARAM_INT);
+            //  For each of the "rejected" updates, loop through
+            //  and remove them from the arrays as well as from the
+            //  updates DB table.
+
+            //  Tables
+            foreach ($values as $table => &$fields) {
+                //  Fields
+                //
+                //  The field key names will come in as
+                //  [id_fieldName] => yes
+                //  [id_fieldName] => no
+                //  The id # is the primary key row id number of the update
+                //  in the updates table.
+                //  The fieldName is the name of column that was updated.
+                foreach ($fields as $k => &$v) {
+                    if ($v == 'no') {
+                        list($id, $field) = explode('_', $k, 2);
+                        $remStmt->bindParam(':field', $field, PDO::PARAM_STR);
+                        $remStmt->bindParam(':db_table', $table, PDO::PARAM_STR);
+                        $remStmt->execute();
+                        unset($fields["{$id}_{$field}"],
+                              $updates["{$id}_{$field}_update"]);
+                    }
+                }
+                //  Do some cleanup while we're right here.
+                //  If all the updates were rejected for a single group,
+                //  then unset this array, that way we won't waste our time
+                //  trying to run updates against it later.
+                if (empty($values[$table])) {
+                    unset($values[$table]);
+                }
+            }
+
+            foreach ($values as $k => &$v) {
+                if (!is_array($updates)) {
+                    $updates = array();
+                }
+                $this->commitUpdates($k, array_keys($v), $updates);
+            }
+            $this->updatePackages($packageUpdates);
+            $this->updateCCards($ccardUpdates);
+            $this->updateAmenities($amenityUpdates);
+            $this->updatePhotos($photoUpdates);
+            $this->updateFiles($fileUpdates);
+            // remove cache file for profile page
+            $cache = new Cache_Lite(Toolkit_Members::getCacheOptions());
+            $cache->remove(
+                "Member-{$this->_mid}",
+                'Profile'
+            );
+            // for the stream send
+            if (   defined('MEMBER_STREAMSEND_API')
+                && MEMBER_STREAMSEND_API) {
+                // send member to streamsend
+                $memberStreamSend = new Toolkit_Members_StreamSend($this->dbh);
+                $memberStreamSend->sendMemberById($this->_mid);
+            }
+        } catch (PDOException $e) {
+            $this->dbh->rollBack();
+            return Toolkit_Common::handleError($e);
+        }
+        return true;
+    }
+
+    //  }}}
+    //  {{{ updateFiles()
+
+    /**
+     * Handle updating any approvals or rejections on the member files
+     *
+     * @param array $files an array of file updates and their update status
+     *
+     * @return bool True on success, false on error.
+     * @access protected
+     */
+    protected function updateFiles($files)
+    {
+        if (empty($files)) {
+            return true;
+        }
+        try {
+            $this->dbh->beginTransaction();
+            //  Stmt to find the newest request in the updates table for
+            //  a specific field.
+            $sql = "
+                SELECT *
+                  FROM {$this->tableName}
+                 WHERE id = :id";
+
+            $fetchStmt = $this->dbh->prepare($sql);
+            //  Stmt to update the file
+            $updateSql = "
+                UPDATE member_files
+                   SET %s = :update
+                 WHERE id      = :id";
+            //  Stmt to remove file from member.
+            $sql = "
+                DELETE FROM member_files
+                 WHERE id   = :id";
+
+            $delStmt = $this->dbh->prepare($sql);
+            //  Stmt to remove any file name updates for a file that
+            //  are still in the updates table.
+            $sql = "
+                DELETE FROM {$this->tableName}
+                 WHERE field        = 'file_name'
+                   AND foreign_key  = :id";
+
+            $delFileNameStmt = $this->dbh->prepare($sql);
+            //  Remove from updates table.
+            $sql = "
+                DELETE FROM {$this->tableName}
+                 WHERE field        = :field
+                   AND foreign_key  = :id";
+
+            $remStmt = $this->dbh->prepare($sql);
+            //  Loop through all the files and see if the update was
+            //  accepted or not.  If it was accepted, get the update
+            //  row from the updates table and find out if the update
+            //  was to add [update = 1] the card or remove [update = 0].
+            //  Perform the update, and then remove the requests from the
+            //  updates table.
+            foreach ($files as $field => $accepted) {
+                list($id, $field) = explode('_', $field, 2);
+                $fetchStmt->bindParam(':id', $id, PDO::PARAM_INT);
+                $fetchStmt->execute();
+                $row = $fetchStmt->fetch(PDO::FETCH_ASSOC);
+
+                if ($accepted == 'yes') {
+                    $updtStmt = $this->dbh->prepare(sprintf($updateSql, $field));
+                    //  we are dealing w/ the file_name
+                    //  update the files name.
+                    $updtStmt->bindParam(':update', $row['update'], PDO::PARAM_STR);
+                    $updtStmt->bindParam(':id', $row['foreign_key'], PDO::PARAM_INT);
+                    $updtStmt->execute();
+
+                    //  Remove field from the member_updates table.
+                    $remStmt->bindParam(':field', $field, PDO::PARAM_STR);
+                    $remStmt->bindParam(':id', $row['foreign_key'], PDO::PARAM_INT);
+                    $remStmt->execute();
+                } else {
+                    //  Remove field from the member_updates table.
+                    $remStmt->bindParam(':field', $field, PDO::PARAM_STR);
+                    $remStmt->bindParam(':id', $row['foreign_key'], PDO::PARAM_INT);
+                    $remStmt->execute();
+
+                    //  a rejected file (we know its a file because it
+                    //  has a data_type of boolean) means we have to not only
+                    //  remove the row from the updates table, but also
+                    //  from the member_files table.
+                    if ($row['data_type'] == 'boolean') {
+                        $delFileNameStmt->bindParam(
+                            ':id',
+                            $row['foreign_key'],
+                            PDO::PARAM_STR
+                        );
+                        $delFileNameStmt->execute();
+
+                        //  Remove the the cards from from the db.
+                        $delStmt->bindParam(
+                            ':id',
+                            $row['foreign_key'],
+                            PDO::PARAM_INT
+                        );
+                        $delStmt->execute();
+                    }
+                }
+            }
+            return $this->dbh->commit();
+        } catch (PDOException $e) {
+            $this->dbh->rollBack();
+            return Toolkit_Common::handleError($e);
+        }
+    }
+
+    //  }}}
+    //  {{{ updatePackages()
+
+    /**
+     * Handle updating any approvals or rejections on the member packages
+     *
+     * @param array $packages an array of package updates and their update status
+     *
+     * @return bool True on success, false on error.
+     * @access protected
+     */
+    protected function updatePackages($packages)
+    {
+        if (empty($packages)) {
+            return true;
+        }
+        try {
+            $this->dbh->beginTransaction();
+            //  Stmt to find the newest request in the updates table for
+            //  a specific field.
+            $sql = "
+                SELECT *
+                  FROM {$this->tableName}
+                 WHERE id = :id";
+
+            $fetchStmt = $this->dbh->prepare($sql);
+            //  Stmt to update the package
+            $updateSql = "
+                UPDATE member_packages
+                   SET %s = :update
+                 WHERE id      = :id";
+            //  Stmt to remove any updates for a package that
+            //  are still in the updates table.
+            //  Remove from updates table.
+            $sql = "
+                DELETE FROM {$this->tableName}
+                 WHERE foreign_key  = :id
+                   AND field        = :field
+                   AND db_table = 'member_packages'";
+
+            $remStmt = $this->dbh->prepare($sql);
+            //  Loop through all the package field and see if the update was
+            //  accepted or not.  If it was accepted, get the update
+            //  row from the updates table and find out if the update
+            //  was to add [update = 1] the card or remove [update = 0].
+            //  Perform the update, and then remove the requests from the
+            //  updates table.
+            $values = $this->getSubmitValues();
+            $checkToResetPending = array();
+            foreach ($packages as $field => $accepted) {
+                list($id, $field) = explode('_', $field, 2);
+                $fetchStmt->bindParam(':id', $id, PDO::PARAM_INT);
+                $fetchStmt->execute();
+                $row = $fetchStmt->fetch(PDO::FETCH_ASSOC);
+
+                if ($accepted == 'yes') {
+                    $sql = "
+                        SELECT foreign_key
+                        FROM member_updates
+                        WHERE id = :id";
+                    $stmt = $this->dbh->prepare($sql);
+                    $stmt->bindParam(':id', $id, PDO::PARAM_INT);
+                    $stmt->execute();
+                    $row = $stmt->fetch(PDO::FETCH_ASSOC);
+                    $checkToResetPending[] = $row['foreign_key'];
+                    $updtStmt = $this->dbh->prepare(sprintf($updateSql, $field));
+                    $update   = $this->getSubmitValue("{$id}_{$field}_update");
+                    if (empty($update)) {
+                        $update = $row['update'];
+                    }
+                    $updtStmt->bindParam(':update', $update, PDO::PARAM_STR);
+                    $updtStmt->bindParam(':id', $row['foreign_key'], PDO::PARAM_INT);
+                    $updtStmt->execute();
+
+                    //  Remove field from the member_updates table.
+                    $remStmt->bindParam(':field', $field, PDO::PARAM_STR);
+                    $remStmt->bindParam(':id', $row['foreign_key'], PDO::PARAM_INT);
+                    $remStmt->execute();
+                } else {
+                    //  Remove field from the member_updates table.
+                    $remStmt->bindParam(':field', $field, PDO::PARAM_STR);
+                    $remStmt->bindParam(':id', $row['foreign_key'], PDO::PARAM_INT);
+                    $remStmt->execute();
+                }
+            }
+
+            $checkToResetPending = array_unique($checkToResetPending);
+            $sql = "
+            SELECT count(*) as total
+              FROM member_updates
+             WHERE db_table = 'member_packages'
+               AND member_id = :mid
+               AND foreign_key = :fk";
+            $selectStmt = $this->dbh->prepare($sql);
+            $selectStmt->bindParam(':mid', $_GET['id'], PDO::PARAM_INT);
+            if (is_array($checkToResetPending) && !empty($checkToResetPending)) {
+                foreach ($checkToResetPending as $i) {
+                    $selectStmt->bindParam(':fk', $i, PDO::PARAM_INT);
+                    $selectStmt->execute();
+
+                    $row = $selectStmt->fetch(PDO::FETCH_ASSOC);
+                    if ($row['total'] == 0) {
+                        $sql = "
+                            UPDATE member_packages
+                            SET pending = false
+                            WHERE member_id = :mid
+                            AND id = :id";
+
+                        $updateStmt = $this->dbh->prepare($sql);
+                        $updateStmt->bindParam(':mid', $_GET['id'], PDO::PARAM_INT);
+                        $updateStmt->bindParam(':id', $i, PDO::PARAM_INT);
+                        $updateStmt->execute();
+                    }
+                }
+            }
+            return $this->dbh->commit();
+        } catch (PDOException $e) {
+            $this->dbh->rollBack();
+            return Toolkit_Common::handleError($e);
+        }
+    }
+
+    //  }}}
+    //  {{{ updatePhotos()
+
+    /**
+     * Handle updating any approvals or rejections on the member photos
+     *
+     * @param array $photos an array of photo updates and thier update status
+     *
+     * @return bool True on success, false on error.
+     * @access protected
+     */
+    protected function updatePhotos($photos)
+    {
+        if (empty($photos)) {
+            return true;
+        }
+        try {
+            $this->dbh->beginTransaction();
+            //  Stmt to find the newest request in the updates table for
+            //  a specific field.
+            $sql = "
+                SELECT *
+                  FROM {$this->tableName}
+                 WHERE id = :id";
+
+            $fetchStmt = $this->dbh->prepare($sql);
+            //  Stmt to update the photo
+            $updateSql = "
+                UPDATE member_photos
+                   SET %s = :update
+                 WHERE id      = :id";
+            //  Stmt to remove photo from member.
+            $sql = "
+                DELETE FROM member_photos
+                 WHERE id = :id";
+
+            $delStmt = $this->dbh->prepare($sql);
+            //  Stmt to remove any caption updates for a photo that
+            //  are still in the updates table.
+            $sql = "
+                DELETE FROM {$this->tableName}
+                 WHERE field = 'caption'
+                   AND foreign_key  = :id";
+
+            $delPhotoCaptionStmt = $this->dbh->prepare($sql);
+            //  Remove from updates table.
+            $sql = "
+                DELETE FROM {$this->tableName}
+                 WHERE foreign_key  = :id
+                   AND field        = :field";
+
+            $remStmt = $this->dbh->prepare($sql);
+            //  Loop through all the photos and see if the update was
+            //  accepted or not.  If it was accepted, get the update
+            //  row from the updates table and find out if the update
+            //  was to add [update = 1] the card or remove [update = 0].
+            //  Perform the update, and then remove the requests from the
+            //  updates table.
+            foreach ($photos as $field => $accepted) {
+                list($id, $field) = explode('_', $field, 2);
+                $fetchStmt->bindParam(':id', $id, PDO::PARAM_INT);
+                $fetchStmt->execute();
+                $row = $fetchStmt->fetch(PDO::FETCH_ASSOC);
+
+                if ($accepted == 'yes') {
+                    $updtStmt = $this->dbh->prepare(sprintf($updateSql, $field));
+                    //  we are dealing w/ the caption
+                    //  update the photos caption.
+                    $updtStmt->bindParam(':update', $row['update'], PDO::PARAM_STR);
+                    $updtStmt->bindParam(':id', $row['foreign_key'], PDO::PARAM_INT);
+                    $updtStmt->execute();
+
+                    //  Remove field from the member_updates table.
+                    $remStmt->bindParam(':field', $field, PDO::PARAM_STR);
+                    $remStmt->bindParam(':id', $row['foreign_key'], PDO::PARAM_INT);
+                    $remStmt->execute();
+                } else {
+                    //  Remove field from the member_updates table.
+                    $remStmt->bindParam(':field', $field, PDO::PARAM_STR);
+                    $remStmt->bindParam(':id', $row['foreign_key'], PDO::PARAM_INT);
+                    $remStmt->execute();
+
+                    //  a rejected photo (we know its a photo because it
+                    //  has a data_type of boolean) means we have to not only
+                    //  remove the row from the updates table, but also
+                    //  from the member_photos table.
+                    if ($row['data_type'] == 'boolean') {
+                        $delPhotoCaptionStmt->bindParam(
+                            ':id',
+                            $row['foreign_key'],
+                            PDO::PARAM_STR
+                        );
+                        $delPhotoCaptionStmt->execute();
+
+                        //  Remove the the cards from from the db.
+                        $delStmt->bindParam(
+                            ':id',
+                            $row['foreign_key'],
+                            PDO::PARAM_INT
+                        );
+                        $delStmt->execute();
+                    }
+                }
+            }
+            return $this->dbh->commit();
+        } catch (PDOException $e) {
+            $this->dbh->rollBack();
+            return Toolkit_Common::handleError($e);
+        }
+    }
+
+    //  }}}
+
+    //  {{{ validated()
+
+    /**
+     * Checks if the form is validated
+     *
+     * If the form is validated display the success msg.
+     * If the form is submitted but not validated, display
+     * the error msg.  Other wise display nothing.
+     *
+     * This function is called from within the template.
+     *
+     * @return string The success or error msg for the user.
+     * @access protected
+     */
+    public function validated()
+    {
+        if ($this->validated) {
+            return $this->successMsg;
+        } elseif ($this->isSubmitted()) {
+            return $this->errorMsg;
+        }
+    }
+
+    //  }}}
+}
+?>
diff --git a/Toolkit/Members/Admin/Auxiliary.php b/Toolkit/Members/Admin/Auxiliary.php
new file mode 100644 (file)
index 0000000..de12384
--- /dev/null
@@ -0,0 +1,512 @@
+<?php
+//     vim:set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker syntax=php:
+
+/**
+ * File Doc Comment
+ *
+ * PHP version 5
+ *
+ * @category MembersDB
+ * @package  Toolkit_Members
+ * @author   Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @license     http://www.gaslightmedia.com Gaslightmedia
+ * @version  CVS: $Id: Auxiliary.php,v 1.13 2010/01/26 19:39:07 jamie Exp $
+ * @link        http://demo.gaslightmedia.com
+ */
+
+/**
+ * Base class used for building forms to accompany the members database
+ *
+ * This class handles all common functions that are to be used in
+ * the auxiliary forms used to assist in the members DB.
+ * (categories, amenities, regions, etc...).
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2008 Gaslight Media
+ * @license      http://www.gaslightmedia.com Gaslightmedia
+ * @link         http://demo.gaslightmedia.com
+ * @see       Toolkit_Members, member_admin
+ */
+abstract class Toolkit_Members_Admin_Auxiliary extends Toolkit_FormBuilder
+{
+       //      {{{ properties
+
+       /**
+        * Primary table used for database calls
+        *
+        * @var string
+        * @access public
+        */
+       public $tableName;
+
+       /**
+        * Array of primary table meta data
+        *
+        * This array will hold the column data types for the fields
+        * that will be manipulated in the database.
+        *
+        * @var array
+        * @access public
+        * @see Toolkit_Common::getTableMetaData()
+        */
+       public $tableMetaData;
+
+       /**
+        * The name of the template used for the form
+        *
+        * @var string
+        * @access protected
+        */
+       protected $formTemplate;
+
+       /**
+        * The object to use inside the form
+        *
+        * This object which is to be populated by the $this object
+        * is used inside the templates and allows access back into the
+        * calling class to call publicly available functions
+        *
+        * @var string
+        * @access protected
+        */
+       protected $view;
+
+       //      }}}
+       //      {{{ __construct()
+
+       /**
+        * Class constructor
+        *
+     * @param string $formName    Form's name.
+     * @param string $method      (optional)Form's method defaults to 'POST'
+     * @param string $action      (optional)Form's action
+     * @param string $target      (optional)Form's target defaults to '_self'
+     * @param mixed  $attributes  (optional)Extra attributes for <form> tag
+     * @param bool   $trackSubmit (optional)Whether to track if the form was
+        *                                                        submitted by adding a special hidden field
+        *
+        * @access public
+        */
+       public function __construct(
+        $formName,
+        $method = 'post',
+        $action = '',
+        $target = '',
+        $attributes = null,
+        $trackSubmit = false
+    ) {
+               parent::__construct(
+            $formName,
+            $method,
+            $action,
+            $target,
+            $attributes,
+            $trackSubmit
+        );
+               $this->registeredRules = array();
+
+               $this->configureElements();
+               $this->configureRules();
+               $this->configureFilters();
+               $this->configureDefaults();
+
+               $this->flexyOptions = Toolkit_Members::getFlexyOptions();
+       }
+
+       //      }}}
+
+       //      {{{ baseUrl()
+
+       /**
+        * Returns the MEDIA_BASE_URL
+        *
+        * This function is used inside the Flexy Templated forms
+        *
+        * @return string MEDIA_BASE_URL
+        * @access public
+        */
+       public function baseUrl()
+       {
+               return MEDIA_BASE_URL;
+       }
+
+       //      }}}
+
+       //      {{{ configureDefaults()
+
+       /**
+        * Configure the default values for the form
+        *
+        * Since each form is different, you will need to redefine this
+        * method inside of each subclass and create your own rules
+        * based on your forms elements.
+        *
+     * @return void
+        * @access public
+        */
+       abstract protected function configureDefaults();
+
+       //      }}}
+       //      {{{ configureElements()
+
+       /**
+        * Configures all form elements
+        *
+        * If you are subclassing this base class out, you will need to define
+        * all your forms elements in your new subclass.
+        *
+     * @return void
+        * @access protected
+        */
+       abstract protected function configureElements();
+
+       //      }}}
+       //      {{{ configureFilters()
+
+       /**
+        * Defines all filters used on form elements when submitted
+        *
+        * Most times this function won't need to be overridden.
+        *
+     * @return void
+        * @access public
+        */
+       protected function configureFilters()
+       {
+               $filters[] = array('element' => '__ALL__', 'filter' => 'trim');
+
+               $this->setupFilters($filters);
+       }
+
+       //      }}}
+       //      {{{ configureRules()
+
+       /**
+        * Sets up the required / not-required rules for forms
+        *
+        * At the bare minimum, the required / not-required rules for form
+        * needs to be defined.  Since these rules are most easily defined
+        * while creating the element definitions themselves the base function
+        * is to call the setupRules function that will instantiate these
+        * rules in the quickform class.
+        *
+     * @return void
+        * @access public
+        */
+       protected function configureRules()
+       {
+               $this->setupRules();
+       }
+
+       //      }}}
+
+       //      {{{ getSetParameters()
+
+       /**
+     * get a string of sql query parameters
+     *
+     * @param array $values parameters to include
+     *
+     * @return string sql query parameters
+        * @access protected
+        */
+       protected function getSetParameters($values)
+       {
+               $params = array_keys($values);
+               $length = count($params);
+               for ($i = 0; $i < $length; ++$i) {
+                       $bindParams .= "{$params[$i]} = :{$params[$i]}";
+            if ($i < ($length - 1)) {
+                $bindParams .= ', ';
+            }
+               }
+               return $bindParams;
+       }
+
+       //      }}}
+       //      {{{ getDataType()
+
+       /**
+     * gets the datatype of the element
+     *
+     * @param string $k name of element
+     *
+     * @return string datatype
+        * @access protected
+        */
+       protected function getDataType($k)
+       {
+               $metaData = $this->tableMetaData[$k];
+               if ($metaData == 'integer') {
+                       return PDO::PARAM_INT;
+               } else if ($metaData == 'boolean') {
+                       return PDO::PARAM_BOOL;
+               } elseif ($metaData == 'double precision') {
+                       return null;
+               } else {
+                       return PDO::PARAM_STR;
+               }
+       }
+
+       //      }}}
+
+       //      {{{ insertData()
+
+       /**
+     * insert the data in the DB
+     *
+     * @param array  $values Submitted form values
+     * @param string $sql    Query to execute to make update
+     *
+     * @return void
+        * @access protected
+        */
+       protected function insertData($values, $sql)
+       {
+               try {
+                       $stmt = $this->dbh->prepare($sql);
+                       foreach ($values as $k => $v) {
+                               $dataType = $this->getDataType($k);
+                               $stmt->bindParam(":$k", $values[$k], $dataType);
+                       }
+                       return $stmt->execute();
+               } catch (PDOException $e) {
+                       return Toolkit_Common::handleError($e);
+               }
+       }
+
+       //      }}}
+       //      {{{ isEdit()
+
+       /**
+        * Determines if we are editing an object or not
+        *
+        * If we are ever editing an object, member, category, city, etc...
+        * There will be the objects DB ID passed along in the url under the
+        * id key.  If we can find this then we know we're editing, if not
+        * then we are adding.
+        *
+     * @return void
+        * @access public
+        */
+       public function isEdit()
+       {
+               return isset($_GET['id']);
+       }
+
+       //      }}}
+
+       //      {{{ processData()
+
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @param array $values Parameter description (if any) ...
+     *
+     * @return array Return description (if any) ...
+     * @access protected
+     */
+       protected function processData($values)
+       {
+               $this->tableMetaData = Toolkit_Common::getTableMetaData(
+            $this->dbh,
+            $this->tableName
+        );
+
+               foreach ($values as $k => $v) {
+                       switch ($k) {
+                       case 'uploaded_file_rmv' :
+                               //      Don't remove this from the values array.
+                               //      we will handle it later.
+                               //      we need to have access to this value
+                               //      in the insert/update functions.
+                               break;
+
+                       case 'MAX_FILE_SIZE' :
+                               unset($values[$k]);
+                               break;
+
+                       default :
+                               if (preg_match('/^.+_rmv$/', $k)) {
+                                       unset($values[$k]);
+                               }
+                               break;
+                       }
+               }
+
+               if (isset($_GET['id'])) {
+                       return $this->updateData($values);
+               } else {
+                       return $this->insertData($values);
+               }
+       }
+
+       //      }}}
+
+       //      {{{ setupElements()
+
+       /**
+        * Add the form elements defined in your class to a QuickForm
+        *
+        * Handles adding singleton elements as well as grouped elements.
+        * If added elements fail to correctly add to the form, the script
+        * should die gracefully, telling the user there was a problem.
+        *
+     * @param array $elements form element definitions
+     *
+        * @throws HTML_QuickForm_Error error raised from QuickForm class.
+     * @return void
+        * @access protected
+        */
+       protected function setupElements($elements)
+       {
+               $this->formElements = $elements;
+               foreach ($elements as $e) {
+                       if ($e['type'] != 'group') {
+                               try {
+                                       $source =& $this->addElement($e['type'], $e['name'], $e['display'], $e['opts'], $e['att'], $e['val']);
+                               } catch (HTML_QuickForm_Error $e) {
+                                       Toolkit_Common::dieGracefully(null, $e);
+                               }
+                               if ($e['type'] == 'advmultiselect') {
+                                       $source->setLabel($e['labels']);
+                               }
+                               if ($e['name'] == 'categories') {
+                                       $res = $source->loadArray($this->categories);
+                                       if (PEAR::isError($res)) {
+                                               Toolkit_Common::dieGracefully(null, $res);
+                                       }
+                               }
+                               if ($e['type'] == 'header') {
+                                       $this->formHeaders[$e['display']] = $e;
+                               }
+                       } elseif (is_array($e['group'])) {
+                               unset($field);
+                               foreach ($e['group'] as $g) {
+                                       $field[] =& HTML_QuickForm::createElement($g['type'], $g['name'], $g['display'], $g['opts'], $g['att'], $g['val']);
+                               }
+                               $source =& $this->addGroup($field, $e['name'], $e['label'], $e['seperator'], $e['appendName']);
+                       }
+               }
+       }
+
+       //      }}}
+       //      {{{ setupRenderers()
+
+       /**
+     * Sets up the rendering engine for the form
+     *
+     * @return void
+        * @access protected
+        */
+       protected function setupRenderers()
+       {
+               $renderer = new HTML_QuickForm_Renderer_Object(true);
+
+               $this->accept($renderer);
+
+               $this->template = new HTML_Template_Flexy($this->flexyOptions);
+
+               //      Make the view a copy of the $this object
+               //      That way we have access to call functions in
+               //      this class from within the template.
+               $this->view = $this;
+               $this->view->form = $renderer->toObject();
+               $this->template->compile($this->formTemplate);
+       }
+
+       //      }}}
+
+       //      {{{ toHTML()
+
+       /**
+        * Renders the form for viewing
+        *
+        * This function validates the form if needed, and if it successfully
+        * validates attempts to insert or update the data record.
+        * If it is unsuccessful, it will return an error to the user
+        * informing them of what went wrong.
+        *
+        * @param string $listPage The page the header should redirect
+        *                                                 to on successful insert or update.
+     *
+        * @return string The compiled and filled form template.
+        * @access public
+        */
+       public function toHTML($listPage = 'index.php')
+       {
+               //      We need to validate (and freeze if needed)
+               //      before we render the form. That way the
+               //      template knows about any errors on the form.
+               $this->validated = $this->validate();
+               $this->setupRenderers();
+
+               if ($this->validated) {
+                       $processed = $this->process(
+                array(&$this, 'processData'),
+                $this->mergeFiles
+            );
+                       if ($processed) {
+                               header("Location: $listPage");
+                       } else {
+                               $errorMsg = "There was an unexpected error. Please try again later.";
+                       }
+               }
+
+               return $errorMsg . $this->template->bufferedOutputObject($this->view);
+       }
+
+       //      }}}
+
+       //      {{{ updateData()
+
+       /**
+     * update the data in the DB
+     *
+     * @param array  $values Submitted form values
+     * @param string $sql    Query to execute to make update
+     *
+     * @return boolean result of query
+        * @access protected
+        */
+       protected function updateData($values, $sql)
+       {
+               try {
+                       $stmt = $this->dbh->prepare($sql);
+                       foreach ($values as $k => &$v) {
+                               $dataType = $this->getDataType($k);
+                               $stmt->bindParam(":$k", $v, $dataType);
+                       }
+                       return $stmt->execute();
+               } catch (PDOException $e) {
+                       return Toolkit_Common::handleError($e);
+               }
+       }
+
+       //      }}}
+
+       //      {{{ validated()
+
+       /**
+        * Returns form message from validation attempts
+        *
+     * @return string validation message
+        * @access public
+        */
+       public function validated()
+       {
+               if ($this->validated) {
+                       return $this->successMsg;
+               } elseif ($this->isSubmitted()) {
+                       return $this->errorMsg;
+               }
+       }
+
+       //      }}}
+}
+?>
diff --git a/Toolkit/Members/Admin/BasicSearch.php b/Toolkit/Members/Admin/BasicSearch.php
new file mode 100644 (file)
index 0000000..4766459
--- /dev/null
@@ -0,0 +1,343 @@
+<?php
+//     vim:set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker syntax=php:
+
+/**
+ * Admin search functionality for memberdb
+ *
+ * PHP version 5
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @version   CVS: $Id: BasicSearch.php,v 1.6 2010/05/25 14:01:20 jamie Exp $
+ * @link      http://demo.gaslightmedia.com
+ */
+
+/**
+ * Form to search the members database
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @link      http://demo.gaslightmedia.com
+ */
+class Toolkit_Members_Admin_BasicSearch
+    extends Toolkit_FormBuilder implements Toolkit_Form
+{
+       //      {{{ configureConstants()
+
+    /**
+     * Form constant definitions
+     *
+     * @return void
+     * @access public
+     */
+       public function configureConstants()
+       {
+               $c = array(
+                       'page' => 'members',
+                       'module' => 'listMembers',
+               );
+
+               $this->setupConstants($c);
+       }
+
+       //      }}}
+       //      {{{ configureElements()
+
+    /**
+     * Form element definitions
+     *
+        * @param PDO                      $dbh Databse handler
+     * @param Config_Container $c   Configuration object
+     *
+     * @return void
+     * @access public
+     */
+       public function configureElements(PDO $dbh, Config_Container $c)
+       {
+        $e = array();
+
+        //  get reference to [listing type] section of config file
+        $config =& $c->getItem('section', 'listing type');
+        //  get coupon
+        $singularDirective =& $config->getItem('directive', 'singular');
+        $singularType = $singularDirective->getContent();
+        $pluralDirective =& $config->getItem('directive', 'plural');
+        $pluralType = $pluralDirective->getContent();
+
+               //      All Grouped Elements are created here.
+
+               //      All Elements are created here.  This includes group element definitions.
+               $e[] = array(
+            'type' => 'hidden',
+            'req' => false,
+            'name' => 'page'
+        );
+               $e[] = array(
+            'type' => 'hidden',
+            'req' => false,
+            'name' => 'module'
+        );
+               $e[] = array(
+            'type' => 'header',
+            'req' => false,
+            'name' => 'recordInfoHdr',
+            'display' => "Search for $pluralType",
+        );
+               $e[] = array(
+            'type' => 'selectglm',
+            'req' => false,
+            'name' => 'category',
+            'display' => 'Category',
+            'opts' => $this->_getCategories($dbh),
+                       'att' => array(
+                               'multiple' => 'multiple',
+                               'size' => 6
+                       )
+        );
+               $e[] = array(
+            'type' => 'text',
+            'req' => false,
+            'name' => 'name',
+            'display' => "$singularType Name"
+        );
+               $e[] = array(
+            'type' => 'text',
+            'req' => false,
+            'name' => 'email',
+            'display' => "$singularType Email"
+        );
+               $e[] = array(
+            'type' => 'submit',
+            'req' => false,
+            'name' => 'submit',
+            'display' => 'Search'
+        );
+
+               $this->setupElements($e);
+       }
+
+       //      }}}
+       //      {{{ configureFilters()
+
+    /**
+     * Form filter definitions
+     *
+     * @return void
+     * @access public
+     */
+       public function configureFilters()
+       {
+        $f = array();
+               $f[] = array(
+            'element' => '__ALL__',
+            'filter' => 'trim'
+        );
+
+               $this->setupFilters($f);
+       }
+
+       //      }}}
+    //  {{{ configureForm()
+
+    /**
+     * Helper function to configure an entire form
+     *
+        * @param PDO                      $dbh Databse handler
+     * @param Config_Container $c   Configuration object
+     *
+     * @return void
+     * @access public
+     */
+    public function configureForm(PDO $dbh, Config_Container $c)
+    {
+        $this->configureElements($dbh, $c);
+        $this->configureRules();
+        $this->configureFilters();
+        $this->configureConstants();
+    }
+
+    //  }}}
+       //      {{{ configureRules()
+
+    /**
+     * Form rule definitions
+     *
+     * @return void
+     * @access public
+     */
+       public function configureRules()
+       {
+        $r = array();
+               $r[] = array(
+            'element' => 'member_contact_email',
+            'message' => 'ERROR: Invalid Email Address!',
+            'type' => 'email',
+        );
+
+               $this->setupRules($r);
+       }
+
+       //      }}}
+
+       //      {{{     _getCategories()
+
+    /**
+     * get all the categories available in alpha order
+     *
+        * @param PDO $dbh Database handler
+        *
+     * @return array alpha order of categories available in the DB
+     * @access private
+     */
+       private function _getCategories(PDO $dbh)
+       {
+               $categories = Toolkit_Common::getHierarchicalTreeStructure(
+                       $dbh,
+            'category',
+            'category_id',
+            'parent_id',
+                       'name'
+        );
+               //  Get only the active categories from
+               //  the nav structure for our select list.
+               $sql = "
+                       SELECT category_id, name
+                         FROM category
+                        WHERE category_id = :id";
+
+               $stmt = $dbh->prepare($sql);
+
+               foreach ($categories as $k => $v) {
+                       unset($row, $category);
+                       $stmt->bindParam(':id', $k, PDO::PARAM_INT);
+                       $stmt->execute();
+
+                       //  If we actually retrieved a row, add it to the select list
+                       //  after we clean it up.
+                       if ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                               $pages[$k] = array(
+                                       'level' => $v - 1,
+                                       'name' => $row['name']
+                               );
+                       }
+               }
+
+               return $pages;
+       }
+
+       //      }}}
+       //      {{{ getCities()
+
+       /**
+        * Configure the cities for member stored in the database into an array
+        *
+     * @return array the cities
+        * @access protected
+        */
+       protected function getCities()
+       {
+               try {
+                       $sql = "
+                               SELECT *
+                                 FROM city
+                                ORDER BY city_name";
+                       foreach ($this->dbh->query($sql) as $row) {
+                               $cities[$row['city_id']] = $row['city_name'];
+                       }
+                       if (!empty($cities)) {
+                               $cities = array('' => '-- Select --') + $cities;
+                       } else {
+                               $cities = array('' => '-- No Cities Created Yet -- ');
+                       }
+                       return $cities;
+               } catch (PDOException $e) {
+                       return Toolkit_Common::handleError($e);
+               }
+       }
+
+       //      }}}
+       //      {{{     _getStates()
+
+    /**
+     * get all the statis available in alpha order
+     *
+     * @return array alpha order of states available in the DB
+     * @access private
+     */
+       private function _getStates()
+       {
+               try {
+                       $sql = "
+                SELECT *
+                  FROM state
+                 ORDER BY state_name";
+
+                       $stmt = $this->dbh->prepare($sql);
+                       $stmt->execute();
+                       $stmt->bindColumn('state_id', $cid);
+                       $stmt->bindColumn('state_name', $name);
+
+                       $states = array('' => '-- Select --');
+                       while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                               $states[$cid] = $name;
+                       }
+
+                       return $states;
+               } catch (PDOException $e) {
+                       return Toolkit_Common::handleError($e);
+               }
+       }
+
+       //      }}}
+
+       //      {{{     setupRenderers()
+    //  @codeCoverageIgnoreStart
+
+    /**
+     * Custom rendering templates for special fields on the form
+     *
+     * @return void
+     * @access protected
+     */
+       protected function setupRenderers()
+       {
+               parent::setupRenderers();
+               $renderer =& $this->defaultRenderer();
+               $tpl = '<tr align="center"><td colspan="2">{element}</td></tr>';
+               $renderer->setElementTemplate($tpl, 'submit');
+       }
+
+    //  @codeCoverageIgnoreEnd
+       //      }}}
+
+       //      {{{     toHtml()
+
+       /**
+        * Call the rendering function to get the form in a string
+        *
+        * @access protected
+        * @return string $output The Form to be rendered or success msg.
+        */
+       public function toHtml()
+       {
+               $this->setupRenderers();
+               if ($this->validate()) {
+                       $output = parent::toHTML();
+               } elseif ($this->isSubmitted()) {
+                       $output = $this->errorMsg;
+                       $output .= parent::toHTML();
+               } else {
+                       $output = parent::toHTML();
+               }
+               return $output;
+       }
+
+       //      }}}
+}
+?>
diff --git a/Toolkit/Members/Admin/BillingController.php b/Toolkit/Members/Admin/BillingController.php
new file mode 100644 (file)
index 0000000..2692509
--- /dev/null
@@ -0,0 +1,105 @@
+<?php
+/**
+ * BillingController.php
+ * 
+ * PHP version 5.2
+ * 
+ * @category  Toolkit
+ * @package   Members_Admin
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @version   SVN: $id$
+ * @link      <>
+ */
+
+/**
+ * Toolkit_Members_Admin_BillingController
+ * 
+ * Description of Toolkit_Members_Admin_BillingController
+ * 
+ * @category  Toolkit
+ * @package   Members_Admin
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @release   Release: $id$
+ * @link      <>
+ */
+
+class Toolkit_Members_Admin_BillingController
+       extends Toolkit_BaseControllerAbstract
+    implements Toolkit_IController
+{
+    /**
+     * Description of getPageLayout()
+     * 
+     * @param string $html HTML string
+     * 
+     * @return string
+     * @access protected 
+     */
+    protected function getPageLayout($html)
+    {
+        $GLOBALS['styleSheets'][]
+            = MEDIA_BASE_URL . 'Toolkit/Members/Billing/billing.css';
+        $this->registry->controllerObject->content = $html;
+               $this->registry->controllerObject->topScripts
+                       = Toolkit_Common::getScripts($GLOBALS['topScripts']);
+               $this->registry->controllerObject->bottomScripts
+                       = Toolkit_Common::getScripts($GLOBALS['bottomScripts']);
+               $this->registry->controllerObject->styles
+                       = Toolkit_Common::getStyleSheets($GLOBALS['styleSheets']);
+               $this->registry->tEngine->compile('admin.tpl');
+               return $this->registry->tEngine->bufferedOutputObject(
+                       $this->registry->controllerObject
+               );
+    }
+    
+    /**
+     * Description of indexAction()
+     * 
+     * @return string
+     * @access public 
+     */
+       public function indexAction()
+       {
+        $paymentTypes = new Toolkit_Members_Billing_ListPaymentTypes(
+            Toolkit_Database::getInstance(),
+            50,
+            null,
+            null
+        );
+        $html = $paymentTypes->toHTML();
+               return $this->getPageLayout($html);
+       }
+    
+    /**
+     * Description of editPaymentTypeAction()
+     * 
+     * @return string
+     * @access public
+     */
+    public function editPaymentTypeAction()
+    {
+        $form = new Toolkit_Members_Billing_EditPaymentType(
+            Toolkit_Database::getInstance(),
+            'edit-payment-type'
+        );
+        $form->configureForm();
+        $html = $form->toHtml();
+        return $this->getPageLayout($html);
+    }
+
+    /**
+     * Description of paymentTypeAction
+     * 
+     * @return string
+     * @accesspublic
+     */
+    public function paymentTypeAction()
+    {
+        return $this->indexAction();
+    }
+    
+}
diff --git a/Toolkit/Members/Admin/CategoriesController.php b/Toolkit/Members/Admin/CategoriesController.php
new file mode 100644 (file)
index 0000000..31ca875
--- /dev/null
@@ -0,0 +1,118 @@
+<?php
+/**
+ * CategoriesController.php
+ * 
+ * PHP versions 4 and 5
+ * 
+ * @category  Toolkit
+ * @package   Members_Admin
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2009 Gaslight Media
+ * @license   Gaslight Media
+ * @version   CVS: $Id: Newsletters.php,v 1.9 2009/09/16 19:00:58 matrix Exp $
+ * @link      http://demo.gaslightmedia.com
+ */
+
+
+/**
+ * Toolkit_Members_Admin_CategoriesController
+ * 
+ * Description of Toolkit_Members_Admin_CategoriesController
+ * 
+ * @category  Toolkit
+ * @package   Members_Admin
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2009 Gaslight Media
+ * @license   Gaslight Media
+ * @link      http://demo.gaslightmedia.com
+ */
+class Toolkit_Members_Admin_CategoriesController
+       extends Toolkit_BaseControllerAbstract implements Toolkit_IController
+{
+       //      {{{     indexAction()
+    /**
+     * Main action for controller. Runs admin template, list out all regions
+     * 
+     * @return string
+     * @access public 
+     */
+       public function indexAction()
+       {
+               $GLOBALS['bottomScripts'][]
+                       = MEDIA_APP_BASE_URL . 'libjs/jsTree/libjs/css.js';
+               $GLOBALS['bottomScripts'][]
+                       = MEDIA_APP_BASE_URL . 'libjs/jquery.listen.js';
+               $GLOBALS['bottomScripts'][]
+                       = MEDIA_APP_BASE_URL . 'libjs/jsTree/source/tree_component.js';
+               $GLOBALS['bottomScripts'][]
+                       = MEDIA_APP_BASE_URL . 'libjs/jquery.cookie.js';
+               $GLOBALS['bottomScripts'][]
+                       = MEDIA_BASE_URL . 'Toolkit/Members/libjs/list-categories.js';
+               $GLOBALS['styleSheets'][]
+                       = MEDIA_APP_BASE_URL . 'libjs/jsTree/source/tree_component.css';
+
+               $categoryList = new Toolkit_Members_Admin_ListCategories($this->registry->dbh);
+               $this->registry->controllerObject->content = $categoryList->renderCategories();
+
+               $this->registry->controllerObject->topScripts
+                       = Toolkit_Common::getScripts($GLOBALS['topScripts']);
+               $this->registry->controllerObject->bottomScripts
+                       = Toolkit_Common::getScripts($GLOBALS['bottomScripts']);
+               $this->registry->controllerObject->styles
+                       = Toolkit_Common::getStyleSheets($GLOBALS['styleSheets']);
+
+               $this->registry->tEngine->compile('admin.tpl');
+               return $this->registry->tEngine->bufferedOutputObject(
+                       $this->registry->controllerObject
+               );
+       }
+
+       //      }}}
+       //      {{{     editCategoryAction()
+    /**
+     * Description of editCategoryAction()
+     * 
+     * @return string
+     * @access public 
+     */
+       public function editCategoryAction()
+       {
+               $GLOBALS['bottomScripts'][]
+                       = MEDIA_BASE_URL . 'Toolkit/Members/libjs/edit-category.js';
+
+               $form = new Toolkit_Members_Admin_EditCategory(
+            $this->registry->dbh,
+            'edit_category'
+        );
+        $form->configureForm();
+               $this->registry->controllerObject->content = $form->toHtml();
+
+               $this->registry->controllerObject->topScripts
+                       = Toolkit_Common::getScripts($GLOBALS['topScripts']);
+               $this->registry->controllerObject->bottomScripts
+                       = Toolkit_Common::getScripts($GLOBALS['bottomScripts']);
+               $this->registry->controllerObject->styles
+                       = Toolkit_Common::getStyleSheets($GLOBALS['styleSheets']);
+
+               $this->registry->tEngine->compile('admin.tpl');
+               return $this->registry->tEngine->bufferedOutputObject(
+                       $this->registry->controllerObject
+               );
+       }
+
+       //      }}}
+       //      {{{     listCategoriesAction()
+    /**
+     * Description of listCategoriesAction()
+     * 
+     * @return array
+     * @access public 
+     */
+       public function listCategoriesAction()
+       {
+               return $this->indexAction();
+       }
+
+       //      }}}
+}
+?>
diff --git a/Toolkit/Members/Admin/CitiesController.php b/Toolkit/Members/Admin/CitiesController.php
new file mode 100644 (file)
index 0000000..58196b5
--- /dev/null
@@ -0,0 +1,109 @@
+<?php
+/**
+ * CitiesController.php
+ *
+ * PHP versions 4 and 5
+ *
+ * @category  Toolkit
+ * @package   Members_Admin
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2009 Gaslight Media
+ * @license   Gaslight Media
+ * @version   CVS: $Id: Newsletters.php,v 1.9 2009/09/16 19:00:58 matrix Exp $
+ * @link      http://demo.gaslightmedia.com
+ */
+
+/**
+ * Toolkit_Members_Admin_CitiesController
+ *
+ * Description for Toolkit_Members_Admin_CitiesController
+ *
+ * @category  Toolkit
+ * @package   Members_Admin
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2009 Gaslight Media
+ * @license   Gaslight Media
+ * @link      http://demo.gaslightmedia.com
+ */
+
+class Toolkit_Members_Admin_CitiesController
+    extends Toolkit_BaseControllerAbstract implements Toolkit_IController
+{
+    //  {{{ indexAction()
+
+    /**
+     * Main action for controller. Runs admin template, list out all regions
+     *
+     * @return string
+     * @access public
+     */
+    public function indexAction()
+    {
+        $cityList = new Toolkit_Members_Admin_ListCities($this->registry->dbh);
+        $this->registry->controllerObject->content = $cityList->renderCities();
+
+        $this->registry->controllerObject->topScripts
+            = Toolkit_Common::getScripts($GLOBALS['topScripts']);
+        $this->registry->controllerObject->bottomScripts
+            = Toolkit_Common::getScripts($GLOBALS['bottomScripts']);
+        $this->registry->controllerObject->styles
+            = Toolkit_Common::getStyleSheets($GLOBALS['styleSheets']);
+
+        $this->registry->tEngine->compile('admin.tpl');
+        return $this->registry->tEngine->bufferedOutputObject(
+            $this->registry->controllerObject
+        );
+    }
+
+    //  }}}
+    //  {{{ editCityAction()
+    /**
+     * Description of editCityAction()
+     *
+     * @return string
+     * @access public
+     */
+    public function editCityAction()
+    {
+        $GLOBALS['bottomScripts'][]
+            = CKEDITOR_JS . '';
+        $GLOBALS['bottomScripts'][]
+            = MEDIA_BASE_URL . 'Toolkit/Members/libjs/edit-city.js';
+
+        $form = new Toolkit_Members_Admin_EditCity(
+            $this->registry->dbh,
+            'edit_city'
+        );
+        $form->setImageServer(new Toolkit_Image_Server());
+        $form->configureForm($this->registry->config);
+        $this->registry->controllerObject->content = $form->toHtml();
+
+        $this->registry->controllerObject->topScripts
+            = Toolkit_Common::getScripts($GLOBALS['topScripts']);
+        $this->registry->controllerObject->bottomScripts
+            = Toolkit_Common::getScripts($GLOBALS['bottomScripts']);
+        $this->registry->controllerObject->styles
+            = Toolkit_Common::getStyleSheets($GLOBALS['styleSheets']);
+
+        $this->registry->tEngine->compile('admin.tpl');
+        return $this->registry->tEngine->bufferedOutputObject(
+            $this->registry->controllerObject
+        );
+    }
+
+    //  }}}
+    //  {{{ listCitiesAction()
+    /**
+     * Description of listCitiesAction()
+     *
+     * @return array
+     * @access public
+     */
+    public function listCitiesAction()
+    {
+        return $this->indexAction();
+    }
+
+    //  }}}
+}
+?>
diff --git a/Toolkit/Members/Admin/CountiesController.php b/Toolkit/Members/Admin/CountiesController.php
new file mode 100644 (file)
index 0000000..1f6c078
--- /dev/null
@@ -0,0 +1,103 @@
+<?php
+/**
+ * CountiesController.php
+ * 
+ * PHP versions 4 and 5
+ * 
+ * @category  Toolkit
+ * @package   Members_Admin
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2009 Gaslight Media
+ * @license   Gaslight Media
+ * @version   CVS: $Id: Newsletters.php,v 1.9 2009/09/16 19:00:58 matrix Exp $
+ * @link      http://demo.gaslightmedia.com
+ */
+
+/**
+ * Toolkit_Members_Admin_CountiesController
+ * 
+ * Description for Toolkit_Members_Admin_CountiesController
+ * 
+ * @category  Toolkit
+ * @package   Members_Admin
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2009 Gaslight Media
+ * @license   Gaslight Media
+ * @link      http://demo.gaslightmedia.com
+ */
+class Toolkit_Members_Admin_CountiesController
+       extends Toolkit_BaseControllerAbstract implements Toolkit_IController
+{
+       //      {{{     indexAction()
+    /**
+     * Main action for controller. Runs admin template, list out all regions
+     * 
+     * @return string
+     * @access public 
+     */
+       public function indexAction()
+       {
+        die('not finished');
+               $countyList = new Toolkit_Members_Admin_ListCounties($this->registry->dbh);
+               $this->registry->controllerObject->content = $countyList->renderCounties();
+
+               $this->registry->controllerObject->topScripts
+                       = Toolkit_Common::getScripts($GLOBALS['topScripts']);
+               $this->registry->controllerObject->bottomScripts
+                       = Toolkit_Common::getScripts($GLOBALS['bottomScripts']);
+               $this->registry->controllerObject->styles
+                       = Toolkit_Common::getStyleSheets($GLOBALS['styleSheets']);
+
+               $this->registry->tEngine->compile('admin.tpl');
+               return $this->registry->tEngine->bufferedOutputObject(
+                       $this->registry->controllerObject
+               );
+       }
+
+       //      }}}
+       //      {{{     editCountyAction()
+    /**
+     * Description for editCountyAction()
+     * 
+     * @return string
+     * @access public 
+     */
+       public function editCountyAction()
+       {
+        die('not finished');
+               $form = new Toolkit_Members_Admin_EditCounty(
+            $this->dbh,
+            'edit_city'
+        );
+        $form->configureForm();
+               $this->registry->controllerObject->content = $form->toHtml();
+
+               $this->registry->controllerObject->topScripts
+                       = Toolkit_Common::getScripts($GLOBALS['topScripts']);
+               $this->registry->controllerObject->bottomScripts
+                       = Toolkit_Common::getScripts($GLOBALS['bottomScripts']);
+               $this->registry->controllerObject->styles
+                       = Toolkit_Common::getStyleSheets($GLOBALS['styleSheets']);
+
+               $this->registry->tEngine->compile('admin.tpl');
+               return $this->registry->tEngine->bufferedOutputObject(
+                       $this->registry->controllerObject
+               );
+       }
+
+       //      }}}
+       //      {{{     listCountiesAction()
+    /**
+     * Description for listCountiesAction()
+     * 
+     * @return array
+     * @access public 
+     */
+       public function listCountiesAction()
+       {
+               return $this->indexAction();
+       }
+
+       //      }}}
+}
+?>
diff --git a/Toolkit/Members/Admin/EditAmenity.php b/Toolkit/Members/Admin/EditAmenity.php
new file mode 100644 (file)
index 0000000..570903d
--- /dev/null
@@ -0,0 +1,365 @@
+<?php
+//     vim:set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker syntax=php:
+
+/**
+ * Controls Amenity definitions for the member db
+ *
+ * PHP version 5
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @version   CVS: $Id: EditAmenity.php,v 1.10 2010/08/09 17:58:32 jamie Exp $
+ * @link      http://demo.gaslightmedia.com
+ */
+
+/**
+ * Handles creating / editing amenities that the members will use
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @link      http://demo.gaslightmedia.com
+ */
+class Toolkit_Members_Admin_EditAmenity
+    extends Toolkit_Members_Auxiliary implements Toolkit_Form
+{
+       //      {{{     properties
+
+    /**
+     * Description for public
+     * @var    string
+     * @access public
+     */
+       public $tableName = 'amenity';
+
+    /**
+     * Description for protected
+     * @var    string
+     * @access protected
+     */
+       protected $formTemplate = 'editAmenity.tpl';
+
+       //      }}}
+       //      {{{ __construct()
+
+       /**
+        * Class constructor
+        *
+     * @param PDO    $pdo         PHP Data Object
+     * @param string $formName    Form's name.
+     * @param string $method      (optional)Form's method defaults to 'POST'
+     * @param string $action      (optional)Form's action
+     * @param string $target      (optional)Form's target defaults to '_self'
+     * @param mixed  $attributes  (optional)Extra attributes for <form> tag
+     * @param bool   $trackSubmit (optional)Whether to track if the form was
+        *                                                        submitted by adding a special hidden field
+        *
+        * @access public
+        */
+       public function __construct(
+        PDO $pdo,
+        $formName,
+        $method = 'post',
+        $action = '',
+        $target = '',
+        $attributes = null,
+        $trackSubmit = false
+    ) {
+               parent::__construct(
+            $formName,
+            $method,
+            $action,
+            $target,
+            $attributes,
+            $trackSubmit
+        );
+
+        $this->dbh = $pdo;
+       }
+
+       //      }}}
+
+       //      {{{ checkAmenityName()
+
+    /**
+     * Check that the amenity name is not in use already
+     *
+     * @param string $name name to check
+     *
+     * @return boolean if the name is valid or not
+     * @access public
+     */
+       public function checkAmenityName($name)
+       {
+               try {
+                       //      If we're editing a amenity, they
+                       //      can save that amenity as its
+                       //      own name. so don't include that
+                       //      amenity in the check.
+                       if (isset($_GET['id'])) {
+                               $and = "AND amenity_id <> :id";
+                       }
+                       $sql = "
+                SELECT count(*) AS total
+                  FROM {$this->tableName}
+                 WHERE amenity_name = :name
+                                 $and";
+                       $stmt = $this->dbh->prepare($sql);
+                       $stmt->bindParam(':name', $name, PDO::PARAM_STR);
+                       if (isset($_GET['id'])) {
+                               $stmt->bindParam(':id', $_GET['id'], PDO::PARAM_STR);
+                       }
+                       $stmt->execute();
+            $stmt->bindColumn('total', $total);
+                       $stmt->fetch();
+
+                       return !(bool) $total;
+               } catch (PDOException $e) {
+                       return Toolkit_Common::handleError($e);
+               }
+       }
+
+       //      }}}
+       //      {{{ configureDefaults()
+
+    /**
+     * Set up the default values for the form
+     *
+     * @return void
+     * @access public
+     */
+       public function configureDefaults()
+       {
+        $d = array();
+
+               if ($amenityId = filter_input(INPUT_GET, 'id', FILTER_VALIDATE_INT)) {
+                       try {
+                               $sql = "
+                                       SELECT *
+                                         FROM {$this->tableName}
+                                        WHERE amenity_id = :id";
+                               $stmt = $this->dbh->prepare($sql);
+                               $stmt->bindParam(':id', $amenityId, PDO::PARAM_INT);
+                               $stmt->execute();
+                               $d = $stmt->fetch();
+                       } catch (PDOException $e) {
+                               $this->handleError($e);
+                       }
+               }
+
+               $this->setupDefaults($d);
+       }
+
+       //      }}}
+       //      {{{ configureElements()
+
+    /**
+     * Form element definitions
+     *
+     * @return void
+     * @access public
+     */
+       public function configureElements()
+       {
+               $e = array();
+               //      All Grouped Elements are created here.
+
+               //      All Elements are created here.
+               //      This includes group element definitions.
+               $e[] = array(
+                       'type'    => 'header',
+                       'req'     => false,
+                       'name'    => 'AmenityInfoHdr',
+                       'display' => 'Member Amenities'
+               );
+               $e[] = array(
+                       'type'    => 'advcheckbox',
+                       'req'     => false,
+                       'name'    => 'display_form',
+                       'display' => 'Display on Form',
+                       'val'     => array(0, 1)
+               );
+               $e[] = array(
+                       'type'    => 'text',
+                       'req'     => true,
+                       'name'    => 'amenity_name',
+                       'display' => 'Amenity Name'
+               );
+
+               $this->setupElements($e);
+       }
+
+       //      }}}
+    //  {{{ configureForm()
+
+    /**
+     * Helper function to configure an entire form
+     *
+     * @return void
+     * @access public
+     */
+    public function configureForm()
+    {
+        $this->configureElements();
+        $this->configureRules();
+        $this->configureDefaults();
+    }
+
+    //  }}}
+       //      {{{ configureRules()
+
+    /**
+     * Form rule definitions
+     *
+     * @return void
+     * @access public
+     */
+       public function configureRules()
+       {
+        $r = array();
+
+               $r[] = array(
+            'element' => 'amenity_name',
+            'message' => 'ERROR: Amenity already exists!',
+            'type' => 'callback',
+            'format' => array($this, 'checkAmenityName'),
+            'validation' => $this->validationType,
+            'reset' => false,
+            'force' => false
+        );
+
+               $this->setupRules($r);
+       }
+
+       //      }}}
+
+       //      {{{ deleteAmenity()
+
+    /**
+     * Remove a region from the db
+     *
+     * @param integer $id region id
+     *
+     * @return boolean result of db query
+     * @access protected
+     */
+       protected function deleteAmenity($id)
+       {
+        if (!is_numeric($id)) {
+            return false;
+        }
+               try {
+                       $sql = "
+                               DELETE FROM {$this->tableName}
+                                WHERE amenity_id = :id";
+                       $stmt = $this->dbh->prepare($sql);
+                       $stmt->bindParam(':id', $id, PDO::PARAM_INT);
+
+                       return $stmt->execute();
+               } catch (PDOException $e) {
+                       Toolkit_Common::handleError($e);
+               }
+       }
+
+       //      }}}
+
+       //      {{{ insertData()
+
+    /**
+     * Create a new amenity
+     *
+     * @param array $values Submitted form values
+     *
+     * @return boolean Result of insertion into DB
+     * @access protected
+     */
+       protected function insertData($values)
+       {
+        $sql = Toolkit_Common::createSQLInsert(
+            $this->tableName,
+            array_keys($values)
+        );
+        try {
+                       $stmt = Toolkit_Common::prepareQuery(
+                $this->dbh,
+                $this->tableName,
+                $sql,
+                $values
+            );
+
+            return $stmt->execute();
+        } catch (PDOException $e) {
+            return Toolkit_Common::handleError($e);
+        }
+       }
+
+       //      }}}
+
+       //      {{{ updateData()
+
+    /**
+     * Update the data for an amenity
+     *
+     * @param array $values submitted form results
+     *
+     * @return boolean result of sql update query
+     * @access protected
+     */
+       protected function updateData($values)
+       {
+               if ($amenityId = filter_input(INPUT_GET, 'id', FILTER_VALIDATE_INT)) {
+                       //      We aren't updating a region, we're deleting it.
+                       if (array_key_exists('delete', $values)) {
+                               return $this->deleteAmenity($amenityId);
+                       }
+
+                       $sql = Toolkit_Common::createSQLUpdate(
+                               $this->tableName,
+                               array_keys($values),
+                               array('amenity_id = :id')
+                       );
+
+                       $values['id'] = $amenityId;
+                       try {
+                               $stmt = Toolkit_Common::prepareQuery(
+                                       $this->dbh,
+                                       $this->tableName,
+                                       $sql,
+                                       $values
+                               );
+                               return $stmt->execute();
+                       } catch (PDOException $e) {
+                               return Toolkit_Common::handleError($e);
+                       }
+               }
+       }
+
+       //      }}}
+
+       //      {{{ toHTML()
+
+       /**
+        * Renders the form for viewing
+        *
+        * This function validates the form if needed, and if it successfully
+        * validates attempts to insert or update the data record.
+        * If it is unsuccessful, it will return an error to the user
+        * informing them of what went wrong.
+        *
+        * @return string The compiled and filled form template.
+        * @access public
+        */
+       public function toHtml()
+       {
+               $listPage = MEDIA_BASE_URL . 'admin/members.php?rt=Amenities&ac=listAmenities';
+               return parent::toHtml($listPage);
+       }
+
+       //      }}}
+}
+?>
diff --git a/Toolkit/Members/Admin/EditCategory.php b/Toolkit/Members/Admin/EditCategory.php
new file mode 100644 (file)
index 0000000..8c5a411
--- /dev/null
@@ -0,0 +1,531 @@
+<?php
+//     vim:set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker syntax=php:
+
+/**
+ * File Doc Comment
+ *
+ * PHP version 5
+ *
+ * @category MembersDB
+ * @package  Toolkit_Members
+ * @author   Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @license     http://www.gaslightmedia.com Gaslightmedia
+ * @version  CVS: $Id: EditCategory.php,v 1.14 2010/07/14 23:27:59 jamie Exp $
+ * @link        http://demo.gaslightmedia.com
+ */
+
+/**
+ * Edit categories used within the member database
+ *
+ * Controls all aspects of creating and rendering the form used to manipulate
+ * the member categories.  Handles both adding / editing category
+ * details, including:
+ *
+ * 1. Modules associated with a category.
+ * 2. Updating sub-categories to mirror parent category module settings.
+ * 3. Removal of top level categories that contain sub-level categories.
+ *             see (deleteCategory())
+ *
+ * Once categories are added, they are not allow to be moved to another parent.
+ * This keeps the updating to a minimum and elminates logic that would be
+ * caused by this move: (maintaining category-module settings after the move).
+ *
+ * @category  MembersDB
+ * @package      Toolkit_Members
+ * @author       Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2008 Gaslight media
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @link         http://demo.gaslightmedia.com
+ * @see                  Toolkit_Members, member_admin
+ */
+
+class Toolkit_Members_Admin_EditCategory
+    extends Toolkit_Members_Auxiliary implements Toolkit_Form
+{
+       //      {{{ properties
+
+       /**
+        * The table name in the database used to store the data of the categories
+        *
+        * @var string
+        * @access public
+        */
+       public $tableName = 'category';
+
+       /**
+        * The template used to render the form
+        *
+        * @var string
+        * @access protected
+        */
+       protected $formTemplate = 'editCategory.tpl';
+
+       //      }}}
+       //      {{{ __construct()
+
+       /**
+        * Class constructor
+        *
+     * @param PDO    $pdo         PHP Data Object
+     * @param string $formName    Form's name.
+     * @param string $method      (optional)Form's method defaults to 'POST'
+     * @param string $action      (optional)Form's action
+     * @param string $target      (optional)Form's target defaults to '_self'
+     * @param mixed  $attributes  (optional)Extra attributes for <form> tag
+     * @param bool   $trackSubmit (optional)Whether to track if the form was
+        *                                                        submitted by adding a special hidden field
+        *
+        * @access public
+        */
+       public function __construct(
+        PDO $pdo,
+        $formName,
+        $method = 'post',
+        $action = '',
+        $target = '',
+        $attributes = null,
+        $trackSubmit = false
+    ) {
+               parent::__construct(
+            $formName,
+            $method,
+            $action,
+            $target,
+            $attributes,
+            $trackSubmit
+        );
+
+        $this->dbh = $pdo;
+       }
+
+       //      }}}
+
+       //      {{{ configureDefaults()
+
+       /**
+        * Sets the defaults for elements in the form.
+        *
+        * @return void
+        * @access public
+        */
+       public function configureDefaults()
+       {
+               if ($categoryId = filter_input(INPUT_GET, 'id', FILTER_VALIDATE_INT)) {
+                       try {
+                               $sql = "
+                                       SELECT *
+                                         FROM category
+                                        WHERE category_id = :cid";
+
+                               $stmt = $this->dbh->prepare($sql);
+                               $stmt->bindParam(':cid', $categoryId, PDO::PARAM_INT);
+                               $stmt->execute();
+                               $category = $stmt->fetch(PDO::FETCH_ASSOC);
+                               $defaults = array(
+                                       'category_id'           => $category['category_id'],
+                                       'name'                          => $category['name'],
+                                       'parent_id'                     => $category['parent_id'],
+                                       'accommodations'        => $category['accommodations'],
+                                       'restaurant'            => $category['restaurant'],
+                                       'golf'                          => $category['golf'],
+                               );
+                       } catch (PDOException $e) {
+                               $this->handleError($e);
+                       }
+               }
+
+               $this->setupDefaults($defaults);
+       }
+
+       //      }}}
+       //      {{{ configureElements()
+
+       /**
+        * Setup the elements to use on the form.
+        *
+        * Categories are populated into the protected class property $categories.
+        * These categories are used to populate the select list of categories.
+        *
+        * @return void
+        * @access public
+        */
+       public function configureElements()
+       {
+        $e = array();
+
+               //      All Grouped Elements are created here.
+
+               //      All Elements are created here.  This includes group element definitions.
+               $e[] = array(
+            'type' => 'header',
+            'req' => false,
+            'name' => 'categoryInfoHdr',
+            'display' => 'Member Categories'
+        );
+               $e[] = array(
+            'type' => 'text',
+            'req' => true,
+            'name' => 'name',
+            'display' => 'Category Name'
+        );
+               $e[] = array(
+            'type' => 'select',
+            'req' => false,
+            'name' => 'parent_id',
+            'display' => 'Parent',
+            'opts' => array(0 => 'No Parent (top level)'),
+            'att' => array('id' => 'categories')
+        );
+               $e[] = array(
+            'type' => 'advcheckbox',
+            'req' => false,
+            'name' => 'accommodations',
+            'display' => 'Is Accommodation Category',
+            'val' => array(0, 1)
+        );
+               $e[] = array(
+            'type' => 'advcheckbox',
+            'req' => false,
+            'name' => 'restaurant',
+            'display' => 'Is Restaurant Category',
+            'val' => array(0, 1)
+        );
+               $e[] = array(
+            'type' => 'advcheckbox',
+            'req' => false,
+            'name' => 'golf',
+            'display' => 'Is Golfing Category',
+            'val' => array(0, 1)
+        );
+
+               $this->setupElements($e);
+        $this->loadMemberCategories();
+       }
+
+       //      }}}
+    //  {{{ configureForm()
+
+    /**
+     * Helper function to configure an entire form
+     *
+     * @return void
+     * @access public
+     */
+    public function configureForm()
+    {
+        $this->configureElements();
+        $this->configureDefaults();
+        $this->configureRules();
+    }
+
+    //  }}}
+       //      {{{ configureRules()
+
+    /**
+     * Form rule definitions
+     *
+     * @return void
+     * @access public
+     */
+       public function configureRules()
+       {
+        $r = array();
+
+               $this->setupRules($r);
+       }
+
+       //      }}}
+
+       //      {{{ deleteCategory()
+
+       /**
+        * Removes a category from the database
+        *
+        * All first level sub-categories will become main level categories
+        * and will hold the structure of any sub-categories beneath them.
+        *
+        * @param int $id The category_id of the category you wish to remove.
+        *
+        * @return bool False on SQL error, otherwise true.
+        * @access protected
+        */
+       protected function deleteCategory($id)
+       {
+        if (!is_numeric($id)) {
+            return false;
+        }
+
+               try {
+                       $sql = "
+                UPDATE {$this->tableName}
+                   SET parent_id   = 0
+                 WHERE parent_id   = :id";
+
+                       $stmt = $this->dbh->prepare($sql);
+                       $stmt->bindParam(':id', $id, PDO::PARAM_INT);
+                       $stmt->execute();
+            
+            if (   defined('MEMBER_STREAMSEND_API')
+                && MEMBER_STREAMSEND_API) {
+                // send member to streamsend
+                $memberStreamSend = new Toolkit_Members_StreamSend($this->dbh);
+                $memberStreamSend->updateOptionByCategoryId($id);
+            }
+            
+                       $sql = "
+                               DELETE FROM {$this->tableName}
+                 WHERE category_id = :id";
+
+                       $stmt = $this->dbh->prepare($sql);
+                       $stmt->bindParam(':id', $id, PDO::PARAM_INT);
+                       return $stmt->execute();
+               } catch (PDOException $e) {
+                       Toolkit_Common::handleError($e);
+               }
+       }
+
+       //      }}}
+
+       //      {{{ insertData()
+
+    /**
+     * Create a new category
+     *
+     * @param array $values Submitted form values
+     *
+     * @return boolean Result of insertion into DB
+     * @access protected
+     */
+       protected function insertData($values)
+       {
+        $sql = Toolkit_Common::createSQLInsert(
+            $this->tableName,
+            array_keys($values)
+        );
+        try {
+            $stmt = Toolkit_Common::prepareQuery(
+                $this->dbh,
+                $this->tableName,
+                $sql,
+                $values
+            );
+            $ret = $stmt->execute();
+            $categoryId = $this->dbh->lastInsertId('category_category_id_seq');
+            if (   defined('MEMBER_STREAMSEND_API')
+                && MEMBER_STREAMSEND_API) {
+                // send member to streamsend
+                $memberStreamSend = new Toolkit_Members_StreamSend($this->dbh);
+                $memberStreamSend->updateOptionByCategoryId($categoryId);
+            }
+            return $ret;
+        } catch (PDOException $e) {
+            return Toolkit_Common::handleError($e);
+        }
+       }
+
+       //      }}}
+
+    //  {{{ loadMemberCategories()
+
+    /**
+     * Loads member categories into the select list
+     *
+     * Gets an array structure of the member categories in a linear tree order
+     * Then walk through the array and load each category into the select list
+     *
+     * @return void
+     * @access protected
+     */
+    protected function loadMemberCategories()
+    {
+        if (!$this->elementExists('parent_id')) {
+            return;
+        }
+        try {
+                       if (isset($_GET['id']) && ctype_digit((string)$_GET['id'])) {
+                               $sql = "
+                    select count(*) AS total
+                      from category
+                     where parent_id = :pid";
+
+                               $stmt = $this->dbh->prepare($sql);
+                               $stmt->bindParam(':pid', $_GET['id'], PDO::PARAM_INT);
+                               $stmt->execute();
+                               $row = $stmt->fetch(PDO::FETCH_ASSOC);
+                               if ($row['total'] > 0) {
+                                       return;
+                               }
+                       }
+            //  Get a tree list of categories in linear order with
+            //  category keys in the values and their level in the tree
+            //  in the value
+            $c = Toolkit_Common::getHierarchicalTreeStructure(
+                $this->dbh,
+                'category',
+                'category_id',
+                'parent_id',
+                               'name',
+                               0,
+                               1
+            );
+
+            //  Get all the data about each category
+            $sql = "
+                SELECT *
+                  FROM category
+                 WHERE category_id = ?";
+
+            $stmt = $this->dbh->prepare($sql);
+            //  Get the member categories select list element
+            $e =& $this->getElement('parent_id');
+            foreach ($c as $i => $j) {
+                $stmt->execute(array($i));
+                $row = $stmt->fetch();
+                //  the class level is always 1 less than what is reported
+                //  from our $c array
+                $x = $j - 1;
+                //  Add the option data to the select list.
+                $e->addOption($row['name'], $i, array('class' => "level-$x"));
+            }
+        } catch (PDOException $e) {
+            return Toolkit_Common::handleError($e);
+        }
+    }
+
+    //  }}}
+
+       //      {{{ toHtml()
+
+       /**
+        * Renders the form
+        *
+        * sets the page the form should be redirected to instead of coming back
+        * around to itself. Calls the Auxiliary base class function to handle
+        * the rendering.
+        *
+        * @return string The rendered form
+        * @access public
+        */
+       public function toHtml()
+       {
+               $listPage = MEDIA_BASE_URL . 'admin/members.php?rt=Categories&ac=listCategories';
+               return parent::toHtml($listPage);
+       }
+
+       //      }}}
+
+       //      {{{ updateChildren()
+
+       /**
+        * Recusively updates all sub-categories to mirror the top category
+        *
+        * @param array $values The array of settings to update and the parent
+        *                                              id so you can find the children.
+        *
+        * @return bool false on error, otherwise true.
+        * @access protected
+        */
+       protected function updateChildren($values)
+       {
+               try {
+                       $sql = "
+                               SELECT *
+                                 FROM connectby('category', 'category_id', 'parent_id',
+                                                                       'pos', :cid, 0)
+                                       AS t(id text, parent text, level int, pos int)";
+
+                       $stmt = $this->dbh->prepare($sql);
+                       $stmt->bindParam(':cid', $_GET['id'], PDO::PARAM_STR);
+                       $stmt->execute();
+
+                       while ($row = $stmt->fetch()) {
+                               $children[] = $row;
+                       }
+                       //      If we actually got some subcategories,
+                       //      update their settings and check to see
+                       //      if they have any subcategories we can update.
+                       if (is_array($children)) {
+                               $sql = "
+                                       UPDATE {$this->tableName}
+                                          SET accommodations = :accommodations,
+                                              restaurant = :restaurant,
+                                                  golf = :golf
+                                        WHERE category_id = :category_id";
+
+                               $stmt = $this->dbh->prepare($sql);
+                               foreach ($children as $k => &$v) {
+                                       $stmt->bindParam('category_id', $v['id'], PDO::PARAM_INT);
+                                       $stmt->bindParam(
+                        'accommodations',
+                        $values['accommodations'],
+                        PDO::PARAM_BOOL
+                    );
+                                       $stmt->bindParam(
+                        'restaurant',
+                        $values['restaurant'],
+                        PDO::PARAM_BOOL
+                    );
+                                       $stmt->bindParam('golf', $values['golf'], PDO::PARAM_BOOL);
+                                       $stmt->execute();
+                               }
+                               return true;
+                       } else {
+                               //      Otherwise we got no subcategories
+                               return true;
+                       }
+               } catch (PDOException $e) {
+                       return Toolkit_Common::handleError($e);
+               }
+       }
+
+       //      }}}
+       //      {{{ updateData()
+
+       /**
+        * Update the settings for this category
+        *
+        * Any modules that are assigned to this category will be updated.
+        * And if this category has any sub-categories, those childrens
+        * module settings will be updated to mirror this categories settings.
+        *
+        * @param array $values Values submitted from the form
+        *
+        * @return bool false on sql error, otherwise true.
+        * @access protected
+        */
+       protected function updateData($values)
+       {
+               //      We aren't updating a category, we're deleting it.
+               if (array_key_exists('delete', $values)) {
+                       return $this->deleteCategory($_GET['id']);
+               }
+
+        $sql = Toolkit_Common::createSQLUpdate(
+            $this->tableName,
+            array_keys($values),
+            array('category_id = :id')
+        );
+
+        $values['id'] = $_GET['id'];
+        try {
+            Toolkit_Common::processQuery(
+                $this->dbh,
+                $this->tableName,
+                $sql,
+                $values
+            );
+            if (   defined('MEMBER_STREAMSEND_API')
+                && MEMBER_STREAMSEND_API) {
+                // send member to streamsend
+                $memberStreamSend = new Toolkit_Members_StreamSend($this->dbh);
+                $memberStreamSend->updateOptionByCategoryId($_GET['id']);
+            }
+            return $this->updateChildren($values);
+        } catch (PDOException $e) {
+            return Toolkit_Common::handleError($e);
+        }
+       }
+
+       //      }}}
+}
+?>
diff --git a/Toolkit/Members/Admin/EditCity.php b/Toolkit/Members/Admin/EditCity.php
new file mode 100644 (file)
index 0000000..cf2849b
--- /dev/null
@@ -0,0 +1,644 @@
+<?php
+//     vim:set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker syntax=php:
+
+/**
+ * Edit cities stored in the DB for members
+ *
+ * PHP version 5
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @version   CVS: $Id: EditCity.php,v 1.13 2010/07/14 23:27:59 jamie Exp $
+ * @link      http://demo.gaslightmedia.com
+ */
+
+/**
+ * Edit cities in the member database
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2008 Gaslight Media
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @version   CVS: $Id: EditCity.php,v 1.13 2010/07/14 23:27:59 jamie Exp $
+ */
+class Toolkit_Members_Admin_EditCity
+    extends Toolkit_Members_Auxiliary implements Toolkit_Form
+{
+       //      {{{      properties
+
+    /**
+     * Table name in DB
+     * @var    string
+     * @access public
+     */
+       public $tableName = 'city';
+
+    /**
+     * Form template
+     * @var    string
+     * @access protected
+     */
+       protected $formTemplate = 'editCity.tpl';
+
+    /**
+     * Image Server
+     * @var    object
+     * @access protected
+     */
+    protected $is;
+
+       //      }}}
+       //      {{{ __construct()
+
+       /**
+        * Class constructor
+        *
+     * @param PDO    $pdo         PHP Data Object
+     * @param string $formName    Form's name.
+     * @param string $method      (optional)Form's method defaults to 'POST'
+     * @param string $action      (optional)Form's action
+     * @param string $target      (optional)Form's target defaults to '_self'
+     * @param mixed  $attributes  (optional)Extra attributes for <form> tag
+     * @param bool   $trackSubmit (optional)Whether to track if the form was
+        *                                                        submitted by adding a special hidden field
+        *
+        * @access public
+        */
+       public function __construct(
+        PDO $pdo,
+        $formName,
+        $method = 'post',
+        $action = '',
+        $target = '',
+        $attributes = null,
+        $trackSubmit = false
+    ) {
+               parent::__construct(
+            $formName,
+            $method,
+            $action,
+            $target,
+            $attributes,
+            $trackSubmit
+        );
+
+        $this->dbh = $pdo;
+       }
+
+       //      }}}
+
+       //      {{{ checkCityName()
+
+    /**
+     * Check that the city name is not in use already
+     *
+     * @param string $name name to check
+     *
+     * @return boolean if the name is valid or not
+     * @access public
+     */
+       public function checkCityName($name)
+       {
+               try {
+                       //      If we're editing a city, they
+                       //      can save that city as its
+                       //      own name. so don't include that
+                       //      city in the check.
+                       if (isset($_GET['id'])) {
+                               $and = "AND city_id <> :id";
+                       }
+                       $sql = "
+                SELECT COUNT(*) AS total
+                  FROM {$this->tableName}
+                 WHERE city_name = :name
+                                 $and";
+                       $stmt = $this->dbh->prepare($sql);
+                       $stmt->bindParam(':name', $name, PDO::PARAM_STR);
+                       if (isset($_GET['id'])) {
+                               $stmt->bindParam(':id', $_GET['id'], PDO::PARAM_STR);
+                       }
+                       $stmt->execute();
+            $stmt->bindColumn('total', $total);
+                       $stmt->fetch();
+
+                       return !(bool) $total;
+               } catch (PDOException $e) {
+                       return Toolkit_Common::handleError($e);
+               }
+       }
+
+       //      }}}
+       //      {{{ configureDefaults()
+
+    /**
+     * Set up the default values for the form
+     *
+     * @return void
+     * @access public
+     */
+       public function configureDefaults()
+       {
+               if (isset($_GET['id'])) {
+                       try {
+                               $sql = "
+                                       SELECT *
+                                         FROM city
+                                        WHERE city_id = :id";
+                               $stmt = $this->dbh->prepare($sql);
+                               $stmt->bindParam(':id', $_GET['id'], PDO::PARAM_INT);
+                               $stmt->execute();
+                               $city = $stmt->fetch(PDO::FETCH_ASSOC);
+                               $defaults = array(
+                                       'city_name'                     => $city['city_name'],
+                                       'region_id'                     => $city['region_id'],
+                                       'state_id'                      => $city['state_id'],
+                                       'description'           => $city['description'],
+                                       'image_rmv'                     => "<image src=".MEMBER_THUMB."{$city['image']} />",
+                                       'uploaded_file_rmv'     => $city['image'],
+                                       'noCharLimit'       => true
+                               );
+
+                               //      If the user is editing a city, and no image has been uploaded yet.
+                               //      there is no point in showing an empty "current image" field, so remove
+                               //      that element from the form.
+                               if (empty($city['image']) && !$this->isSubmitted()) {
+                                       $this->removeElement('image_rmv', false);
+                                       $this->removeElement('remove_image', false);
+                               }
+                       } catch (PDOException $e) {
+                               $this->handleError($e);
+                       }
+               }
+
+               $this->setupDefaults($defaults);
+       }
+
+       //      }}}
+       //      {{{ configureElements()
+
+    /**
+     * Form element definitions
+     *
+     * @param Config_Container $c Configuration object
+     *
+     * @return void
+     * @access public
+     */
+       public function configureElements(Config_Container $c)
+       {
+        $e = array();
+        //  get reference to [listing type] section of config file
+        $config =& $c->getItem('section', 'conf');
+        $regionDirective =& $config->getItem('directive', 'regions');
+               //      All Grouped Elements are created here.
+
+               //      All Elements are created here.  This includes group element definitions.
+               $e[] = array(
+            'type' => 'header',
+            'req' => false,
+            'name' => 'cityInfoHdr',
+            'display' => 'Member Cities'
+        );
+               $e[] = array(
+            'type' => 'text',
+            'req' => true,
+            'name' => 'city_name',
+            'display' => 'City'
+        );
+               if ($regionDirective->getContent()) {
+                       $e[] = array(
+                'type' => 'select',
+                'req' => false,
+                'name' => 'region_id',
+                'display' => 'Region',
+                'opts' => $this->getRegions(),
+            );
+               }
+               $e[] = array(
+            'type' => 'select',
+            'req' => true,
+            'name' => 'state_id',
+            'display' => 'State',
+            'opts' => array('' => '-- Select --') + Toolkit_Common::getStates($this->dbh),
+        );
+               //      If we're editing a city, show the current
+               //      city image in the form.
+               if ($this->_hasImage()) {
+                       $e[] = array(
+                               'type'    => 'checkbox',
+                               'req'     => false,
+                               'name'    => 'remove_image_rmv',
+                               'display' => 'Remove Image'
+                       );
+                       $e[] = array(
+                               'type'    => 'static',
+                               'req'     => false,
+                               'name'    => 'image_rmv',
+                               'display' => 'Current Image'
+                       );
+            $e[] = array(
+                               'type' => 'hidden',
+                               'req'  => false,
+                               'name' => 'old_image_rmv'
+                       );
+               }
+               $e[] = array(
+                       'type'    => 'file',
+                       'req'     => false,
+                       'name'    => 'new_image_rmv',
+                       'display' => 'New Image',
+               );
+               $e[] = array(
+            'type' => 'textarea',
+            'req' => false,
+            'name' => 'description',
+            'display' => 'Description',
+            'opts' => array(
+                'id' => 'description',
+                'rows' => 7,
+                'cols' => 45
+            ),
+            'noCharLimit' => true
+        );
+
+               $this->setupElements($e);
+       }
+
+       //      }}}
+    //  {{{ configureForm()
+
+    /**
+     * Helper function to configure an entire form
+     *
+     * @param Config_Container $c Configuration object
+     *
+     * @return void
+     * @access public
+     */
+    public function configureForm(Config_Container $c)
+    {
+        $this->configureElements($c);
+        $this->configureRules();
+        $this->configureDefaults();
+    }
+
+    //  }}}
+       //      {{{ configureRules()
+
+    /**
+     * Form rule definitions
+     *
+     * @return void
+     * @access public
+     */
+       public function configureRules()
+       {
+        $r = array();
+
+               $mimeTypes = array(
+                       'image/jpe',
+                       'image/jpeg',
+                       'image/jpg',
+                       'image/jfif',
+                       'image/pjpeg',
+                       'image/pjp',
+                       'image/gif',
+                       'image/png',
+               );
+
+        $r[] = array(
+            'element' => 'new_image_rmv',
+            'message' => 'ERROR: Incorrect File Type (.gif, .png, .jpg) only!',
+            'type' => 'mimetype',
+            'format' => $mimeTypes,
+            'validation' => $this->validationType,
+            'reset' => false,
+            'force' => false
+        );
+               $r[] = array(
+            'element' => 'city_name',
+            'message' => 'ERROR: City already exists!',
+            'type' => 'callback',
+            'format' => array($this, 'checkCityName'),
+            'validation' => $this->validationType,
+            'reset' => false,
+            'force' => false
+        );
+
+               $this->setupRules($r);
+       }
+
+       //      }}}
+
+       //      {{{ deleteCity()
+
+    /**
+     * Delete city from db
+     *
+     * @param integer $id city id
+     *
+     * @return boolean result of removing city
+     * @access protected
+     */
+       protected function deleteCity($id)
+       {
+        $city = new Toolkit_Members_City($this->dbh);
+        return $city->deleteCity($id);
+       }
+
+       //      }}}
+
+       //      {{{ getRegions()
+
+    /**
+     * Get the regions stored in the DB
+     *
+     * @return array regions
+     * @access protected
+     */
+       protected function getRegions()
+       {
+               try {
+                       $sql = "
+                SELECT *
+                  FROM region
+                 ORDER BY region_name";
+                       foreach ($this->dbh->query($sql) as $row) {
+                               $regions[$row['region_id']] = $row['region_name'];
+                       }
+                       if (!empty($regions)) {
+                               $regions = array('' => '-- Select --') + $regions;
+                       } else {
+                               $regions = array('' => '-- No Regions Created Yet --');
+                       }
+
+                       return $regions;
+               } catch (PDOException $e) {
+                       return Toolkit_Common::handleError($e);
+               }
+       }
+
+       //      }}}
+
+       //      {{{ _hasImage()
+
+    /**
+     * Determine if the city record has a image
+     *
+     * @return boolean if the record has a image or not
+     * @access private
+     */
+       private function _hasImage()
+       {
+               try {
+            if (!is_numeric($_GET['id'])) {
+                return false;
+            }
+
+                       $sql = "
+                SELECT image
+                  FROM {$this->tableName}
+                 WHERE city_id = :city_id";
+
+                       $stmt = $this->dbh->prepare($sql);
+                       $stmt->bindParam(':city_id', $_GET['id'], PDO::PARAM_INT);
+                       $stmt->execute();
+                       $row = $stmt->fetch();
+
+                       $hasLogo = !is_null($row['image']);
+
+                       return $hasLogo;
+               } catch (PDOException $e) {
+                       return Toolkit_Common::handleError($e);
+               }
+       }
+
+       //      }}}
+
+       //      {{{ insertData()
+
+    /**
+     * Create a new city
+     *
+     * @param array $values Submitted form values
+     *
+     * @return boolean Result of insertion into DB
+     * @access protected
+     */
+       protected function insertData($values)
+       {
+        $city = new Toolkit_Members_City($this->dbh);
+        $city->setName($values['city_name']);
+        $city->setState($values['state_id']);
+        $city->setRegion($values['region_id']);
+        $city->setImage($values['image']);
+        $city->setDescription($values['description']);
+
+        return $city->createNewCity();
+       }
+
+       //      }}}
+
+       //      {{{ processData()
+
+    /**
+     * clean unneeded form elements out of the submitted values array
+     *
+     * @param array $values QuickForm submitted elements
+     *
+     * @return boolean Result of insert/update functions
+     * @access public
+     */
+       public function processData($values)
+       {
+               $this->processImage($values);
+
+               foreach ($values as $k => $v) {
+                       switch ($k) {
+                       case 'MAX_FILE_SIZE' :
+                               unset($values[$k]);
+                               break;
+
+                       default :
+                if (substr($k, -4) == '_rmv') {
+                                       unset($values[$k]);
+                               }
+                               break;
+                       }
+               }
+
+        $function = is_numeric($_GET['id']) ? 'updateData' : 'insertData';
+        return $this->$function($values);
+       }
+
+       //      }}}
+       //      {{{     processImage()
+
+    /**
+     * create or update city images
+     *
+     * @param array $values submitted form values
+     *
+     * @return object    Return description (if any) ...
+     * @access protected
+     */
+       protected function processImage($values)
+       {
+               //      Keep a tidy house.
+               //      There are 2 scenarios to deal w/ images:
+               //      1.  Removing image:
+               //              Delete the image from the image server
+               //              and set the values['image'] to null.
+               //      2.  New image:
+               //              Check to see if old_image_rmv holds a value,
+               //              if it does then remove that image
+               //              Upload the image and put new filename
+               //              into values['image'] variable.
+               $imgServer  = new Toolkit_Image_Server();
+
+               if ($values['remove_image_rmv'] == 1) {
+                       $imgServer->imageDelete($values['old_image_rmv']);
+                       $values['image'] = null;
+               }
+
+               //      If a new image is uploaded, have the image server
+               //      process that image and give us back the file name on
+               //      the server.
+               if ($values['new_image_rmv']['size'] > 0) {
+                       $imgName = $imgServer->imageUpload('new_image_rmv');
+                       $values['image'] = $imgName;
+                       $img = '<img alt="'. $imgName . '" src="'.MEMBER_PHOTOS . $imgName.'">';
+
+                       //      If the old_image_rmv key is set and not empty
+                       //      then we are replacing an existing image and did not
+                       //      check the remove image checkbox.
+                       //      remove this old image just for good house keeping.
+                       if (isset($values['old_image_rmv']) && !empty($values['old_image_rmv'])) {
+                               $imgServer->imageDelete($values['old_image_rmv']);
+                               //      update the elements on the form if we are replacing an existing image.
+                               if ($this->elementExists('old_image_rmv')) {
+                                       $e =& $this->getElement('old_image_rmv');
+                                       $e->setValue($imgName);
+                               }
+                               if ($this->elementExists('image_rmv')) {
+                                       $e =& $this->getElement('image_rmv');
+                                       $e->setValue($img);
+                               }
+                       } else {
+                               $source =& $this->createElement(
+                    'checkbox',
+                    'remove_image_rmv',
+                    'Remove Logo'
+                );
+                               $this->insertElementBefore($source, 'new_image_rmv');
+
+                               $source =& $this->addElement('hidden', 'old_image_rmv');
+                               $source->setValue($imgName);
+
+                               $source =& $this->createElement(
+                    'static',
+                    'image_rmv',
+                    'Current Logo'
+                );
+                               $source->setValue($img);
+                               $element = $this->insertElementBefore($source, 'new_image_rmv');
+
+                               if (PEAR::isError($element)) {
+                                       die ('there was an error uploading your file!');
+                               } else {
+                                       //  I don't know why, but the insertElementBefore
+                                       //  function was erasing the value we set earlier.
+                                       //  so just reset it to make double sure its there.
+                                       $element->setValue($img);
+                               }
+                       }
+               }
+
+               //      We clicked to remove the image and did not upload a new one.
+               if ($values['remove_image_rmv'] == 1 && $values['new_image_rmv']['size'] == 0) {
+                       if ($this->elementExists('remove_image_rmv')) {
+                               $this->removeElement('remove_image_rmv', false);
+                       }
+                       if ($this->elementExists('image_rmv')) {
+                               $this->removeElement('image_rmv', false);
+                       }
+                       if ($this->elementExists('old_image_rmv')) {
+                               $this->removeElement('old_image_rmv', false);
+                       }
+               }
+       }
+
+       //      }}}
+
+    //  {{{ setImageServer()
+
+    /**
+     * Set the image server object to use
+     *
+     * @param Toolkit_Image_Server $is Image Server Object
+     *
+     * @return void
+     * @access public
+     */
+    public function setImageServer(Toolkit_Image_Server $is)
+    {
+        $this->is = $is;
+    }
+
+    //  }}}
+
+       //      {{{ toHtml()
+
+       /**
+        * Renders the form for viewing
+        *
+        * This function validates the form if needed, and if it successfully
+        * validates attempts to insert or update the data record.
+        * If it is unsuccessful, it will return an error to the user
+        * informing them of what went wrong.
+        *
+        * @return string The compiled and filled form template.
+        * @access public
+        */
+       public function toHtml()
+       {
+               $listPage = MEDIA_BASE_URL . 'admin/members.php?rt=Cities&ac=listCities';
+               return parent::toHtml($listPage);
+       }
+
+       //      }}}
+
+       //      {{{ updateData()
+
+    /**
+     * Update the data for a region
+     *
+     * @param array $values submitted form results
+     *
+     * @return boolean result of sql update query
+     * @access protected
+     */
+       protected function updateData($values)
+       {
+               //      We aren't updating a region, we're deleting it.
+               if (array_key_exists('delete', $values)) {
+                       return $this->deleteCity($_GET['id']);
+               }
+
+        $city = new Toolkit_Members_City($this->dbh);
+        $city->setName($values['city_name']);
+        $city->setState($values['state_id']);
+        $city->setRegion($values['region_id']);
+        $city->setImage($values['image']);
+        $city->setDescription($values['description']);
+
+        return $city->updateCity($_GET['id']);
+       }
+
+       //      }}}
+}
+?>
diff --git a/Toolkit/Members/Admin/EditHtmlEmail.php b/Toolkit/Members/Admin/EditHtmlEmail.php
new file mode 100644 (file)
index 0000000..d94dc40
--- /dev/null
@@ -0,0 +1,375 @@
+<?php
+//     vim:set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker syntax=php:
+
+/**
+ * Controls Amenity definitions for the member db
+ *
+ * PHP version 5
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @version   CVS: $Id: EditHtmlEmail.php,v 1.10 2010/07/28 12:42:37 jamie Exp $
+ * @link      http://demo.gaslightmedia.com
+ */
+
+/**
+ * Handles creating / editing amenities that the members will use
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @link      http://demo.gaslightmedia.com
+ */
+class Toolkit_Members_Admin_EditHtmlEmail
+    extends Toolkit_Members_Auxiliary implements Toolkit_Form
+{
+       //      {{{     properties
+
+    /**
+     * Description for public
+     * @var    string
+     * @access public
+     */
+       public $tableName = 'member_newsletters';
+
+    /**
+     * Description for protected
+     * @var    string
+     * @access protected
+     */
+       protected $formTemplate = 'editHtmlEmail.tpl';
+
+       //      }}}
+       //      {{{ __construct()
+
+       /**
+        * Class constructor
+        *
+     * @param PDO    $pdo         PHP Data Object
+     * @param string $formName    Form's name.
+     * @param string $method      (optional)Form's method defaults to 'POST'
+     * @param string $action      (optional)Form's action
+     * @param string $target      (optional)Form's target defaults to '_self'
+     * @param mixed  $attributes  (optional)Extra attributes for <form> tag
+     * @param bool   $trackSubmit (optional)Whether to track if the form was
+        *                                                        submitted by adding a special hidden field
+        *
+        * @access public
+        */
+    public function __construct(
+        PDO $pdo,
+        $formName,
+        $method = 'post',
+        $action = '',
+        $target = '',
+        $attributes = null,
+        $trackSubmit = false
+    ) {
+        parent::__construct(
+            $formName,
+            $method,
+            $action,
+            $target,
+            $attributes,
+            $trackSubmit
+        );
+
+        $this->dbh = $pdo;
+       }
+
+       //      }}}
+
+       //      {{{ checkAmenityName()
+
+    /**
+     * Check that the amenity name is not in use already
+     *
+     * @param string $name name to check
+     *
+     * @return boolean if the name is valid or not
+     * @access public
+     */
+       public function checkSubjectName($name)
+       {
+               try {
+                       //      If we're editing a amenity, they
+                       //      can save that amenity as its
+                       //      own name. so don't include that
+                       //      amenity in the check.
+                       if (isset($_GET['id'])) {
+                               $and = "AND id <> :id";
+                       }
+                       $sql = "
+                SELECT count(*) AS total
+                  FROM {$this->tableName}
+                 WHERE subject = :name
+                                 $and";
+                       $stmt = $this->dbh->prepare($sql);
+                       $stmt->bindParam(':name', $name, PDO::PARAM_STR);
+                       if (isset($_GET['id'])) {
+                               $stmt->bindParam(':id', $_GET['id'], PDO::PARAM_STR);
+                       }
+                       $stmt->execute();
+            $stmt->bindColumn('total', $total);
+                       $stmt->fetch();
+
+                       return !(bool) $total;
+               } catch (PDOException $e) {
+                       return Toolkit_Common::handleError($e);
+               }
+       }
+
+       //      }}}
+       //      {{{ configureDefaults()
+
+    /**
+     * Set up the default values for the form
+     *
+     * @return void
+     * @access public
+     */
+       public function configureDefaults()
+       {
+        $d = array();
+
+               if (isset($_GET['id'])) {
+                       try {
+                               $sql = "
+                                       SELECT *
+                                         FROM {$this->tableName}
+                                        WHERE id = :id";
+                               $stmt = $this->dbh->prepare($sql);
+                               $stmt->bindParam(':id', $_GET['id'], PDO::PARAM_INT);
+                               $stmt->execute();
+                               $d = $stmt->fetch();
+                       } catch (PDOException $e) {
+                           Toolkit_Common::handleError($e);
+                       }
+               }
+
+               $this->setupDefaults($d);
+       }
+
+       //      }}}
+       //      {{{ configureElements()
+
+    /**
+     * Form element definitions
+     *
+     * @return void
+     * @access public
+     */
+       public function configureElements()
+       {
+               $e = array();
+               //      All Grouped Elements are created here.
+
+               //      All Elements are created here.
+               //      This includes group element definitions.
+               $e[] = array(
+                       'type'    => 'header',
+                       'req'     => false,
+                       'name'    => 'HtmlEmailInfoHdr',
+                       'display' => 'Html Newsletters'
+               );
+               $e[] = array(
+                       'type'    => 'advcheckbox',
+                       'req'     => false,
+                       'name'    => 'archived',
+                       'display' => 'Archived'
+               );
+               $e[] = array(
+                       'type'    => 'text',
+                       'req'     => true,
+                       'name'    => 'subject',
+                       'display' => 'Email Subject'
+               );
+               $e[] = array(
+            'type' => 'textarea',
+            'req' => false,
+            'name' => 'response',
+            'display' => 'Email Body',
+            'opts' => array(
+                'id' => 'response',
+                'rows' => 7,
+                'cols' => 45
+            ),
+                       'noCharLimit' => true
+        );
+
+               $this->setupElements($e);
+       }
+
+       //      }}}
+    //  {{{ configureForm()
+
+    /**
+     * Helper function to configure an entire form
+     *
+     * @return void
+     * @access public
+     */
+    public function configureForm()
+    {
+        $this->configureElements();
+        $this->configureRules();
+        $this->configureDefaults();
+    }
+
+    //  }}}
+       //      {{{ configureRules()
+
+    /**
+     * Form rule definitions
+     *
+     * @return void
+     * @access public
+     */
+       public function configureRules()
+       {
+        $r = array();
+
+               $r[] = array(
+            'element' => 'subject',
+            'message' => 'ERROR: Newsletter already exists!',
+            'type' => 'callback',
+            'format' => array($this, 'checkSubjectName'),
+            'validation' => $this->validationType,
+            'reset' => false,
+            'force' => false
+        );
+
+               $this->setupRules($r);
+       }
+
+       //      }}}
+
+       //      {{{ deleteAmenity()
+
+    /**
+     * Remove a region from the db
+     *
+     * @param integer $id region id
+     *
+     * @return boolean result of db query
+     * @access protected
+     */
+       protected function deleteHtmlEmail($id)
+       {
+        if (!is_numeric($id)) {
+            return false;
+        }
+               try {
+                       $sql = "
+                               DELETE FROM {$this->tableName}
+                                WHERE id = :id";
+                       $stmt = $this->dbh->prepare($sql);
+                       $stmt->bindParam(':id', $id, PDO::PARAM_INT);
+
+                       return $stmt->execute();
+               } catch (PDOException $e) {
+                       Toolkit_Common::handleError($e);
+               }
+       }
+
+       //      }}}
+
+       //      {{{ insertData()
+
+    /**
+     * Create a new amenity
+     *
+     * @param array $values Submitted form values
+     *
+     * @return boolean Result of insertion into DB
+     * @access protected
+     */
+       protected function insertData($values)
+       {
+        $values['last_update'] = date('m/d/Y');
+        $values['archived'] = ($values['archived']) ? $values['archived'] : 0;
+        $sql = Toolkit_Common::createSQLInsert(
+            $this->tableName,
+            array_keys($values)
+        );
+        try {
+            return Toolkit_Common::processQuery(
+                $this->dbh,
+                $this->tableName,
+                $sql,
+                $values
+            );
+        } catch (PDOException $e) {
+            return Toolkit_Common::handleError($e);
+        }
+       }
+
+       //      }}}
+
+       //      {{{ updateData()
+
+    /**
+     * Update the data for an amenity
+     *
+     * @param array $values submitted form results
+     *
+     * @return boolean result of sql update query
+     * @access protected
+     */
+       protected function updateData($values)
+       {
+           $values['last_update'] = date('m/d/Y');
+           $values['archived'] = ($values['archived']) ? $values['archived'] : 0;
+               //      We aren't updating a region, we're deleting it.
+               if (array_key_exists('delete', $values)) {
+                       return $this->deleteHtmlEmail($_GET['id']);
+               }
+
+        $sql = Toolkit_Common::createSQLUpdate(
+            $this->tableName,
+            array_keys($values),
+            array('id = :id')
+        );
+
+        $values['id'] = $_GET['id'];
+        try {
+            return Toolkit_Common::processQuery(
+                $this->dbh,
+                $this->tableName,
+                $sql,
+                $values
+            );
+        } catch (PDOException $e) {
+            return Toolkit_Common::handleError($e);
+        }
+       }
+
+       //      }}}
+
+       //      {{{ toHTML()
+
+       /**
+        * Renders the form for viewing
+        *
+        * This function validates the form if needed, and if it successfully
+        * validates attempts to insert or update the data record.
+        * If it is unsuccessful, it will return an error to the user
+        * informing them of what went wrong.
+        *
+        * @return string The compiled and filled form template.
+        * @access public
+        */
+       public function toHtml()
+       {
+               $listPage = MEDIA_BASE_URL . 'admin/members.php?rt=Newsletter&ac=listHtmlEmails';
+               return parent::toHtml($listPage);
+       }
+
+       //      }}}
+}
+?>
diff --git a/Toolkit/Members/Admin/EditPackages.php b/Toolkit/Members/Admin/EditPackages.php
new file mode 100644 (file)
index 0000000..1f11a05
--- /dev/null
@@ -0,0 +1,1573 @@
+<?php
+//    vim:set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker syntax=php:
+
+/**
+ * Handles the packages tab in the member record
+ *
+ * Controls setting up the add package form if applicable, and rendering
+ * each uploaded package edit form to edit/delete the package.
+ *
+ * PHP version 5
+ *
+ * @category  Toolkit
+ * @package   Members_Admin
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @version   CVS: $Id: EditPackages.php,v 1.20 2010/07/16 20:53:24 jamie Exp $
+ * @link      http://demo.gaslightmedia.com
+ * @see       Toolkit/Image/Server.php
+ */
+
+
+/**
+ * The image server processing class
+ */
+require_once BASE . 'Toolkit/Image/Server.php';
+
+/**
+ * Constructor class to setup the page layout
+ *
+ * this class determines if the user can upload any more packages to their
+ * account and if so renders the add package form.  It also controls
+ * rending the individual forms for each previously uploaded package.
+ *
+ * @category  Toolkit
+ * @package   Members_Admin
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @link      http://demo.gaslightmedia.com
+ */
+class Toolkit_Members_Admin_EditPackages
+{
+    //    {{{ properties
+
+    /**
+     * The table name in the database used to store the data
+     * @var string
+     * @access public
+     */
+    public $tableName = 'member_packages';
+
+    /**
+     * Template used to layout form when editing a package
+     * @var    string
+     * @access protected
+     */
+    protected $pageTemplate = 'editPackages.tpl';
+
+    /**
+     * What is the maximum caption length for packages
+     *
+     * @var    array
+     * @access public
+     * @static
+     */
+    static public $maxTitleLength = 60;
+
+    /**
+     * Objects that will go into the page (add form, edit package forms)
+     * @var    object
+     * @access protected
+     */
+    protected $page;
+
+    /**
+     * Description for protected
+     * @var    PDO
+     * @access protected
+     */
+    protected $dbh;
+
+    //    }}}
+
+    //  {{{ __construct()
+
+    /**
+     * Constructor
+     *
+     * @param PDO                 $pdo     PHP Data Object to use for DB calls
+     * @param HTML_Template_Flexy $tEngine Templating Engine
+     *
+     * @return void
+     * @access public
+     */
+    public function __construct(PDO $pdo, HTML_Template_Flexy $tEngine)
+    {
+        $this->dbh = $pdo;
+        $this->tEngine = $tEngine;
+    }
+
+    //  }}}
+
+    //    {{{    canAddPackages()
+
+    /**
+     * Determine if this member can have more packages added to their profile
+     *
+     * Load the entire package gallery into member via a linked list.
+     * Then return if the # of linked list nodes is smaller than
+     * the maximum limit of packages.
+     *
+     * @access protected
+     * @return boolean If the linked list is smaller than max packages allowed
+     */
+    protected function canAddPackages()
+    {
+        $membersConf = new Config;
+        $membersRoot =& $membersConf->parseConfig(
+            BASE . 'Toolkit/Members/config.ini',
+            'IniFile'
+        );
+        $memberPackageLimit = $membersRoot->getItem('section', 'conf')
+            ->getItem('directive', 'memberPackageLimit')
+            ->getContent();
+        if (   !$memberPackageLimit
+            || !filter_var($memberPackageLimit, FILTER_VALIDATE_INT)
+        ) {
+            return true;
+        }
+        $ll = new Toolkit_Members_Packages(null, $_GET['id']);
+        $ll->setDbh($this->dbh);
+        $ll->createMemberList();
+        return ($ll->getListSize() < $memberPackageLimit);
+    }
+
+    //    }}}
+
+    //    {{{    displayPage()
+
+    /**
+     * Displays the page to the screen
+     *
+     * @param Toolkit_Members_RecordNavigation $nav Record subnav object
+     *
+     * @return void
+     * @access public
+     */
+    public function displayPage(Toolkit_Members_RecordNavigation $nav)
+    {
+        echo $this->getPage($nav);
+    }
+
+    //    }}}
+
+    //    {{{    getPage()
+
+    /**
+     * Sets up the flexy template and returns the rendered page
+     *
+     * @param Toolkit_Members_RecordNavigation $nav record subnav object
+     *
+     * @return object rendered page
+     * @access public
+     */
+    public function getPage(Toolkit_Members_RecordNavigation $nav)
+    {
+        $this->page->nav = $nav->getPageNav();
+        $this->tEngine->compile($this->pageTemplate);
+        return $this->tEngine->bufferedOutputObject($this->page);
+    }
+
+    //    }}}
+
+    //    {{{    getUploadedPackages()
+
+    /**
+     * Get an array of package ids from the DB that have been uploaded for this member
+     *
+     * - Create a linked list of all the members packages
+     * - Walk through the linked list extracting the id from each node into an array
+     *
+     * @access protected
+     * @return array Ids of all uploaded packages for this member
+     */
+    protected function getUploadedPackages()
+    {
+        $packages = new Toolkit_Members_Packages(null, $_GET['id']);
+        $packages->setDbh($this->dbh);
+        $packages->createMemberList();
+        $packages->rewind();
+
+        $ids = array();
+        foreach ($packages as $i) {
+            //    Don't show the pending packages here.
+            if (!$i->getPending()) {
+                $ids[] = $i->getId();
+            }
+        }
+
+        return $ids;
+    }
+
+    //    }}}
+
+    //    {{{    setUpPage()
+
+    /**
+     * Sets up the page to manipulate packages for a member
+     *
+     * Checks if all the packages uploaded for a member (pending & non-pending)
+     * exceed or match the maximum # of packages allowed for each member to
+     * upload to their account.
+     *
+     * For every package that is already uploaded, create an edit-package form that
+     * will allow the user to update the caption or delete the package.
+     *
+     * @return void
+     * @access public
+     */
+    public function setUpPage()
+    {
+        $this->page = new StdClass;
+
+        //    Find out if we can still add packages to the record.
+        //    If we can, then add the upload form to the page for the member to see.
+        if ($this->canAddPackages()) {
+            $addForm = new AddAdminPackage(
+                $this->dbh,
+                'new_member_package',
+                'post',
+                '',
+                '',
+                null,
+                true
+            );
+
+            $addForm->configureForm();
+            $this->page->uploadForm = $addForm->toHtml($this->tEngine);
+        }
+
+        //    Find out if we have any packages already uploaded.
+        //    If we do, then add the edit package form to the page for each package
+        //    so the member can edit/delete their packages.
+        if ($packages = $this->getUploadedPackages()) {
+            $this->page->editForm = array();
+            while (list($i, $j) = each($packages)) {
+                $editForm = new EditAdminPackage(
+                    $this->dbh,
+                    "edit_member_package_$j",
+                    'post',
+                    '',
+                    '',
+                    array('id' => $j),
+                    true
+                );
+
+                $editForm->configureForm();
+                $this->page->editForm[] = $editForm->toHtml($this->tEngine);
+            }
+        }
+    }
+
+    //    }}}
+}
+
+/**
+ * Form to handle creating a new package in the members only area
+ *
+ * Handles inserting new package into db as a pending package and creating a
+ * tuple in the member_updates table which will allow the admin to
+ * approve/deny the new package request.
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @link      http://demo.gaslightmedia.com
+ */
+class AddAdminPackage extends Toolkit_FormBuilder
+{
+    //    {{{ properties
+
+    /**
+     * The table name in the database used to store the data
+     *
+     * @var string
+     * @access public
+     */
+    public $tableName = 'member_packages';
+
+    /**
+     * Some special forms dont utlize this stylesheet
+     * Allow classes to override this setting so it doesn't
+     * get included
+     *
+     * @var boolean
+     * @access protected
+     */
+    protected $includeContactStyleSheet = false;
+
+    /**
+     * The template used to render the form
+     *
+     * @var string
+     * @access protected
+     */
+    protected $formTemplate = 'addPackage.tpl';
+
+    /**
+     * Description for protected
+     * @var    array
+     * @access protected
+     */
+    protected $registeredRules = array();
+
+    /**
+     * Description for protected
+     * @var    string
+     * @access protected
+     */
+    protected $successMsg
+        = '<div id="form-success-top">
+            You successfully uploaded your package.
+           </div>';
+
+    /**
+     * Description for protected
+     * @var    array
+     * @access protected
+     */
+    protected $mimeTypes = array(
+        'image/jpe',
+        'image/jpeg',
+        'image/jpg',
+        'image/jfif',
+        'image/pjpeg',
+        'image/pjp',
+        'image/gif',
+        'image/png',
+    );
+
+    //    }}}
+    //    {{{ __construct()
+
+    /**
+     * Class constructor
+     *
+     * @param PDO    $pdo         PHP Data Object to use for DB calls
+     * @param string $formName    Form's name.
+     * @param string $method      (optional) Form's method defaults to 'POST'
+     * @param string $action      (optional) Form's action.
+     * @param string $target      (optional) Form's target defaults to '_self'
+     * @param mixed  $attributes  (optional) Extra attributes for <form> tag.
+     * @param bool   $trackSubmit (optional) Whether to track if the form was
+     *                                         submitted by adding a special hidden
+     *                                         field.
+     *
+     * @access public
+     * @see    Toolkit_Members_Admin_EditPackages
+     */
+    public function __construct(
+        PDO $pdo,
+        $formName,
+        $method = 'post',
+        $action = '',
+        $target = '',
+        $attributes = null,
+        $trackSubmit = false
+    ) {
+        parent::__construct(
+            $formName,
+            $method,
+            $action,
+            $target,
+            $attributes,
+            $trackSubmit
+        );
+        $this->dbh = $pdo;
+    }
+
+    //    }}}
+
+    //  {{{ configureForm()
+
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @return void
+     * @access public
+     */
+    public function configureForm()
+    {
+        $this->configureElements();
+        $this->configureFilters();
+        $this->configureRules();
+    }
+
+    //  }}}
+    //    {{{ configureElements()
+
+    /**
+     * Setup the elements to use on the form.
+     *
+     * @return void
+     * @access public
+     */
+    public function configureElements()
+    {
+        $e = array();
+        //    All Grouped Elements are created here.
+
+        //    All Elements are created here.  This includes group element definitions.
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => 'title',
+            'display' => 'Package Title',
+        );
+        $e[] = array(
+            'type'        => 'textarea',
+            'req'         => false,
+            'name'        => 'description',
+            'display'     => 'Package Description',
+            'opts'        => array('id' => 'descrAdd', 'class' => 'ckeditor'),
+            'noCharLimit' => true
+        );
+        $e[] = array(
+            'type'    => 'date',
+            'req'     => true,
+            'name'    => 'sdate',
+            'display' => 'Start Date',
+            'opts'    => array(
+                'format'           => 'm / d / Y',
+                'minYear'          => date('Y'),
+                'maxYear'          => date('Y') + 10,
+                'addEmptyOption'   => true,
+                'emptyOptionValue' => '',
+                'emptyOptionText'  => array(
+                    'm' => 'mm',
+                    'd' => 'dd',
+                    'Y' => 'yyyy',
+                ),
+            )
+        );
+        $e[] = array(
+            'type'    => 'date',
+            'req'     => true,
+            'name'    => 'edate',
+            'display' => 'End Date',
+            'opts'    => array(
+                'format'           => 'm / d / Y',
+                'minYear'          => date('Y'),
+                'maxYear'          => date('Y') + 10,
+                'addEmptyOption'   => true,
+                'emptyOptionValue' => '',
+                'emptyOptionText'  => array(
+                    'm' => 'mm',
+                    'd' => 'dd',
+                    'Y' => 'yyyy',
+                ),
+            )
+        );
+        $e[] = array(
+            'type'    => 'checkbox',
+            'req'     => false,
+            'name'    => 'remove_img_rmv',
+            'display' => 'Remove Image',
+        );
+        $e[] = array(
+            'type'    => 'static',
+            'req'     => false,
+            'name'    => 'curr_image',
+            'display' => 'Current Image',
+        );
+        $e[] = array(
+            'type'    => 'hidden',
+            'req'     => false,
+            'name'    => 'curr_image_rmv',
+        );
+        $e[] = array(
+            'type'    => 'file',
+            'req'     => false,
+            'name'    => 'image',
+            'display' => 'Upload a Package Photo / Image',
+        );
+        $e[] = array(
+            'type'    => 'submit',
+            'req'     => false,
+            'name'    => 'add_rmv',
+            'display' => 'Upload new package',
+            'opts'    => array('class' => 'submit')
+        );
+
+        $this->setupElements($e);
+    }
+
+    //    }}}
+    //    {{{ configureRules()
+
+    /**
+     * Configure rules for form
+     *
+     * @return void
+     * @access public
+     */
+    public function configureRules()
+    {
+        $r = array();
+
+        $checkDate = create_function('$d', '$d = implode("-", $d); return Validate::date($d, array("format" => "%n-%j-%Y"));');
+        $r[] = array(
+            'element'    => 'sdate',
+            'message'    => 'ERROR: Invalid Date!',
+            'type'       => 'callback',
+            'format'     => $checkDate,
+            'validation' => $this->validationType,
+            'reset'      => false,
+            'force'      => false
+        );
+        $r[] = array(
+            'element'    => 'edate',
+            'message'    => 'ERROR: Invalid Date!',
+            'type'       => 'callback',
+            'format'     => $checkDate,
+            'validation' => $this->validationType,
+            'reset'      => false,
+            'force'      => false
+        );
+        $r[] = array(
+            'element'    => 'image',
+            'message'    => 'ERROR: Incorrect File Type (.gif, .png, .jpg) only!',
+            'type'       => 'mimetype',
+            'format'     => $this->mimeTypes,
+            'validation' => $this->validationType,
+            'reset'      => false,
+            'force'      => false
+        );
+
+        $this->setupRules($r);
+    }
+
+    //    }}}
+    //    {{{ configureConstants()
+
+    /**
+     * Configure constants for form
+     *
+     * @return void
+     * @access public
+     */
+    public function configureConstants()
+    {
+        $c = array(
+            'remove_img_rmv' => false
+        );
+
+        $this->setupConstants($c);
+    }
+
+    //    }}}
+    //    {{{ configureFilters()
+
+    /**
+     * Configure filters for form
+     *
+     * @return void
+     * @access public
+     */
+    public function configureFilters()
+    {
+        $f = array();
+
+        $f[] = array(
+            'element' => '__ALL__',
+            'filter' => 'trim'
+        );
+
+        $this->setupFilters($f);
+    }
+
+    //    }}}
+
+    //  {{{ deleteImage()
+
+    /**
+     * Remove an image record
+     *
+     * @param Toolkit_Image_Server $is  Image Server Object
+     * @param string               $img image name
+     *
+     * @return object    Return description (if any) ...
+     * @access protected
+     */
+    protected function deleteImage(Toolkit_Image_Server $is, $img)
+    {
+        return $is->imageDelete($img);
+    }
+
+    //  }}}
+
+    //    {{{ insertData()
+
+    /**
+     * Create a new package in the db
+     *
+     * @param array &$values Form submitted values
+     *
+     * @return object    db result of adding package
+     * @access protected
+     */
+    protected function insertData(&$values)
+    {
+        try {
+            $sql = Toolkit_Common::createSQLInsert(
+                $this->tableName,
+                array_keys($values)
+            );
+
+            return Toolkit_Common::processQuery(
+                $this->dbh,
+                $this->tableName,
+                $sql,
+                $values
+            );
+        } catch (PDOException $e) {
+            return Toolkit_Common::handleError($e);
+        }
+    }
+
+     //    }}}
+
+    //    {{{ processData()
+
+    /**
+     * Handles setting up the from processing and which function to get it done
+     *
+     * @param array $values Submitted values from the form.
+     *
+     * @return void
+     * @access protected
+     */
+    protected function processData($values)
+    {
+        $cache = new Cache_Lite(Toolkit_Members::getCacheOptions());
+        $cache->remove("Member-{$_GET['id']}", 'Profile');
+
+        $e =& $this->getElement('curr_image_rmv');
+
+        $packages = new Toolkit_Members_Packages(null, $_GET['id']);
+        $packages->setDbh($this->dbh);
+        $packages->createMemberList();
+        $values['pos']       = $packages->getListSize() + 1;
+        $values['image'] = $e->getValue('curr_image_rmv');
+        $values['member_id'] = $_GET['id'];
+        $values['pending']   = 0;
+        $values['sdate'] = implode('-', $values['sdate']);
+        $values['edate'] = implode('-', $values['edate']);
+        unset($values['MAX_FILE_SIZE'],
+              $values['curr_image_rmv'],
+              $values['remove_img_rmv'],
+              $values['add_rmv']);
+
+        $this->tableMetaData = Toolkit_Common::getTableMetaData(
+            $this->dbh,
+            $this->tableName
+        );
+        $this->insertData($values);
+
+        $listPage = MEDIA_BASE_URL .
+            "admin/members.php?rt=Members&ac=editMember&tab=packages&id={$_GET['id']}";
+        header("Location: $listPage");
+    }
+
+    //    }}}
+
+    //    {{{ setupRenderers()
+
+    /**
+     * Set up the rendering engine we are going to use to display this form
+     *
+     * @param HTML_Template_Flexy &$tEngine Templating Engine
+     *
+     * @return void
+     * @access protected
+     */
+    protected function setupRenderers(HTML_Template_Flexy &$tEngine)
+    {
+        $renderer = new HTML_QuickForm_Renderer_ObjectFlexy($tEngine);
+
+        $this->accept($renderer);
+        $this->view              = new stdClass();
+        $this->view->showCurrImg = $this->showCurrImg;
+        $this->view->form        = $renderer->toObject();
+        $tEngine->compile($this->formTemplate);
+    }
+
+    //    }}}
+
+    //  {{{ validNewImg()
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @param array $newImg Parameter description (if any) ...
+     *
+     * @return mixed  Return description (if any) ...
+     * @access public
+     */
+    function validNewImg(array $newImg)
+    {
+        return (   is_numeric($newImg['size'])
+                && $newImg['size'] > 0
+                && in_array($newImg['type'], $this->mimeTypes));
+    }
+
+    //  }}}
+    //  {{{ removeOldImage()
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @param Toolkit_Image_Server $is     Parameter description (if any) ...
+     * @param string               $oldImg Parameter description (if any) ...
+     *
+     * @return void
+     * @access public
+     */
+    function removeOldImage(Toolkit_Image_Server $is, $oldImg)
+    {
+        $this->deleteImage($is, $oldImg);
+        if ($this->elementExists('curr_image_rmv')) {
+            $e =& $this->getElement('curr_image_rmv');
+            $e->setValue(null);
+            $this->_submitValues['curr_image_rmv'] = null;
+        }
+    }
+
+    //  }}}
+    //  {{{ syncCurrImage()
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @return void
+     * @access protected
+     */
+    protected function syncCurrImage()
+    {
+        $is = new Toolkit_Image_Server();
+
+        $delImg = $this->getSubmitValue('remove_img_rmv');
+        $oldImg = $this->getSubmitValue('curr_image_rmv');
+        $newImg = $this->getSubmitValue('image');
+
+        if ($delImg && $oldImg) {
+            $this->removeOldImage($is, $oldImg);
+            unset($oldImg);
+        } elseif ($oldImg && $this->validNewImg($newImg)) {
+            $this->removeOldImage($is, $oldImg);
+            unset($oldImg);
+        }
+
+        if ($this->validNewImg($newImg)) {
+            $image = $this->uploadImage($is, 'image');
+        } else {
+            $image = $oldImg;
+        }
+
+        if ($image) {
+            $this->updatePhotoElements($is, $image);
+            $this->showCurrImg = true;
+        }
+    }
+
+    //  }}}
+    //    {{{ toHtml()
+
+    /**
+     * Renders the form
+     *
+     * sets the page the form should be redirected to instead of coming back
+     * around to itself.
+     *
+     * @param HTML_Template_Flexy $tEngine Templating Engine
+     *
+     * @return string The rendered form
+     * @access public
+     */
+    public function toHtml(HTML_Template_Flexy $tEngine)
+    {
+        //    We need to validate (and freeze if needed)
+        //    before we render the form. That way the
+        //    template knows about any errors on the form.
+        $this->validated = $this->validate();
+
+        //  If they have submitted the form and uploaded a proper image
+        //  but some other element had an error, then we need to show
+        //  their uploaded image in the form
+        if ($this->isSubmitted()) {
+            $this->syncCurrImage();
+        }
+
+        $this->setupRenderers($tEngine);
+
+        if ($this->validated) {
+            $processed = $this->process(
+                array(&$this, 'processData'),
+                $this->mergeFiles
+            );
+        }
+
+        return $tEngine->bufferedOutputObject($this->view);
+    }
+
+    //    }}}
+
+    //  {{{ updatePhotoElements()
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @param Toolkit_Image_Server $is    Parameter description (if any) ...
+     * @param string               $image Parameter description (if any) ...
+     *
+     * @return void
+     * @access public
+     */
+    public function updatePhotoElements(Toolkit_Image_Server $is, $image)
+    {
+        //  Get the dimensions of the image
+        $dimensions = $is->getImageSize(MEMBER_PHOTOS . $image);
+        if (PEAR::isError($dimensions)) {
+            Toolkit_Common::handleError($dimensions);
+        }
+        list($w, $h) = $dimensions;
+        $s = MEMBER_PHOTOS . $image;
+
+        //  Set the image to show in the element
+        $e =& $this->getElement('curr_image');
+        $e->setText('<img width="'.$w.'" height="'.$h.'" src="'.$s.'">');
+
+        //  updated the hidden elements value to make sure it
+        //  holds the most up-to-date image name
+        $e =& $this->getElement('curr_image_rmv');
+        $e->setValue($image);
+    }
+
+    //  }}}
+    //  {{{ uploadImage()
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @param Toolkit_Image_Server $is    Parameter description (if any) ...
+     * @param string               $field Parameter description (if any) ...
+     *
+     * @return object    Return description (if any) ...
+     * @access protected
+     */
+    protected function uploadImage(Toolkit_Image_Server $is, $field)
+    {
+        return $is->imageUpload($field);
+    }
+
+    //  }}}
+}
+
+/**
+ * Form to handle editing/deleting existing packages in members only area
+ *
+ * Handles updating caption requests for a member or to remove a
+ * package from thier profile
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @link      http://demo.gaslightmedia.com
+ */
+class EditAdminPackage extends Toolkit_FormBuilder
+{
+    //    {{{ properties
+
+    /**
+     * The table name in the database used to store the data
+     *
+     * @var string
+     * @access public
+     */
+    public $tableName = 'member_packages';
+
+    /**
+     * Some special forms dont utlize this stylesheet
+     * Allow classes to override this setting so it doesn't
+     * get included
+     *
+     * @var boolean
+     * @access protected
+     */
+    protected $includeContactStyleSheet = false;
+
+    /**
+     * The template used to render the form
+     *
+     * @var string
+     * @access protected
+     */
+    protected $formTemplate = 'editPackage.tpl';
+
+    /**
+     * Id of package in db
+     * @var    integer
+     * @access protected
+     */
+    protected $packageId;
+
+    /**
+     * Description for protected
+     * @var    string
+     * @access protected
+     */
+    protected $successMsg
+        = '<div id="form-success-top">
+            You successfully updated your package.
+           </div>';
+
+    /**
+     * Any rules we want to register for this form
+     * @var    array
+     * @access protected
+     */
+    protected $registeredRules = array();
+
+    /**
+     * Description for protected
+     * @var    array
+     * @access protected
+     */
+    protected $mimeTypes = array(
+        'image/jpe',
+        'image/jpeg',
+        'image/jpg',
+        'image/jfif',
+        'image/pjpeg',
+        'image/pjp',
+        'image/gif',
+        'image/png',
+    );
+
+    //    }}}
+    //    {{{ __construct()
+
+    /**
+     * Class constructor
+     *
+     * @param PDO    $pdo         PHP Data Object to use for DB calls
+     * @param string $formName    Form's name.
+     * @param string $method      (optional) Form's method defaults to 'POST'
+     * @param string $action      (optional) Form's action.
+     * @param string $target      (optional) Form's target defaults to '_self'
+     * @param mixed  $attributes  (optional) Extra attributes for <form> tag.
+     * @param bool   $trackSubmit (optional) Whether to track if the form was
+     *                                         submitted by adding a special hidden
+     *                                         field.
+     *
+     * @access public
+     * @see    Toolkit_Members_Admin_EditPackages
+     */
+    public function __construct(
+        PDO $pdo,
+        $formName,
+        $method = 'post',
+        $action = '',
+        $target = '',
+        $attributes = null,
+        $trackSubmit = false
+    ) {
+        parent::__construct(
+            $formName,
+            $method,
+            $action,
+            $target,
+            $attributes,
+            $trackSubmit
+        );
+
+        $this->packageId = $attributes['id'];
+        $this->packages  = new Toolkit_Members_Packages(null, $_GET['id']);
+        $this->packages->setDbh($pdo);
+        $this->packages->createMemberList();
+        $this->dbh = $pdo;
+    }
+
+    //    }}}
+
+    //  {{{ configureForm()
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @return void
+     * @access public
+     */
+    public function configureForm()
+    {
+        $this->configureElements();
+        $this->configureDefaults();
+        $this->configureFilters();
+        $this->configureRules();
+    }
+
+    //  }}}
+    //    {{{ configureDefaults()
+
+    /**
+     * Setup the element default values for form
+     *
+     * @access public
+     * @see    Toolkit_FormBuilder::setupDefaults()
+     * @return void
+     */
+    public function configureDefaults()
+    {
+        $sql = "
+            SELECT *
+              FROM {$this->tableName}
+             WHERE id = {$this->packageId}";
+
+        $defaults = $this->dbh->query($sql)->fetch(PDO::FETCH_ASSOC);
+        $defaults['curr_image_rmv'] = $defaults['image'];
+        $img = '<img src="%s">';
+        $defaults['curr_image'] = sprintf($img, MEMBER_PHOTOS . $defaults['image']);
+        $this->showCurrImg = $defaults['image'];
+        $this->setupDefaults($defaults);
+    }
+
+    //    }}}
+    //    {{{ configureElements()
+
+    /**
+     * Setup the elements to use on the form.
+     *
+     * @access public
+     * @see    Toolkit_FormBuilder::setupElements()
+     * @return void
+     */
+    public function configureElements()
+    {
+        $e = array();
+        //    All Grouped Elements are created here.
+
+        //    All Elements are created here.  This includes group element definitions.
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => 'title',
+            'display' => 'Package Title',
+        );
+        $e[] = array(
+            'type'        => 'textarea',
+            'req'         => false,
+            'name'        => 'description',
+            'display'     => 'Package Description',
+            'opts'        => array('id' => 'descr' . $this->packageId, 'class' => 'ckeditor'),
+            'noCharLimit' => true
+        );
+        $e[] = array(
+            'type'    => 'date',
+            'req'     => true,
+            'name'    => 'sdate',
+            'display' => 'Start Date',
+            'opts'    => array(
+                'format'           => 'm / d / Y',
+                'minYear'          => date('Y'),
+                'maxYear'          => date('Y') + 10,
+                'addEmptyOption'   => true,
+                'emptyOptionValue' => '',
+                'emptyOptionText'  => array(
+                    'm' => 'mm',
+                    'd' => 'dd',
+                    'Y' => 'yyyy',
+                ),
+            )
+        );
+        $e[] = array(
+            'type'    => 'date',
+            'req'     => true,
+            'name'    => 'edate',
+            'display' => 'End Date',
+            'opts'    => array(
+                'format'           => 'm / d / Y',
+                'minYear'          => date('Y'),
+                'maxYear'          => date('Y') + 10,
+                'addEmptyOption'   => true,
+                'emptyOptionValue' => '',
+                'emptyOptionText'  => array(
+                    'm' => 'mm',
+                    'd' => 'dd',
+                    'Y' => 'yyyy',
+                ),
+            )
+        );
+        $e[] = array(
+            'type'    => 'checkbox',
+            'req'     => false,
+            'name'    => 'remove_img_rmv',
+            'display' => 'Remove Image',
+        );
+        $e[] = array(
+            'type'    => 'static',
+            'req'     => false,
+            'name'    => 'curr_image',
+            'display' => 'Current Image',
+        );
+        $e[] = array(
+            'type'    => 'hidden',
+            'req'     => false,
+            'name'    => 'curr_image_rmv',
+        );
+        $e[] = array(
+            'type'    => 'file',
+            'req'     => false,
+            'name'    => 'image',
+            'display' => 'Upload a Package Photo / Image',
+        );
+        $e[] = array(
+            'type'    => 'submit',
+            'req'     => false,
+            'name'    => 'add_rmv',
+            'display' => 'Update Package',
+            'opts'    => array('class' => 'submit')
+        );
+        $e[] = array(
+            'type'    => 'submit',
+            'req'     => false,
+            'name'    => 'remove_rmv',
+            'display' => 'Remove Package',
+            'opts'    => array('class' => 'submit')
+        );
+
+        $this->setupElements($e);
+    }
+
+    //    }}}
+    //    {{{ configureRules()
+
+    /**
+     * Configure rules for form
+     *
+     * @return void
+     * @access public
+     */
+    public function configureRules()
+    {
+        $r = array();
+
+        $checkDate = create_function('$d', '$d = implode("-", $d); return Validate::date($d, array("format" => "%n-%j-%Y"));');
+        $r[] = array(
+            'element'    => 'sdate',
+            'message'    => 'ERROR: Invalid Date!',
+            'type'       => 'callback',
+            'format'     => $checkDate,
+            'validation' => $this->validationType,
+            'reset'      => false,
+            'force'      => false
+        );
+        $r[] = array(
+            'element'    => 'edate',
+            'message'    => 'ERROR: Invalid Date!',
+            'type'       => 'callback',
+            'format'     => $checkDate,
+            'validation' => $this->validationType,
+            'reset'      => false,
+            'force'      => false
+        );
+        $r[] = array(
+            'element'    => 'image',
+            'message'    => 'ERROR: Incorrect File Type (.gif, .png, .jpg) only!',
+            'type'       => 'mimetype',
+            'format'     => $this->mimeTypes,
+            'validation' => $this->validationType,
+            'reset'      => false,
+            'force'      => false
+        );
+
+        $this->setupRules($r);
+    }
+
+    //    }}}
+    //    {{{ configureConstants()
+
+    /**
+     * Configure constants for form
+     *
+     * @return void
+     * @access public
+     */
+    public function configureConstants()
+    {
+        $c = array(
+            'remove_img_rmv' => false
+        );
+
+        $this->setupConstants($c);
+    }
+
+    //    }}}
+    //    {{{ configureFilters()
+
+    /**
+     * Configure filters for form
+     *
+     * @return void
+     * @access public
+     */
+    public function configureFilters()
+    {
+        $f = array();
+
+        $f[] = array(
+            'element' => '__ALL__',
+            'filter' => 'trim'
+        );
+
+        $this->setupFilters($f);
+    }
+
+    //    }}}
+
+    //  {{{ deleteImage()
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @param Toolkit_Image_Server $is  Parameter description (if any) ...
+     * @param string               $img Parameter description (if any) ...
+     *
+     * @return object    Return description (if any) ...
+     * @access protected
+     */
+    protected function deleteImage(Toolkit_Image_Server $is, $img)
+    {
+        return $is->imageDelete($img);
+    }
+
+    //  }}}
+
+    //    {{{ processData()
+
+    /**
+     * Handles setting up the from processing and which function to get it done
+     *
+     * @param array $values Submitted values from the form.
+     *
+     * @return void
+     * @access protected
+     */
+    protected function processData($values)
+    {
+        $cache = new Cache_Lite(Toolkit_Members::getCacheOptions());
+        $cache->remove("Member-{$_GET['id']}", 'Profile');
+
+        $e =& $this->getElement('curr_image_rmv');
+
+        $values['image'] = $e->getValue('curr_image_rmv');
+        $values['sdate'] = implode('-', $values['sdate']);
+        $values['edate'] = implode('-', $values['edate']);
+        unset($values['MAX_FILE_SIZE'],
+              $values['curr_image_rmv'],
+              $values['remove_img_rmv'],
+              $values['add_rmv']);
+
+        $this->tableMetaData = Toolkit_Common::getTableMetaData(
+            $this->dbh,
+            $this->tableName
+        );
+        $this->updateData($values);
+
+        $listPage = MEDIA_BASE_URL .
+            "admin/members.php?rt=Members&ac=editMember&tab=packages&id={$_GET['id']}";
+        header("Location: $listPage");
+    }
+
+    //    }}}
+
+    //    {{{ setupRenderers()
+
+    /**
+     * Set up the rendering engine we are going to use to display this form
+     *
+     * @param HTML_Template_Flexy &$tEngine Templating Engine
+     *
+     * @return void
+     * @access protected
+     */
+    protected function setupRenderers(HTML_Template_Flexy &$tEngine)
+    {
+        $renderer = new HTML_QuickForm_Renderer_ObjectFlexy($tEngine);
+
+        $this->accept($renderer);
+        $this->view              = new stdClass();
+        $this->view->showCurrImg = $this->showCurrImg;
+        $this->view->form        = $renderer->toObject();
+        $tEngine->compile($this->formTemplate);
+    }
+
+    //    }}}
+
+    //  {{{ validNewImg()
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @param array $newImg Parameter description (if any) ...
+     *
+     * @return mixed  Return description (if any) ...
+     * @access public
+     */
+    function validNewImg(array $newImg)
+    {
+        return (   is_numeric($newImg['size'])
+                && $newImg['size'] > 0
+                && in_array($newImg['type'], $this->mimeTypes));
+    }
+
+    //  }}}
+    //  {{{ removeOldImage()
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @param Toolkit_Image_Server $is     Parameter description (if any) ...
+     * @param string               $oldImg Parameter description (if any) ...
+     *
+     * @return void
+     * @access public
+     */
+    function removeOldImage(Toolkit_Image_Server $is, $oldImg)
+    {
+        $this->deleteImage($is, $oldImg);
+        if ($this->elementExists('curr_image_rmv')) {
+            $e =& $this->getElement('curr_image_rmv');
+            $e->setValue(null);
+            $this->_submitValues['curr_image_rmv'] = null;
+        }
+    }
+
+    //  }}}
+    //  {{{ syncCurrImage()
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @return void
+     * @access protected
+     */
+    protected function syncCurrImage()
+    {
+        $is = new Toolkit_Image_Server();
+
+        $delImg = $this->getSubmitValue('remove_img_rmv');
+        $oldImg = $this->getSubmitValue('curr_image_rmv');
+        $newImg = $this->getSubmitValue('image');
+
+        if ($delImg && $oldImg) {
+            $this->removeOldImage($is, $oldImg);
+            unset($oldImg);
+        } elseif ($oldImg && $this->validNewImg($newImg)) {
+            $this->removeOldImage($is, $oldImg);
+            unset($oldImg);
+        }
+
+        if ($this->validNewImg($newImg)) {
+            $image = $this->uploadImage($is, 'image');
+        } else {
+            $image = $oldImg;
+        }
+
+        if ($image) {
+            $this->updatePhotoElements($is, $image);
+            $this->showCurrImg = true;
+        }
+    }
+
+    //  }}}
+
+    //    {{{ toHtml()
+
+    /**
+     * Renders the form
+     *
+     * sets the page the form should be redirected to instead of coming back
+     * around to itself.
+     *
+     * @param HTML_Template_Flexy $tEngine Templating Engine
+     *
+     * @return string The rendered form
+     * @access public
+     */
+    public function toHtml(HTML_Template_Flexy $tEngine)
+    {
+        //    We need to validate (and freeze if needed)
+        //    before we render the form. That way the
+        //    template knows about any errors on the form.
+        $this->validated = $this->validate();
+
+        //  If they have submitted the form and uploaded a proper image
+        //  but some other element had an error, then we need to show
+        //  their uploaded image in the form
+        if ($this->isSubmitted()) {
+            $this->syncCurrImage();
+        }
+
+        $this->setupRenderers($tEngine);
+
+        if ($this->validated) {
+            $processed = $this->process(
+                array(&$this, 'processData'),
+                $this->mergeFiles
+            );
+        }
+
+        return $tEngine->bufferedOutputObject($this->view);
+    }
+
+    //    }}}
+
+    //    {{{    removePackage()
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @param int $id Parameter description (if any) ...
+     *
+     * @return object    Return description (if any) ...
+     * @access protected
+     */
+    protected function removePackage($id)
+    {
+        try {
+            //  need to delete the image associated w/ this package here.
+            $sql = "
+                DELETE FROM {$this->tableName}
+                 WHERE id = :id";
+
+            $stmt = $this->dbh->prepare($sql);
+            $stmt->bindParam(':id', $id, PDO::PARAM_INT);
+            return $stmt->execute();
+        } catch (PDOException $e) {
+            return Toolkit_Common::handleError($e);
+        }
+    }
+
+    //    }}}
+
+    //    {{{ updateData()
+
+    /**
+     * Update the package caption
+     *
+     * @param array $values Submitted form values
+     *
+     * @return boolean Result of updating the caption in the db
+     * @access public
+     */
+    protected function updateData($values)
+    {
+        try {
+            if (array_key_exists('remove_rmv', $values)) {
+                return $this->removePackage($this->packageId);
+            }
+            $sql = Toolkit_Common::createSQLUpdate(
+                $this->tableName,
+                array_keys($values),
+                array('id = :id')
+            );
+
+            $values['id'] = $this->packageId;
+            return Toolkit_Common::processQuery(
+                $this->dbh,
+                $this->tableName,
+                $sql,
+                $values
+            );
+        } catch (PDOException $e) {
+            return Toolkit_Common::handleError($e);
+        }
+    }
+
+    //    }}}
+    //  {{{ updatePhotoElements()
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @param Toolkit_Image_Server $is    Parameter description (if any) ...
+     * @param string               $image Parameter description (if any) ...
+     *
+     * @return void
+     * @access public
+     */
+    public function updatePhotoElements(Toolkit_Image_Server $is, $image)
+    {
+        //  Get the dimensions of the image
+        $dimensions = $is->getImageSize(MEMBER_PHOTOS . $image);
+        if (PEAR::isError($dimensions)) {
+            Toolkit_Common::handleError($dimensions);
+        }
+        list($w, $h) = $dimensions;
+        $s = MEMBER_PHOTOS . $image;
+
+        //  Set the image to show in the element
+        $e =& $this->getElement('curr_image');
+        $e->setText('<img width="'.$w.'" height="'.$h.'" src="'.$s.'">');
+
+        //  updated the hidden elements value to make sure it
+        //  holds the most up-to-date image name
+        $e =& $this->getElement('curr_image_rmv');
+        $e->setValue($image);
+    }
+
+    //  }}}
+    //  {{{ uploadImage()
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @param Toolkit_Image_Server $is    Parameter description (if any) ...
+     * @param string               $field Parameter description (if any) ...
+     *
+     * @return object    Return description (if any) ...
+     * @access protected
+     */
+    protected function uploadImage(Toolkit_Image_Server $is, $field)
+    {
+        return $is->imageUpload($field);
+    }
+
+    //  }}}
+}
+?>
diff --git a/Toolkit/Members/Admin/EditPhoto.php b/Toolkit/Members/Admin/EditPhoto.php
new file mode 100644 (file)
index 0000000..24bb215
--- /dev/null
@@ -0,0 +1,387 @@
+<?php
+//     vim:set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker syntax=php:
+
+/**
+ * Handles the photos tab in the member record
+ *
+ * Controls setting up the add photo form if applicable, and rendering
+ * each uploaded photo edit form to edit/delete the photo.
+ *
+ * PHP version 5
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @version   CVS: $Id: EditPhoto.php,v 1.4 2010/07/16 20:53:36 jamie Exp $
+ * @link      http://demo.gaslightmedia.com
+ * @see       Toolkit/Image/Server.php
+ */
+
+
+/**
+ * The image server processing class
+ */
+require_once BASE . 'Toolkit/Image/Server.php';
+
+/**
+ * Form to handle editing/deleting existing photos in members only area
+ *
+ * Handles updating caption requests for a member or to remove a
+ * photo from thier profile
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @link      http://demo.gaslightmedia.com
+ */
+class Toolkit_Members_Admin_EditPhoto extends Toolkit_FormBuilder
+{
+       //      {{{ properties
+
+       /**
+        * The table name in the database used to store the data
+        *
+        * @var string
+        * @access public
+        */
+       public $tableName = 'member_photos';
+
+       /**
+        * Some special forms dont utlize this stylesheet
+        * Allow classes to override this setting so it doesn't
+        * get included
+        *
+        * @var boolean
+        * @access protected
+        */
+       protected $includeContactStyleSheet = false;
+
+       /**
+        * The template used to render the form
+        *
+        * @var string
+        * @access protected
+        */
+       protected $formTemplate = 'editPhoto.tpl';
+
+    /**
+     * Id of photo in db
+     * @var integer
+     * @access protected
+     */
+       protected $photoId;
+
+    /**
+     * Success message when a photo gets successfully updated
+     * @var string
+     * @access protected
+     */
+    protected $successMsg = '
+        <div id="form-success-top">
+            You successfully updated your photo.
+        </div>';
+
+    /**
+     * Any rules we want to register for this form
+     * @var array
+     * @access protected
+     */
+       protected $registeredRules = array();
+
+       //      }}}
+       //      {{{ __construct()
+
+       /**
+        * Class constructor
+        *
+        * @param PDO                    $pdo         PHP Data Object
+     * @param Toolkit_Members_Photos $ll          Linked List of member photos
+        * @param string                 $formName    Form's name.
+        * @param string                 $method      (optional) Form's method
+        * @param string                 $action      (optional) Form's action.
+        * @param string                 $target      (optional) Form's target
+        * @param mixed                  $attributes  (optional) Extra attributes
+        * @param bool                   $trackSubmit (optional) Whether to track
+     *                                            if the form was submitted by
+        *                                                                                    adding a special hidden field.
+        *
+        * @access public
+        * @see    Toolkit_Members_Admin_EditPhotos
+        */
+       public function __construct(
+        PDO $pdo,
+        Toolkit_Members_Photos $ll,
+        $formName,
+        $method = 'post',
+        $action = '',
+        $target = '',
+        $attributes = null,
+        $trackSubmit = false
+    ) {
+               parent::__construct(
+            $formName,
+            $method,
+            $action,
+            $target,
+            $attributes,
+            $trackSubmit
+        );
+
+        $this->dbh     = $pdo;
+               $this->photoId = $attributes['id'];
+        $this->photos  = $ll;
+       }
+
+       //      }}}
+
+       //      {{{ configureDefaults()
+
+       /**
+        * Sets the defaults for elements in the form.
+        *
+        * @return void
+        * @access public
+        */
+       public function configureDefaults()
+       {
+               $photo    = $this->photos->findNode($this->photoId);
+               $defaults = array(
+                       'caption' => $photo->getCaption(),
+                       'pos'     => $photo->getPosition(),
+                       'pid'     => $photo->getId(),
+               );
+               $this->setupDefaults($defaults);
+       }
+
+       //      }}}
+       //      {{{ configureElements()
+
+       /**
+        * Setup the elements to use on the form.
+        *
+     * @param Config_Container $c application configuration
+        *
+        * @return void
+        * @access public
+        */
+       public function configureElements(Config_Container $c)
+       {
+               $positions = range(1, $this->photos->getListSize(true));
+               $options   = array_combine($positions, $positions);
+
+        $config =& $c->getItem('section', 'photos');
+        $maxLength =& $config->getItem('directive', 'maxCaptionLength');
+               //      All Grouped Elements are created here.
+
+               //      All Elements are created here.  This includes group element definitions.
+               $cur = $this->photos->findNode($this->photoId);
+
+               $elements[] = array(
+                       'type' => 'hidden',
+                       'req'  => false,
+                       'name' => 'pid'
+               );
+               $elements[] = array(
+                       'type' => 'text',
+                       'req'  => false,
+                       'name' => 'caption',
+            'display' => 'Image Caption',
+                       'opts' => array(
+                               'class'     => 'text',
+                               'maxlength' => $maxLength->getContent(),
+                       ),
+            'noCharLimit' => true
+               );
+               $elements[] = array(
+                       'type'    => 'select',
+                       'req'     => false,
+                       'name'    => 'pos',
+                       'display' => 'Position',
+                       'opts'    => $options,
+                       'att'     => array('id' => "pos{$cur->getPosition()}")
+               );
+               $elements[] = array(
+                       'type'    => 'submit',
+                       'req'     => false,
+                       'name'    => 'update',
+                       'display' => 'Update Photo',
+                       'opts'    => array('class' => 'submit')
+               );
+               $elements[] = array(
+                       'type'    => 'submit',
+                       'req'     => false,
+                       'name'    => 'delete',
+                       'display' => 'Delete Photo',
+                       'opts'    => array('class' => 'photoDelete')
+               );
+
+               $this->setupElements($elements);
+       }
+
+       //      }}}
+    //  {{{ configureForm()
+
+    /**
+        * helper function to set up entire form definition
+        *
+     * @param Config_Container $c application configuration
+        *
+        * @return void
+        * @access public
+     */
+    public function configureForm(Config_Container $c)
+    {
+        $this->configureElements($c);
+        $this->configureDefaults();
+               $this->configureRules($c);
+    }
+
+    //  }}}
+       //      {{{ configureRules()
+
+    /**
+     * Configures any rules we want to use on the form
+        *
+     * @param Config_Container $c application configuration
+     *
+     * @access public
+     * @return void
+     */
+       public function configureRules(Config_Container $c)
+       {
+               $r = array();
+
+        $config =& $c->getItem('section', 'photos');
+        $maxLength =& $config->getItem('directive', 'maxCaptionLength');
+
+               $r[] = array(
+                       'element'    => 'caption',
+                       'message'    => "ERROR: Please limit caption to {$maxLength->getContent()} characters!",
+                       'type'       => 'maxlength',
+                       'format'     => $maxLength->getContent(),
+                       'validation' => $this->validationType,
+                       'reset'      => false,
+                       'force'      => false
+               );
+               $this->setupRules($r);
+       }
+
+       //      }}}
+
+       //      {{{ processData()
+
+       /**
+        * Handles setting up the from processing and which function to get it done
+        *
+        * @param array $values submitted values from the form
+        *
+        * @return array Submitted values from the form.
+        * @access protected
+        */
+       protected function processData($values)
+       {
+               if (!$this->getSubmitValue('delete')) {
+            unset($values['update']);
+            $photo = $this->photos->findNode($values['pid']);
+            try {
+                //     This value might be removed by JS so make sure
+                //     its in place before you try to update the positions
+                //
+                //     This is taken out in JS instead of just hiding it
+                //     b/c if the image gets dragged and repositioned,
+                //     and then the caption is updated, the old positions
+                //     in the hidden select list will override all the
+                //     newly positioned photos.
+                //     This value might be removed by JS so make sure
+                //     its in place before you try to update the positions
+                $this->dbh->beginTransaction();
+                if (isset($values['pos']) && $values['pos'] != $photo->getPosition()) {
+                    $this->photos->moveNode($photo->getId(), $values['pos']);
+                }
+                $curCaption = $photo->getCaption();
+                if ($curCaption != $values['caption']) {
+                    $photo->setCaption($values['caption']);
+                }
+                return $this->dbh->commit();
+            } catch (PDOException $e) {
+                $this->dbh->rollback();
+                return Toolkit_Common::handleError($e);
+            }
+               }
+
+        header('Location:' . $this->getAttribute('action'));
+       }
+
+       //      }}}
+
+       //      {{{ setupRenderers()
+
+    /**
+     * Set up the rendering engine we are going to use to display this form
+     *
+     * @param HTML_Template_Flexy $tEngine Templating Engine
+     *
+     * @return void
+     * @access protected
+     */
+       protected function setupRenderers(HTML_Template_Flexy $tEngine)
+       {
+               $renderer = new HTML_QuickForm_Renderer_ObjectFlexy($tEngine);
+
+               $this->accept($renderer);
+               $this->view          = new stdClass();
+               $this->view->photoId = $this->photoId;
+               $this->view->form    = $renderer->toObject();
+               $photo               = $this->photos->findNode($this->photoId);
+               $this->view->pending = $photo->getPending();
+               $this->view->img_alt = $photo->getCaption();
+               $this->view->img_src = MEMBER_PHOTOS . $photo->getImage();
+               $tEngine->compile($this->formTemplate);
+       }
+
+       //      }}}
+
+       //      {{{ toHtml()
+
+       /**
+        * Renders the form
+        *
+        * sets the page the form should be redirected to instead of coming back
+        * around to itself.
+        *
+     * @param HTML_Template_Flexy  $tEngine Templating Engine
+     * @param Cache_Lite           $cache   Caching Engine
+     * @param Toolkit_Image_Server $is      Image Server
+     * @param Config_Container     $c       Application configuration
+     *
+        * @return string The rendered form
+        * @access public
+        */
+       public function toHtml(
+        HTML_Template_Flexy $tEngine,
+        Cache_Lite $cache,
+        Toolkit_Image_Server $is,
+        Config_Container $c
+    ) {
+               if ($this->validate()) {
+            $res = $cache->remove("Member-{$_GET['id']}", 'Profile');
+
+            if ($this->getSubmitValue('delete')) {
+                $this->photos->removeNode($is, $c, $this->getSubmitValue('pid'));
+            }
+
+                       $this->process(array(&$this, 'processData'), $this->mergeFiles);
+               }
+
+               $this->setupRenderers($tEngine);
+
+               return $tEngine->bufferedOutputObject($this->view);
+       }
+
+       //      }}}
+}
+?>
diff --git a/Toolkit/Members/Admin/EditRegion.php b/Toolkit/Members/Admin/EditRegion.php
new file mode 100644 (file)
index 0000000..55479a9
--- /dev/null
@@ -0,0 +1,334 @@
+<?php
+//     vim:set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker syntax=php:
+
+/**
+ * Edit member regions
+ *
+ * PHP version 5
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @version   CVS: $Id: EditRegion.php,v 1.11 2010/07/14 23:27:59 jamie Exp $
+ * @link      http://demo.gaslightmedia.com
+ */
+
+/**
+ * Edit Member Regions
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @link      http://demo.gaslightmedia.com
+ */
+class Toolkit_Members_Admin_EditRegion
+    extends Toolkit_Members_Auxiliary implements Toolkit_Form
+{
+       //      {{{ properties
+
+    /**
+     * Description for public
+     * @var    string
+     * @access public
+     */
+       public $tableName = 'region';
+
+    /**
+     * Description for protected
+     * @var    string
+     * @access protected
+     */
+       protected $formTemplate = 'editRegion.tpl';
+
+       //      }}}
+
+       //      {{{ checkRegionName()
+
+    /**
+     * Check that the region name is not in use already
+     *
+     * @param string $name name to check
+     *
+     * @return boolean if the name is valid or not
+     * @access public
+     */
+       public function checkRegionName($name)
+       {
+               try {
+                       //      If we're editing a region, they
+                       //      can save that region as its
+                       //      own name. so don't include that region
+                       //      in the check.
+                       if (isset($_GET['id'])) {
+                               $and = "AND region_id <> :rid";
+                       }
+                       $sql = "
+                               SELECT COUNT(*) AS total
+                                 FROM {$this->tableName}
+                                WHERE lower(region_name) = lower(:name)
+                                 $and";
+                       $stmt = $this->dbh->prepare($sql);
+                       $stmt->bindParam(':name', $name, PDO::PARAM_STR);
+                       if (isset($_GET['id'])) {
+                               $stmt->bindParam(':rid', $_GET['id'], PDO::PARAM_STR);
+                       }
+                       $stmt->execute();
+            $stmt->bindColumn('total', $total);
+                       $stmt->fetch();
+
+                       return !(bool) $total;
+               } catch (PDOException $e) {
+                       Toolkit_Common::handleError($e);
+               }
+       }
+
+       //      }}}
+       //      {{{ configureDefaults()
+
+    /**
+     * Set up the default values for the form
+     *
+     * @return void
+     * @access public
+     */
+       public function configureDefaults()
+       {
+        $d = array();
+
+               if (isset($_GET['id']) && ctype_digit($_GET['id'])) {
+                       try {
+                               $sql = "
+                                       SELECT *
+                                         FROM region
+                                        WHERE region_id = :cid";
+                               $stmt = $this->dbh->prepare($sql);
+                               $stmt->bindParam(':cid', $_GET['id'], PDO::PARAM_INT);
+                               $stmt->execute();
+                               $region = $stmt->fetch(PDO::FETCH_ASSOC);
+                               $d = array(
+                                       'region_name'   => $region['region_name'],
+                               );
+                       } catch (PDOException $e) {
+                Toolkit_Common::handleError($e);
+                       }
+               }
+
+               $this->setupDefaults($d);
+       }
+
+       //      }}}
+       //      {{{ configureElements()
+
+    /**
+     * Form element definitions
+     *
+        * @param Config_Container $c Application Configuration
+        *
+     * @return void
+     * @access public
+     */
+       public function configureElements(Config_Container $c)
+       {
+        $e = array();
+
+               $singularType = $c->getItem('section', 'region type')
+                       ->getItem('directive', 'singular')
+                       ->getContent();
+               $pluralType = $c->getItem('section', 'region type')
+                       ->getItem('directive', 'plural')
+                       ->getContent();
+               //      All Grouped Elements are created here.
+
+               //      All Elements are created here.  This includes group element definitions.
+               $e[] = array(
+            'type' => 'header',
+            'req' => false,
+            'name' => 'regionInfoHdr',
+            'display' => "Member {$pluralType}"
+        );
+               $e[] = array(
+            'type' => 'text',
+            'req' => true,
+            'name' => 'region_name',
+            'display' => "{$singularType} Name"
+        );
+
+               $this->setupElements($e);
+       }
+
+       //      }}}
+    //  {{{ configureForm()
+
+    /**
+     * Helper function to configure an entire form
+     *
+        * @param PDO              $dbh Database handler
+        * @param Config_Container $c   Application Configuration
+        *
+     * @return void
+     * @access public
+     */
+    public function configureForm(PDO $dbh, Config_Container $c)
+    {
+               $this->dbh = $dbh;
+        $this->configureElements($c);
+        $this->configureRules();
+        $this->configureDefaults();
+    }
+
+    //  }}}
+       //      {{{ configureRules()
+
+    /**
+     * Form rule definitions
+     *
+     * @return void
+     * @access public
+     */
+       public function configureRules()
+       {
+        $r = array();
+
+               $r[] = array(
+            'element' => 'region_name',
+            'message' => 'ERROR: Name already exists!',
+            'type' => 'callback',
+            'format' => array($this, 'checkRegionName'),
+            'validation' => $this->validationType,
+            'reset' => false,
+            'force' => false
+        );
+
+               $this->setupRules($r);
+       }
+
+       //      }}}
+
+       //      {{{ deleteRegion()
+
+    /**
+     * Remove a region from the db
+     *
+     * @param integer $id region id
+     *
+     * @return boolean result of db query
+     * @access protected
+     */
+       protected function deleteRegion($id)
+       {
+        if (!ctype_digit((string)$id)) {
+            return false;
+        }
+               try {
+                       $sql = "
+                               DELETE FROM {$this->tableName}
+                                WHERE region_id = :id";
+                       $stmt = $this->dbh->prepare($sql);
+                       $stmt->bindParam(':id', $id, PDO::PARAM_INT);
+
+                       return $stmt->execute();
+               } catch (PDOException $e) {
+                       Toolkit_Common::handleError($e);
+               }
+       }
+
+       //      }}}
+
+       //      {{{ insertData()
+
+    /**
+     * Create a new region
+     *
+     * @param array $values Submitted form values
+     *
+     * @return boolean Result of insertion into DB
+     * @access protected
+     */
+       protected function insertData($values)
+       {
+        $sql = Toolkit_Common::createSQLInsert(
+            $this->tableName,
+            array_keys($values)
+        );
+        try {
+            return Toolkit_Common::processQuery(
+                $this->dbh,
+                $this->tableName,
+                $sql,
+                $values
+            );
+        } catch (PDOException $e) {
+            return Toolkit_Common::handleError($e);
+        }
+       }
+
+       //      }}}
+
+       //      {{{ toHtml()
+
+       /**
+        * Renders the form for viewing
+        *
+        * This function validates the form if needed, and if it successfully
+        * validates attempts to insert or update the data record.
+        * If it is unsuccessful, it will return an error to the user
+        * informing them of what went wrong.
+        *
+        * @return string The compiled and filled form template.
+        * @access public
+        */
+       public function toHtml()
+       {
+               //      Regions & Counties are not finished to work correctly together
+               //      at the same time.
+
+               //      currently we've just been converting regions to be counties when needed.
+               $listPage = MEDIA_BASE_URL . 'admin/members.php?rt=Regions&ac=listRegions';
+               return parent::toHtml($listPage);
+       }
+
+       //      }}}
+
+       //      {{{ updateData()
+
+    /**
+     * Update the data for a region
+     *
+     * @param array $values submitted form results
+     *
+     * @return boolean result of sql update query
+     * @access protected
+     */
+       protected function updateData($values)
+       {
+               //      We aren't updating a region, we're deleting it.
+               if (array_key_exists('delete', $values)) {
+                       return $this->deleteRegion($_GET['id']);
+               }
+
+        $sql = Toolkit_Common::createSQLUpdate(
+            $this->tableName,
+            array_keys($values),
+            array('region_id = :id')
+        );
+
+        $values['id'] = $_GET['id'];
+        try {
+            return Toolkit_Common::processQuery(
+                $this->dbh,
+                $this->tableName,
+                $sql,
+                $values
+            );
+        } catch (PDOException $e) {
+            return Toolkit_Common::handleError($e);
+        }
+       }
+
+       //      }}}
+}
+?>
diff --git a/Toolkit/Members/Admin/ExportController.php b/Toolkit/Members/Admin/ExportController.php
new file mode 100644 (file)
index 0000000..10cbed6
--- /dev/null
@@ -0,0 +1,116 @@
+<?php
+/**
+ * ExportController.php
+ * 
+ * PHP version 5.2
+ * 
+ * @category  Toolkit
+ * @package   Members_Admin
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @version   SVN: $id$
+ * @link      <>
+ */
+
+/**
+ * Toolkit_Members_Admin_ExportController
+ * 
+ * Description of Toolkit_Members_Admin_ExportController
+ * 
+ * @category  Toolkit
+ * @package   Members_Admin
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @release   Release: $id$
+ * @link      <>
+ */
+class Toolkit_Members_Admin_ExportController
+       extends Toolkit_BaseControllerAbstract implements Toolkit_IController
+{
+       //      {{{     indexAction()
+    
+    /**
+     * Description for indexAction()
+     * 
+     * @return string
+     * @access public 
+     */
+       public function indexAction()
+       {
+               $GLOBALS['styleSheets'][] = MEDIA_BASE_URL . 'css/contactform.css';
+
+        HTTP_Session2::set('newSearch', true);
+               $searchForm = new Toolkit_Members_Admin_AdvancedSearch(
+            'advanced-record-search',
+                       'get',
+                       MEDIA_BASE_URL . 'admin/members.php',
+                       '',
+                       null,
+                       true
+               );
+
+        $searchForm->configureForm($this->registry->dbh, $this->registry->config);
+
+               $this->registry->controllerObject->content =
+                       $searchForm->toHtml($this->registry->dbh, $this->registry->config);
+               if ($results = $searchForm->getSearchResults()) {
+                       $GLOBALS['bottomScripts'][]
+                               = MEDIA_BASE_URL . 'Toolkit/Members/libjs/member-list.js';
+                       // Export File Form
+                       $export = new Toolkit_Members_Admin_ExportFileForm(
+                               'file_export',
+                               'POST',
+                               urldecode($_SERVER['REQUEST_URI']),
+                               '',
+                               null,
+                               true
+                       );
+
+                       $export->configureForm($this->registry->config);
+                       $this->registry->controllerObject->content .= $export->toHtml(
+                               $results,
+                               $this->registry->dbh,
+                               $this->registry->config
+                       );
+
+                       $membersList = new Toolkit_Members_Admin_AdvancedSearchDataGrid(
+                               $this->registry->dbh,
+                               50
+                       );
+                       if (   isset($_GET['d'])
+                               && $_GET['d'] == 't'
+                               && $memberId = filter_input(INPUT_GET, 'id', FILTER_VALIDATE_INT)
+                       ) {
+                               $membersList->removeMember(
+                                       $memberId,
+                                       new Toolkit_Image_Server()
+                               );
+                       }
+                       $membersList->setQuery($results);
+                       $membersList->setDefaultSort(array('sort_field' => 'ASC'));
+
+                       //  rendering engine to use
+                       $rEngine = new Structures_DataGrid_Renderer_Flexy();
+                       $rEngine->setContainer($this->registry->tEngine);
+
+                       $this->registry->controllerObject->content .=
+                               $membersList->toHtml($rEngine, 'listMembers.tpl');
+               }
+
+               $this->registry->controllerObject->topScripts
+                       = Toolkit_Common::getScripts($GLOBALS['topScripts']);
+               $this->registry->controllerObject->bottomScripts
+                       = Toolkit_Common::getScripts($GLOBALS['bottomScripts']);
+               $this->registry->controllerObject->styles
+                       = Toolkit_Common::getStyleSheets($GLOBALS['styleSheets']);
+
+               $this->registry->tEngine->compile('admin.tpl');
+               return $this->registry->tEngine->bufferedOutputObject(
+                       $this->registry->controllerObject
+               );
+       }
+
+       //      }}}
+}
diff --git a/Toolkit/Members/Admin/ExportFileForm.php b/Toolkit/Members/Admin/ExportFileForm.php
new file mode 100644 (file)
index 0000000..d061632
--- /dev/null
@@ -0,0 +1,207 @@
+<?php
+
+/**
+ * Admin search functionality for memberdb
+ *
+ * PHP version 5
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2009 Gaslight Media
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @version   CVS: $Id: ExportFileForm.php,v 1.8 2010/07/14 23:27:59 jamie Exp $
+ * @link      http://demo.gaslightmedia.com
+ */
+
+/**
+ * Form to search the members database
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2009 Gaslight Media
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @link      http://demo.gaslightmedia.com
+ */
+class Toolkit_Members_Admin_ExportFileForm
+    extends Toolkit_FormBuilder
+{
+    // {{{ configureConstants()
+
+    /**
+     * Form constant definitions
+     *
+     * @return void
+     * @access public
+     */
+    public function configureConstants()
+    {
+        $c = array(
+            'rt' => $_GET['rt'],
+            'ac' => $_GET['ac'],
+        );
+
+        $this->setupConstants($c);
+    }
+
+    // }}}
+    // {{{ configureDefaults()
+
+    /**
+     * Form default value definitions
+     *
+     * @return void
+     * @access public
+     */
+    public function configureDefaults()
+    {
+        $d = array('file_type' => 'csv');
+
+        $this->setupDefaults($d);
+    }
+
+    // }}}
+    // {{{ configureElements()
+
+    /**
+     * Form element definitions
+     *
+     * @param Config_Container $c Configuration object
+     *
+     * @return void
+     * @access public
+     */
+    public function configureElements(Config_Container $c)
+    {
+        $e = array();
+
+        //  get reference to [listing type] section of config file
+        $pluralType = $c->getItem('section', 'listing type')
+            ->getItem('directive', 'plural')
+            ->getContent();
+
+        //     All Grouped Elements are created here.
+        //     All Elements are created here.  This includes group element definitions.
+        $e[] = array(
+            'type' => 'hidden',
+            'req'  => false,
+            'name' => 'rt'
+        );
+        $e[]   = array(
+            'type' => 'hidden',
+            'req'  => false,
+            'name' => 'ac'
+        );
+        $e[]   = array(
+            'type'    => 'select',
+            'req'     => false,
+            'name'    => 'file_type',
+            'display' => 'CSV File Type',
+            'opts'    => array(
+                'csv'  => 'CSV (Comma Seperated)',
+                'tab'  => 'Tab Seperated',
+                'pipe' => 'Pipe Seperated',
+            )
+        );
+        $e[]   = array(
+            'type'    => 'submit',
+            'req'     => false,
+            'name'    => 'submit',
+            'display' => 'Export ' . $pluralType
+        );
+
+        $this->setupElements($e);
+    }
+
+    // }}}
+    //  {{{ configureForm()
+
+    /**
+     * Helper function to configure an entire form
+     *
+     * @param Config_Container $c Configuration object
+     *
+     * @return void
+     * @access public
+     */
+    public function configureForm(Config_Container $c)
+    {
+        $this->configureElements($c);
+        $this->configureConstants();
+        $this->configureDefaults();
+        $this->configureRules();
+    }
+
+    //  }}}
+    // {{{ configureRules()
+
+    /**
+     * Form rule definitions
+     *
+     * @return void
+     * @access public
+     */
+    public function configureRules()
+    {
+        $r = array();
+
+        $this->setupRules($r);
+    }
+
+    // }}}
+    // {{{     setupRenderers()
+    //  @codeCoverageIgnoreStart
+
+    /**
+     * Custom rendering templates for special fields on the form
+     *
+     * @return void
+     * @access protected
+     */
+    protected function setupRenderers()
+    {
+        parent::setupRenderers();
+        $renderer = & $this->defaultRenderer();
+        $tpl      = '<tr align="center"><td colspan="2">{element}</td></tr>';
+        $renderer->setElementTemplate($tpl, 'submit');
+    }
+
+    //  @codeCoverageIgnoreEnd
+    // }}}
+    // {{{     toHtml()
+
+    /**
+     * Call the rendering function to get the form in a string. If the form is
+     * submitting and validated it sets the sorting fields and calls exportAsFile
+     * method and exits
+     *
+     * @param array            $results Search Results
+     * @param PDO              $dbh     Database Connection
+     * @param Config_Container $c       Config Container (members)
+     *
+     * @access protected
+     * @return string $output The Form to be rendered or success msg.
+     */
+    public function toHtml(array $results, PDO $dbh, Config_Container $c)
+    {
+        $this->setupRenderers();
+
+        if ($this->validate()) {
+            $export = new Toolkit_Members_Admin_ExportMembers($dbh);
+            $export->setConfig($c);
+            $export->setQuery($results);
+            $export->setDefaultSort(
+                array(
+                    'sort2_field' => 'ASC',
+                    'sort_field'  => 'ASC'
+                )
+            );
+            $export->exportAsFile();
+        }
+
+        return parent::toHTML();
+    }
+
+    // }}}
+}
diff --git a/Toolkit/Members/Admin/ExportMembers.php b/Toolkit/Members/Admin/ExportMembers.php
new file mode 100644 (file)
index 0000000..11b10f0
--- /dev/null
@@ -0,0 +1,625 @@
+<?php
+
+/**
+ * ExportMembers.php
+ *
+ * PHP version 5
+ *
+ * The license text...
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2009 Gaslight Media
+ * @license   Gaslight Media
+ * @version   SVN: $Id:$
+ * @link      <>
+ */
+
+/**
+ * using our extension of the Pear Structures Datagrid set the
+ * renderer to CSV so we can send it out as a file.
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2009 Gaslight Media
+ * @license   Gaslight Media
+ * @release   Release: @package_version@
+ * @link      <>
+ */
+class Toolkit_Members_Admin_ExportMembers
+    extends Toolkit_DataGridBuilder
+{
+
+    /**
+     * Options to pass to DataGrid
+     *
+     * @var    array
+     * @access protected
+     */
+    protected $options;
+
+    /**
+     * Rendering options for DataGrid
+     *
+     * @var    array
+     * @access protected
+     */
+    protected $rendererOptions = array(
+        'buildFooter'       => true,
+        'buildHeader'       => true,
+        'delimiter'         => ',',
+        'enclosure'         => '"',
+        'encoding'          => 'ISO-8859-1',
+        'filename'          => false,
+        'fillWithEmpytRows' => false,
+        'lineBreak'         => "\n",
+        'numberAlign'       => true,
+        'saveToFile'        => false,
+        'useQuotes'         => true,
+    );
+
+    /**
+     * SQL query used to obtain the DataGrid
+     *
+     * @var    unknown
+     * @access protected
+     */
+    protected $sql;
+
+    /**
+     * How many records must exist in the Datagrid before the sort form shows up
+     *
+     * @var    integer
+     * @access protected
+     */
+    protected $sortableAfter = 10;
+
+    /**
+     * The HTML table id of the DataGrid
+     *
+     * @var    string
+     * @access protected
+     */
+    protected $tableId = 'dataGrid';
+
+    /**
+     * The HTML class name of the DataGrid
+     *
+     * @var    string
+     * @access protected
+     */
+    protected $tableClass = 'dataGrid';
+
+    /**
+     * The HTML id of the DataGrid sorting form (when present)
+     *
+     * @var    string
+     * @access protected
+     */
+    protected $sortFormId = 'gridSorter';
+
+    /**
+     * Message to display to users if no records were found
+     *
+     * @var            String
+     * @access Protected
+     * @see            Toolkit_DataGridBuilder::setNoRecordMessage()
+     */
+    protected $noRecMessage = 'No Records';
+
+    /**
+     * Configures the columns (fields) that will be used in our datagrid renderer.
+     *
+     * @return void
+     * @access public
+     */
+    protected function configureColumns()
+    {
+        //  get reference to [controlledCities] section of config file
+        $ctrlCities = $this->config
+            ->getItem('section', 'conf')
+            ->getItem('directive', 'controlledCities')
+            ->getContent();
+
+        $this->addColumn(
+            new Structures_DataGrid_Column(
+                'Member Name',
+                'member_name'
+            )
+        );
+
+        $this->addColumn(
+            new Structures_DataGrid_Column(
+                'Active',
+                'active'
+            )
+        );
+
+        $this->addColumn(
+            new Structures_DataGrid_Column(
+                'Street Address',
+                'street'
+            )
+        );
+
+        if ($ctrlCities) {
+            $this->addColumn(
+                new Structures_DataGrid_Column(
+                    'City',
+                    'city_name'
+                )
+            );
+        } else {
+            $this->addColumn(
+                new Structures_DataGrid_Column(
+                    'City',
+                    'city'
+                )
+            );
+        }
+
+        $this->addColumn(
+            new Structures_DataGrid_Column(
+                'State',
+                'state_name'
+            )
+        );
+
+        $this->addColumn(
+            new Structures_DataGrid_Column(
+                'Zip',
+                'zip'
+            )
+        );
+
+        $this->addColumn(
+            new Structures_DataGrid_Column(
+                'Phone',
+                'phone'
+            )
+        );
+
+        $this->addColumn(
+            new Structures_DataGrid_Column(
+                'Fax',
+                'fax'
+            )
+        );
+
+        $this->addColumn(
+            new Structures_DataGrid_Column(
+                'Toll Free Phone Number',
+                'toll_free'
+            )
+        );
+
+        $this->addColumn(
+            new Structures_DataGrid_Column(
+                'Website Address',
+                'url'
+            )
+        );
+
+        $this->addColumn(
+            new Structures_DataGrid_Column(
+                'Primary Contact First Name',
+                'primary_contact_fname'
+            )
+        );
+
+        $this->addColumn(
+            new Structures_DataGrid_Column(
+                'Primary Contact Last Name',
+                'primary_contact_lname'
+            )
+        );
+
+        $this->addColumn(
+            new Structures_DataGrid_Column(
+                'Primary Contact Email',
+                'process_email'
+            )
+        );
+
+        $this->addColumn(
+            new Structures_DataGrid_Column(
+                'Email on Website',
+                'member_contact_email'
+            )
+        );
+
+        $this->addColumn(
+            new Structures_DataGrid_Column(
+                'Mailing Street Address',
+                'mailing_address',
+                null,
+                null,
+                null,
+                array($this, 'mailingAddress')
+            )
+        );
+
+        $this->addColumn(
+            new Structures_DataGrid_Column(
+                'Mailing City',
+                'mailing_city',
+                null,
+                null,
+                null,
+                array($this, 'mailingCity')
+            )
+        );
+
+        $this->addColumn(
+            new Structures_DataGrid_Column(
+                'Mailing State',
+                'mailing_state_name',
+                null,
+                null,
+                null,
+                array($this, 'mailingState')
+            )
+        );
+
+        $this->addColumn(
+            new Structures_DataGrid_Column(
+                'Mailing Zip',
+                'mailing_zip',
+                null,
+                null,
+                null,
+                array($this, 'mailingZip')
+            )
+        );
+
+        $this->addColumn(
+            new Structures_DataGrid_Column(
+                'Join Date',
+                'join_date'
+            )
+        );
+
+        $this->addColumn(
+            new Structures_DataGrid_Column(
+                'Create Date',
+                'create_date'
+            )
+        );
+        $this->addColumn(
+            new Structures_DataGrid_Column(
+                'Member Contact',
+                'sort2_field',
+                null,
+                null,
+                null,
+                array($this, 'isMemberContact')
+            )
+        );
+        $categories = $this->getMemberCategories();
+        foreach ($categories as $parent => $cat) {
+            foreach ($cat as $catid => $subCat) {
+                $this->addColumn(new Structures_DataGrid_Column(
+                    $subCat,
+                    $subCat,
+                    null,
+                    null,
+                    null,
+                    array($this, 'renderMemberCategory'),
+                    array('id' => $catid)
+                ));
+            }
+        }
+    }
+
+    public function renderMemberCategory($data, $args = array())
+    {
+        $member_categories = $data['record']['member_categories'];
+        if ($member_categories) {
+            $member_categories = explode(',', $member_categories);
+        } else {
+            return null;
+        }
+        return (is_array($member_categories)
+            && in_array($args['id'], $member_categories))
+            ? $data['fieldName']
+            : null;
+    }
+
+    /**
+     * Returns true if member contact field (sort2_field) is set
+     *
+     * @param array $data Array of data from structure datagrid
+     *
+     * @return string
+     * @access public
+     */
+    public function isMemberContact($data)
+    {
+        return
+            ($data['record']['sort2_field'])
+            ? 'Yes'
+            : '';
+    }
+
+
+    /**
+     * Returns mailing Address
+     *
+     * @param array $data Array of data from structure datagrid
+     *
+     * @return string
+     * @access public
+     */
+    public function mailingAddress($data)
+    {
+        return
+            ($data['record']['mailing_address'])
+            ? $data['record']['mailing_address']
+            : $data['record']['street'];
+    }
+
+    /**
+     * Returns mailing City
+     *
+     * @param array $data Array of data from structure datagrid
+     *
+     * @return string
+     * @access public
+     */
+    public function mailingCity($data)
+    {
+        if ($data['record']['mailing_city'] != '') {
+            $city = $data['record']['mailing_city'];
+        } else if ($data['record']['city_name'] != '') {
+            $city = $data['record']['city_name'];
+        } else {
+            $city = '';
+        }
+        return $city;
+    }
+
+    /**
+     * Returns mailing State
+     *
+     * @param array $data Array of data from structure datagrid
+     *
+     * @return string
+     * @access public
+     */
+    public function mailingState($data)
+    {
+        return
+            ($data['record']['mailing_state_name'])
+            ? $data['record']['mailing_state_name']
+            : $data['record']['state_name'];
+    }
+
+    /**
+     * Returns the mailing zipcode
+     *
+     * @param array $data Array of data from structure datagrid
+     *
+     * @return string
+     * @access public
+     */
+    public function mailingZip($data)
+    {
+        return
+            ($data['record']['mailing_zip'])
+            ? $data['record']['mailing_zip']
+            : $data['record']['zip'];
+    }
+
+    /**
+     * throws out headers to the browser so the file can be downloaded
+     * there's a case for IE browsers which may change in future depending on
+     * Microsoft Whim:(
+     *
+     * @access public
+     * @return void
+     */
+    public function exportAsFile()
+    {
+        // if the file is other than csv then set delimeter
+        if ($_REQUEST['file_type']) {
+            switch ($_REQUEST['file_type']) {
+            case "pipe":
+                $delimiter = "|";
+                break;
+            case "tab":
+                $delimiter = "\t";
+                break;
+            default:
+                $delimiter = ",";
+                break;
+            }
+            $this->rendererOptions['delimiter'] = $delimiter;
+        }
+        $fileOut   = $this->toCSV();
+        if (ini_get('zlib.output_compression')) {
+            ini_set('zlib.output_compression', 'Off');
+        }
+        header("Content-Type: application/force-download\n");
+        /* Correction for the stupid MSIE thing */
+        if (strstr(getenv('HTTP_USER_AGENT'), 'MSIE')) {
+            header("Content-Disposition: inline; filename=\"Member-Export.csv\"");
+        } else {
+            header("Content-Disposition: attachment; filename=\"Member-Export.csv\"");
+        }
+        echo $fileOut;
+        exit();
+    }
+
+    /**
+     * set the configuration object
+     *
+     * @param Config_Container $c App configuration
+     *
+     * @return void
+     * @access public
+     */
+    function setConfig(Config_Container $c)
+    {
+        $this->config = $c;
+    }
+
+    /**
+     * Sets the sql query to use in the DataGrid to get the results
+     *
+     * @param array $ids Array of member id's
+     *
+     * @return void
+     * @access public
+     */
+    public function setQuery(array $ids)
+    {
+        $memberIds = implode(', ', $ids);
+        $sql       = "
+                       SELECT m.*, LOWER(m.member_name) AS sort_field, c1.city_name,
+                              s1.state_name, c2.city_name AS mailing_city,
+                                  s2.state_name AS mailing_state_name,0 as sort2_field
+              FROM member m
+              LEFT JOIN city c1 ON m.city_id           = c1.city_id
+              LEFT JOIN state s1 ON m.state_id         = s1.state_id
+              LEFT JOIN city c2 ON m.mailing_city_id   = c2.city_id
+              LEFT JOIN state s2 ON m.mailing_state_id = s2.state_id
+             WHERE member_id IN ($memberIds)";
+
+        parent::setQuery($sql);
+    }
+    protected function getMemberCategories()
+    {
+        $categories = array();
+        try {
+            $dbh = Toolkit_Database::getInstance();
+            $sql = "
+              SELECT category_id, name
+                FROM category
+               WHERE parent_id = :parent
+            ORDER BY name";
+            $getSubCats = $dbh->prepare($sql);
+            $sql = "
+              SELECT category_id
+                FROM category
+               WHERE parent_id = 0
+            ORDER BY name";
+            $stmt = $dbh->query($sql);
+            while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                $getSubCats->bindParam(
+                    ':parent',
+                    $row['category_id'],
+                    PDO::PARAM_INT
+                );
+                $getSubCats->execute();
+                while ($subRow = $getSubCats->fetch(PDO::FETCH_ASSOC)) {
+                    $categories[$row['category_id']][$subRow['category_id']]
+                        = $subRow['name'];
+                }
+            }
+        } catch(PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+        return $categories;
+    }
+
+    protected function setMemberCategories($memberId)
+    {
+        $categories = array();
+        try {
+            $dbh = Toolkit_Database::getInstance();
+            $sql = "
+            SELECT category_id
+              FROM member_category
+             WHERE member_id = :member_id";
+            $stmt = $dbh->prepare($sql);
+            $stmt->bindParam(
+               ':member_id',
+                $memberId,
+                PDO::PARAM_INT
+            );
+            $stmt->execute();
+            while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                $categories[] = $row['category_id'];
+            }
+            return implode(',', $categories);
+        } catch(PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+    }
+    /**
+     * returns a CSV file of the datagrid
+     *
+     * @return string|boolean
+     * @access public
+     * @throws PEAR_Error
+     */
+    public function toCSV()
+    {
+        $this->configureColumns();
+        $members = array();
+        try {
+            $dbh         = Toolkit_Database::getInstance();
+            $sql         = "
+            SELECT m.member_id,m.member_name,
+                   LOWER(m.member_name) AS sort_field,
+                   1 as sort2_field,
+                   false as active,
+                   '' as street,
+                   '' as city_name,
+                   '' as state_name,
+                   '' as zip,
+                   mc.phone,
+                   '' as fax,
+                   '' as toll_free,
+                   '' as url,
+                   mc.fname as primary_contact_fname,
+                   mc.lname as primary_contact_lname,
+                   mc.email as process_email,
+                   '' as member_contact_email,
+                   '' as mailing_address,
+                   '' as mailing_city,
+                   '' as mailing_state,
+                   '' as mailing_zip,
+                   '' as join_date,
+                   '' as create_date
+              FROM member_contacts mc, member m
+             WHERE mc.send_mail = 't'
+               AND mc.member_id = :mid
+               AND mc.member_id = m.member_id";
+            $getContacts = $dbh->prepare($sql);
+            $stmt        = $dbh->query($this->sql);
+            while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                $row['member_categories'] = $this->setMemberCategories($row['member_id']);
+                $members[] = $row;
+                $getContacts->bindParam(
+                    ':mid', $row['member_id'], PDO::PARAM_INT
+                );
+                $getContacts->execute();
+                while($row2 = $getContacts->fetch(PDO::FETCH_ASSOC)) {
+                    $row2['member_categories'] = null;
+                    $members[] = $row2;
+                }
+            }
+            $bind      = $this->bind($members, $this->options, 'Array');
+        } catch (PDOException $e) {
+            return Toolkit_Common::handleError($e);
+        }
+
+        if (PEAR::isError($bind)) {
+            return Toolkit_Common::handleError($bind);
+        } elseif (($recCount = $this->getRecordCount()) > 0) {
+            $this->setRendererOptions($this->rendererOptions);
+            $csv = $this->getOutput(DATAGRID_RENDER_CSV, $this->rendererOptions);
+            if (PEAR::isError($gridBody)) {
+                return Toolkit_Common::handleError($gridBody);
+            }
+
+            return $csv;
+        } else {
+            return false;
+        }
+    }
+
+}
diff --git a/Toolkit/Members/Admin/IndexController.php b/Toolkit/Members/Admin/IndexController.php
new file mode 100644 (file)
index 0000000..548e3d6
--- /dev/null
@@ -0,0 +1,93 @@
+<?php
+/**
+ * IndexController.php
+ * 
+ * PHP versions 4 and 5
+ * 
+ * @category  Toolkit
+ * @package   Members_Admin
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2009 Gaslight Media
+ * @license   Gaslight Media
+ * @version   CVS: $Id: Newsletters.php,v 1.9 2009/09/16 19:00:58 matrix Exp $
+ * @link      http://demo.gaslightmedia.com
+ */
+
+/**
+ * Toolkit_Members_Admin_IndexController
+ * 
+ * Description for Toolkit_Members_Admin_IndexController
+ * 
+ * @category  Toolkit
+ * @package   Members_Admin
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2009 Gaslight Media
+ * @license   Gaslight Media
+ * @link      http://demo.gaslightmedia.com
+ */
+class Toolkit_Members_Admin_IndexController
+       extends Toolkit_BaseControllerAbstract implements Toolkit_IController
+{
+       //      {{{     indexAction()
+
+    /**
+     * Main action for controller. Runs admin template, list out all regions
+     * 
+     * @return string
+     * @access public 
+     */
+       public function indexAction()
+       {
+               $GLOBALS['styleSheets'][]   = MEDIA_BASE_URL . 'css/contactform.css';
+               $GLOBALS['bottomScripts'][]
+                       = MEDIA_BASE_URL . 'Toolkit/Members/libjs/member-list.js';
+
+        HTTP_Session2::set('newSearch', true);
+               $search = new Toolkit_Members_Admin_BasicSearch(
+            'record_search',
+                       'get',
+                       MEDIA_BASE_URL . 'admin/members.php',
+                       '',
+                       null,
+                       true
+               );
+
+        $search->configureForm($this->registry->dbh, $this->registry->config);
+
+               $membersList = new Toolkit_Members_Admin_ListMembers(
+                       $this->registry->dbh,
+                       50
+               );
+               $memberId     = filter_input(INPUT_GET, 'id', FILTER_VALIDATE_INT);
+               $deleteMember = filter_input(INPUT_GET, 'd', FILTER_SANITIZE_STRING);
+
+               if ($memberId && $deleteMember == 't') {
+            $membersList->removeMember($memberId, new Toolkit_Image_Server());
+        }
+
+        $membersList->setQuery($this->registry->config);
+        $membersList->setDefaultSort(array('sort_field' => 'ASC'));
+
+        //  rendering engine to use
+        $rEngine = new Structures_DataGrid_Renderer_Flexy();
+        $rEngine->setContainer($this->registry->tEngine);
+
+               $this->registry->controllerObject->content =
+                       $search->toHtml() .
+                   $membersList->toHtml($rEngine, 'listMembers.tpl');
+
+               $this->registry->controllerObject->topScripts
+                       = Toolkit_Common::getScripts($GLOBALS['topScripts']);
+               $this->registry->controllerObject->bottomScripts
+                       = Toolkit_Common::getScripts($GLOBALS['bottomScripts']);
+               $this->registry->controllerObject->styles
+                       = Toolkit_Common::getStyleSheets($GLOBALS['styleSheets']);
+
+               $this->registry->tEngine->compile('admin.tpl');
+               return $this->registry->tEngine->bufferedOutputObject(
+                       $this->registry->controllerObject
+               );
+       }
+
+       //      }}}
+}
diff --git a/Toolkit/Members/Admin/InvoicingController.php b/Toolkit/Members/Admin/InvoicingController.php
new file mode 100644 (file)
index 0000000..352a323
--- /dev/null
@@ -0,0 +1,245 @@
+<?php
+/**
+ * InvoicingController.php
+ * 
+ * PHP version 5.2
+ * 
+ * @category  Toolkit
+ * @package   Members_Admin
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @version   SVN: $id$
+ * @link      <>
+ */
+
+/**
+ * Toolkit_Members_Admin_InvoicingController
+ * 
+ * Description of Toolkit_Members_Admin_InvoicingController
+ * 
+ * @category  Toolkit
+ * @package   Members_Admin
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @release   Release: $id$
+ * @link      <>
+ */
+class Toolkit_Members_Admin_InvoicingController
+       extends Toolkit_BaseControllerAbstract
+    implements Toolkit_IController
+{
+    
+    /**
+     * Description of createInvoicesAction()
+     * 
+     * @return mixed
+     * @access public 
+     */
+    public function createInvoicesAction()
+    {
+        $dbh = Toolkit_Database::getInstance();
+        $rt = filter_input(
+            INPUT_GET,
+            'rt',
+            FILTER_SANITIZE_STRING
+        );
+        $ac = filter_input(
+            INPUT_GET,
+            'ac',
+            FILTER_SANITIZE_STRING
+        );
+        $tab = filter_input(
+            INPUT_GET,
+            'tab',
+            FILTER_SANITIZE_STRING
+        );
+        $filterForm = new Toolkit_Members_Billing_InvoiceFilterForm(
+            'filter-form',
+            'post',
+            MEDIA_BASE_URL 
+            . "admin/members.php?rt={$rt}&ac={$ac}&tab={$tab}"
+        );
+        $filterForm->configureForm();
+        if ($_POST['submit']) {
+            $mc  = new Toolkit_Members_Billing_Invoices();
+            $out = $mc->createInvoices($dbh);
+        } else {
+            $mc  = new Toolkit_Members_Billing_MemberLists($dbh);
+            $out = $filterForm->toHTML() . $mc->getNeededInvoices();
+        }
+        return $this->getPageLayout($out);
+    }
+    
+    /**
+     * Description of createLabelsAction()
+     * 
+     * @return string
+     * @access public 
+     */
+    public function createLabelsAction()
+    {
+        $dbh = Toolkit_Database::getInstance();
+        $rt = filter_input(
+            INPUT_GET,
+            'rt',
+            FILTER_SANITIZE_STRING
+        );
+        $ac = filter_input(
+            INPUT_GET,
+            'ac',
+            FILTER_SANITIZE_STRING
+        );
+        $tab = filter_input(
+            INPUT_GET,
+            'tab',
+            FILTER_SANITIZE_STRING
+        );
+        $filterForm = new Toolkit_Members_Billing_InvoiceFilterForm(
+            'filter-form',
+            'post',
+            MEDIA_BASE_URL 
+            . "admin/members.php?rt={$rt}&ac={$ac}&tab={$tab}"
+        );
+        $filterForm->configureForm();
+        if ($_POST['submit']) {
+            $out = '<pre>'.print_r($_REQUEST, true).'</pre>';
+            $label = new Toolkit_Members_Billing_MailingLabelPdf();
+            $out = $label->printMailingLabels($dbh);
+        } else {
+            $mc  = new Toolkit_Members_Billing_MemberLists($dbh);
+            $out = $filterForm->toHTML() . $mc->getMailableInvoices();
+        }
+        return $this->getPageLayout($out);
+    }
+    
+    /**
+     * Runs admin template, list out all regions
+     * 
+     * @param string $html Description of $html
+     * 
+     * @return string 
+     */
+    protected function getPageLayout($html)
+    {
+        $GLOBALS['styleSheets'][]
+            = MEDIA_BASE_URL . 'Toolkit/Members/Billing/billing.css';
+        $this->registry->controllerObject->content = $html;
+               $this->registry->controllerObject->topScripts
+                       = Toolkit_Common::getScripts($GLOBALS['topScripts']);
+               $this->registry->controllerObject->bottomScripts
+                       = Toolkit_Common::getScripts($GLOBALS['bottomScripts']);
+               $this->registry->controllerObject->styles
+                       = Toolkit_Common::getStyleSheets($GLOBALS['styleSheets']);
+               $this->registry->tEngine->compile('admin.tpl');
+               return $this->registry->tEngine->bufferedOutputObject(
+                       $this->registry->controllerObject
+               );
+    }
+    
+    /**
+     * Description of printInvoicesAction
+     * 
+     * @return string
+     * @access public 
+     */
+    public function printInvoicesAction()
+    {
+        $dbh = Toolkit_Database::getInstance();
+        $rt = filter_input(
+            INPUT_GET,
+            'rt',
+            FILTER_SANITIZE_STRING
+        );
+        $ac = filter_input(
+            INPUT_GET,
+            'ac',
+            FILTER_SANITIZE_STRING
+        );
+        $tab = filter_input(
+            INPUT_GET,
+            'tab',
+            FILTER_SANITIZE_STRING
+        );
+        $filterForm = new Toolkit_Members_Billing_InvoiceFilterForm(
+            'filter-form',
+            'post',
+            MEDIA_BASE_URL 
+            . "admin/members.php?rt={$rt}&ac={$ac}&tab={$tab}"
+        );
+        $filterForm->configureForm();
+        if ($_POST['submit']) {
+            $inv = new Toolkit_Members_Billing_PrintInvoices();
+            $ret = $inv->printInvoices($dbh);
+            if (!$ret) {
+                $out = '<p>Nothing to print</p>';
+            }
+        } else {
+            $mc  = new Toolkit_Members_Billing_MemberLists($dbh);
+            $out = $filterForm->toHTML() . $mc->getPrintableInvoices();
+        }
+        return $this->getPageLayout($out);
+    }
+    
+    /**
+     * Description of indexAction()
+     * 
+     * @return string
+     * @access public
+     */
+       public function indexAction()
+       {
+        $mc  = new Toolkit_Members_Billing_MemberLists(
+            Toolkit_Database::getInstance()
+        );
+        $html = $mc->getOpenAccounts();
+               return $this->getPageLayout($html);
+       }
+       
+    /**
+     * Description of sendEmailAction()
+     * 
+     * @return string
+     * @access public 
+     */
+    public function sendEmailAction()
+    {
+        $dbh = Toolkit_Database::getInstance();
+        $rt = filter_input(
+            INPUT_GET,
+            'rt',
+            FILTER_SANITIZE_STRING
+        );
+        $ac = filter_input(
+            INPUT_GET,
+            'ac',
+            FILTER_SANITIZE_STRING
+        );
+        $tab = filter_input(
+            INPUT_GET,
+            'tab',
+            FILTER_SANITIZE_STRING
+        );
+        $filterForm = new Toolkit_Members_Billing_InvoiceFilterForm(
+            'filter-form',
+            'post',
+            MEDIA_BASE_URL 
+            . "admin/members.php?rt={$rt}&ac={$ac}&tab={$tab}"
+        );
+        $filterForm->configureForm();
+        if ($_POST['submit']) {
+            $emailInvoice = new Toolkit_Members_Billing_EmailInvoices();
+            $ret = $emailInvoice->emailInvoices($dbh);
+            if ($ret) {
+                $out = '<p>Emails sent to members</p>';
+            } else {
+                $out = '<p>Nothing to email</p>';
+            }
+        } else {
+            $mc  = new Toolkit_Members_Billing_MemberLists($dbh);
+            $out = $filterForm->toHTML() . $mc->getEmailableInvoices();
+        }
+        return $this->getPageLayout($out);
+    }
+}
diff --git a/Toolkit/Members/Admin/ListAmenities.php b/Toolkit/Members/Admin/ListAmenities.php
new file mode 100644 (file)
index 0000000..e6d48be
--- /dev/null
@@ -0,0 +1,133 @@
+<?php
+//     vim:set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker syntax=php:
+
+/**
+ * List the amenities available for members
+ *
+ * PHP version 5
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @version   CVS: $Id: ListAmenities.php,v 1.11 2010/01/26 19:46:15 jamie Exp $
+ * @link      http://demo.gaslightmedia.com
+ */
+
+/**
+ * List the amenities available for members
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @link      http://demo.gaslightmedia.com
+ */
+class Toolkit_Members_Admin_ListAmenities
+{
+       //      {{{ properties
+
+       /**
+        * Database Handler
+        *
+        * @var object
+        * @access protected
+        */
+       protected $dbh;
+
+       /**
+        * The Table name used to store the data of the member record in the database.
+        *
+        * @var string
+        * @access public
+        */
+       public $tableName = 'amenity';
+
+       /**
+        * Flexy options used in the renderer
+        *
+        * @var array
+        * @access protected
+        */
+       protected $flexyOptions;
+
+       /**
+        * The flexy template object which holds the rendered object
+        *
+        * @var object
+        * @access private
+        */
+       private $_template = 'listAmenities.tpl';
+
+       //      }}}
+       //      {{{ __construct()
+
+       /**
+        * Constructor
+     *
+     * @param PDO $pdo PHP Data Object
+        *
+        * @access public
+        */
+       public function __construct(PDO $pdo)
+       {
+               $this->dbh = $pdo;
+
+        $this->flexyOptions = Toolkit_Members::getFlexyOptions();
+       }
+
+       //      }}}
+
+       //      {{{ _getAmenities()
+
+    /**
+     * Get the amenities
+     *
+     * @return array regions stored in the db
+     * @access private
+     */
+       private function _getAmenities()
+       {
+               try {
+                       $amenities = array();
+                       $sql = "
+                               SELECT *
+                                 FROM {$this->tableName}
+                                ORDER BY amenity_name";
+
+                       foreach ($this->dbh->query($sql) as $row) {
+                               $amenities[$row['amenity_id']] = $row['amenity_name'];
+                       }
+
+                       return $amenities;
+               } catch (PDOException $e) {
+                       return Toolkit_Common::handleError($e);
+               }
+       }
+
+       //      }}}
+
+       //      {{{ renderAmenities()
+
+    /**
+     * Render the output
+     *
+     * @return html page
+     * @access public
+     */
+       public function renderAmenities()
+       {
+               $template = new HTML_Template_Flexy($this->flexyOptions);
+               $page     = new stdClass();
+
+               $page->amenities = $this->_getAmenities();
+
+               $template->compile($this->_template);
+               return $template->bufferedOutputObject($page);
+       }
+
+       //      }}}
+}
+?>
diff --git a/Toolkit/Members/Admin/ListCategories.php b/Toolkit/Members/Admin/ListCategories.php
new file mode 100644 (file)
index 0000000..c812410
--- /dev/null
@@ -0,0 +1,231 @@
+<?php
+//     vim:set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker syntax=php:
+
+/**
+ * List the categories available for members
+ *
+ * PHP version 5
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @version   CVS: $Id: ListCategories.php,v 1.15 2010/08/09 16:55:44 jamie Exp $
+ * @link      http://demo.gaslightmedia.com
+ */
+
+/**
+ * List the categories available for members
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @link      http://demo.gaslightmedia.com
+ */
+class Toolkit_Members_Admin_ListCategories
+{
+       //      {{{ properties
+
+       /**
+        * Database Handler
+        *
+        * @var object
+        * @access public
+        */
+       public $dbh;
+
+       /**
+        * The Table name used to store the data of the member record in the database.
+        *
+        * @var string
+        * @access public
+        */
+       public $tableName = 'category';
+
+       /**
+        * Flexy options used in the renderer
+        *
+        * @var array
+        * @access protected
+        */
+       protected $flexyOptions;
+
+       /**
+        * The flexy template object which holds the rendered object
+        *
+        * @var object
+        * @access private
+        */
+       private $_template = 'listCategories.tpl';
+
+       //      }}}
+       //      {{{ __construct()
+
+       /**
+        * Constructor
+     *
+     * @param PDO $pdo PHP Data Object
+        *
+        * @access public
+        */
+       public function __construct(PDO $pdo)
+       {
+               $this->dbh = $pdo;
+
+        $this->flexyOptions = Toolkit_Members::getFlexyOptions();
+       }
+
+       //      }}}
+
+       //      {{{ _getCategoryList()
+
+    /**
+     * Gets a list of all the available categories in a UL list
+     *
+     * @return string  html list of categories
+     * @access private
+     */
+       private function _getCategoryList()
+       {
+               $tree = Toolkit_Common::getHierarchicalTreeStructure(
+            $this->dbh,
+            'category',
+            'category_id',
+            'parent_id',
+            'name'
+        );
+
+               try {
+                       $sql = "
+                               SELECT *
+                                 FROM {$this->tableName}
+                                WHERE category_id = :catid";
+
+                       $stmt = $this->dbh->prepare($sql);
+
+                       reset($tree);
+                       $prevLevel = 1;
+                       while (list($catid, $level) = each($tree)) {
+                               $stmt->bindParam(':catid', $catid, PDO::PARAM_INT);
+                               $stmt->execute();
+                               $row = $stmt->fetch();
+
+                               if ($level == $prevLevel) {
+                                       $list .= $this->_createNode($row, $list);
+                               } elseif ($level > $prevLevel) {
+                                       $list .= $this->_createNewLvlNode($row);
+                               } elseif ($level < $prevLevel) {
+                                       while ($prevLevel-- != $level) {
+                                               $list .= $this->_createLastLvlNode($row);
+                                       }
+                               }
+
+                               $prevLevel = $level;
+                       }
+
+                       while ($prevLevel-- > 1) {
+                               $list .= "</li>\n</ul>\n";
+                       }
+                       $list .= "</li>\n";
+
+                       return "<ul>\n$list</ul>";
+               } catch (PDOException $e) {
+                       return Toolkit_Common::handleError($e);
+               }
+       }
+
+       //      }}}
+
+       //      {{{     _createNode()
+
+    /**
+     * Create a LI node for the category list
+     *
+     * Long description (if any) ...
+     *
+     * @param array   $cat  Category data
+     * @param string  $list Current UL list being built
+     *
+     * @return string  current list plus new appended LI node
+     * @access private
+     */
+       private function _createNode(array $cat, $list)
+       {
+               //      check for closing node.
+               if (strlen($list) > 0) {
+                       if (substr_compare($list, '</a>', -5, 4) == 0) {
+                               $node = "</li>\n";
+                       }
+               }
+               $url = MEDIA_BASE_URL . "admin/members.php?rt=Categories&amp;ac=editCategory&amp;id={$cat['category_id']}";
+               $node .= "\t<li id=\"predef_{$cat['category_id']}\">\n";
+               $node .= "\t\t<a href=\"$url\">{$cat['name']}</a>\n";
+
+               return $node;
+       }
+
+       //      }}}
+       //      {{{     _createNextLvlNode()
+
+    /**
+     * Create a new level for the UL list
+     *
+     * @param array $cat Category data
+     *
+     * @return string  current UL list plus new level
+     * @access private
+     */
+       private function _createNewLvlNode(array $cat)
+       {
+               $node  = "\t\t<ul>\n";
+               $node .= $this->_createNode($cat, $node);
+
+               return $node;
+       }
+
+       //      }}}
+       //      {{{     _createLastLvlNode()
+
+    /**
+     * Create the closing tags of the current LI node and then append a new node
+     *
+     * @param array $cat Category data
+     *
+     * @return string  current UL list plus new node
+     * @access private
+     */
+       private function _createLastLvlNode(array $cat)
+       {
+               $node  = "</li>\n</ul>\n</li>\n";
+               $node .= $this->_createNode($cat, $node);
+
+               return $node;
+       }
+
+       //      }}}
+       //      {{{ renderCategories()
+
+    /**
+     * Render the output
+     *
+     * @return html page
+     * @access public
+     */
+       public function renderCategories()
+       {
+               $template = new HTML_Template_Flexy($this->flexyOptions);
+               $page     = new stdClass();
+
+               $page->baseUrl = MEDIA_BASE_URL;
+               $page->tree = $this->_getCategoryList();
+
+               $template->compile($this->_template);
+               return $template->bufferedOutputObject($page);
+       }
+
+       //      }}}
+}
+?>
diff --git a/Toolkit/Members/Admin/ListCities.php b/Toolkit/Members/Admin/ListCities.php
new file mode 100644 (file)
index 0000000..68c1469
--- /dev/null
@@ -0,0 +1,136 @@
+<?php
+//     vim:set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker syntax=php:
+
+/**
+ * List the cities available for members
+ *
+ * PHP version 5
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @version   CVS: $Id: ListCities.php,v 1.9 2010/01/26 19:46:47 jamie Exp $
+ * @link      http://demo.gaslightmedia.com
+ */
+
+/**
+ * List the cities available for members
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @link      http://demo.gaslightmedia.com
+ */
+class Toolkit_Members_Admin_ListCities
+{
+       //      {{{ properties
+
+       /**
+        * Database Handler
+        *
+        * @var object
+        * @access protected
+        */
+       public $dbh;
+
+       /**
+        * The Table name used to store the data of the member record in the database.
+        *
+        * @var string
+        * @access public
+        */
+       public $tableName = 'city';
+
+       /**
+        * Flexy options used in the renderer
+        *
+        * @var array
+        * @access protected
+        */
+       protected $flexyOptions;
+
+       /**
+        * The flexy template object which holds the rendered object
+        *
+        * @var object
+        * @access private
+        */
+       private $_template = 'listCities.tpl';
+
+       //      }}}
+       //      {{{ __construct()
+
+       /**
+        * Constructor
+        *
+     * @param PDO $pdo PHP Data Object
+     *
+        * @access public
+        */
+       public function __construct(PDO $pdo)
+       {
+               $this->dbh = $pdo;
+
+        $this->flexyOptions = Toolkit_Members::getFlexyOptions();
+       }
+
+       //      }}}
+
+       //      {{{ _getCities()
+
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @return array   Return description (if any) ...
+     * @access private
+     */
+       private function _getCities()
+       {
+               try {
+                       $cities = array();
+                       $sql = "
+                               SELECT *
+                                 FROM {$this->tableName}
+                                ORDER BY city_name";
+
+                       foreach ($this->dbh->query($sql) as $row) {
+                               $cities[$row['city_id']] = $row['city_name'];
+                       }
+
+                       return $cities;
+               } catch (PDOException $e) {
+                       return Toolkit_Common::handleError($e);
+               }
+       }
+
+       //      }}}
+
+       //      {{{ renderCities()
+
+    /**
+     * Render the output
+     *
+     * @return html page
+     * @access public
+     */
+       public function renderCities()
+       {
+               $template = new HTML_Template_Flexy($this->flexyOptions);
+               $page     = new stdClass();
+
+               $page->cities  = $this->_getCities();
+
+               $template->compile($this->_template);
+               return $template->bufferedOutputObject($page);
+       }
+
+       //      }}}
+}
+?>
diff --git a/Toolkit/Members/Admin/ListHtmlEmails.php b/Toolkit/Members/Admin/ListHtmlEmails.php
new file mode 100644 (file)
index 0000000..9765561
--- /dev/null
@@ -0,0 +1,141 @@
+<?php
+//     vim:set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker syntax=php:
+
+/**
+ * List the amenities available for members
+ *
+ * PHP version 5
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @version   CVS: $Id: ListHtmlEmails.php,v 1.4 2010/01/28 16:26:52 jamie Exp $
+ * @link      http://demo.gaslightmedia.com
+ */
+
+/**
+ * List the amenities available for members
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @link      http://demo.gaslightmedia.com
+ */
+class Toolkit_Members_Admin_ListHtmlEmails
+{
+       //      {{{ properties
+
+       /**
+        * Database Handler
+        *
+        * @var object
+        * @access protected
+        */
+       protected $dbh;
+
+       /**
+        * The Table name used to store the data of the member record in the database.
+        *
+        * @var string
+        * @access public
+        */
+       public $tableName = 'member_newsletters';
+
+       /**
+        * Flexy options used in the renderer
+        *
+        * @var array
+        * @access protected
+        */
+       protected $flexyOptions;
+
+       /**
+        * The flexy template object which holds the rendered object
+        *
+        * @var object
+        * @access private
+        */
+       private $_template = 'listHtmlEmails.tpl';
+
+       //      }}}
+       //      {{{ __construct()
+
+       /**
+        * Constructor
+     *
+     * @param PDO $pdo PHP Data Object
+        *
+        * @access public
+        */
+       public function __construct(PDO $pdo)
+       {
+               $this->dbh = $pdo;
+
+        $this->flexyOptions = Toolkit_Members::getFlexyOptions();
+       }
+
+       //      }}}
+
+       //      {{{ _getNewsletters()
+
+    /**
+     * Get the newsletters
+     * 
+     * @return array regions stored in the db
+     * @access private
+     */
+       private function _getNewsletters()
+       {
+               try {
+                       $newsletters = array();
+            $where = ($_REQUEST['archived']) ? "WHERE archived = 't'": "WHERE (archived = 'f' OR archived IS NULL)";
+                       $sql = "
+                               SELECT *
+                                 FROM {$this->tableName}
+            $where
+                                ORDER BY last_update DESC";
+
+                       foreach ($this->dbh->query($sql) as $row) {
+                $newsletters[] = array(
+                    'id'          => $row['id'],
+                    'subject'     => $row['subject'],
+                    'last_update' => $row['last_update'],
+                    'archived'    => $row['archived']
+                );
+                       }
+
+                       return $newsletters;
+               } catch (PDOException $e) {
+                       return Toolkit_Common::handleError($e);
+               }
+       }
+
+       //      }}}
+
+       //      {{{ renderHtmlEmails()
+
+    /**
+     * Render the output
+     *
+     * @return html page
+     * @access public
+     */
+       public function renderHtmlEmails()
+       {
+               $template = new HTML_Template_Flexy($this->flexyOptions);
+               $page     = new stdClass();
+
+               $page->newsletters = $this->_getNewsletters();
+        $page->title = ($_REQUEST['archived']) ? "Archived HTML Emails": "HTML Emails";
+
+               $template->compile($this->_template);
+               return $template->bufferedOutputObject($page);
+       }
+
+       //      }}}
+}
+?>
diff --git a/Toolkit/Members/Admin/ListMembers.php b/Toolkit/Members/Admin/ListMembers.php
new file mode 100644 (file)
index 0000000..2c3c003
--- /dev/null
@@ -0,0 +1,361 @@
+<?php
+//     vim:set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker syntax=php:
+
+/**
+ * File Doc Comment
+ *
+ * PHP version 5
+ *
+ * @category MembersDB
+ * @package  Toolkit_Members
+ * @author   Jamie Kahgee <steve@gaslightmedia.com>
+ * @license     http://www.gaslightmedia.com Gaslightmedia
+ * @release  CVS: $Id: ListMembers.php,v 1.32 2010/07/19 20:35:14 jamie Exp $
+ * @link        http://demo.gaslightmedia.com
+ */
+
+/**
+ * List the members in the database in a datagrid
+ *
+ * Handles listing of the members in the memberDB, pagnation, and sorting.
+ * Also controls the resulting number of members that are rendered on the page.
+ *
+ * @category  MembersDB
+ * @package      Toolkit_Members
+ * @author       Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2008 Gaslight media
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ */
+class Toolkit_Members_Admin_ListMembers extends Toolkit_FlexyDataGridBuilder
+{
+       //      {{{ configureColumns()
+
+       /**
+        * Configures the columns (fields) that will be used in our datagrid renderer.
+        *
+        * @return void
+        * @access protected
+        */
+       protected function configureColumns()
+       {
+               $memberId = new Structures_DataGrid_Column(
+                   'Created',
+            'member_id',
+            'member_id',
+            null,
+            null,
+            array(&$this, 'url')
+        );
+               $this->addColumn($memberId);
+
+               $delUrl = new Structures_DataGrid_Column(
+            'Delete',
+            'del_url',
+            'del_url',
+            null,
+            null,
+            array(&$this, 'delUrl')
+        );
+               $this->addColumn($delUrl);
+
+               $name = new Structures_DataGrid_Column(
+            'Member Name',
+            'member_name',
+            'member_name'
+           );
+               $this->addColumn($name);
+
+               $phone = new Structures_DataGrid_Column(
+            'Member Phone',
+            'phone',
+            'phone'
+           );
+               $this->addColumn($phone);
+
+               $email = new Structures_DataGrid_Column(
+            'Contact Email',
+            'member_contact_email',
+            'member_contact_email'
+           );
+               $this->addColumn($email);
+       }
+
+       //      }}}
+
+       //      {{{ delUrl()
+
+       /**
+        * Returns the url to delete a member.
+        *
+        * Used when configuring the columns for the data grid. This
+        * function generates the url to get to delete the member
+        *
+        * @param array $data The row from the db record
+        *
+        * @return string The url to the edit member page.
+        * @access public
+        */
+       public function delUrl($data)
+       {
+               $queryString = urldecode($_SERVER['QUERY_STRING']);
+               extract($data['record']);
+               $format = MEDIA_BASE_URL . 'admin/members.php?%s&d=t&id=%s';
+               return sprintf(
+                       $format,
+                       $queryString,
+                       $member_id
+               );
+       }
+
+       //      }}}
+
+    //  {{{ removeMember()
+
+    /**
+     * Remove a member from the list / database
+     *
+     * @param integer                     $mid Member id to remove
+        * @param Toolkit_Image_Server $is  Image Server
+     *
+     * @return void
+     * @access public
+     * @throws PEAR_Error
+     */
+    public function removeMember($mid, Toolkit_Image_Server $is)
+    {
+        try {
+                       $sql = "
+                SELECT *
+                  FROM member_photos
+                 WHERE member_id = :id";
+
+                       $stmt = $this->dbh->prepare($sql);
+                       $stmt->bindParam(':id', $mid, PDO::PARAM_INT);
+                       $stmt->execute();
+
+                       while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                               $is->imageDelete($row['image']);
+                       }
+
+            $sql  = "
+                DELETE FROM member
+                 WHERE member_id = :id";
+            $stmt = $this->dbh->prepare($sql);
+            $stmt->bindParam(':id', $mid, PDO::PARAM_INT);
+            $stmt->execute();
+        } catch (PDOException $e) {
+            return Toolkit_Common::handleError($e);
+        }
+    }
+
+    //  }}}
+
+    //  {{{ setControlObject()
+
+    /**
+     * set the control object to use with the datagrid
+     *
+     * @return void
+     * @access protected
+     * @throws PEAR_Error
+     */
+    protected function setControlObject()
+    {
+        try {
+            $letters = array();
+
+            $sql = "
+                SELECT substr(upper(member_name), 1, 1) AS letter
+                  FROM member
+                                WHERE new_member = CAST(0 AS BOOLEAN)
+                 GROUP BY letter
+                 ORDER BY letter";
+
+            foreach ($this->dbh->query($sql) as $row) {
+                $letters[$row['letter']] = $row['letter'];
+            }
+
+                       $addNumeric = false;
+                       while ($i = current($letters)) {
+                               if (is_numeric($i)) {
+                                       $addNumeric = true;
+                                       unset($letters[key($letters)]);
+                               } else {
+                                       break;
+                               }
+                       }
+
+                       if ($addNumeric) {
+                               array_unshift($letters, '0-9');
+                       }
+
+        } catch (PDOException $e) {
+            return Toolkit_Common::handleError($e);
+        }
+
+        //  We only need to show the links of alphabet if we have more than
+        //  one letter to display.
+        if (count($letters) > 1) {
+            foreach ($_GET as $k => $v) {
+                if ($k != 'alpha') {
+                    if (is_array($v)) {
+                        foreach ($v as $i => $j) {
+                            $queryString .= $k . '[' . $i . ']=' . $j . '&';
+                        }
+                    } else {
+                        $queryString .= "$k=$v&";
+                    }
+                }
+            }
+            $links['All']['url'] = MEDIA_BASE_URL . "admin/members.php?{$queryString}";
+            $links['All']['class'] = empty($_GET['alpha']) ? 'curr' : null;
+            foreach ($letters as $v) {
+                $links[$v]['url'] = MEDIA_BASE_URL . "admin/members.php?{$queryString}alpha=$v";
+                if ($_GET['alpha'] == $v) {
+                    $links[$v]['class'] = 'curr';
+                }
+            }
+            $this->ctrlObj['letters'] = $links;
+        }
+    }
+
+    //  }}}
+    //  {{{ setQuery()
+
+    /**
+     * Sets the query to use to fetch the datagrid results
+     *
+     * @param Config_Container $c Configuration container
+     *
+     * @return void
+     * @access public
+     */
+    public function setQuery(Config_Container $c)
+    {
+        //  get reference to [conf] section of config file
+        $config =& $c->getItem('section', 'conf');
+        //  get controlled cities
+        $citiesDirective =& $config->getItem('directive', 'controlledCities');
+
+               $sql = "
+                       SELECT distinct m.*, LOWER(m.member_name) AS sort_field
+              FROM member m, exploded_members_name emn";
+
+               $p = "m.new_member <> '1' AND emn.mid = m.member_id";
+               $params = array($p);
+
+        //  Limit to members whose first letter starts with the letter
+        //  a user clicked in the narrow alphabetically list.
+               if (   isset($_GET['alpha'])
+            && preg_match('/[0-9]/', $_GET['alpha'])
+        ) {
+                       $params[] = "substr(m.member_name, 1, 1) ~ '[0-9]'";
+               } elseif (isset($_GET['alpha']) && ctype_alpha($_GET['alpha'])) {
+                       $letter = $_GET['alpha'][0];
+            $params[] = "upper(substr(member_name, 1, 1)) = upper(" .
+                               $this->dbh->quote($letter) . ')';
+        }
+
+               if (isset($_GET['category']) && is_array($_GET['category'])) {
+                       $categoryKeys = array();
+                       foreach ($_GET['category'] as $k => $v) {
+                               if (ctype_digit($v)) {
+                                       $tree = Toolkit_Common::getHierarchicalTreeStructure(
+                                               $this->dbh,
+                                               'category',
+                                               'category_id',
+                                               'parent_id',
+                                               'pos',
+                                               $v,
+                                               0,
+                                               false
+                                       );
+                                       $categoryKeys = array_merge($categoryKeys, array_keys($tree));
+                               }
+                       }
+
+                       $params[] = "
+                                 member_id in (
+                                                SELECT member_id
+                                                  FROM member_category
+                                 WHERE category_id in (" . implode(', ', $categoryKeys) . "))";
+               }
+               if (isset($_GET['name']) && !empty($_GET['name'])) {
+                       $noSpaceName = preg_replace('/[^[:alnum:]]/', '', urldecode($_GET['name']));
+                       $spaceName = urldecode($_GET['name']);
+                       $sanitizedNoSpaceName = $this->dbh->quote($noSpaceName);
+                       $sanitizedSpaceName = $this->dbh->quote($spaceName);
+                       //      fuzzy name search
+            /* OR metaphone(m.member_name, 4) = metaphone($sanitizedSpaceName, 4)
+                               )
+                        OR (
+                                    m.member_id = emn.mid
+                                        AND m.new_member <> '1'
+                                AND (
+                                                $sanitizedNoSpaceName ~* regexp_replace(emn.part, '[^[:alnum:]]', '', 'g')
+                                         OR (metaphone($sanitizedSpaceName, 4) = metaphone(emn.part, 4))
+                                        )
+                                AND char_length(emn.part) > 1
+            */
+            $params[] = "(regexp_replace(m.member_name, '[^[:alnum:]]', '', 'g') ~* $sanitizedNoSpaceName)";
+               }
+
+               if (isset($_GET['email']) && !empty($_GET['email'])) {
+                       $email = preg_replace('/[^[:alnum:]]/', '', urldecode($_GET['email']));
+                       $params[] = "regexp_replace(member_contact_email, '[^[:alnum:]]', '', 'g') ~* " .
+                               $this->dbh->quote($email);
+               }
+        if ($citiesDirective->getContent()) {
+                       if ($cityId = filter_input(INPUT_GET, 'city_id', FILTER_VALIDATE_INT)) {
+                $params[] = "city_id = " . $this->dbh->quote($cityId);
+            }
+        } else {
+            if (isset($_GET['city']) && !empty($_GET['city'])) {
+                               $city = preg_replace('/[^[:alnum:]]/', '', urldecode($_GET['city']));
+                $params[] = "reqexp_replace(city, '[^[:alnum:]]', '', 'g') ~* " .
+                                       $this->dbh->quote($city);
+            }
+        }
+               if (   isset($_GET['state'])
+                       && $stateId = filter_input(INPUT_GET, 'state', FILTER_VALIDATE_INT)
+               ) {
+                       $params[] = "state_id = " . $this->dbh->quote($stateId);
+               }
+               if (isset($_GET['zip']) && !empty($_GET['zip'])) {
+                       $params[] = 'zip = ' . $this->dbh->quote($_GET['zip']);
+               }
+
+               if (isset($_GET['_qf__record_search']) && !empty($params)) {
+                       $params = implode(' AND ', $params);
+                       $sql    = "{$sql} WHERE $params";
+               } elseif (!isset($_GET['_qf__record_search'])) {
+                       $sql .= ' WHERE ' . implode(' AND ', $params);
+               }
+
+        parent::setQuery($sql);
+    }
+
+    //  }}}
+
+       //      {{{ url()
+
+       /**
+        * Returns the url to edit a member.
+        *
+        * Used when configuring the columns for the data grid. This
+        * function generates the url to get to the members edit page.
+        *
+        * @param array $data The row from the db record
+        *
+        * @return string The url to the edit member page.
+        * @access public
+        */
+       public function url($data)
+       {
+               $target = $data['record']['member_id'];
+               return MEDIA_BASE_URL . "admin/members.php?rt=Members&ac=editMember&tab=info&id=$target";
+       }
+
+       //      }}}
+}
+?>
diff --git a/Toolkit/Members/Admin/ListNewRequests.php b/Toolkit/Members/Admin/ListNewRequests.php
new file mode 100755 (executable)
index 0000000..2b061d1
--- /dev/null
@@ -0,0 +1,130 @@
+<?php
+//     vim:set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker syntax=php:
+
+/**
+ * File Doc Comment
+ *
+ * PHP version 5
+ *
+ * @category MembersDB
+ * @package  Toolkit_Members
+ * @author   Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @license     http://www.gaslightmedia.com Gaslightmedia
+ * @version  CVS: $Id: ListNewRequests.php,v 1.14 2010/07/14 23:27:59 jamie Exp $
+ * @link        <>
+ */
+
+/**
+ * List the new member requests in the database in a datagrid
+ *
+ * Handles listing of the new member requests in the memberDB, pagnation, and sorting.
+ * Also controls the resulting number of members that are rendered on the page.
+ *
+ * @category  MembersDB
+ * @package      Toolkit_Members
+ * @author       Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2008 Gaslight media
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ */
+class Toolkit_Members_Admin_ListNewRequests
+       extends Toolkit_FlexyDataGridBuilder
+{
+       //      {{{ configureColumns()
+
+       /**
+        * Configures the columns (fields) that will be used in our datagrid renderer.
+        *
+        * @return void
+        * @access public
+        */
+       protected function configureColumns()
+       {
+               $url = new Structures_DataGrid_Column('Created',
+                                               'member_id',
+                                               'member_id',
+                                               null,
+                                               null,
+                                               array(&$this, 'url'));
+               $this->addColumn($url);
+
+               $name = new Structures_DataGrid_Column('Member Name',
+                                                'member_name',
+                                                'member_name');
+               $this->addColumn($name);
+
+               $phone = new Structures_DataGrid_Column('Member Phone',
+                                                 'phone',
+                                                 'phone');
+               $this->addColumn($phone);
+
+               $email = new Structures_DataGrid_Column('Contact Email',
+                                                 'member_contact_email',
+                                                 'member_contact_email');
+               $this->addColumn($email);
+       }
+
+       //      }}}
+
+       //      {{{ setControlObject()
+
+       /**
+        * Sets any control object variables that are going to be used in the template
+        *
+        * @return void
+        * @access public
+        */
+       protected function setControlObject()
+       {
+               $this->ctrlObj['baseUrl'] = MEDIA_BASE_URL;
+               $this->ctrlObj['glmAppBaseUrl'] = MEDIA_APP_BASE_URL;
+       }
+
+       //      }}}
+    //  {{{ setQuery()
+
+    /**
+     * Sets the query to use to fetch the datagrid results
+     *
+     * @return void
+     * @access public
+     */
+    public function setQuery()
+    {
+               $sql = "
+                       SELECT *
+              FROM member
+                        WHERE new_member = true";
+
+        parent::setQuery($sql);
+    }
+
+    //  }}}
+
+       //      {{{ url()
+
+       /**
+        * Returns the url to edit a member.
+        *
+        * Used when configuring the columns for the data grid. This
+        * function generates the url to get to the members edit page.
+        *
+        * @param array $data The row from the db record
+        *
+        * @return string The url to the edit member page.
+        * @access public
+        */
+       public function url($data)
+       {
+               extract($data['record']);
+               $format = MEDIA_BASE_URL . 'admin/members.php?rt=%s&ac=%s&id=%s';
+               return sprintf(
+                       $format,
+                       'Members',
+                       'newMemberRequests',
+                       $member_id
+               );
+       }
+
+       //      }}}
+}
+?>
diff --git a/Toolkit/Members/Admin/ListPendingMembers.php b/Toolkit/Members/Admin/ListPendingMembers.php
new file mode 100755 (executable)
index 0000000..bd9e33c
--- /dev/null
@@ -0,0 +1,120 @@
+<?php
+//     vim:set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker syntax=php:
+
+/**
+ * File Doc Comment
+ *
+ * PHP version 5
+ *
+ * @category MembersDB
+ * @package  Toolkit_Members
+ * @author   Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @license     http://www.gaslightmedia.com Gaslightmedia
+ * @version  CVS: $Id: ListPendingMembers.php,v 1.10 2010/07/14 23:27:59 jamie Exp $
+ * @link        <>
+ */
+
+/**
+ * List the pending members in the database in a datagrid
+ *
+ * Handles listing of the pending members in the memberDB, pagnation,
+ * and sorting. Also controls the resulting number of members that are
+ * rendered on the page.
+ *
+ * @category  MembersDB
+ * @package      Toolkit_Members
+ * @author       Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2008 Gaslight media
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @link         http://devsys2.gaslightmedia.com/demo.gaslightmedia.com/admin/members/index.php?nav_id=2
+ */
+class Toolkit_Members_Admin_ListPendingMembers
+       extends Toolkit_FlexyDataGridBuilder
+{
+       //      {{{ configureColumns()
+
+       /**
+        * Configures the columns (fields) that will be used in our datagrid renderer
+        *
+        * @return void
+        * @access public
+        */
+       protected function configureColumns()
+       {
+               $url = new Structures_DataGrid_Column(
+                       'Created',
+            'member_id',
+            'member_id',
+            null,
+            null,
+            array(&$this, 'url')
+               );
+               $this->addColumn($url);
+
+               $name = new Structures_DataGrid_Column(
+                       'Member Name',
+            'member_name',
+            'member_name'
+               );
+               $this->addColumn($name);
+
+               $email = new Structures_DataGrid_Column(
+                       'Contact Email',
+            'member_contact_email',
+            'member_contact_email'
+               );
+               $this->addColumn($email);
+       }
+
+       //      }}}
+
+    //  {{{ setQuery()
+
+    /**
+     * Sets the query to use to fetch the datagrid results
+     *
+     * @return void
+     * @access public
+     */
+    public function setQuery()
+    {
+               $sql = "
+            SELECT *
+              FROM member
+             WHERE member_id IN (
+                                       SELECT DISTINCT member_id
+              FROM member_updates)";
+
+        Toolkit_DataGridBuilder::setQuery($sql);
+    }
+
+    //  }}}
+
+       //      {{{ url()
+
+       /**
+        * Returns the url to edit a member
+        *
+        * Used when configuring the columns for the data grid. This
+        * function generates the url to get to the members edit page.
+        *
+        * @param array $data The row from the db record
+        *
+        * @return string The url to the edit member page.
+        * @access public
+        */
+       public function url($data)
+       {
+               extract($data['record']);
+               $format = MEDIA_BASE_URL . 'admin/members.php?rt=%s&ac=%s&id=%s';
+               return sprintf(
+                       $format,
+                       'Members',
+                       'pendingUpdates',
+                       $member_id
+               );
+       }
+
+       //      }}}
+}
+?>
diff --git a/Toolkit/Members/Admin/ListRegions.php b/Toolkit/Members/Admin/ListRegions.php
new file mode 100644 (file)
index 0000000..d612ee1
--- /dev/null
@@ -0,0 +1,133 @@
+<?php
+//     vim:set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker syntax=php:
+
+/**
+ * List the regions available for members
+ *
+ * PHP version 5
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @version   CVS: $Id: ListRegions.php,v 1.9 2009/12/29 14:14:11 jamie Exp $
+ * @link      http://demo.gaslightmedia.com
+ */
+
+/**
+ * List the regions available for members
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @link      http://demo.gaslightmedia.com
+ */
+class Toolkit_Members_Admin_ListRegions
+{
+       //      {{{ properties
+
+       /**
+        * Database Handler
+        *
+        * @var object
+        * @access protected
+        */
+       protected $dbh;
+
+       /**
+        * The Table name used to store the data of the member record in the database.
+        *
+        * @var string
+        * @access public
+        */
+       public $tableName = 'region';
+
+       /**
+        * Flexy options used in the renderer
+        *
+        * @var array
+        * @access protected
+        */
+       protected $flexyOptions;
+
+       /**
+        * The flexy template object which holds the rendered object
+        *
+        * @var object
+        * @access private
+        */
+       private $_template = 'listRegions.tpl';
+
+       //      }}}
+       //      {{{ __construct()
+
+       /**
+        * Class constructor
+        *
+     * @param PDO $pdo PHP Data Object
+     *
+        * @access public
+        */
+       public function __construct(PDO $pdo)
+       {
+               $this->dbh = $pdo;
+
+        $this->flexyOptions = Toolkit_Members::getFlexyOptions();
+       }
+
+       //      }}}
+
+       //      {{{ _getRegions()
+
+    /**
+     * Get the regions
+     *
+     * @return array regions stored in the db
+     * @access private
+     */
+       private function _getRegions()
+       {
+               try {
+                       $regions = array();
+                       $sql = "
+                               SELECT *
+                                 FROM {$this->tableName}
+                                ORDER BY region_name";
+
+                       foreach ($this->dbh->query($sql) as $row) {
+                               $regions[$row['region_id']] = $row['region_name'];
+                       }
+
+                       return $regions;
+               } catch (PDOException $e) {
+                       return Toolkit_Common::handleError($e);
+               }
+       }
+
+       //      }}}
+
+       //      {{{ renderRegions()
+
+    /**
+     * Render the output
+     *
+     * @return html page
+     * @access public
+     */
+       public function renderRegions()
+       {
+               $template = new HTML_Template_Flexy($this->flexyOptions);
+               $page     = new stdClass();
+
+               $page->regions = $this->_getRegions();
+
+               $template->compile($this->_template);
+               return $template->bufferedOutputObject($page);
+       }
+
+       //      }}}
+}
+?>
diff --git a/Toolkit/Members/Admin/MailOut.php b/Toolkit/Members/Admin/MailOut.php
new file mode 100644 (file)
index 0000000..2cd0e71
--- /dev/null
@@ -0,0 +1,391 @@
+<?php
+//     vim:set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker syntax=php:
+
+/**
+ * MailOut.php
+ *
+ * PHP version 5
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2009 Gaslight Media
+ * @license   Gaslight Media
+ * @version   CVS: $Id: MailOut.php,v 1.12 2010/06/29 13:32:05 matrix Exp $
+ * @link      <>
+ */
+
+/**
+ * Member Newsletter Integration with Spamerizer App
+ * Spamerizer runs on the dev and prod servers every 5 minutes it parses out
+ * the directory /var/spool/Spamerizer
+ * there's four files and the base name of the file must all match
+ * GLMSAMPLE.head  (mailout setting file)
+ * GLMSAMPLE.body  (the body of the email it is sending out)
+ * GLMSAMPLE.to    (all emails one on each line)
+ * GLMSAMPLE.ready (empty file let's script know all files are ready)
+ * (head file format)
+ * ProcessName:    {name of this mail process}
+ * NotifyAddr:     {Owner Notification E-Mail Address}
+ * BlockSize:      {number of address per sendmail call} (keep at one)
+ * ProductionMode: ON
+ * (body file format)
+ * first lines must contain headers From: and To: then two blank lines
+ * (to file format)
+ * One email address per line
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2009 Gaslight Media
+ * @license   Gaslight Media
+ * @version   Release: @package_version@
+ * @link      <>
+ */
+class Toolkit_Members_Admin_MailOut
+{
+    // {{{ Class Properties
+
+
+    /**
+     * table that stores the newsletter
+     * @var    string
+     * @access public
+     */
+    public $tableName = 'member_newsletters';
+
+    /**
+     * the unique name of the file without ext
+     * @var    string
+     * @access private
+     */
+    private $_fileBaseName;
+
+    /**
+     * template for newsletter layout
+     * @var    string
+     * @access private
+     */
+       private $_template = 'previewHtmlEmail.tpl';
+
+    /**
+     * Spamerizer temp directory
+     * @var    string
+     * @access private
+     */
+    private $_tmpDir = '/var/spool/SPAMerizer';
+
+    /**
+     * SQL query used to obtain the DataGrid
+        *
+     * @var    unknown
+     * @access protected
+     */
+       protected $sql;
+    // }}}
+    // {{{ __construct()
+
+
+    /**
+        * Constructor
+     *
+     * @param PDO $pdo PHP Data Object
+     *
+     * @return void
+     * @access public
+     */
+    public function __construct(PDO $pdo)
+    {
+        // PDO
+        $this->dbh = $pdo;
+
+        // flexy options
+        $this->flexyOptions = Toolkit_Members::getFlexyOptions();
+    }
+    // }}}
+    // {{{ _getMembers()
+
+
+    /**
+     * get the members based on the search parameters that are passed
+     * either by post or get (use $_REQUEST)
+     * limit the result funther to those who only have process_email
+     *
+     * @return object  Array of member data with emails
+     * @access private
+     */
+    private function _getMembers()
+    {
+        try {
+            $res = $this->dbh->query($this->sql);
+            return $res->fetchAll(PDO::FETCH_ASSOC);
+        } catch(PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+    }
+    // }}}
+    // {{{ _getMembersContacts()
+
+
+    /**
+     * get the members based on the search parameters that are passed
+     * either by post or get (use $_REQUEST)
+     * limit the result funther to those who only have process_email
+     *
+     * @param array &$members Member data array
+     *
+     * @return object  Array of member data with emails
+     * @access private
+     */
+    private function _getMembersContacts(&$members)
+    {
+        $sql = "
+        SELECT email
+          FROM member_contacts
+         WHERE send_mail = 't'
+           AND member_id = :mid";
+        $stmt = $this->dbh->prepare($sql);
+        if (is_array($members)) {
+            foreach ($members as &$row) {
+                try {
+                    $stmt->bindParam(":mid", $row['member_id'], PDO::PARAM_INT);
+                    $stmt->execute();
+                    $email = $stmt->fetchColumn();
+                    $memberContactData[] = $email;
+                } catch(PDOException $e) {
+                    Toolkit_Common::handleError($e);
+                }
+            }
+        }
+        return $memberContactData;
+    }
+    // }}}
+    // {{{ _getNewsletter()
+
+
+    /**
+     * get the newsletter main content area
+     *
+     * @param unknown $news_id id of the record
+     *
+     * @return object  Return HTML content of newsletter
+     * @access private
+     */
+    private function _getNewsletter($news_id)
+    {
+        $sql = "
+        SELECT subject,response
+          FROM {$this->tableName}
+          WHERE id = :id";
+        try {
+            $stmt = $this->dbh->prepare($sql);
+            $stmt->bindParam(":id", $news_id, PDO::PARAM_INT);
+            $stmt->execute();
+            return $stmt->fetch(PDO::FETCH_ASSOC);
+        } catch(PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+    }
+    // }}}
+    // {{{ _getTempFileName()
+
+
+    /**
+     * creates a unique filename and deletes the file created by tempnam
+     * call. Sets the class private variable _fileBaseName to the name
+     *
+     * @return void
+     * @access private
+     */
+    private function _getTempFileName()
+    {
+        $filename = tempnam($this->_tmpDir, 'GLMMN');
+        unlink($filename);
+        $this->_fileBaseName = $filename;
+    }
+    // }}}
+    // {{{ _writeBodyFile()
+
+
+    /**
+     * writes out the .body file for Spamerizer
+     *
+     * @return void
+     * @access private
+     */
+    private function _writeBodyFile()
+    {
+        $template = new HTML_Template_Flexy($this->flexyOptions);
+        $page     = new stdClass;
+
+        // get data and set response
+        $newsletter     = $this->_getNewsletter($_REQUEST['news_id']);
+        $page->response = $newsletter['response'];
+        $subject        = $newsletter['subject'];
+        // set the sitename
+        $page->sitename = SITENAME;
+        // set the cancel email
+        $page->membersEnewsEmail = FROM_MEMBER_NEWS_EMAIL;
+
+        $template->compile($this->_template);
+        // email body headers
+        $headers = "From: ".FROM_MEMBER_NEWS_EMAIL."\n".
+                               "To: ".OWNER_EMAIL."\n".
+                               "Subject: $subject\n".
+                               "Reply-to: ".REPLY_MEMBER_NEWS_EMAIL."\n".
+                               "Mime-Version: 1.0\n".
+                               "Content-Type: multipart/alternative; boundary=ContentBoundry\n\n";
+        $fileContents = wordwrap($template->bufferedOutputObject($page), 72);
+        $textContents = htmlspecialchars_decode(strip_tags($fileContents));
+        $textContents = preg_replace('/&nbsp;/', ' ', $textContents);
+        $emailBody = $headers
+        . "--ContentBoundry\nContent-Type: text/plain; charset=\"UTF-8\"\n"
+        . $textContents
+        . "\n--ContentBoundry\nContent-Type: text/html; charset=\"UTF-8\"\n"
+        . $fileContents
+        . "\n--ContentBoundry--";
+        $e = File::write($this->_fileBaseName.'.body', $emailBody, FILE_MODE_WRITE);
+        if (PEAR::isError($e)) {
+            Toolkit_Common::handleError($e);
+        }
+        $old = umask(0);
+        chmod($this->_fileBaseName.'.body', 0770);
+        umask($old);
+    }
+    // }}}
+    // {{{ _writeHeadFile()
+
+
+    /**
+     * writes out the .head file for Spamerizer
+     *
+     * @return void
+     * @access private
+     */
+    private function _writeHeadFile()
+    {
+        $fileContents = "NotifyAddr: ".OWNER_EMAIL."\n"
+            . "ProcessName: ".SITENAME."\n"
+            . "From: ".FROM_MEMBER_NEWS_EMAIL."\n"
+            . "ReportInterval: 2\n"
+            . "BlockSize: 1\n"
+            . "ProductionMode: ".PRODUCTION_MODE."\n";
+        $e = File::write($this->_fileBaseName.'.head', $fileContents, FILE_MODE_WRITE);
+        if (PEAR::isError($e)) {
+            Toolkit_Common::handleError($e);
+        }
+        $old = umask(0);
+        chmod($this->_fileBaseName.'.head', 0770);
+        umask($old);
+    }
+    // }}}
+    // {{{ _writeReadyFile()
+
+
+    /**
+     * writes out the .ready file for Spamerizer
+     *
+     * @return void
+     * @access private
+     */
+    private function _writeReadyFile()
+    {
+        $e = File::write($this->_fileBaseName.'.ready', '', FILE_MODE_WRITE);
+        if (PEAR::isError($e)) {
+            Toolkit_Common::handleError($e);
+        }
+        $old = umask(0);
+        chmod($this->_fileBaseName.'.ready', 0770);
+        umask($old);
+    }
+    // }}}
+    // {{{ _writeToFile()
+
+
+    /**
+     * writes out the .to file for Spamerizer
+     *
+     * @param array $data Parameter description (if any) ...
+     *
+     * @return void
+     * @access private
+     */
+    private function _writeToFile($data)
+    {
+        if (is_array($data)) {
+            $fileContents = implode("\n", $data);
+        }
+        $e = File::write($this->_fileBaseName.'.to', $fileContents, FILE_MODE_APPEND);
+        if (PEAR::isError($e)) {
+            Toolkit_Common::handleError($e);
+        }
+        $old = umask(0);
+        chmod($this->_fileBaseName.'.to', 0770);
+        umask($old);
+    }
+    // }}}
+    // {{{ sendEmails()
+
+
+    /**
+     * Runs all the functins grabing the member data and creating the
+     * files to be processed by spamerizer
+     *
+     * @return string Return description (if any) ...
+     * @access public
+     */
+    public function sendEmails()
+    {
+        // get member array
+        $members  = $this->_getMembers();
+        $contacts = $this->_getMembersContacts($members);
+        $this->_getTempFileName();
+        $this->_writeHeadFile();
+        $this->_writeBodyFile();
+        // setup the email array for the to file with no duplicates
+        if (is_array($members)) {
+            foreach ($members as $row) {
+                $emails[$row['process_email']] = $row['process_email'];
+            }
+        }
+        if (is_array($contacts)) {
+            foreach ($contacts as $email) {
+                $emails[$email] = $email;
+            }
+        }
+        $this->_writeToFile($emails);
+        $this->_writeReadyFile();
+        $out .= '<div id="form-warning-top">Do not refresh this page!</div>'
+            . '<p>Your Newsletter has been setup for Processing. '
+            . 'You should receive at least 2 emails at ' . FROM_MEMBER_NEWS_EMAIL
+            . '. One at the beginning, One at the completion of the processing '
+            . 'and may receive others depending on how many emails are being Processed.'
+            . ' Refreshing this page will send out the same newsletter again.</p>';
+        return $out;
+    }
+    // }}}
+       //      {{{ setQuery()
+
+    /**
+     * Sets the sql query to use in the DataGrid to get the results
+     *
+     * @param array $ids An array of member_id1s for the search sql
+     *
+     * @return void
+     * @access public
+     */
+       public function setQuery(array $ids)
+       {
+               $memberIds = implode(', ', $ids);
+               $sql = "
+                       SELECT member_id, process_email
+              FROM member
+             WHERE member_id IN ($memberIds)
+                ";
+
+        $this->sql = $sql;
+       }
+
+       //      }}}
+}
+?>
diff --git a/Toolkit/Members/Admin/MailOutForm.php b/Toolkit/Members/Admin/MailOutForm.php
new file mode 100644 (file)
index 0000000..c299374
--- /dev/null
@@ -0,0 +1,257 @@
+<?php
+//     vim:set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker syntax=php:
+
+/**
+ * Admin search functionality for memberdb
+ *
+ * PHP version 5
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2009 Gaslight Media
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @version   CVS: $Id: MailOutForm.php,v 1.10 2010/05/25 14:01:21 jamie Exp $
+ * @link      http://demo.gaslightmedia.com
+ */
+
+/**
+ * Form to search the members database
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2009 Gaslight Media
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @link      http://demo.gaslightmedia.com
+ */
+class Toolkit_Members_Admin_MailOutForm
+    extends Toolkit_FormBuilder
+{
+       //      {{{ configureConstants()
+
+    /**
+     * Form constant definitions
+     *
+     * @return void
+     * @access public
+     */
+       public function configureConstants()
+       {
+               $c = array(
+                       'page'   => $_GET['page'],
+                       'module' => $_GET['module'],
+               );
+
+               $this->setupConstants($c);
+       }
+
+       //      }}}
+       //      {{{ configureElements()
+
+    /**
+     * Form element definitions
+     *
+        * @param PDO              $dbh Database handler
+     * @param Config_Container $c   Configuration object
+     *
+     * @return void
+     * @access public
+     */
+       public function configureElements(PDO $dbh, Config_Container $c)
+       {
+        $e = array();
+
+               //  get reference to [listing type] section of config file
+               $pluralType = $c->getItem('section', 'listing type')
+                       ->getItem('directive', 'plural')
+                       ->getContent();
+
+               $newsletters = $this->_getNewsletters($dbh);
+               //      All Grouped Elements are created here.
+
+               //      All Elements are created here.  This includes group element definitions.
+        $e[] = array(
+            'type' => 'hidden',
+            'req' => false,
+            'name' => 'page'
+        );
+        $e[] = array(
+            'type' => 'hidden',
+            'req' => false,
+            'name' => 'module'
+        );
+               $e[] = array(
+            'type' => 'select',
+            'req' => true,
+            'name' => 'news_id',
+            'display' => 'Newsletter',
+            'opts' => array('' => '-- Select --') + $newsletters
+        );
+               $e[] = array(
+            'type'    => 'submit',
+            'req'     => false,
+            'name'    => 'submit',
+            'display' => 'Send to ' . $pluralType,
+            'opts'    => array('id' => 'member-newsletter-mailout')
+        );
+
+               $this->setupElements($e);
+       }
+
+       //      }}}
+       //      {{{ configureFilters()
+
+    /**
+     * Form filter definitions
+     *
+     * @return void
+     * @access public
+     */
+       public function configureFilters()
+       {
+        $f = array();
+               $f[] = array(
+            'element' => '__ALL__',
+            'filter' => 'trim'
+        );
+
+               $this->setupFilters($f);
+       }
+
+       //      }}}
+    //  {{{ configureForm()
+
+    /**
+     * Helper function to configure an entire form
+     *
+        * @param PDO              $dbh Database handler
+     * @param Config_Container $c   Configuration object
+     *
+     * @return void
+     * @access public
+     */
+    public function configureForm(PDO $dbh, Config_Container $c)
+    {
+        $this->configureElements($dbh, $c);
+        $this->configureRules();
+        $this->configureFilters();
+        $this->configureConstants();
+    }
+
+    //  }}}
+       //      {{{ configureRules()
+
+    /**
+     * Form rule definitions
+     *
+     * @return void
+     * @access public
+     */
+       public function configureRules()
+       {
+        $r = array();
+               $r[] = array(
+            'element' => 'news_id',
+            'message' => 'ERROR: Invalid Newsletter!',
+            'type' => 'numeric',
+            'format' => null,
+            'validation' => $this->validationType,
+            'reset' => false,
+            'force' => false
+        );
+
+               $this->setupRules($r);
+       }
+
+       //      }}}
+
+       //      {{{     _getNewsletters()
+
+    /**
+     * get all the categories available in alpha order
+     * 
+     * @param PDO $dbh PDO
+     *
+     * @return array|void alpha order of categories available in the DB
+     * @access private
+     * @throws PEAR_Error
+     */
+       private function _getNewsletters(PDO $dbh)
+       {
+               try {
+                       $sql = "
+                               SELECT *, subject || ' (' ||
+                                               to_char(last_update, 'Month DDth, YYYY')
+                                               || ')' AS header
+                  FROM member_newsletters
+                 ORDER BY last_update DESC, subject";
+
+                       $stmt = $dbh->prepare($sql);
+                       $stmt->execute();
+                       $stmt->bindColumn('id', $nid);
+                       $stmt->bindColumn('header', $subject);
+
+                       $categories = array();
+                       while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                               $categories[$nid] = $subject;
+                       }
+
+                       return $categories;
+               } catch (PDOException $e) {
+                       return Toolkit_Common::handleError($e);
+               }
+       }
+
+       //      }}}
+
+       //      {{{     setupRenderers()
+    //  @codeCoverageIgnoreStart
+
+    /**
+     * Custom rendering templates for special fields on the form
+     *
+     * @return void
+     * @access protected
+     */
+       protected function setupRenderers()
+       {
+               parent::setupRenderers();
+               $renderer =& $this->defaultRenderer();
+               $tpl = '<tr align="center"><td colspan="2">{element}</td></tr>';
+               $renderer->setElementTemplate($tpl, 'submit');
+       }
+
+    //  @codeCoverageIgnoreEnd
+       //      }}}
+
+       //      {{{     toHtml()
+
+       /**
+        * Call the rendering function to get the form in a string
+        *
+     * @param array  $results array of member_id's
+     * @param PDO    $dbh     PDO object reference
+        *
+        * @access protected
+        * @return string $output The Form to be rendered or success msg.
+        */
+       public function toHtml(array $results, PDO $dbh)
+       {
+               $this->setupRenderers();
+               if ($this->validate()) {
+            $mailOut = new Toolkit_Members_Admin_MailOut($dbh);
+                       $mailOut->setQuery($results);
+            $output = $mailOut->sendEmails();
+               } elseif ($this->isSubmitted()) {
+                       $output = $this->errorMsg;
+                       $output .= parent::toHTML();
+               } else {
+                       $output = parent::toHTML();
+               }
+               return $output;
+       }
+
+       //      }}}
+}
+?>
diff --git a/Toolkit/Members/Admin/MemberReportSearch.php b/Toolkit/Members/Admin/MemberReportSearch.php
new file mode 100644 (file)
index 0000000..4b24107
--- /dev/null
@@ -0,0 +1,215 @@
+<?php
+
+/**
+ * Admin search functionality for memberdb
+ *
+ * PHP version 5
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @version   CVS: $Id: AdvancedSearch.php,v 1.14 2010/08/15 19:35:15 jamie Exp $
+ * @link      http://demo.gaslightmedia.com
+ */
+
+/**
+ * Form to search the members database
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @link      http://demo.gaslightmedia.com
+ */
+class Toolkit_Members_Admin_MemberReportSearch
+    extends Toolkit_FormBuilder
+{
+
+    /**
+     * Description of $_members
+     *
+     * @var unknown
+     * @access private
+     */
+       private $_members;
+
+       /**
+        * Sets the defaults for the form
+        *
+        * @param PDO $dbh Database handler
+        *
+        * @access public
+     * @return void
+        */
+       public function configureDefaults()
+       {
+               $d = array(
+                       'rt' => $_GET['rt'],
+                       'ac' => $_GET['ac']
+               );
+        $d['reportMonth']
+            = ($_REQUEST['reportMonth'])
+            ? $_REQUEST['reportMonth']
+            : date('n').'|'.date('Y');
+
+               $this->setupDefaults($d);
+       }
+
+    /**
+     * Form element definitions
+     *
+        * @param PDO                      $dbh Databse handler
+     * @param Config_Container $c   Configuration object
+     *
+     * @return void
+     * @access public
+     */
+       public function configureElements(PDO $dbh)
+       {
+        $e = array();
+
+               //      All Elements are created here.  This includes group element definitions.
+               $e[] = array(
+            'type' => 'hidden',
+            'req' => false,
+            'name' => 'rt'
+        );
+               $e[] = array(
+            'type' => 'hidden',
+            'req' => false,
+            'name' => 'ac'
+        );
+        $e[] = array(
+            'type'    => 'hidden',
+            'req'     => false,
+            'name'    => 'searching',
+            'display' => '1'
+        );
+       $e[] = array(
+            'type'    => 'select',
+            'name'    => 'reportMonth',
+            'display' => 'Show Summary for month of ',
+            'opts'    => $this->getMonths(),
+            'req'     => false
+       );
+       $e[] = array(
+            'type'    => 'text',
+            'name'    => 'name',
+            'display' => 'Member Name',
+            'req'     => false
+       );
+       $e[] = array(
+            'type' => 'submit',
+            'req' => false,
+            'name' => 'submit',
+            'display' => 'Search'
+        );
+
+               $this->setupElements($e);
+       }
+
+    protected function getMonths()
+    {
+        $months = array();
+        try {
+            $sql = "
+            SELECT distinct ON (date_part('year', edate),
+                                date_part('month', edate))
+                                date_part('month', edate) as month,
+                                date_part('year', edate) as year
+              FROM exposure
+             WHERE member_id in (
+                    SELECT distinct member_id
+                      FROM member)
+             ORDER BY date_part('year', edate), date_part('month', edate)";
+            if ($monthData = $this->dbh->query($sql)->fetchAll()) {
+                if (!$_GET['monthYear']) {
+                    $m          = date("n");
+                    $F          = date("F");
+                    $Y          = date("Y");
+                    $monthYear .= $m.' '.$Y;
+                } else {
+                    $monthYear .= $_GET['monthYear'];
+                }
+                foreach ($monthData as $data) {
+                    $monthName = date(
+                        "F",
+                        mktime(0, 0, 0, $data['month'], 1, date('year'))
+                    );
+                    $months[$data['month'].'|'.$data['year']] = $monthName.
+                        ' '.$data['year'];
+                }
+            }
+        } catch (PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+        return $months;
+    }
+
+    /**
+     * Form filter definitions
+     *
+     * @return void
+     * @access public
+     */
+       public function configureFilters()
+       {
+        $f = array();
+               $f[] = array(
+            'element' => '__ALL__',
+            'filter' => 'trim'
+        );
+
+               $this->setupFilters($f);
+       }
+
+    /**
+     * Helper function to configure an entire form
+     *
+        * @param PDO                      $dbh Databse handler
+     * @param Config_Container $c   Configuration object
+     *
+     * @return void
+     * @access public
+     */
+    public function configureForm(PDO $dbh)
+    {
+        $this->configureElements($dbh);
+        $this->configureDefaults();
+        $this->configureFilters();
+    }
+
+    /**
+     * Custom rendering templates for special fields on the form
+     *
+     * @return void
+     * @access protected
+     */
+       protected function setupRenderers()
+       {
+               parent::setupRenderers();
+               $renderer =& $this->defaultRenderer();
+               $tpl = '<tr align="center"><td colspan="2">{element}</td></tr>';
+               $renderer->setElementTemplate($tpl, 'submit');
+       }
+
+       /**
+        * Call the rendering function to get the form in a string
+        *
+        * @param PDO              $dbh Database handler
+     * @param Config_Container $c   Configuration object
+        *
+        * @access protected
+        * @return string $output The Form to be rendered or success msg.
+        */
+       public function toHtml(PDO $dbh)
+       {
+               $this->setupRenderers();
+
+               return parent::toHTML();
+       }
+
+}
diff --git a/Toolkit/Members/Admin/MembersController.php b/Toolkit/Members/Admin/MembersController.php
new file mode 100644 (file)
index 0000000..504e9e4
--- /dev/null
@@ -0,0 +1,310 @@
+<?php
+/**
+ * MembersController.php
+ *
+ * PHP versions 4 and 5
+ *
+ * @category  Toolkit
+ * @package   Members_Admin
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2009 Gaslight Media
+ * @license   Gaslight Media
+ * @version   CVS: $Id: Newsletters.php,v 1.9 2009/09/16 19:00:58 matrix Exp $
+ * @link      http://demo.gaslightmedia.com
+ */
+
+/**
+ * Toolkit_Members_Admin_MembersController
+ *
+ * Description for Toolkit_Members_Admin_MembersController
+ *
+ * @category  Toolkit
+ * @package   Members_Admin
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2009 Gaslight Media
+ * @license   Gaslight Media
+ * @link      http://demo.gaslightmedia.com
+ */
+class Toolkit_Members_Admin_MembersController
+    extends Toolkit_BaseControllerAbstract implements Toolkit_IController
+{
+    //  {{{ _authorizeMemberUpdates()
+    /**
+     * Description of _authorizeMemberUpdates()
+     *
+     * @param int $memberId Int
+     *
+     * @return string
+     * @access private
+     */
+    private function _authorizeMemberUpdates($memberId)
+    {
+        if (filter_input(INPUT_GET, 'id', FILTER_VALIDATE_INT)) {
+            $GLOBALS['bottomScripts'][]
+                = CKEDITOR_JS . '';
+            $GLOBALS['bottomScripts'][]
+                = MEDIA_BASE_URL . 'Toolkit/Members/libjs/authorizeChanges.js';
+        }
+
+        $updateForm = new Toolkit_Members_Admin_AuthorizeUpdates(
+            $this->registry->dbh,
+            'auth_updates'
+        );
+        $updateForm->setMember($memberId);
+        $updateForm->configureForm();
+        return $updateForm->toHtml();
+    }
+
+    //  }}}
+    //  {{{ _authorizeNewMember()
+    /**
+     * Description of _authorizeNewMember()
+     *
+     * @return string
+     * @access private
+     */
+    private function _authorizeNewMember()
+    {
+        $GLOBALS['styleSheets'][]  = MEDIA_BASE_URL . 'css/contactform.css';
+        $form = new Toolkit_Members_Admin_AuthorizeNewMemberForm(
+            $this->registry->dbh,
+            'authorize_form'
+        );
+        $form->setConfig($this->registry->config);
+        $form->configureForm();
+        return $form->toHtml();
+    }
+
+    //  }}}
+    //  {{{ _listNewMembers()
+    /**
+     * Description of _listNewMembers()
+     *
+     * @return string
+     * @access private
+     */
+    private function _listNewMembers()
+    {
+        $GLOBALS['bottomScripts'][]
+            = MEDIA_BASE_URL . 'Toolkit/Members/libjs/member-list.js';
+        $GLOBALS['styleSheets'][]   = MEDIA_BASE_URL . 'css/contactform.css';
+
+        $nmr = new Toolkit_Members_Admin_ListNewRequests($this->registry->dbh);
+        $nmr->setQuery();
+        $nmr->setDefaultSort(array('member_name' => 'ASC'));
+
+        //  rendering engine to use
+        $rEngine = new Structures_DataGrid_Renderer_Flexy();
+        $rEngine->setContainer($this->registry->tEngine);
+
+        return $nmr->toHtml($rEngine, 'listNewMembers.tpl');
+    }
+
+    //  }}}
+    //  {{{ _listPendingMembers()
+    /**
+     * Description of _listPendingMembers()
+     *
+     * @return string
+     * @access private
+     */
+    private function _listPendingMembers()
+    {
+        $GLOBALS['bottomScripts'][]
+            = MEDIA_BASE_URL . 'Toolkit/Members/libjs/member-pending-list.js';
+
+        $pml = new Toolkit_Members_Admin_ListPendingMembers(
+            $this->registry->dbh
+        );
+        $pml->setQuery();
+        $pml->setDefaultSort(array('member_name' => 'asc'));
+
+        //  rendering engine to use
+        $rEngine = new Structures_DataGrid_Renderer_Flexy();
+        $rEngine->setContainer($this->registry->tEngine);
+
+        return $pml->toHtml($rEngine, 'listPendingMembers.tpl');
+    }
+
+    //  }}}
+
+    //  {{{ editMemberAction()
+    /**
+     * Description of editMemberAction()
+     *
+     * @return string
+     * @access public
+     */
+    public function editMemberAction()
+    {
+        $member = new Toolkit_Members();
+
+        $this->registry->controllerObject->content = $member->toHtml();
+        $this->registry->controllerObject->breadcrumbs = true;
+        $this->registry->controllerObject->listingType
+            = $this->registry->config
+            ->getItem('section', 'listing type')
+            ->getItem('directive', 'plural')
+            ->getContent();
+        $this->registry->controllerObject->searchResults
+            = HTTP_Session2::get('searchResults');
+
+        $this->registry->controllerObject->topScripts
+            = Toolkit_Common::getScripts($GLOBALS['topScripts']);
+        $this->registry->controllerObject->bottomScripts
+            = Toolkit_Common::getScripts($GLOBALS['bottomScripts']);
+        $this->registry->controllerObject->styles
+            = Toolkit_Common::getStyleSheets($GLOBALS['styleSheets']);
+
+        $this->registry->tEngine->compile('admin.tpl');
+        return $this->registry->tEngine->bufferedOutputObject(
+            $this->registry->controllerObject
+        );
+    }
+
+    //  }}}
+    //  {{{ indexAction()
+    /**
+     * Description of indexAction()
+     *
+     * @return unknown
+     * @access public
+     */
+    public function indexAction()
+    {
+        $indexController = new Toolkit_Members_Admin_IndexController($this->registry);
+        return $indexController->indexAction();
+    }
+
+    //  }}}
+    //  {{{ listMembersAction()
+    /**
+     * Description of listMembersAction()
+     *
+     * @return unknown
+     * @access public
+     */
+    public function listMembersAction()
+    {
+        return $this->indexAction();
+    }
+
+    //  }}}
+    //  {{{ memberReportsAction()
+    /**
+     * Description of memberReportsAction()
+     *
+     * @return string
+     * @access public
+     */
+    public function memberReportsAction()
+    {
+        //  rendering engine to use
+        $rEngine = new Structures_DataGrid_Renderer_Flexy();
+        $rEngine->setContainer($this->registry->tEngine);
+
+        if ($memberId = filter_input(INPUT_GET, 'member_id', FILTER_VALIDATE_INT)) {
+            $detail = new Toolkit_Members_ExposureDetailReports($this->registry->dbh);
+            $detail->setQuery($memberId);
+            $detail->setDefaultSort(array('month' => 'desc'));
+            $urlFormat = '<a href="%s">Reports</a>';
+            $url = MEDIA_BASE_URL . 'admin/members.php?rt=Members&ac=memberReports';
+            if ($_REQUEST['reportMonth']) {
+                $url .= '&reportMonth=' . $_REQUEST['reportMonth'];
+            }
+            $this->registry->controllerObject->content = sprintf(
+                $urlFormat,
+                $url
+            );
+            $this->registry->controllerObject->content .= $detail->toHtml($rEngine);
+        } else {
+            $form = new Toolkit_Members_Admin_MemberReportSearch(
+                'MemberSearchReport',
+                'get'
+            );
+            $form->configureForm($this->registry->dbh);
+            $this->registry->controllerObject->content .= $form->toHtml($this->registry->dbh);
+            if ($_REQUEST['searching']) {
+                $reports = new Toolkit_Members_ExposureReports(
+                    $this->registry->dbh,
+                    50
+                );
+                $reports->setQuery();
+                $this->registry->controllerObject->content .= $reports->toHtml($rEngine);
+            }
+        }
+        $this->registry->controllerObject->topScripts
+            = Toolkit_Common::getScripts($GLOBALS['topScripts']);
+        $this->registry->controllerObject->bottomScripts
+            = Toolkit_Common::getScripts($GLOBALS['bottomScripts']);
+        $this->registry->controllerObject->styles
+            = Toolkit_Common::getStyleSheets($GLOBALS['styleSheets']);
+
+        $this->registry->tEngine->compile('admin.tpl');
+        return $this->registry->tEngine->bufferedOutputObject(
+            $this->registry->controllerObject
+        );
+    }
+
+    //  }}}
+    //  {{{ newMemberRequestsAction()
+    /**
+     * Description of newMemberRequestsAction()
+     *
+     * @return string
+     * @access public
+     */
+    public function newMemberRequestsAction()
+    {
+        if ($memberId = filter_input(INPUT_GET, 'id', FILTER_VALIDATE_INT)) {
+            $this->registry->controllerObject->content
+                = $this->_authorizeNewMember($memberId);
+        } else {
+            $this->registry->controllerObject->content = $this->_listNewMembers();
+        }
+
+        $this->registry->controllerObject->topScripts
+            = Toolkit_Common::getScripts($GLOBALS['topScripts']);
+        $this->registry->controllerObject->bottomScripts
+            = Toolkit_Common::getScripts($GLOBALS['bottomScripts']);
+        $this->registry->controllerObject->styles
+            = Toolkit_Common::getStyleSheets($GLOBALS['styleSheets']);
+
+        $this->registry->tEngine->compile('admin.tpl');
+        return $this->registry->tEngine->bufferedOutputObject(
+            $this->registry->controllerObject
+        );
+    }
+
+    //  }}}
+    //  {{{ pendingUpdatesAction()
+    /**
+     * Description of pendingUpdatesAction()
+     *
+     * @return string
+     * @access public
+     */
+    public function pendingUpdatesAction()
+    {
+        if ($memberId = filter_input(INPUT_GET, 'id', FILTER_VALIDATE_INT)) {
+            $this->registry->controllerObject->content
+                = $this->_authorizeMemberUpdates($memberId);
+        } else {
+            $this->registry->controllerObject->content = $this->_listPendingMembers();
+        }
+
+        $this->registry->controllerObject->topScripts
+            = Toolkit_Common::getScripts($GLOBALS['topScripts']);
+        $this->registry->controllerObject->bottomScripts
+            = Toolkit_Common::getScripts($GLOBALS['bottomScripts']);
+        $this->registry->controllerObject->styles
+            = Toolkit_Common::getStyleSheets($GLOBALS['styleSheets']);
+
+        $this->registry->tEngine->compile('admin.tpl');
+        return $this->registry->tEngine->bufferedOutputObject(
+            $this->registry->controllerObject
+        );
+    }
+
+    //  }}}
+}
diff --git a/Toolkit/Members/Admin/Navigation.php b/Toolkit/Members/Admin/Navigation.php
new file mode 100644 (file)
index 0000000..b62f038
--- /dev/null
@@ -0,0 +1,403 @@
+<?php
+/**
+ * Navigation.php
+ * 
+ * PHP version 5.2
+ * 
+ * @category  Toolkit
+ * @package   Members_Admin
+ * @author    SJamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @version   SVN: $id$
+ * @link      <>
+ */
+
+/**
+ * Toolkit_Members_Admin_Navigation
+ * 
+ * Description of Toolkit_Members_Admin_Navigation
+ * 
+ * @category  Toolkit
+ * @package   Members_Admin
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @release   Release: $id$
+ * @link      <>
+ */
+class Toolkit_Members_Admin_Navigation extends Toolkit_NavigationAbstract
+       implements Toolkit_INavigation
+{
+       //      {{{     __construct()
+
+    /**
+     * Class constructor
+     * 
+     * @param HTML_Menu          $menu    Description for $menu
+     * @param HTML_Menu_Renderer $rEngine Description for $rEngine
+     * 
+     * @access public
+     */
+       public function __construct(
+               HTML_Menu $menu,
+               HTML_Menu_Renderer $rEngine
+       ) {
+               $this->menu      = $menu;
+               $this->rEngine   = $rEngine;
+               $this->currIndex = 'members';
+       }
+
+       //      }}}
+
+       //      {{{     _hasPendingMemberUpdates()
+
+    /**
+     * Description of _hasPendingMemberUpdates()
+     * 
+     * @param PDO $dbh PDO
+     * 
+     * @return string|boolean|mixedd
+     * @access private
+     */
+       private function _hasPendingMemberUpdates(PDO $dbh)
+       {
+               try {
+                       $sql = "
+                               SELECT count(*) AS total
+                                 FROM member_updates";
+
+                       $row = $dbh->query($sql)->fetch();
+                       return $row['total'] ? 'hasPending' : false;
+               } catch (PDOException $e) {
+                       return Toolkit_Common::handleError($e);
+               }
+       }
+
+       //      }}}
+       //      {{{     _hasNewMemberRequests()
+
+    /**
+     * Description of _hasNewMemberRequests
+     * 
+     * @param PDO $dbh PDO
+     * 
+     * @return string|boolean|mixed
+     * @access private 
+     */
+       private function _hasNewMemberRequests(PDO $dbh)
+       {
+               try {
+                       $sql = "
+                               SELECT count(*) AS total
+                                 FROM member
+                                WHERE new_member";
+
+                       $row = $dbh->query($sql)->fetch();
+                       return $row['total'] ? 'hasPending' : false;
+               } catch (PDOException $e) {
+                       return Toolkit_Common::handleError($e);
+               }
+       }
+
+       //      }}}
+
+       //      {{{     getNavSructure()
+       //      @codeCoverageIgnoreStart
+
+    /**
+     * Sets up a multi dimensional array used for the nav structure
+        *
+        * @param PDO                      $dbh Database handler
+        * @param Config_Container $c   Application configuration
+     *
+     * @return mixed false on sql error. otherwise void
+     * @access public
+     */
+       public function getNavStructure(PDO $dbh, Config_Container $c)
+       {
+        //  get reference to [listing type] section of config file
+        $singularType = strtolower(
+                       $c->getItem('section', 'listing type')
+                         ->getItem('directive', 'singular')
+                         ->getContent()
+               );
+        $pluralType = strtolower(
+                       $c->getItem('section', 'listing type')
+                         ->getItem('directive', 'plural')
+                         ->getContent()
+               );
+        $singularRegion = strtolower(
+                       $c->getItem('section', 'region type')
+              ->getItem('directive', 'singular')
+                         ->getContent()
+               );
+        $pluralRegion = strtolower(
+                       $c->getItem('section', 'region type')
+              ->getItem('directive', 'plural')
+                         ->getContent()
+               );
+
+               $nav = array(
+                       'Members' => array(
+                               'Title' => ucfirst($pluralType),
+                               'url' => MEDIA_BASE_URL . 'admin/members.php?rt=Members',
+                               'desc' => "View and edit $pluralType in the database",
+                               'sub' => array(
+                                       'listMembers' => array(
+                                               'Title' => 'List ' . ucfirst($pluralType),
+                                               'url' => MEDIA_BASE_URL . 'admin/members.php?rt=Members&amp;ac=listMembers',
+                                               'desc' => "Show all $pluralType in the database",
+                                       ),
+                                       'editMember' => array(
+                                               'Title' => 'Add ' . ucfirst($singularType) . ' Listing',
+                                               'url' => MEDIA_BASE_URL . 'admin/members.php?rt=Members&amp;ac=editMember&amp;tab=info',
+                                               'desc' => "Add a $singularType record to the database",
+                                       ),
+                                       'pendingUpdates' => array(
+                                               'Title' => 'Pending ' . ucfirst($singularType) . ' Listing Update',
+                                               'url' => MEDIA_BASE_URL . "admin/members.php?rt=Members&amp;ac=pendingUpdates",
+                                               'desc' => "Accept or reject any $singularType record changes",
+                                               'class' => $this->_hasPendingMemberUpdates($dbh)
+                                       ),
+                                       'newMemberRequests' => array(
+                                               'Title' => 'New ' . ucfirst($singularType) . ' Requests',
+                                               'url' => MEDIA_BASE_URL . 'admin/members.php?rt=Members&amp;ac=newMemberRequests',
+                                               'desc' => "Accept or reject any new $singularType requests",
+                                               'class' => $this->_hasNewMemberRequests($dbh)
+                                       ),
+                                       'memberReports' => array(
+                                               'Title' => 'Reports',
+                                               'url' => MEDIA_BASE_URL . 'admin/members.php?rt=Members&amp;ac=memberReports',
+                                               'desc' => 'View reports on view,detail,or clicks',
+                                       )
+                               )
+                       ),
+                       'Settings' => array(
+                               'Title' => 'Settings',
+                               'url' => MEDIA_BASE_URL . 'admin/members.php?rt=Settings',
+                               'desc' => 'Application settings',
+                               'sub' => array(
+                                       'Categories' => array(
+                                               'Title' => 'Categories',
+                                               'url' => MEDIA_BASE_URL . 'admin/members.php?rt=Categories',
+                                               'desc' => "View and edit $singularType categories",
+                                               'sub' => array(
+                                                       'listCategories' => array(
+                                                               'Title' => 'List Categories',
+                                                               'url' => MEDIA_BASE_URL . 'admin/members.php?rt=Categories&amp;ac=listCategories',
+                                                               'desc' => "Show a list of all $singularType categories in the databse",
+                                                       ),
+                                                       'editCategory' => array(
+                                                               'Title' => 'Add Category',
+                                                               'url' => MEDIA_BASE_URL . 'admin/members.php?rt=Categories&amp;ac=editCategory',
+                                                               'desc' => "Add a $singularType category to the database",
+                                                       )
+                                               )
+                                       ),
+                                       'Amenities' => array(
+                                               'Title' => "Amenities",
+                                               'url' => MEDIA_BASE_URL . 'admin/members.php?rt=Amenities',
+                                               'desc' => "View and edit $singularType amenities",
+                                               'sub' => array(
+                                                       'listAmenities' => array(
+                                                               'Title' => 'List Amenities',
+                                                               'url' => MEDIA_BASE_URL . 'admin/members.php?rt=Amenities&amp;ac=listAmenities',
+                                                               'desc' => "Show a list of all $singularType amenities in the database",
+                                                       ),
+                                                       'editAmenity' => array(
+                                                               'Title' => 'Add Amenity',
+                                                               'url' => MEDIA_BASE_URL . 'admin/members.php?rt=Amenities&amp;ac=editAmenity',
+                                                               'desc' => "Add a $singularType amenity to the database",
+                                                       )
+                                               )
+                                       ),
+                                       'Regions' => array(
+                                               'Title' => ucfirst($pluralRegion),
+                                               'url' => MEDIA_BASE_URL . 'admin/members.php?rt=Regions',
+                                               'desc' => "View and edit " . strtolower($singularType) . ' ' . $pluralRegion,
+                                               'sub' => array(
+                                                       'listRegions' => array(
+                                                               'Title' => 'List ' . ucfirst($pluralRegion),
+                                                               'url' => MEDIA_BASE_URL . 'admin/members.php?rt=Regions&amp;ac=listRegions',
+                                                               'desc' => "Show a list of all $singularType $pluralRegion in the database",
+                                                       ),
+                                                       'editRegion' => array(
+                                                               'Title' => 'Add ' . ucfirst($singularRegion),
+                                                               'url' => MEDIA_BASE_URL . 'admin/members.php?rt=Regions&amp;ac=editRegion',
+                                                               'desc' => "Add a $singularType $singularRegion to the database",
+                                                       )
+                                               )
+                                       ),
+                                       'Cities' => array(
+                                               'Title' => "Cities",
+                                               'url' => MEDIA_BASE_URL . 'admin/members.php?rt=Cities',
+                                               'desc' => "View and edit $singularType cities",
+                                               'sub' => array(
+                                                       'listCities' => array(
+                                                               'Title' => 'List Cities',
+                                                               'url' => MEDIA_BASE_URL . 'admin/members.php?rt=Cities&amp;ac=listCities',
+                                                               'desc' => "Show a list of all $singularType cities in the database",
+                                                       ),
+                                                       'editCity' => array(
+                                                               'Title' => 'Add City',
+                                                               'url' => MEDIA_BASE_URL . 'admin/members.php?rt=Cities&amp;ac=editCity',
+                                                               'desc' => "Add a $singularType city to the database",
+                                                       )
+                                               )
+                                       ),
+                                       'Counties' => array(
+                                               'Title' => "Counties",
+                                               'url' => MEDIA_BASE_URL . 'admin/members.php?rt=Counties',
+                                               'desc' => "View and edit $singularType counties",
+                                               'sub' => array(
+                                                       'listCounties' => array(
+                                                               'Title' => 'List Counties',
+                                                               'url' => MEDIA_BASE_URL . 'admin/members.php?rt=Counties&amp;ac=listCounties',
+                                                               'desc' => "Show a list of all $singularType counties in the database",
+                                                       ),
+                                                       'editCounty' => array(
+                                                               'Title' => 'Add County',
+                                                               'url' => MEDIA_BASE_URL . 'admin/members.php?rt=County&amp;ac=editCounty',
+                                                               'desc' => "Add a $singularType county to the database",
+                                                       )
+                                               )
+                                       )
+                               )
+                       ),
+                       'Newsletter' => array(
+                               'Title' => "Newsletter",
+                               'url' => MEDIA_BASE_URL . 'admin/members.php?rt=Newsletter',
+                               'desc' => "Prepare a $singularType newsletter mailing",
+                               'sub' => array(
+                                       'search' => array(
+                                               'Title' => 'Search',
+                                               'url' => MEDIA_BASE_URL . 'admin/members.php?rt=Newsletter&amp;ac=search',
+                                               'desc' => 'View and edit HTML email templates to use in the newsletter',
+                                       ),
+                                       'listHtmlEmails' => array(
+                                               'Title' => 'HTML Emails',
+                                               'url' => MEDIA_BASE_URL . 'admin/members.php?rt=Newsletter&amp;ac=listHtmlEmails',
+                                               'desc' => 'View and edit HTML email templates to use in the newsletter',
+                                       ),
+                                       'listArchivedHtmlEmails' => array(
+                                               'Title' => 'Archived HTML Emails',
+                                               'url' => MEDIA_BASE_URL . 'admin/members.php?rt=Newsletter&amp;archived=1&amp;ac=listArchivedHtmlEmails',
+                                               'desc' => 'View and edit HTML email templates to use in the newsletter',
+                                       ),
+                                       'editHtmlEmail' => array(
+                                               'Title' => 'Add HTML Email',
+                                               'url' => MEDIA_BASE_URL . 'admin/members.php?rt=Newsletter&amp;ac=editHtmlEmail',
+                                               'desc' => 'Create a HTML email templates to use in the newsletter',
+                                       ),
+                                       //'memberExportFile' => array( 'Title' => ucfirst($singularType) . ' Export File', 'url' => MEDIA_BASE_URL . 'admin/members.php?rt=Newsletter&amp;ac=memberExportFile', 'desc' => '',),
+                                       //85 => array('Title' => $singularType . ' Subscriptions', 'url' => MEDIA_BASE_URL . 'admin/members.php?cat=8&subCat=4', 'desc' => '', 'ac' => 'memberSubscriptions',),
+                               )
+                       ),
+                       'AdvancedSearch' => array(
+                               'Title' => 'Advanced Search',
+                               'url' => MEDIA_BASE_URL . 'admin/members.php?rt=AdvancedSearch',
+                               'desc' => 'Advanced database search'
+                       ),
+                       'Export' => array(
+                               'Title' => 'Export',
+                               'url' => MEDIA_BASE_URL . 'admin/members.php?rt=Export',
+                               'desc' => 'Export records from the database'
+                       )
+               );
+
+        //  get reference to [conf] section of config file
+        $config =& $c->getItem('section', 'conf');
+
+        //  get region
+        $directive =& $config->getItem('directive', 'regions');
+               if (!$directive->getContent()) {
+                       unset($nav['Settings']['sub']['Regions']);
+               }
+        //  get controlled cities
+        $directive =& $config->getItem('directive', 'controlledCities');
+               if (!$directive->getContent()) {
+                       unset($nav['Settings']['sub']['Cities']);
+               }
+        //  get counties
+        $directive =& $config->getItem('directive', 'counties');
+               if (!$directive->getContent()) {
+            unset($nav['Settings']['sub']['Counties']);
+        }
+        //  get amenities
+        $directive =& $config->getItem('directive', 'amenities');
+               if (!$directive->getContent()) {
+                       unset($nav['Settings']['sub']['Amenities']);
+               }
+
+        //  get new member requests
+        $directive =& $config->getItem('directive', 'newMemberRequests');
+               if (!$directive->getContent()) {
+                       unset($nav['Members']['sub']['newMemberRequests']);
+               }
+        
+        $billingModule = $c->getItem('section', 'admin')
+            ->getItem('directive', 'billing')
+            ->getContent();
+        
+
+        if ($billingModule) {
+            $billingNav = new Toolkit_Members_Billing_AdminNavigation();
+            $nav['Billing'] = $billingNav->getNavigationArray($c);
+        }
+               return $nav;
+       }
+
+       //      @codeCoverageIgnoreEnd
+       //      }}}
+
+       //      {{{     setCurrentIndex()
+
+    /**
+     * Description of setCurrentIndex()
+     * 
+     * @return void
+     * @access protected 
+     */
+       protected function setCurrentIndex()
+       {
+               if ($action = filter_input(INPUT_GET, 'ac')) {
+                       $this->menu->forceCurrentIndex($action);
+               } elseif ($route = filter_input(INPUT_GET, 'rt')) {
+                       $this->menu->forceCurrentIndex($route);
+               } else {
+                       $this->menu->forceCurrentIndex('Members');
+               }
+       }
+
+       //      }}}
+       //      {{{     setNavTemplates()
+
+    /**
+     * Description of setNavTemplates()
+     * 
+     * @return void
+     * @access protected 
+     */
+       protected function setNavTemplates()
+       {
+               $tpl = '<li><a class="%s" href="%s" title="%s">{Title}</a></li>';
+               $this->rEngine->setEntryTemplate(
+                       HTML_MENU_ENTRY_INACTIVE,
+                       sprintf($tpl, '{class}', '{url}', '{desc}', '{Title}')
+               );
+               $this->rEngine->setEntryTemplate(
+                       HTML_MENU_ENTRY_ACTIVE,
+                       sprintf($tpl, 'active {class}', '{url}', '{desc}', '{Title}')
+               );
+               $this->rEngine->setEntryTemplate(
+                       HTML_MENU_ENTRY_ACTIVEPATH,
+                       sprintf($tpl, 'active {class}', '{url}', '{desc}', '{Title}')
+               );
+               $this->rEngine->setMenuTemplate('<div>', '</div>');
+               $this->rEngine->setRowTemplate('<ul class="navlist">', '</ul>');
+       }
+
+       //      }}}
+}
+?>
diff --git a/Toolkit/Members/Admin/NewsletterController.php b/Toolkit/Members/Admin/NewsletterController.php
new file mode 100644 (file)
index 0000000..28c00e2
--- /dev/null
@@ -0,0 +1,230 @@
+<?php
+/**
+ * NewsletterController.php
+ *
+ * PHP version 5.2
+ *
+ * @category  Toolkit
+ * @package   Members_Admin
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @version   SVN: $id$
+ * @link      <>
+ */
+
+/**
+ * Toolkit_Members_Admin_NewsletterController
+ *
+ * Description of Toolkit_Members_Admin_NewsletterController
+ *
+ * @category  Toolkit
+ * @package   Members_Admin
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @release   Release: $id$
+ * @link      <>
+ */
+class Toolkit_Members_Admin_NewsletterController
+    extends Toolkit_BaseControllerAbstract implements Toolkit_IController
+{
+    //  {{{ editHtmlEmailAction()
+    /**
+     * Description of editHtmlEmailAction()
+     *
+     * @return string
+     * @access public
+     */
+    public function editHtmlEmailAction()
+    {
+        if (isset($_GET['preview'])
+            && $newsId = filter_input(INPUT_GET, 'news_id', FILTER_VALIDATE_INT)
+        ) {
+            $preview = new Toolkit_Members_Admin_PreviewHtmlEmail(
+                $this->registry->dbh,
+                $this->registry->tEngine
+            );
+            $this->registry->controllerObject->content
+                = $preview->renderPreviewIFrame(
+                    $newsId,
+                    'previewNewsletterWrapper.html'
+                );
+        } else if (isset($_GET['previewFrame'])
+            && $newsId = filter_input(INPUT_GET, 'news_id', FILTER_VALIDATE_INT)
+        ) {
+            $preview = new Toolkit_Members_Admin_PreviewHtmlEmail(
+                $this->registry->dbh,
+                $this->registry->tEngine
+            );
+            echo $this->registry->controllerObject->content
+                = $preview->renderPreview(
+                    $newsId,
+                    'previewHtmlEmail.tpl'
+                );
+            exit;
+        } else {
+            $GLOBALS['bottomScripts'][]
+                = CKEDITOR_JS . '';
+            $GLOBALS['bottomScripts'][]
+                = MEDIA_BASE_URL . 'Toolkit/Members/libjs/edit-html-email.js';
+
+            $form = new Toolkit_Members_Admin_EditHtmlEmail(
+                $this->registry->dbh,
+                'edit_html_email'
+            );
+            $form->configureForm();
+            $this->registry->controllerObject->content = $form->toHtml();
+        }
+
+        $this->registry->controllerObject->topScripts
+            = Toolkit_Common::getScripts($GLOBALS['topScripts']);
+        $this->registry->controllerObject->bottomScripts
+            = Toolkit_Common::getScripts($GLOBALS['bottomScripts']);
+        $this->registry->controllerObject->styles
+            = Toolkit_Common::getStyleSheets($GLOBALS['styleSheets']);
+
+        $this->registry->tEngine->compile('admin.tpl');
+        return $this->registry->tEngine->bufferedOutputObject(
+            $this->registry->controllerObject
+        );
+    }
+
+    //  }}}
+
+    //  {{{ indexAction()
+    /**
+     * Description of indexAction()
+     *
+     * @return string
+     * @access public
+     */
+    public function indexAction()
+    {
+        $GLOBALS['styleSheets'][] = MEDIA_BASE_URL . 'css/contactform.css';
+
+        HTTP_Session2::set('newSearch', true);
+        $searchForm = new Toolkit_Members_Admin_AdvancedSearch(
+            'advanced-record-search',
+            'get',
+            MEDIA_BASE_URL . 'admin/members.php',
+            '',
+            null,
+            true
+        );
+
+        $searchForm->configureForm($this->registry->dbh, $this->registry->config);
+
+        $this->registry->controllerObject->content
+            = $searchForm->toHtml($this->registry->dbh, $this->registry->config);
+        if ($results = $searchForm->getSearchResults()) {
+            $GLOBALS['bottomScripts'][]
+                = MEDIA_BASE_URL . 'Toolkit/Members/libjs/member-list.js';
+
+            // Newsletter Mailout Form
+            $news = new Toolkit_Members_Admin_MailOutForm(
+                'mailout_news',
+                'POST',
+                urldecode($_SERVER['REQUEST_URI']),
+                '',
+                null,
+                true
+            );
+
+            $news->configureForm($this->registry->dbh, $this->registry->config);
+            $this->registry->controllerObject->content
+                .= $news->toHtml($results, $this->registry->dbh);
+
+            $membersList = new Toolkit_Members_Admin_AdvancedSearchDataGrid(
+                $this->registry->dbh,
+                50
+            );
+            if (   isset($_GET['d'])
+                && $_GET['d'] == 't'
+                && $memberId = filter_input(INPUT_GET, 'id', FILTER_VALIDATE_INT)
+            ) {
+                $membersList->removeMember(
+                    $memberId,
+                    new Toolkit_Image_Server()
+                );
+            }
+            $membersList->setQuery($results);
+            $membersList->setDefaultSort(array('sort_field' => 'ASC'));
+
+            //  rendering engine to use
+            $rEngine = new Structures_DataGrid_Renderer_Flexy();
+            $rEngine->setContainer($this->registry->tEngine);
+
+            $this->registry->controllerObject->content
+                .= $membersList->toHtml($rEngine, 'listMembers.tpl');
+        }
+
+        $this->registry->controllerObject->topScripts
+            = Toolkit_Common::getScripts($GLOBALS['topScripts']);
+        $this->registry->controllerObject->bottomScripts
+            = Toolkit_Common::getScripts($GLOBALS['bottomScripts']);
+        $this->registry->controllerObject->styles
+            = Toolkit_Common::getStyleSheets($GLOBALS['styleSheets']);
+
+        $this->registry->tEngine->compile('admin.tpl');
+        return $this->registry->tEngine->bufferedOutputObject(
+            $this->registry->controllerObject
+        );
+    }
+
+    //  }}}
+
+    //  {{{ listArchivedHtmlEmailsAction()
+    /**
+     * Description of listArchivedHtmlEmailsAction
+     *
+     * @return string
+     * @access public
+     */
+    public function listArchivedHtmlEmailsAction()
+    {
+        return $this->listHtmlEmailsAction();
+    }
+
+    //  }}}
+    //  {{{ listHtmlEmailsAction()
+    /**
+     * Description of listHtmlEmailsAction()
+     *
+     * @return string
+     * @access public
+     */
+    public function listHtmlEmailsAction()
+    {
+        $emailList = new Toolkit_Members_Admin_ListHtmlEmails($this->registry->dbh);
+        $this->registry->controllerObject->content = $emailList->renderHtmlEmails();
+
+        $this->registry->controllerObject->topScripts
+            = Toolkit_Common::getScripts($GLOBALS['topScripts']);
+        $this->registry->controllerObject->bottomScripts
+            = Toolkit_Common::getScripts($GLOBALS['bottomScripts']);
+        $this->registry->controllerObject->styles
+            = Toolkit_Common::getStyleSheets($GLOBALS['styleSheets']);
+
+        $this->registry->tEngine->compile('admin.tpl');
+        return $this->registry->tEngine->bufferedOutputObject(
+            $this->registry->controllerObject
+        );
+    }
+
+    //  }}}
+
+    //  {{{ searchAction()
+    /**
+     * Description of searchAction()
+     *
+     * @return string
+     * @access public
+     */
+    public function searchAction()
+    {
+        return $this->indexAction();
+    }
+
+    //  }}}
+}
diff --git a/Toolkit/Members/Admin/Newsletters.php b/Toolkit/Members/Admin/Newsletters.php
new file mode 100644 (file)
index 0000000..bd451e2
--- /dev/null
@@ -0,0 +1,340 @@
+<?php
+/**
+ * Newsletters.php
+ * 
+ * PHP versions 4 and 5
+ * 
+ * @category  Toolkit
+ * @package   Members_Admin
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2009 Gaslight Media
+ * @license   Gaslight Media
+ * @version   CVS: $Id: Newsletters.php,v 1.9 2009/09/16 19:00:58 matrix Exp $
+ * @link      http://demo.gaslightmedia.com
+ */
+
+
+/**
+ * Toolkit_Members_Admin_Newsletters
+ * 
+ * Search for Members to send out E-Blast
+ * 
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2009 Gaslight Media
+ * @license   Gaslight Media
+ * @link      http://demo.gaslightmedia.com
+ */
+class Toolkit_Members_Admin_Newsletters extends Toolkit_FlexyDataGridBuilder
+{
+    //  {{{  Properties
+
+    /**
+     * Message for no record found
+     * @var string
+     * @access protected
+     */
+    protected $noRecMessage = 'No Data Found';
+    /**
+     * Table name for tracking
+     * @var    string   
+     * @access protected
+     */
+    protected $tableName = 'member';
+
+    /**
+     * Description for protected
+     * @var    array    
+     * @access protected
+     */
+    protected $queryParams = array();
+
+    /**
+     * Template File
+     * @var    string   
+     * @access protected
+     */
+    protected $template = 'memberContactsList.tpl';
+
+    //  }}}
+    //  {{{ __construct()
+
+    /**
+     * Class constructor
+     *
+     * define where templates for the data grid are at, 
+     * then call the parent constructor which will handle
+     * finishing the settings for the datagrid.
+     *
+     * After all settings are finished you can call the toHTML()
+     * function on this object and the datagrid
+     * will be rendered and returned as a string.  
+     * Optionally you could call show() and the datagrid would
+     * be rendered and output immediatley to the screen.
+     *
+     * @param PDO    $pdo          PHP Data Object to use for DB calls
+     * @param string $limit        The number of records to display per page.
+     * @param int    $page                The current page viewed.
+     *                             In most cases, this is useless.
+     *                             Note: if you specify this, the "page" GET 
+     *                             variable will be ignored.
+     * @param string $rendererType The type of renderer to use.
+     *                             You may prefer to use the $type argument
+     *                             of {@link render}, {@link fill} or 
+     *                             {@link getOutput}
+     * 
+     * @access public
+     */
+    public function __construct(
+        PDO $pdo,
+        $limit = null,
+        $page = null,
+        $rendererType = null
+    ) {
+        $this->pagerOptions['containerClass'] = 'pages';
+        parent::__construct($pdo, $limit, $page, $rendererType);
+    }
+
+    //  }}}
+
+    //  {{{ configureColumns()
+
+    /**
+     * Configures the columns (fields) that will be used in our datagrid renderer.
+     *
+     * @return  void
+     * @access public
+     */
+    protected function configureColumns()
+    {
+        $directUrl = new Structures_DataGrid_Column(
+            'DirectURL',
+            'directUrl',
+            'directUrl',
+            null,
+            null,
+            array(&$this, 'recordUrl')
+        );
+        $this->addColumn($directUrl);
+
+        $memberName = new Structures_DataGrid_Column(
+            'Member Name',
+            'member_name',
+            'member_name'
+        );
+        $this->addColumn($memberName);
+
+        $fname = new Structures_DataGrid_Column(
+            'First Name',
+            'fname',
+            'fname'
+        );
+        $this->addColumn($fname);
+
+        $lname = new Structures_DataGrid_Column(
+            'Last Name',
+            'lname',
+            'lname'
+        );
+        $this->addColumn($lname);
+    }
+
+    //  }}}
+
+    //  {{{ getMemberCats()
+
+    /**
+     * getMemberCats 
+     * 
+     * @return array
+     * @access protected
+     * @throws PEAR_Error
+     */
+    protected function getMemberCats()
+    {
+        $sql = "
+                       SELECT *
+                         FROM category
+                        WHERE parent_id = 0
+                        ORDER BY parent_id, name";
+        try {
+            $stmt = $this->dbh->query($sql);
+            //$categories[] = '';
+            while ($row = $stmt->fetch()) {
+                unset($subs);
+                $sql = "
+                                       SELECT *
+                                         FROM category
+                                        WHERE parent_id = :id
+                                        ORDER BY parent_id, name";
+                try {
+                    $stmt2 = $this->dbh->prepare($sql);
+                    $stmt2->bindParam(":id", $row['category_id'], PDO::PARAM_INT);
+                    $stmt2->execute();
+                    while ($row2 = $stmt2->fetch()) {
+                        $subs[$row2['category_id']] = $row2['name'];
+                    }
+                } catch(PDOException $e) {
+                                       return Toolkit_Common::handleError($e);
+                }
+                if ($subs) {
+                    $categories[$row['name']] = $subs;
+                }
+            }
+            if (is_array($categories)) {
+                $select = '<select name="Categories[]" multiple="multiple" size="10">';
+                foreach ($categories as $label => $cat_id) {
+                    $select .= '<optgroup label="'.$label.'">';
+                    foreach ($cat_id as $category_id => $name) {
+                        $select .= '<option value="'.$category_id.'"';
+                        if ($_REQUEST['Categories'] && in_array($category_id, $_REQUEST['Categories'])) {
+                            $select .= 'selected';
+                        }
+                        $select .= '>'.$name.'</option>';
+                    }
+                    $select .= '</optgroup>';
+                }
+                $select .= '</select>';
+            }
+            return $select;
+        } catch(PDOException $e) {
+                       return Toolkit_Common::handleError($e);
+        }
+    }
+
+    //  }}}
+    //  {{{ getNewsletters()
+
+    /**
+     * getNewsletters 
+     * 
+     * @return array
+     * @access public
+     * @throws PEAR_Error
+     */
+    protected function getNewsletters()
+    {
+        $sql = "
+                       SELECT id, subject
+                         FROM news_response
+                        ORDER BY subject";
+        try {
+            $stmt = $this->dbh->query($sql);
+            while ($row = $stmt->fetch()) {
+                $newsletters[$row['id']] = $row['subject'];
+            }
+            return $newsletters;
+        } catch(PDOException $e) {
+                       return Toolkit_Common::handleError($e);
+        }
+    }
+        
+    //  }}}
+
+    //  {{{ recordUrl()
+
+    /**
+     * Returns the url for a member.
+     *
+     * Used when configuring the columns for the data grid. This
+     * function generates the url to get to the member detail page
+     *
+     * @param array $data Structure_DataGrid
+     *
+     * @return string
+     * @access public
+     */
+    public function recordUrl($data)
+    {
+        return "members.php?".
+            "id={$data['record']['member_id']}".
+            "&cat=2".
+            "&subCat=2";
+    }
+
+    //  }}}
+
+    //  {{{ setQuery()
+
+    /**
+     * Sets the query to use to fetch the datagrid results
+     *
+     * @return void
+     * @access public
+     */
+    public function setQuery()
+    {
+        $sql = "
+                       SELECT mc.member_id, m.member_name, mc.fname, mc.lname
+                         FROM member m LEFT OUTER JOIN member_contacts mc USING (member_id)";
+        if ($_REQUEST['Categories']) {
+            $where[] = "
+            m.member_id IN 
+                (SELECT member_id
+                FROM    member_category
+                WHERE   category_id IN (".implode(",", $_REQUEST['Categories'])."))";
+        }
+        if ($_REQUEST['Search']) {
+            $where[] = "
+            m.member_name ilike '%{$_REQUEST['Search']}%'";
+        }
+        if (is_array($where)) {
+            $sql .= " WHERE ".implode(" AND ", $where);
+        }
+
+        parent::setQuery($sql);
+    }
+
+    //  }}}
+    //  {{{ setControlObject()
+
+    /**
+     * Sets any control object variables that are going to be used in the template
+     *
+     * @return void
+     * @access public
+     */
+    protected function setControlObject()
+    {
+        $form = new HTML_QuickForm('ReportsListForm', 'get');
+        $form->addElement('hidden', 'cat', $_REQUEST['cat']);
+        $form->addElement('hidden', 'subCat', $_REQUEST['subCat']);
+        $form->addElement('text', 'Search', 'Search', $_REQUEST['Search']);
+        $form->addElement('static', 'Categories', 'Categories', $this->getMemberCats());
+        $form->addElement(
+            'select', 
+            'Output', 
+            'Output', 
+            array(
+                'HTML'=>'HTML',
+                'FILE'=>'FILE'
+            )
+        );
+        $form->addElement('submit', 'reportSubmit', 'Search Contacts');
+        if ($_REQUEST['reportMonth']) {
+            $form->setDefaults(
+                array(
+                    'reportMonth' => $_REQUEST['reportMonth']
+                )
+            );
+        } else {
+            $form->setDefaults(
+                array(
+                    'reportMonth' => date('n').'|'.date('Y')
+                )
+            );
+        }
+        $this->ctrlObj['memberContactSearchForm'] = $form->toHTML();
+        // second form will send the list out to either 
+        // a file download page
+        // or mailout page
+        $form2 = new HTML_QuickForm('memberSendForm', 'get');
+        $form2->addElement('select', 'newsletter', 'Newsletter', $this->getNewsletters());
+        $form2->addElement('submit', 'sendSubmit', 'Send Newsletter');
+        $this->ctrlObj['memberSendForm']          = $form2->toHTML();
+    }
+
+    //  }}}
+}
+?>
diff --git a/Toolkit/Members/Admin/PaymentController.php b/Toolkit/Members/Admin/PaymentController.php
new file mode 100644 (file)
index 0000000..56a1353
--- /dev/null
@@ -0,0 +1,82 @@
+<?php
+/**
+ * PaymentController.php
+ * 
+ * PHP version 5.2
+ * 
+ * @category  Toolkit
+ * @package   Members_Admin
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @version   SVN: $id$
+ * @link      <>
+ */
+
+/**
+ * Toolkit_Members_Admin_PaymentController
+ * 
+ * Description of Toolkit_Members_Admin_PaymentController
+ * 
+ * @category  Toolkit
+ * @package   Members_Admin
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @release   Release: $id$
+ * @link      <>
+ */
+class Toolkit_Members_Admin_PaymentController
+       extends Toolkit_BaseControllerAbstract
+    implements Toolkit_IController
+{
+    
+    /**
+     * Description of getPageLayout()
+     * 
+     * @param string $html Description of $html
+     * 
+     * @return string
+     * @access protected
+     */
+    protected function getPageLayout($html)
+    {
+        $this->registry->controllerObject->content = $html;
+               $this->registry->controllerObject->topScripts
+                       = Toolkit_Common::getScripts($GLOBALS['topScripts']);
+               $this->registry->controllerObject->bottomScripts
+                       = Toolkit_Common::getScripts($GLOBALS['bottomScripts']);
+               $this->registry->controllerObject->styles
+                       = Toolkit_Common::getStyleSheets($GLOBALS['styleSheets']);
+               $this->registry->tEngine->compile('admin.tpl');
+               return $this->registry->tEngine->bufferedOutputObject(
+                       $this->registry->controllerObject
+               );
+    }
+    
+    /**
+     * Description of indeAction()
+     * 
+     * @return string
+     * @access public
+     */
+       public function indexAction()
+       {
+        $form = new Toolkit_Members_Billing_PaymentForm(
+            Toolkit_Database::getInstance(),
+            'paymentForm',
+            'post',
+            MEDIA_BASE_URL . 'admin/members.php?rt=Payment' 
+        );
+        $form->configureForm();
+        $out = '';
+        if ($_REQUEST['formSubmitGood']) {
+            $out = '<div id="form-success-top">
+                The information below has been successfully submitted.
+            </div>';
+        }
+        $out .= $form->toHtml();
+               return $this->getPageLayout($out);
+       }
+    
+}
diff --git a/Toolkit/Members/Admin/PreviewHtmlEmail.php b/Toolkit/Members/Admin/PreviewHtmlEmail.php
new file mode 100644 (file)
index 0000000..cf55a38
--- /dev/null
@@ -0,0 +1,145 @@
+<?php
+
+/**
+ * PreviewHtmlEmail.php
+ *
+ * PHP version 5
+ *
+ * @category  Toolkit
+ * @package   Members_Admin
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2009 Gaslight Media
+ * @license   Gaslight Media
+ * @version   CVS: $Id: PreviewHtmlEmail.php,v 1.3 2009/12/30 16:48:56 matrix Exp $
+ * @link      <>
+ */
+
+/**
+ * Generate a preview of the newsletter
+ *
+ * @category  Toolkit
+ * @package   Members_Admin
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2009 Gaslight Media
+ * @license   Gaslight Media
+ * @version   Release: @package_version@
+ * @link      <>
+ */
+class Toolkit_Members_Admin_PreviewHtmlEmail
+{
+       //      {{{ properties
+
+       /**
+        * Database Handler
+        *
+        * @var PDO
+        * @access private
+        */
+       private $_dbh;
+
+       /**
+        * Templating engine to use
+        *
+        * @var HTML_Template_Flexy
+        * @access private
+        */
+       private $_tEngine;
+
+       //      }}}
+       //      {{{ __construct()
+
+       /**
+        * Class constructor
+        *
+     * @param PDO                 $pdo     PHP Data Object
+     * @param HTML_Template_Flexy $tEngine Templating engine to use
+     *
+        * @access public
+        */
+       public function __construct(PDO $pdo, HTML_Template_Flexy $tEngine)
+       {
+               $this->_dbh     = $pdo;
+               $this->_tEngine = $tEngine;
+       }
+
+       //      }}}
+
+    // {{{ _getNewsResponse()
+
+    /**
+     * Get the response field the table based on id
+     *
+     * @param int $id mathe id field
+     *
+     * @return string  Return response
+     * @access private
+     * @throws Toolkit_Members_Exception
+     */
+    private function _getNewsResponse($id)
+    {
+        try {
+            $sql = "
+                               SELECT response
+                                 FROM member_newsletters
+                                WHERE id = :id";
+            $stmt = $this->_dbh->prepare($sql);
+            $stmt->bindParam(":id", $id, PDO::PARAM_INT);
+            $stmt->execute();
+            return $stmt->fetchColumn();
+        } catch(PDOException $e) {
+                       Toolkit_Logger::logException('DB Error', $e);
+                       throw new Toolkit_Members_Exception(
+                               "unable to fetch member newsletter `$id`"
+                       );
+        }
+    }
+    // }}}
+    /**
+     * Renders the preview IFrame for the newsletter preview
+     * inner iframe src is directed to the method renderPreview
+     *
+     * @param int     $id       news id
+     * @param unknown $template template file
+     * 
+     * @return string
+     * @access public
+     */
+    public function renderPreviewIFrame($id, $template)
+    {
+        $page = new stdClass();
+
+        // get data and set response
+        $page->src = MEDIA_BASE_URL . 'admin/members.php?' .
+            'rt=Newsletter&ac=editHtmlEmail&previewFrame=1&news_id=' .
+            $id;
+
+        $this->_tEngine->compile($template);
+        return $this->_tEngine->bufferedOutputObject($page);
+    }
+    // {{{ renderPreview()
+
+    /**
+     * render the preview
+     *
+     * @param int     $id       id of newsletter to preview
+     * @param unknown $template Template to use
+     *
+     * @return string  Return html of template
+     * @access public
+     */
+    public function renderPreview($id, $template)
+    {
+        $page = new stdClass();
+
+        // get data and set response
+        $page->response = $this->_getNewsResponse($id);
+        // set the sitename
+        $page->sitename = SITENAME;
+        // set the cancel email
+        $page->membersEnewsEmail = FROM_MEMBER_NEWS_EMAIL;
+
+        $this->_tEngine->compile($template);
+        return $this->_tEngine->bufferedOutputObject($page);
+    }
+    // }}}
+}
diff --git a/Toolkit/Members/Admin/RegionsController.php b/Toolkit/Members/Admin/RegionsController.php
new file mode 100644 (file)
index 0000000..32defb4
--- /dev/null
@@ -0,0 +1,102 @@
+<?php
+/**
+ * RegionsController.php
+ * 
+ * PHP version 5.2
+ * 
+ * @category  Toolkit
+ * @package   Members_Admin
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @version   SVN: $id$
+ * @link      <>
+ */
+
+/**
+ * Toolkit_Members_Admin_RegionsController
+ * 
+ * Description of Toolkit_Members_Admin_RegionsController
+ * 
+ * @category  Toolkit
+ * @package   Members_Admin
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @release   Release: $id$
+ * @link      <>
+ */
+class Toolkit_Members_Admin_RegionsController
+       extends Toolkit_BaseControllerAbstract implements Toolkit_IController
+{
+       //      {{{     indexAction()
+    /**
+     * Main action for controller. Runs admin template, list out all regions
+     * 
+     * @return string
+     * @access public 
+     */
+       public function indexAction()
+       {
+               $regionList = new Toolkit_Members_Admin_ListRegions($this->registry->dbh);
+               $this->registry->controllerObject->content = $regionList->renderRegions();
+
+               $this->registry->controllerObject->topScripts
+                       = Toolkit_Common::getScripts($GLOBALS['topScripts']);
+               $this->registry->controllerObject->bottomScripts
+                       = Toolkit_Common::getScripts($GLOBALS['bottomScripts']);
+               $this->registry->controllerObject->styles
+                       = Toolkit_Common::getStyleSheets($GLOBALS['styleSheets']);
+
+               $this->registry->tEngine->compile('admin.tpl');
+               return $this->registry->tEngine->bufferedOutputObject(
+                       $this->registry->controllerObject
+               );
+       }
+
+       //      }}}
+       //      {{{     editRegionAction()
+    /**
+     * Description of editRegionAction
+     * 
+     * @return string
+     * @access public 
+     */
+       public function editRegionAction()
+       {
+               $GLOBALS['bottomScripts'][]
+                       = MEDIA_BASE_URL . 'Toolkit/Members/libjs/edit-region.js';
+
+               $form = new Toolkit_Members_Admin_EditRegion('edit_region');
+        $form->configureForm($this->registry->dbh, $this->registry->config);
+               $this->registry->controllerObject->content = $form->toHtml();
+
+               $this->registry->controllerObject->topScripts
+                       = Toolkit_Common::getScripts($GLOBALS['topScripts']);
+               $this->registry->controllerObject->bottomScripts
+                       = Toolkit_Common::getScripts($GLOBALS['bottomScripts']);
+               $this->registry->controllerObject->styles
+                       = Toolkit_Common::getStyleSheets($GLOBALS['styleSheets']);
+
+               $this->registry->tEngine->compile('admin.tpl');
+               return $this->registry->tEngine->bufferedOutputObject(
+                       $this->registry->controllerObject
+               );
+       }
+
+       //      }}}
+       //      {{{     listRegionsAction()
+    /**
+     * Description of listRegionsAction()
+     * 
+     * @return string
+     * @access public
+     */
+       public function listRegionsAction()
+       {
+               return $this->indexAction();
+       }
+
+       //      }}}
+}
+?>
diff --git a/Toolkit/Members/Admin/ReportsController.php b/Toolkit/Members/Admin/ReportsController.php
new file mode 100644 (file)
index 0000000..5b7a92b
--- /dev/null
@@ -0,0 +1,175 @@
+<?php
+/**
+ * PHP version 5
+ *
+ * @category  Toolkit
+ * @package   Members_Admin
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @version   CVS: $Id: AdvancedSearch.php,v 1.14 2010/08/15 19:35:15 jamie Exp $
+ * @link      http://demo.gaslightmedia.com
+ */
+
+/**
+ * Toolkit_Members_Admin_ReportsController
+ *
+ * @category  Toolkit
+ * @package   Members_Admin
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @link      http://demo.gaslightmedia.com
+ */
+class Toolkit_Members_Admin_ReportsController
+       extends Toolkit_BaseControllerAbstract
+    implements Toolkit_IController
+{
+    /**
+     * Runs admin template, list out all regions
+     * 
+     * @param string $html html
+     * 
+     * @return string
+     * @access protected 
+     */
+    protected function getPageLayout($html)
+    {
+        $GLOBALS['styleSheets'][]
+            = MEDIA_BASE_URL . 'Toolkit/Members/Billing/billing.css';
+        $this->registry->controllerObject->content = $html;
+               $this->registry->controllerObject->topScripts
+                       = Toolkit_Common::getScripts($GLOBALS['topScripts']);
+               $this->registry->controllerObject->bottomScripts
+                       = Toolkit_Common::getScripts($GLOBALS['bottomScripts']);
+               $this->registry->controllerObject->styles
+                       = Toolkit_Common::getStyleSheets($GLOBALS['styleSheets']);
+               $this->registry->tEngine->compile('admin.tpl');
+               return $this->registry->tEngine->bufferedOutputObject(
+                       $this->registry->controllerObject
+               );
+    }
+    
+    /**
+     * Description for indexAction
+     * 
+     * @return string 
+     * @access public
+     */
+       public function indexAction()
+       {
+               return $this->getPageLayout('');
+       }
+    
+    /**
+     * Description of accountByAgeAction
+     * 
+     * @return string
+     * @access public
+     */
+    public function accountByAgeAction()
+    {
+        $mc  = new Toolkit_Members_Billing_MemberLists(
+            Toolkit_Database::getInstance()
+        );
+        $out = $mc->getAccountsByAge();
+        return $this->getPageLayout($out);
+    }
+    
+    /**
+     * Description of openAccountsAction
+     * 
+     * @return string
+     * @access public 
+     */
+    public function openAccountsAction()
+    {
+        $mc  = new Toolkit_Members_Billing_MemberLists(
+            Toolkit_Database::getInstance()
+        );
+        $out = $mc->getOpenAccounts();
+        return $this->getPageLayout($out);
+    }
+    
+    /**
+     * Description of closedAccounts()
+     * 
+     * @return string
+     * @access public
+     */
+    public function closedAccounts()
+    {
+        $mc  = new Toolkit_Members_Billing_MemberLists(
+            Toolkit_Database::getInstance()
+        );
+        $out = $mc->getClosedAccounts();
+        return $this->getPageLayout($out);
+    }
+    
+    /**
+     * Description for allAccountsAction()
+     * 
+     * @return string
+     * @access public 
+     */
+    public function allAccountsAction()
+    {
+        $mc  = new Toolkit_Members_Billing_MemberLists(
+            Toolkit_Database::getInstance()
+        );
+        $out = $mc->getAllAccounts();
+        return $this->getPageLayout($out);
+    }
+    
+    /**
+     *Description for noAccountsAction()
+     * 
+     * @return string
+     * @access public  
+     */
+    public function noAccountsAction()
+    {
+        $mc  = new Toolkit_Members_Billing_MemberLists(
+            Toolkit_Database::getInstance()
+        );
+        $out = $mc->getAccountsNeedingData();
+        return $this->getPageLayout($out);
+    }
+    
+    /**
+     * Description for searchReportsAction()
+     * 
+     * @return string
+     * @access public
+     */
+    public function searchReportsAction()
+    {
+        $dbh = Toolkit_Database::getInstance();
+        $form = new Toolkit_Members_Billing_ReportSearch(
+            'search-reports',
+            'get',
+            'members.php',
+            null,
+            null,
+            null
+        );
+        $form->configureForm();
+        $reports = new Toolkit_Members_Billing_Report(
+            $dbh,
+            null,
+            null,
+            null
+        );
+        $params = $reports->setQuery();
+        if ($_REQUEST['report_type'] == 'file') {
+            $reports->exportFile($dbh, $params);
+            exit;
+            return '<pre>'.print_r($_REQUEST, true).'</pre>
+                <pre>'.print_r($params, true).'</pre>';
+        }
+        $out = $form->toHtml()
+           . $reports->toHTML()
+           . $reports->getGrandTotals($dbh, $params);
+        return $this->getPageLayout($out);
+    }
+}
diff --git a/Toolkit/Members/Admin/Search.php b/Toolkit/Members/Admin/Search.php
new file mode 100644 (file)
index 0000000..279e45a
--- /dev/null
@@ -0,0 +1,468 @@
+<?php
+//     vim:set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker syntax=php:
+
+/**
+ * Admin search functionality for memberdb
+ *
+ * PHP version 5
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @version   CVS: $Id: Search.php,v 1.18 2010/05/25 14:01:21 jamie Exp $
+ * @link      http://demo.gaslightmedia.com
+ */
+
+/**
+ * Form to search the members database
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @link      http://demo.gaslightmedia.com
+ */
+class Toolkit_Members_Admin_Search
+    extends Toolkit_FormBuilder implements Toolkit_Form
+{
+       //      {{{     properties
+
+       /**
+        * The default rules to register for validating
+        *
+        * We have to register these rules, or any others we want, before
+        * we are able to use them in our forms.
+        *
+        * @var         string
+        * @access      protected
+        */
+       protected $registeredRules = array('zip');
+
+       //      }}}
+
+       //      {{{     __construct()
+
+       /**
+        * Constructor
+        *
+     * @param PDO    $pdo         PHP Data Object
+     * @param string $formName    Form's name.
+     * @param string $method      (optional)Form's method defaults to 'POST'
+     * @param string $action      (optional)Form's action
+     * @param string $target      (optional)Form's target defaults to '_self'
+     * @param mixed  $attributes  (optional)Extra attributes for <form> tag
+     * @param bool   $trackSubmit (optional)Whether to track if the form was
+        *                                                        submitted by adding a special hidden field
+        *
+        * @see    HTML_QuickForm
+        * @access public
+        */
+       public function __construct(
+        PDO $pdo,
+        $formName,
+        $method = 'post',
+        $action = '',
+        $target = '',
+        $attributes = null,
+        $trackSubmit = false
+    ) {
+               parent::__construct(
+            $formName,
+            $method,
+            $action,
+            $target,
+            $attributes,
+            $trackSubmit
+        );
+
+        $this->dbh = $pdo;
+               $this->template = BASE . 'Toolkit/Members/templates/currentTables/';
+       }
+
+       //      }}}
+
+       //      {{{ configureConstants()
+
+    /**
+     * Form constant definitions
+     *
+     * @return void
+     * @access public
+     */
+       public function configureConstants()
+       {
+               $c = array(
+                       'page' => 'members',
+                       'module' => 'listMembers',
+               );
+
+               $this->setupConstants($c);
+       }
+
+       //      }}}
+       //      {{{ configureElements()
+
+    /**
+     * Form element definitions
+     *
+     * @param Config_Container $c Configuration object
+     *
+     * @return void
+     * @access public
+     */
+       public function configureElements(Config_Container $c)
+       {
+        $e = array();
+
+        //  get reference to [listing type] section of config file
+        $config =& $c->getItem('section', 'listing type');
+        //  get coupon
+        $singularDirective =& $config->getItem('directive', 'singular');
+        $singularType = $singularDirective->getContent();
+        $pluralDirective =& $config->getItem('directive', 'plural');
+        $pluralType = $pluralDirective->getContent();
+
+        //  get reference to [conf] section of config file
+        $config =& $c->getItem('section', 'conf');
+        //  get controlled cities
+        $ctrlCtyDirective =& $config->getItem('directive', 'controlledCities');
+
+               $states = $this->_getStates();
+               //      All Grouped Elements are created here.
+
+               //      All Elements are created here.  This includes group element definitions.
+               $e[] = array(
+            'type' => 'hidden',
+            'req' => false,
+            'name' => 'page'
+        );
+               $e[] = array(
+            'type' => 'hidden',
+            'req' => false,
+            'name' => 'module'
+        );
+               $e[] = array(
+            'type' => 'header',
+            'req' => false,
+            'name' => 'categoryInfoHdr',
+            'display' => 'Search by Category'
+        );
+               $e[] = array(
+            'type' => 'selectglm',
+            'req' => false,
+            'name' => 'category',
+            'display' => 'Category',
+            'opts' => array(
+               '' => array(
+                       'level' => -1,
+                                       'name' => '-- All --'
+                               )
+                       ) + $this->_getCategories()
+        );
+               $e[] = array(
+            'type' => 'header',
+            'req' => false,
+            'name' => 'recordInfoHdr',
+            'display' => "Find $pluralType",
+        );
+               $e[] = array(
+            'type' => 'text',
+            'req' => false,
+            'name' => 'name',
+            'display' => "$singularType Name"
+        );
+               $e[] = array(
+            'type' => 'text',
+            'req' => false,
+            'name' => 'email',
+            'display' => "$singularType Email"
+        );
+        if ($ctrlCtyDirective->getContent()) {
+            $e[] = array(
+                'type'    => 'select',
+                'req'     => false,
+                'name'    => 'city_id',
+                'display' => 'City',
+                'opts'    => $this->getCities(),
+            );
+        } else {
+            $e[] = array(
+                'type'    => 'text',
+                'req'     => false,
+                'name'    => 'city',
+                'display' => 'City',
+            );
+        }
+               $e[] = array(
+            'type' => 'select',
+            'req' => false,
+            'name' => 'state',
+            'display' => 'State',
+            'opts' => $states
+        );
+               $e[] = array(
+            'type' => 'text',
+            'req' => false,
+            'name' => 'zip',
+            'display' => 'Zip'
+        );
+               $e[] = array(
+            'type' => 'submit',
+            'req' => false,
+            'name' => 'submit',
+            'display' => 'Search'
+        );
+
+               $this->setupElements($e);
+       }
+
+       //      }}}
+       //      {{{ configureFilters()
+
+    /**
+     * Form filter definitions
+     *
+     * @return void
+     * @access public
+     */
+       public function configureFilters()
+       {
+        $f = array();
+               $f[] = array(
+            'element' => '__ALL__',
+            'filter' => 'trim'
+        );
+
+               $this->setupFilters($f);
+       }
+
+       //      }}}
+    //  {{{ configureForm()
+
+    /**
+     * Helper function to configure an entire form
+     *
+     * @param Config_Container $c Configuration object
+     *
+     * @return void
+     * @access public
+     */
+    public function configureForm(Config_Container $c)
+    {
+        $this->configureElements($c);
+        $this->configureRules();
+        $this->configureFilters();
+        $this->configureConstants();
+    }
+
+    //  }}}
+       //      {{{ configureRules()
+
+    /**
+     * Form rule definitions
+     *
+     * @return void
+     * @access public
+     */
+       public function configureRules()
+       {
+        $r = array();
+               $r[] = array(
+            'element' => 'member_contact_email',
+            'message' => 'ERROR: Invalid Email Address!',
+            'type' => 'email',
+            'format' => null,
+            'validation' => $this->validationType,
+            'reset' => false,
+            'force' => false
+        );
+               $r[] = array(
+            'element' => 'category',
+            'message' => 'ERROR: Invalid Category!',
+            'type' => 'numeric',
+            'format' => null,
+            'validation' => $this->validationType,
+            'reset' => false,
+            'force' => false
+        );
+               $r[] = array(
+            'element' => 'state_id',
+            'message' => 'ERROR: Invalid State!',
+            'type' => 'numeric',
+            'format' => null,
+            'validation' => $this->validationType,
+            'reset' => false,
+            'force' => false
+        );
+               $r[] = array(
+            'element' => 'zip',
+            'message' => 'ERROR: Invalid Zip!',
+            'type' => 'zip',
+            'format' => null,
+            'validation' => $this->validationType,
+            'reset' => false,
+            'force' => false
+        );
+
+               $this->setupRules($r);
+       }
+
+       //      }}}
+
+       //      {{{     _getCategories()
+
+    /**
+     * get all the categories available in alpha order
+     *
+     * @return array alpha order of categories available in the DB
+     * @access private
+     */
+       private function _getCategories()
+       {
+               $categories = Toolkit_Common::getHierarchicalTreeStructure(
+                       $this->dbh,
+            'category',
+            'category_id',
+            'parent_id',
+                       'name'
+        );
+               //  Get only the active categories from
+               //  the nav structure for our select list.
+               $sql = "
+                       SELECT category_id, name
+                         FROM category
+                        WHERE category_id = :id";
+
+               $stmt = $this->dbh->prepare($sql);
+
+               foreach ($categories as $k => $v) {
+                       unset($row, $category);
+                       $stmt->bindParam(':id', $k, PDO::PARAM_INT);
+                       $stmt->execute();
+
+                       //  If we actually retrieved a row, add it to the select list
+                       //  after we clean it up.
+                       if ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                               $pages[$k] = array(
+                                       'level' => $v - 1,
+                                       'name' => $row['name']
+                               );
+                       }
+               }
+
+               return $pages;
+       }
+
+       //      }}}
+       //      {{{ getCities()
+
+       /**
+        * Configure the cities for member stored in the database into an array
+        *
+     * @return array the cities
+        * @access protected
+        */
+       protected function getCities()
+       {
+               try {
+                       $sql = "
+                               SELECT *
+                                 FROM city
+                                ORDER BY city_name";
+                       foreach ($this->dbh->query($sql) as $row) {
+                               $cities[$row['city_id']] = $row['city_name'];
+                       }
+                       if (!empty($cities)) {
+                               $cities = array('' => '-- Select --') + $cities;
+                       } else {
+                               $cities = array('' => '-- No Cities Created Yet -- ');
+                       }
+                       return $cities;
+               } catch (PDOException $e) {
+                       return Toolkit_Common::handleError($e);
+               }
+       }
+
+       //      }}}
+       //      {{{     _getStates()
+
+    /**
+     * get all the statis available in alpha order
+     *
+     * @return array alpha order of states available in the DB
+     * @access private
+     */
+       private function _getStates()
+       {
+               try {
+                       $sql = "
+                SELECT *
+                  FROM state
+                 ORDER BY state_name";
+
+                       $stmt = $this->dbh->prepare($sql);
+                       $stmt->execute();
+                       $stmt->bindColumn('state_id', $cid);
+                       $stmt->bindColumn('state_name', $name);
+
+                       $states = array('' => '-- Select --');
+                       while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                               $states[$cid] = $name;
+                       }
+
+                       return $states;
+               } catch (PDOException $e) {
+                       return Toolkit_Common::handleError($e);
+               }
+       }
+
+       //      }}}
+
+       //      {{{     setupRenderers()
+    //  @codeCoverageIgnoreStart
+
+    /**
+     * Custom rendering templates for special fields on the form
+     *
+     * @return void
+     * @access protected
+     */
+       protected function setupRenderers()
+       {
+               parent::setupRenderers();
+               $renderer =& $this->defaultRenderer();
+               $tpl = '<tr align="center"><td colspan="2">{element}</td></tr>';
+               $renderer->setElementTemplate($tpl, 'submit');
+       }
+
+    //  @codeCoverageIgnoreEnd
+       //      }}}
+
+       //      {{{     toHtml()
+
+       /**
+        * Call the rendering function to get the form in a string
+        *
+        * @access protected
+        * @return string $output The Form to be rendered or success msg.
+        */
+       public function toHtml()
+       {
+               $this->setupRenderers();
+               if ($this->validate()) {
+                       $output = parent::toHTML();
+               } elseif ($this->isSubmitted()) {
+                       $output = $this->errorMsg;
+                       $output .= parent::toHTML();
+               } else {
+                       $output = parent::toHTML();
+               }
+               return $output;
+       }
+
+       //      }}}
+}
+?>
diff --git a/Toolkit/Members/Admin/SettingsController.php b/Toolkit/Members/Admin/SettingsController.php
new file mode 100644 (file)
index 0000000..2b2d70e
--- /dev/null
@@ -0,0 +1,77 @@
+<?php
+/**
+ * SettingsController.php
+ * 
+ * PHP version 5.2
+ * 
+ * @category  Toolkit
+ * @package   Members_Admin
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @version   SVN: $id$
+ * @link      <>
+ */
+
+/**
+ * Toolkit_Members_Admin_SettingsController
+ * 
+ * Description of Toolkit_Members_Admin_SettingsController
+ * 
+ * @category  Toolkit
+ * @package   Members_Admin
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @release   Release: $id$
+ * @link      <>
+ */
+class Toolkit_Members_Admin_SettingsController
+       extends Toolkit_BaseControllerAbstract implements Toolkit_IController
+{
+       //      {{{     indexAction()
+    /**
+     * Main action for controller. Runs admin template, list out all regions
+     * 
+     * @return string
+     * @access public
+     */
+       public function indexAction()
+       {
+               $settingsControllerObject = new stdClass();
+               $dom = new DOMDocument();
+               $dom->loadHTML($this->registry->controllerObject->nav);
+
+               $uls = $dom->getElementsByTagName('ul');
+               $ul = $uls->item(1);
+               $ul->setAttribute('class', 'settings');
+
+               $anchors = $ul->getElementsByTagName('a');
+               foreach ($anchors as $anchor) {
+                       $anchor->removeAttribute('class');
+               }
+               $settingsControllerObject->nav = substr(
+                       $dom->saveXML($dom->getElementsByTagName('ul')->item(1)),
+                       0
+               );
+
+               $this->registry->tEngine->compile('settings.html');
+               $this->registry->controllerObject->content =
+                       $this->registry->tEngine->bufferedOutputObject($settingsControllerObject);
+
+               $this->registry->controllerObject->topScripts
+                       = Toolkit_Common::getScripts($GLOBALS['topScripts']);
+               $this->registry->controllerObject->bottomScripts
+                       = Toolkit_Common::getScripts($GLOBALS['bottomScripts']);
+               $this->registry->controllerObject->styles
+                       = Toolkit_Common::getStyleSheets($GLOBALS['styleSheets']);
+
+               $this->registry->tEngine->compile('admin.tpl');
+               return $this->registry->tEngine->bufferedOutputObject(
+                       $this->registry->controllerObject
+               );
+       }
+
+       //      }}}
+}
+?>
diff --git a/Toolkit/Members/Auth.php b/Toolkit/Members/Auth.php
new file mode 100644 (file)
index 0000000..833018f
--- /dev/null
@@ -0,0 +1,736 @@
+<?php
+//     vim:set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker syntax=php:
+
+/**
+ * Member Authentication
+ *
+ * PHP version 5
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @version   CVS: $Id: Auth.php,v 1.22 2010/08/10 18:08:44 jamie Exp $
+ * @link      http://demo.gaslightmedia.com
+ * @see       Toolkit_Members_Auth-LoginForm, Toolkit_Members_Auth-PasswordForm
+ */
+
+require_once 'Auth.php';
+
+/**
+ * Methods for the memberdb authentication system
+ *
+ * Handles Cookie and session generation, id challenges and security for
+ * the memberdb application
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @link      http://demo.gaslightmedia.com
+ * @see       Toolkit_Members_Auth-LoginForm, Toolkit_Members_Auth-PasswordForm
+ */
+class Toolkit_Members_Auth extends Auth
+{
+       //      {{{     properties
+
+    /**
+     * Maximum idle time
+        *
+        * If more seconds pass before a new page request, then the user
+        * will have to re-authenticate back into the application.
+        * 1800 = 30 min
+        * 3600 = 1 hr
+        *
+     * @var    integer
+     * @access protected
+     */
+       protected $idleTime = 1800;
+
+       //      }}}
+       //      {{{     __construct()
+
+    /**
+     * Constructor
+     *
+     * Sets up the storage driver
+     *
+     * @param Config_Container              $c             Configuration object
+     * @param Toolkit_Members_AuthContainer $storageDriver storage driver
+     * @param string                        $loginFunction (optional)Name of the function
+     *                                                     that creates the login form
+     * @param boolean                       $showLogin     (optional)Should the login form
+     *                                                     be displayed if neccessary?
+        *
+     * @return void
+     * @access public
+     */
+       public function __construct(
+        Config_Container $c,
+        Toolkit_Members_AuthContainer $storageDriver,
+        $loginFunction = '',
+        $showLogin = true
+    ) {
+        $this->config = $c;
+
+               parent::Auth($storageDriver, '', $loginFunction, $showLogin);
+       }
+
+       //      }}}
+
+       //      {{{     loginForm()
+
+    /**
+     * Function to set up the regular login form
+     *
+     * @param unknown $uname  Last attempted username
+     * @param unknown $status The authorization status
+     * @param unknown &$auth  The authentication object
+        *
+     * @return void
+     * @access public
+     */
+       protected function loginForm($uname = null, $status = null, &$auth = null)
+       {
+               $login = new LoginForm(
+                       'member_login',
+            'post',
+            MEDIA_BASE_URL . 'members-only-area/'
+               );
+
+        $login->setDbh(Toolkit_Database::getInstance());
+        $login->configureForm($auth->config);
+        echo $login->toHtml();
+       }
+
+       //      }}}
+
+       //      {{{     passwordForm()
+
+    /**
+     * Function to set up the forgot password form
+     *
+     * @return void
+     * @access public
+     */
+       protected function passwordForm()
+       {
+               $pword = new PasswordForm('member_password');
+        $pword->setDbh(Toolkit_Database::getInstance());
+        $pword->configureForm();
+               echo $pword->toHtml();
+       }
+
+       //      }}}
+
+       //      {{{     setIdle()
+
+    /**
+     * Set the maximum idle time
+     *
+     * @param integer $time time in seconds
+     * @param boolean $add  (optional)add time to current maximum idle time or not
+        *
+     * @return void
+     * @access public
+     */
+       public function setIdle($time = null, $add = false)
+       {
+               $time = is_null($time) ? $this->idleTime : $time;
+               parent::setIdle($time, $add);
+       }
+
+       //      }}}
+}
+
+/**
+ * Handles rendering and validating the member login form
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @link      http://demo.gaslightmedia.com
+ * @see       Toolkit_FormBuilder
+ */
+class LoginForm extends Toolkit_FormBuilder
+{
+       //      {{{     properties
+
+    /**
+     * Custom rules to check for when validating the form
+        *
+     * @var    array
+     * @access protected
+     */
+       protected $registeredRules = array();
+
+    /**
+     * Where to perform validation
+        *
+     * @var    string
+     * @access protected
+     */
+       protected $validationType = 'client';
+
+       //      }}}
+
+       //      {{{     configureElements()
+
+    /**
+     * Sets up the elements to be configured for use with the form
+     *
+     * @param Config_Container $c Configuration object
+     *
+     * @return void
+     * @access protected
+     */
+       protected function configureElements(Config_Container $c)
+       {
+        $e = array();
+
+        //  get reference to [listing type] section of config file
+        $config =& $c->getItem('section', 'listing type');
+        $singularDirective =& $config->getItem('directive', 'singular');
+        $singularType = $singularDirective->getContent();
+
+               $e[] = array(
+            'type' => 'header',
+            'req' => false,
+            'name' => 'formHdr',
+            'display' => "$singularType Login"
+        );
+               $e[] = array(
+            'type' => 'text',
+            'req' => true,
+            'name' => 'username',
+            'display' => 'Username'
+        );
+               $e[] = array(
+            'type' => 'password',
+            'req' => true,
+            'name' => 'password',
+            'display' => 'Password'
+        );
+               $e[] = array(
+            'type' => 'submit',
+            'req' => false,
+            'name' => 'doLogin',
+            'display' => 'Login now',
+            'opts' => array('id' => 'doLogin')
+        );
+               $e[] = array(
+            'type' => 'link',
+            'req' => false,
+            'name' => 'forgot',
+            'display' => 'Forgot your password? Click',
+            'opts' => MEDIA_BASE_URL . 'index.php?catid='.MEMBERS_CATEGORY.'&forgot=',
+            'att' => 'here'
+        );
+
+               $this->setupElements($e);
+       }
+
+       //      }}}
+       //      {{{     configureFilters()
+
+    /**
+     * Sets up the filters to be used with the form when submitted
+     *
+     * @return void
+     * @access protected
+     */
+       protected function configureFilters()
+       {
+        $f = array();
+
+               $f[] = array(
+            'element' => '__ALL__',
+            'filter' => 'trim'
+        );
+               $this->setupFilters($f);
+       }
+
+       //      }}}
+    //  {{{ configureForm()
+
+    /**
+     * Helper method to setup form
+     *
+     * @param Config_Container $c Configuration object
+     *
+     * @return void
+     * @access public
+     */
+    public function configureForm(Config_Container $c)
+    {
+        $this->configureElements($c);
+        $this->configureFilters();
+        $this->configureRules();
+    }
+
+    //  }}}
+       //      {{{     configureRules()
+
+    /**
+     * Sets up required rules and extra defined rules for the form
+     *
+     * @return void
+     * @access protected
+     */
+       protected function configureRules()
+       {
+               $this->setupRules($r);
+       }
+
+       //      }}}
+
+    //  {{{ setDbh()
+
+    /**
+     * set the pdo to use for db calls
+     *
+     * @param PDO $pdo PHP Data Object to use
+     *
+     * @return void
+     * @access public
+     */
+    public function setDbh(PDO $pdo)
+    {
+        $this->dbh = $pdo;
+    }
+
+    //  }}}
+       //      {{{     setupRenderers()
+
+    /**
+     * Inject custom renderers into the forms elements for custom display
+     *
+     * @return void
+     * @access protected
+     */
+       protected function setupRenderers()
+       {
+               parent::setupRenderers();
+
+               $renderer =& $this->defaultRenderer();
+               $required = '<!-- BEGIN required --><span class="req">*</span><!-- END required -->';
+               $error    = '<!-- BEGIN error --><div class="form-warning-inside">{error}</div><!-- END error -->';
+
+               $renderer->setElementTemplate('<tr><td colspan="2" class="member-login-forgot">{label} {element}</td></tr>', 'forgot');
+               $renderer->setElementTemplate('<tr><td colspan="2" align="center">{element}</td></tr>', 'doLogin');
+       }
+
+       //      }}}
+
+       //      {{{     toHtml()
+
+    /**
+     * Returns an HTML version of the form
+     *
+     * @return string HTML version of the form
+     * @access public
+     */
+       public function toHtml()
+       {
+               $this->setupRenderers();
+               if ($this->validate()) {
+                       header('Location: ' . MEDIA_BASE_URL . 'memberdb/index.php');
+               } elseif ($this->isSubmitted()) {
+                       $output  = $this->errorMsg;
+                       $output .= parent::toHtml();
+               } else {
+                       if ($_GET['status']) {
+                               switch ($_GET['status']) {
+                               case -1 :
+                                       $error = 'Your session has exceeded the maximum idle time';
+                                       break;
+
+                               case -2 :
+                                       $error = 'Your session has expired.';
+                                       break;
+
+                               case -3 :
+                                       $error = 'Invalid username or password.';
+                                       break;
+
+                               case -4 :
+                                       //      This is primarily used for Development.
+                                       //      Users should never be presented with this error.
+                                       $error = 'Invalid Container';
+                                       break;
+
+                               case -5 :
+                                       //      This is only thrown if the advanced security system
+                                       //      has detected a breach into the system.
+                                       $error = 'The system has encountered an error. Reference code: -5';
+                                       break;
+                               }
+                               $output = "<div id=\"form-warning-top\">$error</div>";
+                       }
+                       $output .= parent::toHtml();
+               }
+
+               return $output;
+       }
+
+       //      }}}
+}
+
+/**
+ * Handles rendering and validating the member password form
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @link      http://demo.gaslightmedia.com
+ * @see       Toolkit_FormBuilder
+ */
+class PasswordForm extends Toolkit_FormBuilder
+{
+       //      {{{     properties
+
+    /**
+     * Table to query when gathering information
+        *
+     * @var    string
+     * @access public
+     */
+       public $tableName = 'member';
+
+    /**
+     * Custom defined rules to validate against when the form is submitted
+        *
+     * @var    array
+     * @access protected
+     */
+       protected $registeredRules = array();
+
+    /**
+     * Where to perform validation
+        *
+     * @var    string
+     * @access protected
+     */
+       protected $validationType = 'client';
+
+       //      }}}
+       //      {{{     __construct()
+
+    /**
+     * Constructor
+     *
+     * @param string  $formName    Form's name
+     * @param string  $method      (optional)Form's method defaults to 'POST'
+     * @param string  $action      (optional)Form's action
+     * @param string  $target      (optional)Form's target
+     * @param mixed   $attributes  (optional)Extra attributes for the <form> tag
+     * @param boolean $trackSubmit (optional)Whether to track if the form
+        *                                                         was submitted by adding a special hidden field
+        *
+     * @return void
+     * @access public
+     */
+       public function __construct(
+        $formName,
+        $method = 'post',
+        $action = '',
+        $target = '',
+        $attributes = null,
+        $trackSubmit = false
+    ) {
+               parent::__construct(
+            $formName,
+            $method,
+            $action,
+            $target,
+            $attributes,
+            $trackSubmit
+        );
+
+               $this->template = dirname(__FILE__) . '/templates/currentTables/';
+       }
+
+       //      }}}
+
+       //      {{{     checkAddressExists()
+
+    /**
+     * Checks to see if the email address exists before allowing an email to go out
+     *
+     * @param string $value submitted email address
+        *
+     * @return boolean If the email address exists or not
+     * @access public
+     */
+       public function checkAddressExists($value)
+       {
+               try {
+                       $sql = "
+                SELECT count(*) AS total
+                  FROM {$this->tableName}
+                 WHERE member_contact_email = :mce";
+
+                       $stmt = $this->dbh->prepare($sql);
+                       $stmt->bindParam(':mce', $value, PDO::PARAM_STR);
+                       $stmt->execute();
+                       $stmt->bindColumn('total', $exists);
+                       $stmt->fetch();
+
+                       return (bool) $exists;
+               } catch (PDOException $e) {
+                       Toolkit_Common::handleError($e);
+               }
+       }
+
+       //      }}}
+       //      {{{     configureElements()
+
+    /**
+     * Sets up the elements to be configured for use with the form
+     *
+     * @return void
+     * @access protected
+     */
+       protected function configureElements()
+       {
+        $e = array();
+
+               $e[] = array(
+            'type' => 'header',
+            'req' => false,
+            'name' => 'formHdr',
+            'display' => 'Email Reminder'
+        );
+               $e[] = array(
+            'type' => 'text',
+            'req' => true,
+            'name' => 'email',
+            'display' => 'Your Member Contact Email Address'
+        );
+               $e[] = array(
+            'type' => 'submit',
+            'req' => false,
+            'name' => 'submit',
+            'display' => 'Send'
+        );
+
+               $this->setupElements($e);
+       }
+
+       //      }}}
+       //      {{{     configureFilters()
+
+    /**
+     * Sets up the filters to be used with the form when submitted
+     *
+     * @return void
+     * @access protected
+     */
+       protected function configureFilters()
+       {
+        $f = array();
+
+               $f[] = array(
+            'element' => '__ALL__',
+            'filter' => 'trim'
+        );
+               $this->setupFilters($f);
+       }
+
+       //      }}}
+    //  {{{ configureForm()
+
+    /**
+     * Helper method to setup form
+     *
+     * @return void
+     * @access public
+     */
+    public function configureForm()
+    {
+        $this->configureElements();
+        $this->configureFilters();
+        $this->configureRules();
+    }
+
+    //  }}}
+       //      {{{     configureRules()
+
+    /**
+     * Sets up required rules and extra defined rules for the form
+     *
+     * @return void
+     * @access protected
+     */
+       protected function configureRules()
+       {
+        $r = array();
+
+               $r[] = array(
+            'element' => 'email',
+            'message' => 'ERROR: Invalid Email Format!',
+            'type' => 'email',
+            'format' => null,
+            'validation' => $this->validationType,
+            'reset' => true,
+            'force' => false
+        );
+               $r[] = array(
+            'element' => 'email',
+            'message' => 'ERROR: Cannot locate email address!',
+            'type' => 'callback',
+            'format' => array(&$this,
+            'checkAddressExists'),
+            'validation' => $this->validationType,
+            'reset' => true,
+            'force' => false
+        );
+
+               $this->setupRules($r);
+       }
+
+       //      }}}
+
+    //  {{{ setDbh()
+
+    /**
+     * set the pdo to use for db calls
+     *
+     * @param PDO $pdo PHP Data Object to use
+     *
+     * @return void
+     * @access public
+     */
+    public function setDbh(PDO $pdo)
+    {
+        $this->dbh = $pdo;
+    }
+
+    //  }}}
+       //      {{{     setupRenderers()
+
+    /**
+     * Inject custom renderers into the forms elements for custom display
+     *
+     * @return void
+     * @access protected
+     */
+       protected function setupRenderers()
+       {
+               parent::setupRenderers();
+
+               $renderer =& $this->defaultRenderer();
+               $required = '<!-- BEGIN required --><span class="req">*</span><!-- END required -->';
+               $error    = '<!-- BEGIN error --><div class="form-warning-inside">{error}</div><!-- END error -->';
+
+               $renderer->setElementTemplate('<tr><td colspan="2" align="center">{element}</td></tr>', 'submit');
+       }
+
+       //      }}}
+
+       //      {{{     processData()
+
+    /**
+     * Processes the data submitted by the form
+     *
+        * Gets the login credentials for the matching email address and mails
+        * them to that email address
+     *
+     * @param array $values submitted form values
+        *
+     * @return boolean Result of mail
+     * @access protected
+     */
+       protected function processData($values)
+       {
+               try {
+                       $sql = "
+                SELECT member_login, member_passwd
+                  FROM {$this->tableName}
+                 WHERE member_contact_email = :mce";
+
+                       $stmt = $this->dbh->prepare($sql);
+                       $stmt->bindParam(':mce', $values['email'], PDO::PARAM_STR);
+                       $stmt->execute();
+                       $row = $stmt->fetch();
+               } catch (PDOException $e) {
+                       Toolkit_Common::handlError($e);
+               }
+
+               $htmlMsg
+                       = "Here is your " . SITENAME . " password:<br><br>" .
+                         "Login: {$row['member_login']}<br>" .
+                         "Email: {$values['email']}<br>" .
+                         "Password: {$row['member_passwd']}<br><br>";
+
+               $msg
+                       = "Here is your " . SITENAME . " password:\n\n" .
+                         "Login: {$row['member_login']}\n" .
+                         "Email: {$values['email']}\n" .
+                         "Password: {$row['member_passwd']}";
+
+               $mimeMail = new Mail_mime("\n");
+               $mimeMail->setHTMLBody($htmlMsg);
+               $mimeMail->setTXTBody($msg);
+
+               $body = $mimeMail->get();
+               $hdrs = $mimeMail->headers(
+                       array(
+                               'From' => DO_NOT_REPLY_EMAIL,
+                               'Subject' => 'Your ' . htmlspecialchars_decode(SITENAME) . ' Password',
+                               'Reply-To' => SITENAME . '<' . MEMBER_FORGOT_PASSWORD_EMAIL_REPLY_TO . '>'
+                       )
+               );
+
+               $mail =& Mail::factory('mail');
+
+               $res = $mail->send($values['email'], $hdrs, $body);
+
+               return PEAR::isError($res) ?
+                               Toolkit_Common::handleError($res) :
+                               $res;
+       }
+
+       //      }}}
+
+       //      {{{     toHtml()
+
+    /**
+     * Returns an HTML version of the form
+     *
+     * @return string HTML version of the form
+     * @access public
+     */
+       public function toHtml()
+       {
+               $this->setupRenderers();
+               if ($this->validate()) {
+                       if ($this->process(array(&$this, 'processData'))) {
+                               $url    = MEDIA_BASE_URL . 'index.php?catid=' . MEMBERS_CATEGORY;
+                               $e      =& $this->getElement('email');
+                               $email  = $e->getValue();
+                               $output
+                    = "<p>Your Login Information has been sent to $email</p>" .
+                                         "<p>Continue to <a href=\"$url\">Member Login</a></p>";
+                       } else {
+                               $output = '<p>Email address not found.</p>';
+                       }
+               } elseif ($this->isSubmitted()) {
+                       $output  = $this->errorMsg;
+                       $output .= parent::toHtml();
+               } else {
+                       $output .= parent::toHtml();
+               }
+
+               return $output;
+       }
+
+       //      }}}
+}
+?>
diff --git a/Toolkit/Members/AuthContainer.php b/Toolkit/Members/AuthContainer.php
new file mode 100644 (file)
index 0000000..994333a
--- /dev/null
@@ -0,0 +1,203 @@
+<?php
+
+/**
+ * Authentication container for members only area
+ *
+ * Custom container which allows us to utilize our PDO Singleton which
+ * takes advantage of schema based partitioning of our tables
+ *
+ * PHP version 5
+ *
+ * The license text...
+ *
+ * @category  Members
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2010 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com/ Gaslightmedia
+ * @version   CVS: $Id: AuthContainer.php,v 1.3 2010/06/22 11:45:34 jamie Exp $
+ * @link      <>
+ * @see       References to other sections (if any)...
+ */
+
+/**
+ * Authentication container for members only area
+ *
+ * Custom container which allows us to utilize our PDO Singleton which
+ * takes advantage of schema based partitioning of our tables
+ *
+ * @category  Members
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2010 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com/ Gaslightmedia
+ * @version   Release: @package_version@
+ * @link      <>
+ * @see       References to other sections (if any)...
+ */
+class Toolkit_Members_AuthContainer extends Auth_Container
+{
+    //  {{{ properties
+
+    /**
+     * Database handler
+     * @var    PDO
+     * @access private
+     */
+    private $_dbh;
+
+    /**
+     * Addition options for the storage container
+     * @var array
+     * @access private
+     */
+    private $_options = array();
+
+    //  }}}
+    //  {{{ __construct()
+
+    /**
+     * Constructor
+     *
+     * @param PDO   $dbh     Database handler
+     * @param array $options Addition options for the storage container
+     *
+     * @return void
+     * @access public
+     */
+    public function __construct(PDO $dbh, array $options = null)
+    {
+        $this->_dbh = $dbh;
+        $this->_setDefaults();
+        if (is_array($options)) {
+            $this->_parseOptions($options);
+        }
+    }
+
+    //  }}}
+    //  {{{ _setDefaults()
+
+    /**
+     * Set some default options
+     *
+     * @access private
+     * @return void
+     */
+    private function _setDefaults()
+    {
+        $this->_options['table']       = 'member';
+        $this->_options['usernamecol'] = 'member_login';
+        $this->_options['passwordcol'] = 'member_passwd';
+        $this->_options['db_fields']   = '';
+        $this->_options['cryptType']   = 'md5';
+        $this->_options['db_where']    = '';
+    }
+
+    //  }}}
+    //  {{{ _parseOptions()
+
+    /**
+     * Parse options passed to the container class
+     *
+     * @param array $array options for class
+     *
+     * @access private
+     * @return void
+     */
+    private function _parseOptions(array $array)
+    {
+        foreach ($array as $key => $value) {
+            if (isset($this->_options[$key])) {
+                $this->_options[$key] = $value;
+            }
+        }
+    }
+
+    //  }}}
+    //  {{{ fetchData()
+
+    /**
+     * Get the user information from the database
+     *
+     * @param string $username username to authenticate
+     * @param string $password password to authenticate against username
+     *
+     * @return boolean If the user was authenticated or not
+     * @access public
+     * @throws Toolkit_Members_Exception upon error querying DB for user
+     */
+    public function fetchData($username, $password)
+    {
+        if (   is_string($this->_options['db_fields'])
+            && strstr($this->_options['db_fields'], '*')
+        ) {
+            $sqlFrom = '*';
+        } else {
+            $sqlFrom  = $this->_options['usernamecol'];
+            $sqlFrom .= ', ' . $this->_options['passwordcol'];
+
+            if (strlen($fields = $this->_getDBFields()) > 0) {
+                $sqlFrom .= ", $fields";
+            }
+
+        }
+
+        $pword = ($this->_options['cryptType'] == 'md5') ? 'MD5(:pword)' : ':pword';
+
+        $sql = "
+            SELECT $sqlFrom
+              FROM {$this->_options['table']}
+             WHERE {$this->_options['usernamecol']} = :uname
+               AND {$this->_options['passwordcol']} = $pword";
+
+
+        //  check if there is an optional parameter db_where
+        if ($this->_options['db_where'] != '') {
+            //  There is one, so add it to the query
+            $sql .= " AND {$this->_options['db_where']}";
+        }
+
+        try {
+            $stmt = $this->_dbh->prepare($sql);
+            $stmt->bindParam(':uname', $username, PDO::PARAM_STR);
+            $stmt->bindParam(':pword', $password, PDO::PARAM_STR);
+            $stmt->execute();
+            $row = $stmt->fetch(PDO::FETCH_ASSOC);
+
+            if ($row !== false) {
+                foreach ($row as $key => $value) {
+                    $this->_auth_obj->setAuthData($key, $value);
+                }
+                return true;
+            }
+
+            return false;
+        } catch (PDOException $e) {
+            Toolkit_Logger::logException('DB Error', $e);
+            throw new Toolkit_Members_Exception(
+                "Error validating user `$username` - `$password`"
+            );
+        }
+    }
+
+    //  }}}
+    //  {{{ _getDBFields()
+
+    /**
+     * Get extra db fields to fetch and set in the auth data
+     *
+     * @return array comma separated string of extra db fields for a SQL query
+     * @access private
+     */
+    private function _getDBFields()
+    {
+        if (isset($this->_options['db_fields'])) {
+            if (is_array($this->_options['db_fields'])) {
+                return implode(', ', $this->_options['db_fields']);
+            }
+        }
+    }
+
+    //  }}}
+}
+?>
diff --git a/Toolkit/Members/Auxiliary.php b/Toolkit/Members/Auxiliary.php
new file mode 100644 (file)
index 0000000..a496b80
--- /dev/null
@@ -0,0 +1,404 @@
+<?php
+//     vim:set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker syntax=php:
+
+/**
+ * File Doc Comment
+ *
+ * PHP version 5
+ *
+ * @category MembersDB
+ * @package  Toolkit_Members
+ * @author   Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @license     http://www.gaslightmedia.com Gaslightmedia
+ * @version  CVS: $Id: Auxiliary.php,v 1.18 2010/08/09 17:57:37 jamie Exp $
+ * @link        http://demo.gaslightmedia.com
+ */
+
+/**
+ * Base class used for building forms to accompany the members database
+ *
+ * This class handles all common functions that are to be used in
+ * the auxiliary forms used to assist in the members DB.
+ * (categories, amenities, regions, etc...).
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2008 Gaslight Media
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @version   CVS: $Id: Auxiliary.php,v 1.18 2010/08/09 17:57:37 jamie Exp $
+ * @see       Toolkit_Members, member_admin
+ */
+abstract class Toolkit_Members_Auxiliary
+    extends Toolkit_FormBuilder implements Toolkit_Form
+{
+       //      {{{ properties
+
+       /**
+        * Primary table used for database calls
+        *
+        * @var         String
+        * @access      Public
+        */
+       public $tableName;
+
+       /**
+        * Array of primary table meta data
+        *
+        * This array will hold the column data types for the fields
+        * that will be manipulated in the database.
+        *
+        * @var         Array
+        * @access      Public
+        */
+       public $tableMetaData;
+
+       /**
+        * The name of the template used for the form
+        *
+        * @var         String
+        * @access      Protected
+        */
+       protected $formTemplate;
+
+       /**
+        * The object to use inside the form
+        *
+        * This object which is to be populated by the $this object
+        * is used inside the templates and allows access back into the
+        * calling class to call publicly available functions
+        *
+        * @var         String
+        * @access      Protected
+        */
+       protected $view;
+
+    /**
+     * rules to use with the form
+     * @var    array
+     * @access protected
+     */
+       protected $registeredRules = array();
+
+    /**
+     * Setup options to use for rendering the template engine
+     * @var    array
+     * @access protected
+     */
+       protected $flexyOptions = array();
+
+       //      }}}
+       //      {{{ __construct()
+
+       /**
+        * Class constructor
+        *
+     * @param string $formName    Form's name.
+     * @param string $method      (optional)Form's method defaults to 'POST'
+     * @param string $action      (optional)Form's action
+     * @param string $target      (optional)Form's target defaults to '_self'
+     * @param mixed  $attributes  (optional)Extra attributes for <form> tag
+     * @param bool   $trackSubmit (optional)Whether to track if the form was
+        *                                                        submitted by adding a special hidden field
+     *
+        * @access public
+        */
+       public function __construct(
+        $formName,
+        $method = 'post',
+        $action = '',
+        $target = '',
+        $attributes = null,
+        $trackSubmit = false
+    ) {
+               parent::__construct(
+            $formName,
+            $method,
+            $action,
+            $target,
+            $attributes,
+            $trackSubmit
+        );
+
+        $this->flexyOptions = Toolkit_Members::getFlexyOptions();
+       }
+
+       //      }}}
+
+       //      {{{ configureDefaults()
+
+       /**
+        * Configure the default values for the form
+        *
+        * Since each form is different, you will need to redefine this
+        * method inside of each subclass and create your own rules
+        * based on your forms elements.
+        *
+        * @return void
+        * @access public
+        */
+       abstract protected function configureDefaults();
+
+       //      }}}
+       //      {{{ configureFilters()
+
+       /**
+        * Defines all filters used on form elements when submitted
+        *
+        * Most times this function won't need to be overridden.
+        *
+        * @return void
+        * @access public
+        */
+       protected function configureFilters()
+       {
+        $f = array();
+
+               $f[] = array(
+            'element' => '__ALL__',
+            'filter' => 'trim'
+        );
+
+               $this->setupFilters($f);
+       }
+
+       //      }}}
+
+       //      {{{ getSetParameters()
+
+       /**
+     * get a string of sql query parameters
+     *
+     * @param array $values parameters to include
+     *
+     * @return string sql query parameters
+        * @access protected
+        */
+       protected function getSetParameters($values)
+       {
+               $params = array_keys($values);
+               $length = count($params);
+               for ($i = 0; $i < $length; ++$i) {
+                       $bindParams .= "{$params[$i]} = :{$params[$i]}";
+            if ($i < ($length - 1)) {
+                $bindParams .= ', ';
+            }
+               }
+               return $bindParams;
+       }
+
+       //      }}}
+       //      {{{ getDataType()
+
+       /**
+     * Get the data type of the element
+     *
+     * @param string $k element name
+     *
+     * @return element type in the DB
+        * @access protected
+        */
+       protected function getDataType($k)
+       {
+               $metaData = $this->tableMetaData[$k];
+               if ($metaData == 'integer') {
+                       return PDO::PARAM_INT;
+               } else if ($metaData == 'boolean') {
+                       return PDO::PARAM_BOOL;
+               } elseif ($metaData == 'double precision') {
+                       return null;
+               } else {
+                       return PDO::PARAM_STR;
+               }
+       }
+
+       //      }}}
+
+       //      {{{ isEdit()
+
+       /**
+        * Determines if we are editing an object or not
+        *
+        * If we are ever editing an object, member, category, etc...
+        * There will be the objects DB ID passed along in the url under the
+        * id key.  If we can find this then we know we're editing, if not
+        * then we are adding.
+        *
+        * @return boolean if the key id is set in the $_GET array
+        * @access public
+        */
+       public function isEdit()
+       {
+               return isset($_GET['id']);
+       }
+
+       //      }}}
+
+       //      {{{ processData()
+
+    /**
+     * clean unneeded form elements out of the submitted values array
+     *
+     * @param array $values QuickForm submitted elements
+     *
+     * @return boolean Result of insert/update functions
+     * @access public
+     */
+       public function processData($values)
+       {
+               foreach ($values as $k => $v) {
+                       switch ($k) {
+                       case 'MAX_FILE_SIZE' :
+                               unset($values[$k]);
+                               break;
+
+                       default :
+                if (substr($k, -4) == '_rmv') {
+                                       unset($values[$k]);
+                               }
+                               break;
+                       }
+               }
+
+        $function = is_numeric($_GET['id']) ? 'updateData' : 'insertData';
+        return $this->$function($values);
+       }
+
+       //      }}}
+
+       //      {{{ setupElements()
+
+       /**
+        * Add the form elements defined in your class to a QuickForm
+        *
+        * Handles adding singleton elements as well as grouped elements.
+        * If added elements fail to correctly add to the form, the script
+        * should die gracefully, telling the user there was a problem.
+        *
+     * @param array $elements element definitions to setup
+     *
+        * @return void
+        * @throws HTML_QuickForm_Error error raised from QuickForm class.
+        * @access protected
+        */
+       protected function setupElements($elements)
+       {
+               $this->formElements = $elements;
+               foreach ($elements as $e) {
+                       if ($e['type'] != 'group') {
+                               try {
+                                       $source =& $this->addElement($e['type'], $e['name'], $e['display'], $e['opts'], $e['att'], $e['val']);
+                               } catch (HTML_QuickForm_Error $e) {
+                                       Toolkit_Common::dieGracefully(null, $e);
+                               }
+                               if ($e['type'] == 'advmultiselect') {
+                                       $source->setLabel($e['labels']);
+                               }
+                               if ($e['name'] == 'categories') {
+                                       $res = $source->loadArray($this->categories);
+                                       if (PEAR::isError($res)) {
+                                               Toolkit_Common::dieGracefully(null, $res);
+                                       }
+                               }
+                               if ($e['type'] == 'header') {
+                                       $this->formHeaders[$e['display']] = $e;
+                               }
+                       } elseif (is_array($e['group'])) {
+                               unset($field);
+                               foreach ($e['group'] as $g) {
+                                       $field[] = HTML_QuickForm::createElement($g['type'], $g['name'], $g['display'], $g['opts'], $g['att'], $g['val']);
+                               }
+                               $source = $this->addGroup($field, $e['name'], $e['label'], $e['seperator'], $e['appendName']);
+                       }
+               }
+       }
+
+       //      }}}
+       //      {{{ setupRenderers()
+
+       /**
+     * Set up the rendering engine templates
+     *
+     * @return void
+        * @access protected
+        */
+       protected function setupRenderers()
+       {
+               $renderer = new HTML_QuickForm_Renderer_Object(true);
+
+               $this->accept($renderer);
+
+               $this->template = new HTML_Template_Flexy($this->flexyOptions);
+
+               //      Make the view a copy of the $this object
+               //      That way we have access to call functions in
+               //      this class from within the template.
+               $this->view = $this;
+               $this->view->form = $renderer->toObject();
+               $this->template->compile($this->formTemplate);
+       }
+
+       //      }}}
+
+       //      {{{ toHtml()
+
+       /**
+        * Renders the form for viewing
+        *
+        * This function validates the form if needed, and if it successfully
+        * validates attempts to insert or update the data record.
+        * If it is unsuccessful, it will return an error to the user
+        * informing them of what went wrong.
+        *
+        * @param string $listPage The page the header should redirect
+        *                                                 to on successful insert or update.
+        *
+        * @return string The compiled and filled form template.
+        * @access public
+        */
+       public function toHtml($listPage = 'index.php')
+       {
+               //      We need to validate (and freeze if needed)
+               //      before we render the form. That way the
+               //      template knows about any errors on the form.
+               $this->validated = $this->validate();
+               $this->setupRenderers();
+
+               if ($this->validated) {
+                       $processed = $this->process(
+                array(&$this, 'processData'),
+                $this->mergeFiles
+            );
+                       if ($processed) {
+                               header("Location: $listPage");
+                       } else {
+                $this->validated = false;
+                               $errorMsg = "There was an unexpected error. Please try again later.";
+                       }
+               }
+
+               return $errorMsg . $this->template->bufferedOutputObject($this->view);
+       }
+
+       //      }}}
+
+       //      {{{ validated()
+
+       /**
+        * Returns form message from validation attempts
+        *
+     * @return void
+        * @access public
+        */
+       public function validated()
+       {
+               if ($this->validated) {
+                       return $this->successMsg;
+               } elseif ($this->isSubmitted()) {
+                       return $this->errorMsg;
+               }
+       }
+
+       //      }}}
+}
+?>
diff --git a/Toolkit/Members/Billing/AdminNavigation.php b/Toolkit/Members/Billing/AdminNavigation.php
new file mode 100644 (file)
index 0000000..05e0666
--- /dev/null
@@ -0,0 +1,143 @@
+<?php
+/**
+ * AdminNavigation.php
+ * 
+ * PHP version 5.2
+ * 
+ * @category  Toolkit
+ * @package   Members_Billing
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @version   SVN: $id$
+ * @link      <>
+ */
+
+/**
+ * Toolkit_Members_Billing_AdminNavigation
+ * 
+ * Description of Toolkit_Members_Billing_AdminNavigation
+ * 
+ * @category  Toolkit
+ * @package   Members_Billing
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @release   Release: $id$
+ * @link      <>
+ */
+class Toolkit_Members_Billing_AdminNavigation
+{
+    /**
+     * Description of getNavigationArray
+     * 
+     * @param Config_Container $c Description of $c
+     * 
+     * @return string 
+     * @access public
+     */
+    public function getNavigationArray(Config_Container $c)
+    {
+        $nav = array(
+            'Title'  => 'Billing',
+            'url'    => MEDIA_BASE_URL . 'admin/members.php?rt=Billing',
+            'desc'   => 'Billing Module',
+            'sub'    => array(
+                'PaymentType' => array(
+                    'Title'  => 'Payment Types',
+                    'url'    => MEDIA_BASE_URL
+                        . 'admin/members.php?rt=Billing&ac=PaymentType',
+                    'desc'   => 'Payment Types'
+                ),
+                'EditPaymentType' => array(
+                    'Title'   => 'Add Payment Type',
+                    'url'     => MEDIA_BASE_URL
+                        . 'admin/members.php?rt=Billing&ac=EditPaymentType',
+                    'desc'    => 'Add a Payment Type'
+                ),
+                'Invoicing' => array(
+                    'Title'   => 'Invoicing',
+                    'url'     => MEDIA_BASE_URL . 'admin/members.php?rt=Invoicing',
+                    'desc'    => 'Invoicing',
+                    'sub'     => array(
+                        'createInvoices' => array(
+                            'Title'  => 'Create Invoices',
+                            'url'    => MEDIA_BASE_URL
+                                . 'admin/members.php?rt=Invoicing&ac=createInvoices',
+                            'desc'   => 'Create Invoices'
+                        ),
+                        'printInvoices' => array(
+                            'Title'  => 'Print Invoices',
+                            'url'    => MEDIA_BASE_URL
+                                . 'admin/members.php?rt=Invoicing&ac=printInvoices',
+                            'desc'   => 'Print Out Invoices'
+                        ),
+                        'createLabels' => array(
+                            'Title'  => 'Create Labels',
+                            'url'    => MEDIA_BASE_URL
+                                . 'admin/members.php?rt=Invoicing&ac=createLabels',
+                            'desc'   => 'Create Mailing Labels'
+                        ),
+                        'sendEmail' => array(
+                            'Title'  => 'Send Email',
+                            'url'    => MEDIA_BASE_URL
+                                . 'admin/members.php?rt=Invoicing&ac=sendEmail',
+                            'desc'   => 'Send Invoices by Email'
+                        )
+                    )
+                ),
+                'Reports' => array(
+                    'Title'   => 'Reports',
+                    'url'     => MEDIA_BASE_URL
+                        . 'admin/members.php?rt=Reports',
+                    'desc'    => 'Billing Reports',
+                    'sub'    => array(
+                        'openAccounts' => array(
+                            'Title'  => 'Open Accounts',
+                            'url'    => MEDIA_BASE_URL
+                                . 'admin/members.php?rt=Reports&ac=openAccounts',
+                            'desc'   => 'Show All Open Accounts'
+                        ),
+                        'closedAccounts' => array(
+                            'Title'  => 'Closed Accounts',
+                            'url'    => MEDIA_BASE_URL
+                                . 'admin/members.php?rt=Reports&ac=closedAccounts',
+                            'desc'   => 'Show All Closed Accounts'
+                        ),
+                        'accountByAge' => array(
+                            'Title'  => 'Accounts By Age',
+                            'url'    => MEDIA_BASE_URL
+                                . 'admin/members.php?rt=Reports&ac=accountByAge',
+                            'desc'   => 'Show Accounts By Age'
+                        ),
+                        'searchReports' => array(
+                            'Title'  => 'Report Generator',
+                            'url'    => MEDIA_BASE_URL
+                                . 'admin/members.php?rt=Reports&ac=searchReports',
+                            'desc'   => 'List All'
+                        ),
+                        'noAccounts' => array(
+                            'Title'  => 'No Accounts',
+                            'url'    => MEDIA_BASE_URL
+                                . 'admin/members.php?rt=Reports&ac=noAccounts',
+                            'desc'   => 'show member without account data'
+                        ),
+                        'allAccounts' => array(
+                            'Title'  => 'All Accounts',
+                            'url'    => MEDIA_BASE_URL
+                                . 'admin/members.php?rt=Reports&ac=allAccounts',
+                            'desc'   => 'Show All Accounts'
+                        ),
+                    )
+                ),
+                'Payment' => array(
+                    'Title'  => 'Make Payment',
+                    'url'    => MEDIA_BASE_URL
+                        . 'admin/members.php?rt=Payment',
+                    'desc'   => 'scan or enter invoiceid for payment'
+                )
+            )
+        );
+        return $nav;
+    }
+}
diff --git a/Toolkit/Members/Billing/Auxiliary.php b/Toolkit/Members/Billing/Auxiliary.php
new file mode 100644 (file)
index 0000000..d85d3ee
--- /dev/null
@@ -0,0 +1,402 @@
+<?php
+
+/**
+ * Auxiliary.php
+ *
+ * PHP version 5
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members_Billing
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2011 Gaslight Media
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @link      http://demo.gaslightmedia.com
+ */
+
+/**
+ * Toolkit_Members_Billing_Auxiliary
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members_Billing
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2011 Gaslight Media
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @link      http://demo.gaslightmedia.com
+ */
+abstract class Toolkit_Members_Billing_Auxiliary
+    extends Toolkit_FormBuilder
+{
+
+    /**
+     * Base of the billing template directory
+     *  
+     * @var string
+     * @access protected
+     */
+    protected $templateBase = 'Toolkit/Members/Billing/';
+    /**
+     * The directory of the templates
+     *
+     * @var string
+     * @access protected
+     */
+    protected $templatesDir = 'templates';
+
+    /**
+     * The directory of the flexy-compiled templates
+     *
+     * @var string
+     * @access protected
+     */
+    protected $compiledDir = 'templates/compiled';
+
+    /**
+     * The name of the template used for the form
+     *
+     * @var string
+     * @access protected
+     */
+    protected $formTemplate;
+
+    /**
+     * The object to use inside the form
+     *
+     * This object which is to be populated by the $this object                
+     * is used inside the templates and allows access back into the
+     * calling class to call publicly available functions
+     *
+     * @var    string
+     * @access protected
+     */
+    protected $view;
+
+    /**
+     * Class constructor
+     *
+     * @param string $formName    Form's name.
+     * @param string $method      (optional)Form's method defaults to 'POST'
+     * @param string $action      (optional)Form's action
+     * @param string $target      (optional)Form's target defaults to '_self'
+     * @param mixed  $attributes  (optional)Extra attributes for <form> tag
+     * @param bool   $trackSubmit (optional)Whether to track if the form was
+     *                              submitted by adding a special hidden field
+     * 
+     * @access public
+     */
+    public function __construct(
+        $formName,
+        $method = 'post',
+        $action = '',
+        $target = '',
+        $attributes = null,
+        $trackSubmit = false
+    ) {
+        parent::__construct(
+            $formName,
+            $method,
+            $action,
+            $target,
+            $attributes,
+            $trackSubmit
+        );
+        $this->registeredRules = array();
+
+        $options =& PEAR::getStaticProperty('HTML_Template_Flexy', 'options');
+        $options = array(
+            'templateDir'  => BASE . "{$this->templateBase}{$this->templatesDir}",
+            'compileDir'   => BASE . "{$this->templateBase}{$this->compiledDir}",
+            'forceCompile' => 1,
+            'debug'        => 0,
+            'locale'       => 'en',
+        );
+    }
+
+    /**
+     * Returns the MEDIA_BASE_URL
+     *
+     * This function is used inside the Flexy Templated forms
+     *
+     * @return string MEDIA_BASE_URL
+     * @access public
+     */
+    public function baseUrl()
+    {
+        return MEDIA_BASE_URL;
+    }
+
+    /**
+     * Set up the default values for the form
+     *
+     * @return void
+     * @access public
+     */
+    public function configureDefaults()
+    {
+        $d = array();
+
+        if (isset($_GET['id'])) {
+            $factory = new Toolkit_Members_Billing_Factory();
+            $dbObject = $factory->createDbObjectById(
+                $this->dbh,
+                $this->className,
+                $_GET['id']
+            );
+            if ($dbObject) {
+                $d = $dbObject->getPropertiesAsArray();
+            }
+        }
+
+        $this->setupDefaults($d);
+    }
+
+    /**
+     * Configures all form elements
+     *
+     * If you are subclassing this base class out, you will need to define
+     * all your forms elements in your new subclass.
+     *
+     * @return void
+     * @access protected
+     */
+    abstract protected function configureElements();
+
+    /**
+     * Defines all filters used on form elements when submitted
+     *
+     * Most times this function won't need to be overridden.
+     *
+     * @return void
+     * @access public
+     */
+    protected function configureFilters()
+    {
+        $filters[] = array('element' => '__ALL__', 'filter' => 'trim');
+
+        $this->setupFilters($filters);
+    }
+
+    /**
+     * Sets up the required / not-required rules for forms
+     *
+     * At the bare minimum, the required / not-required rules for form
+     * needs to be defined.  Since these rules are most easily defined
+     * while creating the element definitions themselves the base function
+     * is to call the setupRules function that will instantiate these
+     * rules in the quickform class.
+     *
+     * @return void
+     * @access public
+     */
+    protected function configureRules()
+    {
+        $this->setupRules();
+    }
+
+    /**
+     * if the id is set in _GET array
+     * 
+     * @return Boolean
+     */
+    public function isEdit()
+    {
+        return isset($_GET['id']);
+    }
+
+    /**
+     * Short description for function
+     * 
+     * Long description (if any) ...
+     * 
+     * @param array $values Parameter description (if any) ...
+     *
+     * @return array Return description (if any) ...
+     * @access protected
+     */
+    protected function processData($values)
+    {
+        foreach ($values as $k => $v) {
+            switch ($k) {
+            case 'uploaded_file_rmv' :
+                //    Don't remove this from the values array.
+                //    we will handle it later.
+                //    we need to have access to this value
+                //    in the insert/update functions.
+                break;
+
+            case 'MAX_FILE_SIZE' :
+                unset($values[$k]);
+                break;
+
+            default :
+                if (preg_match('/^.+_rmv$/', $k)) {
+                    unset($values[$k]);
+                }
+                break;
+            }
+        }
+        
+        if (isset($_GET['id'])) {
+            $values['id'] = $_GET['id'];
+        }
+        $factory = new Toolkit_Members_Billing_Factory();
+        $dbObject = $factory->createDBObjectByValues(
+            $this->className,
+            $values
+        );
+        $dbObject->save($this->dbh);
+        return true;
+    }
+
+    /**
+     * Add the form elements defined in your class to a QuickForm
+     *
+     * Handles adding singleton elements as well as grouped elements.
+     * If added elements fail to correctly add to the form, the script
+     * should die gracefully, telling the user there was a problem.
+     *
+     * @param array $elements form element definitions
+     *
+     * @throws HTML_QuickForm_Error error raised from QuickForm class.
+     * @return void
+     * @access protected
+     */
+    protected function setupElements($elements)
+    {
+        $this->formElements = $elements;
+        foreach ($elements as $e) {
+            if ($e['type'] != 'group') {
+                try {
+                    $source =& $this->addElement(
+                        $e['type'],
+                        $e['name'],
+                        $e['display'],
+                        $e['opts'],
+                        $e['att'],
+                        $e['val']
+                    );
+                } catch (HTML_QuickForm_Error $e) {
+                    Toolkit_Common::dieGracefully(null, $e);
+                }
+                if ($e['type'] == 'advmultiselect') {
+                    $source->setLabel($e['labels']);
+                }
+                if ($e['name'] == 'categories') {
+                    $res = $source->loadArray($this->categories);
+                    if (PEAR::isError($res)) {
+                        Toolkit_Common::dieGracefully(null, $res);
+                    }
+                }
+                if ($e['type'] == 'header') {
+                    $this->formHeaders[$e['display']] = $e;
+                }
+            } elseif (is_array($e['group'])) {
+                unset($field);
+                foreach ($e['group'] as $g) {
+                    $field[] =& HTML_QuickForm::createElement(
+                        $g['type'],
+                        $g['name'],
+                        $g['display'],
+                        $g['opts'],
+                        $g['att'],
+                        $g['val']
+                    );
+                }
+                $source =& $this->addGroup(
+                    $field,
+                    $e['name'],
+                    $e['label'],
+                    $e['seperator'],
+                    $e['appendName']
+                );
+            }
+        }
+    }
+
+    /**
+     * Sets up the rendering engine for the form
+     *
+     * @return void
+     * @access protected
+     */
+    protected function setupRenderers()
+    {
+        $renderer =& new HTML_QuickForm_Renderer_Object(true);
+
+        $this->accept($renderer);
+
+        $this->template =& new HTML_Template_Flexy($this->options);
+
+        //    Make the view a copy of the $this object
+        //    That way we have access to call functions in
+        //    this class from within the template.
+        $this->view = $this;
+        $this->view->form = $renderer->toObject();
+        $this->template->compile($this->formTemplate);
+    }
+
+    /**
+     * Renders the form for viewing
+     *
+     * This function validates the form if needed, and if it successfully
+     * validates attempts to insert or update the data record.
+     * If it is unsuccessful, it will return an error to the user
+     * informing them of what went wrong.
+     *
+     * @param string $listPage The page the header should redirect
+     *                           to on successful insert or update.
+     *
+     * @return string The compiled and filled form template.
+     * @access public
+     */
+    public function toHTML($listPage = 'index.php')
+    { 
+        if ($_POST['delete'] && is_numeric($_POST['id'])) {
+            $factory = new Toolkit_Members_Billing_Factory();
+            $dbObject = $factory->createDbObjectById(
+                $this->dbh,
+                $this->className,
+                $_POST['id']
+            );
+            $dbObject->delete($this->dbh);
+            header('Location: ' . $listPage);
+            exit;
+        }
+        
+        //    We need to validate (and freeze if needed)
+        //    before we render the form. That way the
+        //    template knows about any errors on the form.
+        $this->validated = $this->validate();
+        $this->setupRenderers();
+
+        if ($this->validated) {
+            $processed = $this->process(
+                array(&$this, 'processData'),
+                $this->mergeFiles
+            );
+            if ($processed) {
+                header("Location: $listPage");
+            } else {
+                $errorMsg = "There was an unexpected error.
+                    Please try again later.";
+            }
+        }
+
+        return $errorMsg . $this->template->bufferedOutputObject($this->view);
+    }
+
+    /**
+     * Returns form message from validation attempts
+     *
+     * @return string validation message
+     * @access public
+     */
+    public function validated()
+    {
+        if ($this->validated) {
+            return $this->successMsg;
+        } elseif ($this->isSubmitted()) {
+            return $this->errorMsg;
+        }
+    }
+
+}
diff --git a/Toolkit/Members/Billing/Billing.php b/Toolkit/Members/Billing/Billing.php
new file mode 100644 (file)
index 0000000..87a1b6e
--- /dev/null
@@ -0,0 +1,473 @@
+<?php
+/**
+ * Billing.php
+ *
+ * PHP version 5
+ *
+ * @category Toolkit
+ * @package  Members_Billing
+ * @author   Steve Sutton <steve@gaslightmedia.com>
+ * @license  http://www.gaslightmedia.com Gaslightmedia
+ * @link     <>
+ */
+
+/**
+ * Toolkit_Members_Billing_Billing
+ * 
+ * Member Billing Module Billing table class
+ *
+ * @category Toolkit
+ * @package  Members_Billing
+ * @author   Steve Sutton <steve@gaslightmedia.com>
+ * @license  http://www.gaslightmedia.com Gaslightmedia
+ * @link     <>
+ */
+class Toolkit_Members_Billing_Billing
+    extends Toolkit_Table
+{
+    /**
+     * Description of $tableName
+     * @var string 
+     * @access public
+     */
+    public $tableName = 'billing';
+   
+    /**
+     * Description of $id
+     * @var int 
+     * @access protected
+     */
+    protected $id;
+    
+    /**
+     * Description of $invoice_id
+     * @var int 
+     * @access protected
+     */
+    protected $invoice_id;
+    
+    /**
+     * Description of $transaction_time
+     * @var timestamp 
+     * @access protected
+     */    
+    protected $transaction_time;
+    
+    /**
+     * Description of $transaction_date
+     * @var date 
+     * @access protected
+     */    
+    protected $transaction_date;
+    
+    /**
+     * Description of $member_name
+     * @var string 
+     * @access protected
+     */    
+    protected $member_name;
+    
+    /**
+     * Description of $member_id
+     * @var int 
+     * @access protected
+     */    
+    protected $member_id;
+    
+    /**
+     * Description of $account_id
+     * @var int 
+     * @access protected
+     */    
+    protected $account_id;
+    
+    /**
+     * Description of $account_number
+     * @var string 
+     * @access protected
+     */    
+    protected $account_number;
+    
+    /**
+     * Description of $billing_type
+     * @var int 
+     * @access protected
+     */    
+    protected $billing_type;
+    
+    /**
+     * Description of $amount
+     * @var int 
+     * @access protected
+     */    
+    protected $amount;
+    
+    /**
+     * Description of $balance
+     * @var int 
+     * @access protected
+     */    
+    protected $balance;
+    
+    /**
+     * Description of $payment_method
+     * @var int 
+     * @access protected
+     */    
+    protected $payment_method;
+    
+    /**
+     * Description of $payment_data
+     * @var string 
+     * @access protected
+     */    
+    protected $payment_data;
+    
+    /**
+     * Description of $emailed
+     * @var boolean 
+     * @access protected
+     */    
+    protected $emailed;
+    
+    /**
+     * Description of $printed
+     * @var boolean 
+     * @access protected
+     */    
+    protected $printed;
+    
+    /**
+     * Description of $paid
+     * @var boolean 
+     * @access protected
+     */    
+    protected $paid;
+    
+    /**
+     * Description of $invoice
+     * @var string 
+     * @access protected
+     */    
+    protected $invoice;
+    
+    /**
+     * Description of $notes
+     * @var string 
+     * @access protected
+     */    
+    protected $notes;
+
+    /**
+     * return amount as float
+     *
+     * @return float
+     * @access public
+     */
+    public function getAmount()
+    {
+        return (float)$this->amount;
+    }
+
+    /**
+     * return balance as float
+     *
+     * @return float
+     * @access public
+     * 
+     */
+    public function getBalance()
+    {
+        return (float)$this->balance;
+    }
+    
+    /**
+     * Description of getInvoiceAsPdf
+     * 
+     * @param PDO $dbh Database handler
+     * 
+     * @return boolean|string
+     * @access public
+     */
+    public function getInvoiceAsPdf(PDO $dbh)
+    {
+        $memberId  = $this->getMember_id();
+        $invoiceId = $this->getInvoice_id();
+        $accountId = $this->getAccount_id();
+        // if there's an empty value in any of these return false
+        if (!$memberId || !$invoiceId || !$accountId) {
+            return false;
+        }
+        $pdf = new Toolkit_Members_Billing_InvoicePdf();
+        $pdfOut = $pdf->createPdfInvoice(
+            $dbh,
+            $memberId,
+            $invoiceId,
+            $accountId
+        );
+        return $pdfOut;
+    }
+
+    /**
+     * insert the object
+     *
+     * @param PDO $dbh Database Connection
+     *
+     * @return Toolkit_Table
+     * @access public
+     */
+    public function insert(PDO $dbh)
+    {
+
+        try {
+            $values = get_object_vars($this);
+            unset($values['id'], $values['tableName']);
+            if ($values['invoice_id'] == null) {
+                unset($values['invoice_id']);
+            }
+            $sql = Toolkit_Common::createSQLInsert(
+                $this->tableName,
+                array_keys($values)
+            );
+            $sql .= ' RETURNING id, invoice_id';
+            $stmt = Toolkit_Common::prepareQuery(
+                $dbh,
+                $this->tableName,
+                $sql,
+                $values
+            );
+            $stmt->execute();
+            $res = $stmt->fetch(PDO::FETCH_ASSOC);
+            $this->setId($res['id']);
+            $this->setInvoiceId($res['invoice_id']);
+            return $this;
+        } catch(PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+    }
+    
+    /**
+     * Check to see if given an id that it is numeric
+     *
+     * @param Mixed $id account_id field
+     *
+     * @return Toolkit_Members_Billing_Billing
+     * @access public
+     * @throws InvalidArgumentException
+     */
+    public function setAccountId($id)
+    {
+        if (isset($id) && !is_numeric($id)) {
+            throw new InvalidArgumentException('If account_id is given must be numeric');
+        }
+        $this->account_id = $id;
+        return $this;
+    }
+
+    /**
+     * Checking that the amount is a valid numeric value
+     *
+     * @param mixed $amount billing amount
+     *
+     * @return Toolkit_Members_Billing_Billing
+     * @access public
+     * @throws InvalidArgumentException
+     */
+    public function setAmount($amount)
+    {
+        if (isset($amount) && !is_numeric($amount)) {
+            throw new InvalidArgumentException('If amount is given must be numeric');
+        }
+        $this->amount
+            = ($amount)
+            ? (float)$amount
+            : (float)'0.00';
+        return $this;
+    }
+
+    /**
+     * Checking that the amount is a valid numeric value
+     *
+     * @param mixed $balance balance
+     *
+     * @return Toolkit_Members_Billing_Billing
+     * @access public
+     * @throws InvalidArgumentException
+     */
+    public function setBalance($balance)
+    {
+        if (isset($balance) && !is_numeric($balance)) {
+            throw new InvalidArgumentException(
+                'If balance is given must be numeric'
+            );
+        }
+        $this->balance
+            = ($balance)
+            ? (float)$balance
+            : (float)'0.00';
+        return $this;
+    }
+
+    /**
+     * Checking that billing type is between 1 and 5
+     *
+     * @param Mixed $billing_type integer of billing type
+     *
+     * @return Toolkit_Members_Billing_Billing
+     * @access public
+     * @throws InvalidArgumentException
+     */
+    public function setBillingType($billing_type)
+    {
+        if (!isset($billing_type)) {
+            throw new InvalidArgumentException('Billing Type must not be null');
+        }
+        if (!in_array($billing_type, array(1,2,3,4,5))) {
+            throw new InvalidArgumentException(
+                'Billing Type must be a numeric value between 1 and 5'
+            );
+        }
+        $this->billing_type = $billing_type;
+        return $this;
+    }
+
+    /**
+     * Make sure this is a boolean value
+     *
+     * @param Mixed $emailed Emailed to the member
+     *
+     * @return Toolkit_Members_Billing_Billing
+     * @access public
+     */
+    public function setEmailed($emailed)
+    {
+        $this->emailed = ($emailed);
+        return $this;
+    }
+    
+    /**
+     * Check to see if given an id that it is numeric
+     *
+     * @param Mixed $id invoice_id field
+     *
+     * @return Toolkit_Members_Billing_Billing
+     * @access public
+     * @throws InvalidArgumentException
+     */
+    public function setInvoiceId($id)
+    {
+        if (isset($id) && !is_numeric($id)) {
+            throw new InvalidArgumentException('If invoice_id is given must be numeric');
+        }
+        $this->invoice_id = $id;
+        return $this;
+    }
+
+    /**
+     * Make sure this is a boolean value
+     *
+     * @param Mixed $paid Paid
+     *
+     * @return Toolkit_Members_Billing_Billing
+     * @access public
+     */
+    public function setPaid($paid)
+    {
+        $this->paid = ($paid);
+        return $this;
+    }
+
+    /**
+     * Check that payment method is between 1 and 4
+     *
+     * @param Mixed $payment_method Payment method
+     *
+     * @return Toolkit_Members_Billing_Billing
+     * @access public
+     * @throws InvalidArgumentException
+     */
+    public function setPaymentMethod($payment_method)
+    {
+        if (!isset($payment_method)) {
+            $this->payment_method = null;
+            return $this;
+        }
+        if (!in_array($payment_method, array(1,2,3,4))) {
+            throw new InvalidArgumentException(
+                'Payment Method must be a numeric value between 1 and 4'
+            );
+        }
+        $this->payment_method = $payment_method;
+        return $this;
+    }
+
+    /**
+     * Check to see if given an id that it is numeric
+     *
+     * @param Mixed $id member_id field
+     *
+     * @return Toolkit_Members_Billing_Billing
+     * @access public
+     * @throws InvalidArgumentException
+     */
+    public function setMemberId($id)
+    {
+        if (isset($id) && !is_numeric($id)) {
+            throw new InvalidArgumentException('If member_id is given must be numeric');
+        }
+        $this->member_id = $id;
+        return $this;
+    }
+
+    /**
+     * force the value to be boolean
+     *
+     * @param mixed $printed Printed for member
+     *
+     * @return Toolkit_Members_Billing_Billing
+     * @access public
+     */
+    public function setPrinted($printed)
+    {
+        $this->printed = ($printed);
+        return $this;
+    }
+
+    /**
+     * update the object
+     *
+     * @param PDO $dbh Database connection
+     *
+     * @return Toolkit_Table
+     * @access public
+     */
+    public function update(PDO $dbh)
+    {
+        try {
+            $values = get_object_vars($this);
+            $values['amount']
+                = ($values['amount'])
+                ? (float)$values['amount']
+                : (float)0.00;
+            if ($values['invoice_id'] == null) {
+                unset($values['invoice_id']);
+            }
+            unset($values['tableName']);
+            $sql = Toolkit_Common::createSQLUpdate(
+                $this->tableName,
+                array_keys($values),
+                array('id = :id')
+            );
+            $stmt = Toolkit_Common::processQuery(
+                $dbh,
+                $this->tableName,
+                $sql,
+                $values
+            );
+            return $this;
+        } catch(PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+    }
+}
+
diff --git a/Toolkit/Members/Billing/Database/application.sql b/Toolkit/Members/Billing/Database/application.sql
new file mode 100644 (file)
index 0000000..aa14248
--- /dev/null
@@ -0,0 +1,8 @@
+--
+-- setup the members billing tables
+--
+
+\i ./tables/member.sql
+\i ./tables/billing.sql
+\i ./tables/payment_types.sql
+\i ./tables/member_account.sql
\ No newline at end of file
diff --git a/Toolkit/Members/Billing/Database/tables/billing.sql b/Toolkit/Members/Billing/Database/tables/billing.sql
new file mode 100644 (file)
index 0000000..ba429d8
--- /dev/null
@@ -0,0 +1,27 @@
+CREATE SEQUENCE members.billing_inv_id_seq START WITH 1;
+
+CREATE TABLE members.billing (
+    id SERIAL UNIQUE,
+    invoice_id INTEGER DEFAULT nextval('members.billing_inv_id_seq'),
+    transaction_time TIMESTAMP(0) WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP,
+    transaction_date DATE DEFAULT CURRENT_DATE,
+    member_name TEXT,
+    member_id INTEGER,
+    account_id INTEGER,
+    account_number TEXT NOT NULL,
+    billing_type INTEGER NOT NULL,
+    amount numeric(11,2) NOT NULL,
+    balance numeric(11,2) NOT NULL,
+    payment_method INTEGER,
+    payment_data TEXT,
+    emailed BOOLEAN,
+    printed BOOLEAN,
+    paid BOOLEAN,
+    invoice TEXT,
+    notes TEXT,
+    PRIMARY KEY (id)
+);
+
+GRANT ALL ON members.billing TO nobody;
+GRANT ALL ON members.billing_id_seq TO nobody;
+GRANT ALL ON members.billing_inv_id_seq TO nobody;
diff --git a/Toolkit/Members/Billing/Database/tables/member.sql b/Toolkit/Members/Billing/Database/tables/member.sql
new file mode 100644 (file)
index 0000000..1d79171
--- /dev/null
@@ -0,0 +1,4 @@
+ALTER TABLE members.member ADD account_number TEXT;
+CREATE UNIQUE INDEX member_account_number_indx ON members.member(account_number);
+ALTER TABLE members.member ADD billing_contact TEXT;
+UPDATE members.member SET billing_contact = primary_contact_fname||' '||primary_contact_lname;
diff --git a/Toolkit/Members/Billing/Database/tables/member_account.sql b/Toolkit/Members/Billing/Database/tables/member_account.sql
new file mode 100644 (file)
index 0000000..7500df0
--- /dev/null
@@ -0,0 +1,18 @@
+DROP table members.member_account;
+
+CREATE TABLE members.member_account (
+ id SERIAL,
+ member_id INTEGER NOT NULL
+    REFERENCES members.member (member_id)
+    ON DELETE CASCADE,
+ payment_type INTEGER NOT NULL
+    REFERENCES members.payment_types (id)
+    ON DELETE CASCADE,
+ email_invoice BOOLEAN NOT NULL DEFAULT false,
+ usmail_invoice BOOLEAN NOT NULL DEFAULT false,
+ fax_invoice BOOLEAN NOT NULL DEFAULT false,
+ PRIMARY KEY (id)
+);
+
+GRANT ALL ON members.member_account TO nobody;
+GRANT ALL ON members.member_account_id_seq TO nobody;
diff --git a/Toolkit/Members/Billing/Database/tables/payment_types.sql b/Toolkit/Members/Billing/Database/tables/payment_types.sql
new file mode 100644 (file)
index 0000000..0d9157b
--- /dev/null
@@ -0,0 +1,13 @@
+CREATE TABLE members.payment_types (
+    id SERIAL UNIQUE,
+    name TEXT NOT NULL,
+    qcode INT,
+    category TEXT NOT NULL,
+    amount numeric(8,2) NOT NULL,
+    notes TEXT,
+    dynamic_amount BOOLEAN DEFAULT FALSE,
+    PRIMARY KEY (id)
+);
+
+GRANT ALL ON members.payment_types TO nobody;
+GRANT ALL ON members.payment_types_id_seq TO nobody;
\ No newline at end of file
diff --git a/Toolkit/Members/Billing/EditBillingForm.php b/Toolkit/Members/Billing/EditBillingForm.php
new file mode 100644 (file)
index 0000000..4a4cf67
--- /dev/null
@@ -0,0 +1,319 @@
+<?php
+/**
+ * EditBillingForm.php
+ * 
+ * PHP version 5
+ * 
+ * @category  MembersDB
+ * @package   Toolkit_Members_Billing
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2011 Gaslight Media
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @link      http://demo.gaslightmedia.com
+ */
+
+/**
+ * Form for billing edit
+ * 
+ * @category  MembersDB
+ * @package   Toolkit_Members_Billing
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2011 Gaslight Media
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @link      http://demo.gaslightmedia.com
+ */
+class Toolkit_Members_Billing_EditBillingForm
+    extends Toolkit_Members_Billing_Auxiliary
+{
+    /**
+     * class used for the database object
+     *
+     * @var    string
+     * @access public
+     */
+    public $className = 'Toolkit_Members_Billing_Billing';
+
+    /**
+     * template for edit form
+     *
+     * @var    string   
+     * @access protected
+     */
+    protected $formTemplate = 'editBilling.html';
+
+    /**
+     * Class constructor
+     *
+     * @param PDO    $pdo         PHP Data Object
+     * @param string $formName    Form's name.
+     * @param string $method      (optional)Form's method defaults to 'POST'
+     * @param string $action      (optional)Form's action
+     * @param string $target      (optional)Form's target defaults to '_self'
+     * @param mixed  $attributes  (optional)Extra attributes for <form> tag
+     * @param bool   $trackSubmit (optional)Whether to track if the form was
+     *                              submitted by adding a special hidden field
+     * 
+     * @access public
+     */
+    public function __construct(
+        PDO $pdo,
+        $formName,
+        $method = 'post',
+        $action = '',
+        $target = '',
+        $attributes = null,
+        $trackSubmit = false
+    ) {
+        parent::__construct(
+            $formName,
+            $method,
+            $action,
+            $target,
+            $attributes,
+            $trackSubmit
+        );
+
+        $this->dbh = $pdo;
+    }
+    /**
+     * Set up the default values for the form
+     *
+     * @return void
+     * @access public
+     */
+    public function configureDefaults()
+    {
+        $d = array();
+        if (isset($_GET['invoice_id'])) {
+            $factory = new Toolkit_Members_Billing_Factory();
+            $dbObject = $factory->createDbObjectById(
+                $this->dbh,
+                $this->className,
+                $_GET['invoice_id']
+            );
+            if ($dbObject) {
+                $d = $dbObject->getPropertiesAsArray();
+            }
+            $d['id'] = $_REQUEST['id'];
+            $d['currentAmount'] = $dbObject->getAmount();
+        }
+
+        $this->setupDefaults($d);
+    }
+    /**
+     * Form element definitions
+     * 
+     * @return void     
+     * @access public
+     */
+    public function configureElements()
+    {
+        $e = array();
+        //    All Grouped Elements are created here.
+
+        //    All Elements are created here.
+        //    This includes group element definitions.
+        $e[] = array(
+            'type'    => 'header',
+            'req'     => false,
+            'name'    => 'PaymentTypeInfoHdr',
+            'display' => 'Billing Invoice'
+        );
+        $e[] = array(
+            'type'    => 'static',
+            'req'     => false,
+            'name'    => 'member_name',
+            'display' => 'Member Name'
+        );
+        $e[] = array(
+            'type'    => 'hidden',
+            'req'     => false,
+            'name'    => 'id'
+        );
+        $e[] = array(
+            'type'    => 'hidden',
+            'req'     => false,
+            'name'    => 'currentAmount'
+        );
+        $e[] = array(
+            'type'    => 'hidden',
+            'req'     => false,
+            'name'    => 'invoice_id'
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => true,
+            'name'    => 'amount',
+            'display' => 'Amount'
+        );
+
+
+        $this->setupElements($e);
+    }
+
+    /**
+     * Helper function to configure an entire form
+     *
+     * @return void
+     * @access public
+     */
+    public function configureForm()
+    {
+        $this->configureElements();
+        $this->configureRules();
+        $this->configureDefaults();
+    }
+
+    /**
+     * Form rule definitions
+     * 
+     * @return void     
+     * @access public
+     */
+    public function configureRules()
+    {
+        $r = array();
+
+        $r[] = array(
+            'element'    => 'amount',
+            'message'    => 'ERROR: Invalid amount!',
+            'type'       => 'numeric',
+            'format'     => null,
+            'validation' => $this->validationType,
+            'reset'      => true,
+            'force'      => false
+        );
+        
+        $this->setupRules($r);
+    }
+    
+    /**
+     * Short description for function
+     * 
+     * Long description (if any) ...
+     * 
+     * @param array $values Parameter description (if any) ...
+     *
+     * @return array Return description (if any) ...
+     * @access protected
+     */
+    protected function processData($values)
+    {
+        foreach ($values as $k => $v) {
+            switch ($k) {
+            case 'uploaded_file_rmv' :
+                //    Don't remove this from the values array.
+                //    we will handle it later.
+                //    we need to have access to this value
+                //    in the insert/update functions.
+                break;
+
+            case 'MAX_FILE_SIZE' :
+                unset($values[$k]);
+                break;
+
+            default :
+                if (preg_match('/^.+_rmv$/', $k)) {
+                    unset($values[$k]);
+                }
+                break;
+            }
+        }
+        // calculate the difference of the old amount and new one
+        // this will be used for reseting all the amount and balances for 
+        // the billing records for the invoic_id
+        $diffAmount = (float)$values['currentAmount'] - (float)$values['amount'];
+        try {
+            $sql = "
+              SELECT id
+                FROM billing
+               WHERE invoice_id = :invoice_id
+                 AND billing_type IN (1,2,3)
+            ORDER BY transaction_time";
+            $stmt = $this->dbh->prepare($sql);
+            $stmt->bindParam(
+                ':invoice_id',
+                $values['invoice_id'],
+                PDO::PARAM_INT
+            );
+            $stmt->execute();
+            while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                $this->resetBillingAmounts($row['id'], $diffAmount);
+            }
+        } catch(PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+        
+        return true;
+    }
+    
+    /**
+     * If the function is called with an Amount then it is the first
+     * all others should have just a diff
+     *
+     * @param int $billingId  Id of the billing record
+     * @param int $diffAmount Difference in new amount   
+     * 
+     * @return void 
+     */
+    public function resetBillingAmounts($billingId, $diffAmount)
+    {
+        $factory = new Toolkit_Members_Billing_Factory();
+        $billing = $factory->createDBObjectById(
+            $this->dbh,
+            $this->className,
+            $billingId
+        );
+        $currentAmount  = $billing->getAmount();
+        $currentBalance = $billing->getBalance();
+        if ($billing->getBilling_type() == 1) {
+            $newAmount  = (float)$currentAmount - $diffAmount;
+            $billing->setAmount($newAmount);
+        }
+        
+        $newBalance = (float)$currentBalance - $diffAmount;
+        $billing->setBalance($newBalance);
+        if ($newBalance == (float)'0.00') {
+            $billing->setPaid(true);
+        } else {
+            $billing->setPaid(false);
+        }
+        $billing->save($this->dbh);
+        // need to get original invoice
+        $statement = new Toolkit_Members_Billing_Statement();
+        $originalInvoice = $statement->getOriginalInvoice(
+            $this->dbh,
+            $billing->getMember_id(),
+            $billing->getInvoice_id()
+        );
+        if ($originalInvoice) {
+            if ($originalInvoice->getId() != $billing->getId()) {
+                $invoiceId = $originalInvoice->getInvoice_id();
+                if ($newBalance == (float)'0.00') {
+                    $originalInvoice
+                        ->setPaid(true)
+                        ->save($this->dbh);
+                }
+            }
+        }        
+        $billing->save($this->dbh);
+    }
+
+    /**
+     * Renders the form for viewing
+     *
+     * This function validates the form if needed, and if it successfully
+     * validates attempts to insert or update the data record.
+     * If it is unsuccessful, it will return an error to the user
+     * informing them of what went wrong.
+     *
+     * @return string The compiled and filled form template.
+     * @access public
+     */
+    public function toHtml()
+    {
+        $listPage = MEDIA_BASE_URL . 'admin/members.php?rt=Members&ac=editMember&tab=invoices&id='
+            . $_REQUEST['id'];
+        return parent::toHtml($listPage);
+    }
+}
diff --git a/Toolkit/Members/Billing/EditMemberAccount.php b/Toolkit/Members/Billing/EditMemberAccount.php
new file mode 100644 (file)
index 0000000..ca4bc08
--- /dev/null
@@ -0,0 +1,831 @@
+<?php
+
+/**
+ * EditMemberAccount.php
+ *
+ * PHP version 5
+ *
+ * @category Toolkit
+ * @package  Members_Billing
+ * @author   Steve Sutton <steve@gaslightmedia.com>
+ * @license  http://www.gaslightmedia.com Gaslightmedia
+ * @version  CVS: $Id: EditMemberAccount.php,v 1.1 2011/03/25 20:18:28 matrix Exp $
+ * @link     http://demo.gaslightmedia.com
+ */
+
+/**
+ * Handle associating contacts along with member records
+ *
+ * Controls all aspects of creating and rendering the form used to manipulate
+ * the billing. Form is not rendered until the user is added into
+ * the Database.
+ *
+ * @category  Toolkit
+ * @package   Members_Billing
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2008 Gaslight Media
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @link      http://demo.gaslightmedia.com
+ * @see       Toolkit_Members
+ */
+class Toolkit_Members_Billing_EditMemberAccount
+    extends Toolkit_Members_EditMemberInfo
+    implements Toolkit_Form
+{
+    
+    /**
+     * The table name in the database used to store the data of the files
+     *
+     * @var string
+     * @access public
+     */
+    public $tableName = 'member';
+
+    /**
+     * The template used to render the form
+     *
+     * @var string
+     * @access protected
+     */
+    protected $formTemplate = 'editBilling.tpl';
+
+    /**
+     * Message to return if the form successfully submits
+     *
+     * @var string
+     * @access protected
+     */
+    protected $successMsg = '
+        <div id="form-success-top">
+            You successfully updated your Billing Info.
+        </div>';
+
+    /**
+     * Description for $memberStatements
+     * 
+     * @var string
+     * @access public
+     */
+    public $memberStatements = '';
+    /**
+     * Primary account id
+     *
+     * @var integer
+     * @access protected
+     */
+    protected $primaryAccountId = null;
+    /**
+     * secondary account number
+     *
+     * @var integer
+     * @access protected
+     */
+    protected $secondaryAccountId = null;
+
+    /**
+     * Class constructor
+     *
+     * @param PDO    $pdo         PHP Data Object to use for DB calls
+     * @param string $formName    Form's name.
+     * @param string $method      (optional)Form's method defaults to 'POST'
+     * @param string $action      (optional)Form's action
+     * @param string $target      (optional)Form's target defaults to '_self'
+     * @param mixed  $attributes  (optional)Extra attributes for <form> tag
+     * @param bool   $trackSubmit (optional)Whether to track if the form was
+     *                              submitted by adding a special hidden field
+     *                            
+     * @access public
+     * @see    Toolkit_Members_EditMemberInfo
+     */
+    public function __construct(
+        PDO $pdo,
+        $formName,
+        $method = 'post',
+        $action = '',
+        $target = '',
+        $attributes = null,
+        $trackSubmit = false
+    ) {
+        parent::__construct(
+            $pdo,
+            $formName,
+            $method,
+            $action,
+            $target,
+            $attributes,
+            $trackSubmit
+        );
+        // Member config
+        $config = new Config();
+        $root = $config->parseConfig(
+            BASE . 'Toolkit/Members/config.ini',
+            'IniFile'
+        );
+        $this->config = $root;
+        // Billing config
+        $config2 = new Config();
+        $root2 = $config2->parseConfig(
+            BASE . 'Toolkit/Members/Billing/config.ini',
+            'IniFile'
+        );
+        $this->config2 = $root2;
+    }
+
+    /**
+     * Validate date input
+     * 
+     * allows for empty dates to be valid
+     *
+     * @param array $date date group from form
+     *
+     * @return boolean true if valid, false if not
+     * @access public
+     */
+    public function checkDate($date)
+    {
+        return strtotime($date);
+    }
+
+    /**
+     * Makes sure there are no duplicate account numbers already in the table
+     * 
+     * @param string $data Data
+     * 
+     * @return boolean|mixed
+     * @access public
+     */
+    public function checkAccountNumber($data)
+       {
+               try {
+                       //      If we're editing a member, they
+                       //      can save that account_number as its
+                       //      own. so don't include that 
+                       //      member in the check.
+                       if (is_numeric($_GET['id'])) {
+                               $and = "AND member_id != :id";
+                       }
+                       $sql = "
+                               SELECT count(*) AS total
+                                 FROM member
+                                WHERE account_number = :account_number
+                                 $and";
+                       $stmt = $this->dbh->prepare($sql);
+                       $stmt->bindParam(':account_number', $data, PDO::PARAM_STR);
+                       if (is_numeric($_GET['id'])) {
+                               $stmt->bindParam(':id', $_GET['id'], PDO::PARAM_STR);
+                       }
+                       $stmt->execute();
+            $stmt->bindColumn('total', $valid);
+                       $stmt->fetch();
+
+                       return !(bool) $valid;
+               } catch (PDOException $e) {
+                       return Toolkit_Common::handleError($e);
+               }
+       }
+    
+    /**
+     * Description of hasInfo
+     * 
+     * @return boolean
+     * @access public
+     */
+    public function hasInfo()
+    {
+        return (bool)$this->primaryAccountId;
+    }
+    
+    /**
+     * Sets the defaults for elements in the form.
+     *
+     * @return void
+     * @access public
+     */
+    public function configureDefaults()
+    {
+        $d = array();
+        $memberData = $this->getMemberData($_GET['id']);
+        $d['account_number']  = $memberData['account_number'];
+        $d['billing_contact'] = $memberData['billing_contact'];
+        if ($this->primaryAccountId) {
+            $account1 = Toolkit_Members_Billing_Factory::createDbObjectById(
+                $this->dbh,
+                'Toolkit_Members_Billing_MemberAccount',
+                $this->primaryAccountId
+            );
+            
+            $d['account1_id']     = $account1->getId();
+            $d['payment_type1']   = $account1->getPayment_type();
+            $email_invoice
+                = ($account1->getEmail_invoice())
+                ? 'email_invoice'
+                : null;
+            $usmail_invoice
+                = ($account1->getUsmail_invoice())
+                ? 'usmail_invoice'
+                : null;
+            $fax_invoice
+                = ($account1->getFax_invoice())
+                ? 'fax_invoice'
+                : null;
+            $d['invoice_method1'] = array(
+                'email_invoice'  => $email_invoice,
+                'usmail_invoice' => $usmail_invoice,
+                'fax_invoice'    => $fax_invoice
+            );
+        }
+
+        if ($this->secondaryAccountId) {
+            $account2 = Toolkit_Members_Billing_Factory::createDbObjectById(
+                $this->dbh,
+                'Toolkit_Members_Billing_MemberAccount',
+                $this->secondaryAccountId
+            );
+            $d['account2_id']     = $account2->getId();
+            $d['payment_type2']   = $account2->getPayment_type();
+            $email_invoice
+                = ($account2->getEmail_invoice())
+                ? 'email_invoice'
+                : null;
+            $usmail_invoice
+                = ($account2->getUsmail_invoice())
+                ? 'usmail_invoice'
+                : null;
+            $fax_invoice
+                = ($account2->getFax_invoice())
+                ? 'fax_invoice'
+                : null;
+            $d['invoice_method2'] = array(
+                'email_invoice'  => $email_invoice,
+                'usmail_invoice' => $usmail_invoice,
+                'fax_invoice'    => $fax_invoice
+            );
+        }
+        $d['member_id'] = $_GET['id'];
+        $this->setupDefaults($d);
+    }
+
+    /**
+     * Setup the elements to use on the form.
+     *
+     * @return void
+     * @access public
+     */
+    public function configureElements()
+    {
+        $e          = array();
+        //    All Grouped Elements are created here.
+        $paymentTypes   = $this->getPaymentTypes();
+
+        //    All Elements are created here.
+        //    This includes group element definitions.
+        $e[] = array(
+            'type'    => 'header',
+            'req'     => false,
+            'name'    => 'infoHdr',
+            'display' => 'Account Information'
+        );
+        $e[] = array(
+            'type' => 'hidden',
+            'req'  => false,
+            'name' => 'member_id'
+        );
+        $e[] = array(
+            'type' => 'hidden',
+            'req'  => false,
+            'name' => 'account1_id'
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => true,
+            'name'    => 'account_number',
+            'display' => 'Account Number',
+            'opts'    => array('class' => 'text')
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => 'billing_contact',
+            'display' => 'Billing Contact',
+            'opts'    => array('class' => 'text')
+        );
+        $e[] = array(
+            'type'    => 'header',
+            'req'     => false,
+            'name'    => 'infoHdr',
+            'display' => 'Primary Billing information'
+        );
+        $e[] = array(
+            'type'    => 'select',
+            'req'     => true,
+            'name'    => 'payment_type1',
+            'display' => 'Payment Type',
+            'opts'    => array('' => '') + $paymentTypes
+        );
+        $e[] = array(
+            'type'    => 'select',
+            'req'     => true,
+            'name'    => 'invoice_method1',
+            'display' => 'Invoice Delivery Methods',
+            'opts'    => array(
+                'email_invoice'  => 'By Email',
+                'usmail_invoice' => 'By US Mail',
+                'fax_invoice'    => 'By Fax'
+            ),
+            'att'     => array('multiple' => 'multiple')
+        );
+        $e[] = array(
+            'type'    => 'header',
+            'req'     => false,
+            'name'    => 'infoHdr',
+            'display' => 'Secondary Billing information'
+        );
+        $e[] = array(
+            'type' => 'hidden',
+            'req'  => false,
+            'name' => 'account2_id'
+        );
+        $e[] = array(
+            'type'    => 'select',
+            'req'     => false,
+            'name'    => 'payment_type2',
+            'display' => 'Payment Type',
+            'opts'    => array('' => '') + $paymentTypes
+        );
+        $e[] = array(
+            'type'    => 'select',
+            'req'     => false,
+            'name'    => 'invoice_method2',
+            'display' => 'Invoice Delivery Methods',
+            'opts'    => array(
+                'email_invoice'  => 'By Email',
+                'usmail_invoice' => 'By US Mail',
+                'fax_invoice'    => 'By Fax'
+            ),
+            'att'     => array('multiple' => 'multiple')
+        );
+    
+        $this->setupElements($e);
+    }
+
+    /**
+     * Wrapper function to handle setting up the form
+     *
+     * @return void
+     * @access public
+     */
+    public function configureForm()
+    {
+        $this->configureElements();
+        $this->configureRules();
+        $this->configureDefaults();
+    }
+
+    /**
+     * Sets up all the rules to be used when the form is validated.
+     *
+     * @return void
+     * @access public
+     */
+    public function configureRules()
+    {
+        //  form rules
+        $r = array();
+        $this->registeredRules = array();
+        $r[] = array(
+            'element' => 'amount',
+            'message' => 'ERROR: Must be a number!',
+            'type' => 'numeric',
+            'format' => null,
+            'validation' => $this->validationType,
+            'reset' => false,
+            'force' => false
+        );
+        $r[] = array(
+            'element'    => 'transaction_date',
+            'message'    => 'ERROR: Invalid date!',
+            'type'       => 'callback',
+            'format'     => array(&$this, 'checkDate'),
+            'validation' => $this->validationType,
+            'reset'      => false,
+            'force'      => false
+        );
+        $r[] = array(
+            'element'    => 'account_number',
+            'message'    => 'ERROR: Sorry, but this Account Number has already been used for a different Member!',
+            'type'       => 'callback',
+            'format'     => array(&$this, 'checkAccountNumber'),
+            'validation' => $this->validationType,
+            'reset'      => false,
+            'force'      => false
+        );
+        $this->setupRules($r);
+    }
+
+    /**
+     * Get the billing types from the config object
+     *
+     * @return array
+     * @access protected
+     */
+    protected function getBillingTypes()
+    {
+        return $this->config2
+            ->getItem('section', 'conf')
+            ->getItem('directive', 'billingType')
+            ->getContent();
+    }
+
+    /**
+     * Description of setJscalScripts
+     * 
+     * @return void
+     * @access public 
+     */
+    public function setJscalScripts()
+    {
+        $this->jsCalScripts = '
+<script type="text/javascript" src="http://app.gaslightmedia.com/libjs/Jscal/utils.js"></script>
+<script type="text/javascript" src="http://app.gaslightmedia.com/libjs/Jscal/calendar.js"></script>
+<script type="text/javascript" src="http://app.gaslightmedia.com/libjs/Jscal/calendar-en.js"></script>
+<script type="text/javascript" src="http://app.gaslightmedia.com/libjs/Jscal/calendar-setup.js"></script>
+';
+    }
+
+    /**
+     * Description of setPrimaryAccountId()
+     * 
+     * @param int $id Primary account ID
+     * 
+     * @return void
+     * @access public
+     * @throws InvalidArgumentException 
+     */
+    public function setPrimaryAccountId($id)
+    {
+        if (!is_numeric($id)) {
+            throw new InvalidArgumentException('Id must be an integer');
+        }
+        $this->primaryAccountId = $id;
+    }
+    
+    /**
+     * Description of setSecondaryAccountId()
+     * 
+     * @param int $id Secondary Account ID
+     * 
+     * @return void
+     * @access public
+     */
+    public function setSecondaryAccountId($id)
+    {
+        if (!is_numeric($id)) {
+            $this->secondaryAccountId = null;
+        }
+        $this->secondaryAccountId = $id;
+    }
+    // {{{ toHTML()
+
+       /**
+        * Renders the form
+        *
+        * sets the page the form should be redirected to instead of coming back
+        * around to itself.
+        *
+        * @return      string  The rendered form
+        * @access      public
+        */
+       public function toHTML()
+       {
+        if ($_REQUEST['delete']) {
+            $this->deleteBillingInfo();
+            exit;
+        }
+               //      We need to validate (and freeze if needed)
+               //      before we render the form. That way the
+               //      template knows about any errors on the form.
+               $this->validated = $this->validate();
+               if ($this->validated) {
+                       $processed = $this->process(array(&$this, 'processData'), $this->mergeFiles);
+               }
+
+               //      ProcessData handles settingup the lat/lon coordinates if they were not entered
+               //      into the form.  these values ar calculated and then inserted into the forms
+               //      element values.  So we need to process the data first and then render the form.
+               $this->setupRenderers();
+
+               return $this->template->bufferedOutputObject($this->view);
+       }
+
+       //      }}}
+    /**
+     * Delete the member accounts the member account_number and any billing data 
+     * related to the member
+     * 
+     * @return void
+     * @access protected
+     */
+    protected function deleteBillingInfo()
+    {
+        try {
+            // delete the member_accounts
+            $sql = "
+            DELETE FROM member_account
+             WHERE member_id = :member_id";
+            $stmt = $this->dbh->prepare($sql);
+            $stmt->bindParam(
+                ':member_id',
+                $_GET['id'],
+                PDO::PARAM_INT
+            );
+            $stmt->execute();
+            
+            // empty the account_number in member record
+            $sql = "
+            UPDATE member
+               SET account_number = null
+             WHERE member_id = :member_id";
+            $stmt = $this->dbh->prepare($sql);
+            $stmt->bindParam(
+                ':member_id',
+                $_GET['id'],
+                PDO::PARAM_INT
+            );
+            $stmt->execute();
+            
+            // delete any billing data for this member
+            $sql = "
+            DELETE FROM billing
+             WHERE member_id = :member_id";
+            $stmt = $this->dbh->prepare($sql);
+            $stmt->bindParam(
+                ':member_id',
+                $_GET['id'],
+                PDO::PARAM_INT
+            );
+            $stmt->execute();
+        } catch(PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+        // return page to jump to
+        $listPage = MEDIA_BASE_URL .
+            "admin/members.php?cat=2&subCat=2&tab=7&id={$_GET['id']}&formSubmitGood=1";
+        header('Location: '.$listPage);
+    }
+    /**
+     * From the member_id get the member name
+     *
+     * @param integer $id Member_id field
+     *
+     * @return string
+     * @access protected
+     */
+    protected function getMemberData($id)
+    {
+        try {
+            $sql = "
+            SELECT *
+              FROM member
+             WHERE member_id = :member_id";
+            $stmt = $this->dbh->prepare($sql);
+            $stmt->bindParam(
+                ':member_id',
+                $id,
+                PDO::PARAM_INT
+            );
+            $stmt->execute();
+            return $stmt->fetch(PDO::FETCH_ASSOC);
+        } catch(PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+    }
+
+    /**
+     * grab from the config object the payment methods
+     * take out for this client the credit card one they only do cash or check
+     *
+     * @return array
+     * @access protected
+     */
+    protected function getPaymentMethods()
+    {
+        $types = $this->config2
+            ->getItem('section', 'conf')
+            ->getItem('directive', 'paymentMethod')
+            ->getContent();
+        unset($types[2]);
+        return $types;
+    }
+
+    /**
+     * Get a prorated price for this memebr based on the date field from
+     * the form (billing) submit
+     *
+     * @param array $values Form values
+     *
+     * @return float
+     * @access protected
+     * @throws Exception
+     */
+    protected function getProRatedPrice($values)
+    {
+        $invoice         = new Toolkit_Members_Billing_Invoices();
+        $nextInvoiceDate = $invoice->getNextInvoiceDate();
+        $invDate = new Date(
+            date('c', $nextInvoiceDate)
+        );
+        $paymentType = Toolkit_Members_Billing_Factory::createDbObjectById(
+            $this->dbh,
+            'Toolkit_Members_Billing_PaymentTypes',
+            $values['payment_type']
+        );
+        if (!($paymentType instanceof Toolkit_Members_Billing_PaymentTypes)) {
+            throw new Exception(
+                'Payment Type is not an instance of
+                Toolkit_Members_Billing_PaymentTypes'
+            );
+        }
+        $transactionDate = new Date(
+            date('c', strtotime($values['transaction_date']))
+        );
+        $cDate = Date::compare($invDate, $transactionDate);
+        if ($cDate == 0) {
+            return (float)$paymentType->getAmount();
+        } else if ($cDate == -1) {
+            // pro-rated for next year
+            // increase the invoice date by one year
+            $timestamp = $invDate->getTime();
+            $nextYear  = strtotime('+ 1 year', $timestamp);
+            if ($nextYear) {
+                $invDate = new Date($nextYear);
+            }
+        }
+        $span = new Date_Span();
+        $span->setFromDateDiff($transactionDate, $invDate);
+        $days = round($span->toDays());  
+        $dailyPrice = (float)($paymentType->getAmount() / 365);
+        return (float)round(($days * $dailyPrice), 2);
+    }
+
+    /**
+     * build the $this->memberStatements string with the rows of billing table
+     * records and a line with balance due at end
+     *
+     * @return void
+     * @access protected
+     */
+    protected function getStatements()
+    {
+        $statement = new Toolkit_Members_Billing_Statement();
+        $this->memberStatements = $statement->createMemberStatements(
+            $this->dbh,
+            $_REQUEST['id']
+        );
+    }
+
+    /**
+     * Get all payment types for a select list
+     *
+     * @return array
+     * @access protected
+     */
+    protected function getPaymentTypes()
+    {
+        $paymentTypes = array();
+        try {
+            $sql = "
+               SELECT *
+                FROM payment_types
+            ORDER BY name";
+            $stmt = $this->dbh->prepare($sql);
+            $stmt->execute();
+            while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                $paymentTypes[$row['id']] = $row['name'] . ' ($'.$row['amount'].')';
+            }
+            return $paymentTypes;
+        } catch (PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+    }
+
+    /**
+     * Short description for function
+     * 
+     * Long description (if any) ...
+     * 
+     * @param unknown $sec Parameter description (if any) ...
+     *
+     * @return unknown Return description (if any) ...
+     * @access public 
+     */
+    public function isForm($sec)
+    {
+        return !$sec;
+    }
+
+    /**
+     * Short description for processData()
+     * 
+     * Long description (if any) ...
+     * 
+     * @param array $values Parameter description (if any) ...
+     *
+     * @return boolean     
+     * @access protected
+     */
+    public function processData($values)
+    {
+        if (   $values['account_number'] && $values['member_id']) {
+            $sql = "
+            UPDATE member
+               SET account_number = :account_number,
+                   billing_contact = :billing_contact
+             WHERE member_id = :member_id"; 
+            try {
+                $stmt = $this->dbh->prepare($sql);
+                $stmt->bindParam(
+                    ':account_number',
+                    $values['account_number'],
+                    PDO::PARAM_INT
+                );
+                $stmt->bindParam(
+                    ':billing_contact',
+                    $values['billing_contact'],
+                    PDO::PARAM_INT
+                );
+                $stmt->bindParam(
+                    ':member_id',
+                    $values['member_id'],
+                    PDO::PARAM_INT
+                );
+                $stmt->execute();
+            } catch(PDOException $e) {
+                Toolkit_Common::handleError($e);
+            }
+        }
+        if (is_numeric($values['account1_id'])) {
+            $account = Toolkit_Members_Billing_Factory::createDbObjectById(
+                $this->dbh,
+                'Toolkit_Members_Billing_MemberAccount',
+                $values['account1_id']
+            );
+            $account->setPayment_type($values['payment_type1'])
+                ->setEmailInvoice(in_array('email_invoice', $values['invoice_method1']))
+                ->setUsmailInvoice(in_array('usmail_invoice', $values['invoice_method1']))
+                ->setFaxInvoice(in_array('fax_invoice', $values['invoice_method1']))
+                ->save($this->dbh);
+        } else {
+            $account1Values = array(
+                'member_id' => $values['member_id'],
+                'payment_type' => $values['payment_type1'],
+                'email_invoice' => in_array('email_invoice', $values['invoice_method1']),
+                'usmail_invoice' => in_array('usmail_invoice', $values['invoice_method1']),
+                'fax_invoice' => in_array('fax_invoice', $values['invoice_method1'])
+            );
+            $account = Toolkit_Members_Billing_Factory::createDBObjectByValues(
+                'Toolkit_Members_Billing_MemberAccount',
+                $account1Values
+            )->save($this->dbh);
+        }
+        if (is_numeric($values['account2_id'])) {
+            $account2 = Toolkit_Members_Billing_Factory::createDbObjectById(
+                $this->dbh,
+                'Toolkit_Members_Billing_MemberAccount',
+                $values['account2_id']
+            );
+            if (   $values['payment_type2']
+                && !empty($values['invoice_method2'])
+            ) {
+                $account2->setPayment_type($values['payment_type2'])
+                ->setEmailInvoice(in_array('email_invoice', $values['invoice_method2']))
+                ->setUsmailInvoice(in_array('usmail_invoice', $values['invoice_method2']))
+                ->setFaxInvoice(in_array('fax_invoice', $values['invoice_method2']))
+                ->save($this->dbh);
+            } else {
+                // delete record if there's no data
+                $account2->delete($this->dbh);
+            }
+        } else {
+            if (   $values['payment_type2']
+                && !empty($values['invoice_method2'])
+            ) {
+                $account2Values = array(
+                    'member_id' => $values['member_id'],
+                    'payment_type' => $values['payment_type2'],
+                    'email_invoice' => in_array('email_invoice', $values['invoice_method2']),
+                    'usmail_invoice' => in_array('usmail_invoice', $values['invoice_method2']),
+                    'fax_invoice' => in_array('fax_invoice', $values['invoice_method2'])
+                );
+                $account2 = Toolkit_Members_Billing_Factory::createDBObjectByValues(
+                    'Toolkit_Members_Billing_MemberAccount',
+                    $account2Values
+                )->save($this->dbh);
+            }
+        }
+        // return page to jump to
+        $listPage = MEDIA_BASE_URL .
+            "admin/members.php?rt=Members&ac=editMember&tab=billingInfo&id={$_GET['id']}&formSubmitGood=1";
+        header('Location: '.$listPage);
+        return true;
+    }
+}
diff --git a/Toolkit/Members/Billing/EditMemberPayment.php b/Toolkit/Members/Billing/EditMemberPayment.php
new file mode 100644 (file)
index 0000000..dfc1bf1
--- /dev/null
@@ -0,0 +1,918 @@
+<?php
+
+/**
+ * EditMemberBilling.php
+ *
+ * PHP version 5
+ *
+ * @category MembersDB
+ * @package  Toolkit_Members
+ * @author   Steve Sutton <steve@gaslightmedia.com>
+ * @license  http://www.gaslightmedia.com Gaslightmedia
+ * @version  CVS: $Id: EditMemberPayment.php,v 1.2 2011/04/01 18:08:11 matrix Exp $
+ * @link     http://demo.gaslightmedia.com
+ */
+
+/**
+ * Handle associating contacts along with member records
+ *
+ * Controls all aspects of creating and rendering the form used to manipulate
+ * the billing. Form is not rendered until the user is added into
+ * the Database.
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2008 Gaslight Media
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @link      http://demo.gaslightmedia.com
+ * @see       Toolkit_Members
+ */
+class Toolkit_Members_Billing_EditMemberPayment
+    extends Toolkit_Members_EditMemberInfo
+    implements Toolkit_Form
+{
+    
+    /**
+     * The table name in the database used to store the data of the files
+     *
+     * @var string
+     * @access public
+     */
+    public $tableName = 'member';
+
+    /**
+     * The template used to render the form
+     *
+     * @var string
+     * @access protected
+     */
+    protected $formTemplate = 'editBilling.tpl';
+
+    /**
+     * Message to return if the form successfully submits
+     *
+     * @var string
+     * @access protected
+     */
+    protected $successMsg = '
+        <div id="form-success-top">
+            You successfully updated your Billing Info.
+        </div>';
+
+    /**
+     * Description of $memberStatements
+     * 
+     * @var string
+     * @access public 
+     */
+    public $memberStatements = '';
+
+    /**
+     * Class constructor
+     *
+     * @param PDO    $pdo         PHP Data Object to use for DB calls
+     * @param string $formName    Form's name.
+     * @param string $method      (optional)Form's method defaults to 'POST'
+     * @param string $action      (optional)Form's action
+     * @param string $target      (optional)Form's target defaults to '_self'
+     * @param mixed  $attributes  (optional)Extra attributes for <form> tag
+     * @param bool   $trackSubmit (optional)Whether to track if the form was
+     *                              submitted by adding a special hidden field
+     *                            
+     * @access public
+     * @see    Toolkit_Members_EditMemberInfo
+     */
+    public function __construct(
+        PDO $pdo,
+        $formName,
+        $method = 'post',
+        $action = '',
+        $target = '',
+        $attributes = null,
+        $trackSubmit = false
+    ) {
+        parent::__construct(
+            $pdo,
+            $formName,
+            $method,
+            $action,
+            $target,
+            $attributes,
+            $trackSubmit
+        );
+        // Member config
+        $config = new Config();
+        $root = $config->parseConfig(
+            BASE . 'Toolkit/Members/config.ini',
+            'IniFile'
+        );
+        $this->config = $root;
+        // Billing config
+        $config2 = new Config();
+        $root2 = $config2->parseConfig(
+            BASE . 'Toolkit/Members/Billing/config.ini',
+            'IniFile'
+        );
+        $this->config2 = $root2;
+    }
+
+    /**
+     * Description of addAdjustment()
+     * 
+     * @param array $values Values array
+     * 
+     * @return void
+     * @access protected
+     */
+    protected function addAdjustment($values)
+    {
+        $statement = new Toolkit_Members_Billing_Statement();
+        $balanceDue = (float)$statement->getBalanceDue(
+            $this->dbh,
+            $_GET['id'],
+            $values['account_id']
+        );
+        $balance = (float)($balanceDue + (float)$values['amount']);
+        $paid
+            = ($balance == (float)'0.00')
+            ? true
+            : false;
+        // need to get the original invoice id
+        $originalInvoice = $statement->getOriginalInvoice(
+            $this->dbh,
+            $_GET['id'],
+            $values['account_id']
+        );
+        $invoiceId = $originalInvoice->getInvoice_id();
+        if ($values['amount']) {
+            $invoice = Toolkit_Members_Billing_Factory::createDBObjectByValues(
+                'Toolkit_Members_Billing_Billing',
+                array(
+                    'member_name'      => $values['member_name'],
+                    'invoice_id'       => $invoiceId,
+                    'member_id'        => $_GET['id'],
+                    'transaction_date' => date('m/d/Y'),
+                    'transaction_time' => date('m/d/Y H:i:s'),
+                    'account_number'   => $values['account_number'],
+                    'account_id'       => $values['account_id'],
+                    'billing_type'     => 3,
+                    'emailed'          => 0,
+                    'printed'          => 0,
+                    'amount'           => (float)$values['amount'],
+                    'balance'          => $balance,
+                    'paid'             => $paid,
+                    'payment_method'   => $values['payment_method'],
+                    'payment_data'     => $values['payment_data']
+                )
+            )->save($this->dbh);
+        }
+    }
+
+    /**
+     * Description of addComment()
+     * 
+     * @param array $values Values array
+     * 
+     * @return void
+     * @access protected
+     */
+    protected function addComment($values)
+    {
+        if ($values['notes']) {
+            $statement = new Toolkit_Members_Billing_Statement();
+            $balanceDue = (float)$statement->getBalanceDue(
+                $this->dbh,
+                $_GET['id'],
+                $values['account_id']
+            );
+            $invoice = Toolkit_Members_Billing_Factory::createDBObjectByValues(
+                'Toolkit_Members_Billing_Billing',
+                array(
+                    'member_name'      => $this->memberData[0]['member_name'],
+                    'invoice_id'       => 0,
+                    'member_id'        => $_GET['id'],
+                    'transaction_date' => date('m/d/Y'),
+                    'transaction_time' => date('m/d/Y H:i:s'),
+                    'account_id'       => $values['account_id'],
+                    'account_number'   => $this->memberData[0]['account_number'],
+                    'billing_type'     => 4,
+                    'emailed'          => 0,
+                    'printed'          => 0,
+                    'notes'            => $values['notes'],
+                    'amount'           => '0.00',
+                    'balance'          => (float)$balanceDue,
+                )
+            )->save($this->dbh);
+        }
+    }
+
+    /**
+     * Description of addInvoice()
+     * 
+     * @param array   $mData          Member Data
+     * @param date    $date           Invoice Date
+     * @param boolean $dynamic_amount The dynamic amount to use
+     * 
+     * @return void
+     * @access public
+     */
+    protected function addInvoice(
+        $mData,
+        $date,
+        $dynamic_amount
+    ) {
+        if ($date) {
+            $date = str_replace('-', '/', $date);
+            $date = str_replace('.', '/', $date);
+            $amount = $this->getProRatedPrice(
+                $mData,
+                $date,
+                $dynamic_amount
+            );
+            $transactionDate = date(
+                'm/d/Y',
+                strtotime($date)
+            );
+            $invoice = Toolkit_Members_Billing_Factory::createDBObjectByValues(
+                'Toolkit_Members_Billing_Billing',
+                array(
+                    'member_name'      => $mData['member_name'],
+                    'invoice_id'       => null,
+                    'member_id'        => $mData['member_id'],
+                    'transaction_date' => $transactionDate,
+                    'transaction_time' => date('m/d/Y H:i:s'),
+                    'account_id'       => $mData['account_id'],
+                    'account_number'   => $mData['account_number'],
+                    'billing_type'     => 1,
+                    'amount'           => (float)$amount,
+                    'emailed'          => 0,
+                    'printed'          => 0,
+                    'notes'            => '',
+                    'paid'             => false,
+                    'balance'          => (float)($balanceDue + (float)$amount)
+                )
+            );
+            $invoice->save($this->dbh);
+            
+            //$invoicesobj = new Toolkit_Members_Billing_Invoices();
+            //$pdf = new Toolkit_Members_Billing_InvoicePdf();
+            //$pdfInvoice = $pdf->createPdfInvoice(
+            //    $this->dbh,
+            //   $mData['member_id'],
+            //    $invoice->getInvoice_id(),
+            //    $mData['account_id']
+            //);
+            //
+            //$invoice->setInvoice(base64_encode($pdfInvoice));
+            //$invoice->save($this->dbh);
+        }
+    }
+
+    /**
+     * Description of addPayment()
+     * 
+     * @param array $values Values array
+     * 
+     * @return void
+     * @access protected
+     */
+    protected function addPayment($values)
+    {
+        $statement = new Toolkit_Members_Billing_Statement();
+        $balanceDue = (float)$statement->getBalanceDue(
+            $this->dbh,
+            $_GET['id'],
+            $values['account_id']
+        );
+        if ($values['amount']) {
+            $balanceDue = (float)($balanceDue - (float)$values['amount']);
+            $paid
+                = ($balanceDue == (float)'0.00')
+                ? true
+                : false;
+            // need to get the original invoice id
+            $originalInvoice = $statement->getOriginalInvoice(
+                $this->dbh,
+                $_GET['id'],
+                $values['account_id']
+            );
+            $invoiceId = $originalInvoice->getInvoice_id();
+            if ($paid) {
+                $originalInvoice
+                    ->setPaid(true)
+                    ->save($this->dbh);
+                
+            }
+            $invoice = Toolkit_Members_Billing_Factory::createDBObjectByValues(
+                'Toolkit_Members_Billing_Billing',
+                array(
+                    'member_name'      => $values['member_name'],
+                    'invoice_id'       => $invoiceId,
+                    'member_id'        => $_GET['id'],
+                    'transaction_date' => date('m/d/Y'),
+                    'transaction_time' => date('m/d/Y H:i:s'),
+                    'account_number'   => $values['account_number'],
+                    'account_id'       => $values['account_id'],
+                    'billing_type'     => 2,
+                    'emailed'          => 0,
+                    'printed'          => 0,
+                    'amount'           => (float)$values['amount'],
+                    'balance'          => $balanceDue,
+                    'paid'             => $paid,
+                    'payment_method'   => $values['payment_method'],
+                    'payment_data'     => $values['payment_data']
+                )
+            )->save($this->dbh);
+        }
+    }
+
+    /**
+     * Validate date input
+     * 
+     * allows for empty dates to be valid
+     *
+     * @param array $date date group from form
+     *
+     * @return boolean true if valid, false if not
+     * @access public
+     */
+    public function checkDate($date)
+    {
+        return strtotime($date);
+    }
+    
+    /**
+     * Description of checkForReqAmount()
+     * 
+     * @param array $values Values array
+     * 
+     * @return boolean 
+     * @access public
+     */
+    public function checkForReqAmount($values)
+    {
+        $member_account_id = $values[0];
+        $dueAmount         = $values[1];
+        $memberAccount
+            = Toolkit_Members_Billing_Factory::createDbObjectById(
+                $this->dbh,
+                'Toolkit_Members_Billing_MemberAccount',
+                $member_account_id
+            );
+        $paymentType
+            = Toolkit_Members_Billing_Factory::createDbObjectById(
+                $this->dbh,
+                'Toolkit_Members_Billing_PaymentTypes',
+                $memberAccount->getPayment_type()
+            );
+        if (   $paymentType->getDynamic_amount() == true
+            && !is_numeric($dueAmount)
+        ) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Sets the defaults for elements in the form.
+     *
+     * @return void
+     * @access public
+     */
+    public function configureDefaults()
+    {
+        $d = array();
+        $this->setupDefaults($d);
+    }
+
+    /**
+     * Setup the elements to use on the form.
+     *
+     * @return void
+     * @access public
+     */
+    public function configureElements()
+    {
+        $paymentForm = array();
+        $invoiceForm = array();
+        // set element array
+        $e = array();
+        // set invoice object
+        $invoice    = new Toolkit_Members_Billing_Invoices();
+        $memberData = $this->getMemberData($_REQUEST['id']);
+        //    All Grouped Elements are created here.
+        $paymentMethods = $this->getPaymentMethods();
+        $paymentMethods['5'] = 'Adjustment';
+        if (!empty($memberData)) {
+            foreach ($memberData as $mData) {
+                $hasInvoice = $invoice->hasInvoice($this->dbh, $mData);
+                if ($hasInvoice) {
+                    $paymentForm[] = $mData;
+                } else {
+                    $invoiceForm[] = $mData;
+                }
+            }
+            
+            if (!empty($invoiceForm)) {
+                $this->setJscalScripts();
+                $invAccounts = array('' => '-- Select --');
+                foreach ($invoiceForm as $memberInv) {
+                    $memberAccount
+                        = Toolkit_Members_Billing_Factory::createDbObjectById(
+                            $this->dbh,
+                            'Toolkit_Members_Billing_MemberAccount',
+                            $memberInv['account_id']
+                        );
+                    $payment_type
+                        = Toolkit_Members_Billing_Factory::createDbObjectById(
+                            $this->dbh,
+                            'Toolkit_Members_Billing_PaymentTypes',
+                            $memberAccount->getPayment_type()
+                        );
+                    if (   $payment_type->getDynamic_amount() == false
+                        && (int)$payment_type->getAmount() > 0
+                    ) {
+                        $invAccounts[$memberAccount->getId()] = $payment_type->getName();
+                    } else if ($payment_type->getDynamic_amount() == true) {
+                        $invAccounts[$memberAccount->getId()] = $payment_type->getName();     
+                    }
+                    
+                }
+                if (count($invAccounts) > 1) {
+                    $e[] = array(
+                        'type'    => 'header',
+                        'req'     => false,
+                        'name'    => 'invoiceHdr',
+                        'display' => 'Create Invoice (Pro Rated to Invoice Date)'
+                    );
+                    $e[] = array(
+                        'type'    => 'select',
+                        'req'     => false,
+                        'name'    => 'inv_account_id',
+                        'display' => 'Payment Type',
+                        'opts'    => $invAccounts,
+                        'att'     => array('id' => 'inv_account_id')
+                    );
+                    $e[] = array(
+                        'type'    => 'text',
+                        'req'     => (($accountNumber == 1)?true:false),
+                        'name'    => 'transaction_date',
+                        'display' => 'Invoice Date',
+                        'opts'    => array(
+                            'size'=>'10',
+                            'id' => 'transDate')
+                    );
+                    $e[] = array(
+                    'type'    => 'text',
+                    'req'     => false,
+                    'name'    => 'dynamic_amount',
+                    'display' => 'Dynamic Amount Only',
+                    'opts'    => array('size' => 6)
+                    );
+                }
+            }
+            if (!empty($paymentForm)) {
+                $paymentAccounts = array();
+                foreach ($paymentForm as $memberInv) {
+                    $memberAccount
+                        = Toolkit_Members_Billing_Factory::createDbObjectById(
+                            $this->dbh,
+                            'Toolkit_Members_Billing_MemberAccount',
+                            $memberInv['account_id']
+                        );
+                    $payment_type
+                        = Toolkit_Members_Billing_Factory::createDbObjectById(
+                            $this->dbh,
+                            'Toolkit_Members_Billing_PaymentTypes',
+                            $memberAccount->getPayment_type()
+                        );
+                    $paymentAccounts[$memberAccount->getId()] = $payment_type->getName();
+                }
+                $e[] = array(
+                    'type'    => 'header',
+                    'req'     => false,
+                    'name'    => 'paymentHdr',
+                    'display' => 'Make A Payment'
+                );
+                $e[] = array(
+                    'type'    => 'select',
+                    'req'     => false,
+                    'name'    => 'account_id',
+                    'display' => 'Payment Type',
+                    'opts'    => $paymentAccounts
+                );
+                $e[] = array(
+                    'type'    => 'text',
+                    'req'     => false,
+                    'name'    => 'amount',
+                    'display' => 'Amount',
+                    'opts'    => array('size' => 6)
+                );
+                $e[] = array(
+                    'type'    => 'select',
+                    'req'     => false,
+                    'name'    => 'payment_method',
+                    'display' => 'Payment Method',
+                    'opts'    => $paymentMethods
+                );
+                $e[] = array(
+                    'type'    => 'text',
+                    'req'     => false,
+                    'name'    => 'payment_data',
+                    'display' => 'Payment Details',
+                    'opts'    => array('class' => 'text')
+                );
+
+                $e[] = array(
+                    'type'    => 'header',
+                    'req'     => false,
+                    'name'    => 'paymentHdr',
+                    'display' => 'Add Comment'
+                );
+                $e[] = array(
+                    'type'    => 'textarea',
+                    'req'     => false,
+                    'name'    => 'notes',
+                    'display' => 'Comment',
+                    'opts'    => array('class' => 'text')
+                );
+            }
+        }
+        //    All Elements are created here.
+        //    This includes group element definitions.
+        $this->setupElements($e);
+    }
+
+    /**
+     * Wrapper function to handle setting up the form
+     *
+     * @return void
+     * @access public
+     */
+    public function configureForm()
+    {
+        $this->configureElements();
+        $this->configureRules();
+        $this->configureDefaults();
+        $this->getStatements();
+    }
+
+    /**
+     * Sets up all the rules to be used when the form is validated.
+     *
+     * @return void
+     * @access public
+     */
+    public function configureRules()
+    {
+        //  form rules
+        $r = array();
+        $this->registeredRules = array();
+        $r[] = array(
+            'element' => 'dynamic_amount',
+            'message' => 'ERROR: Must be a number!',
+            'type' => 'numeric',
+            'format' => null,
+            'validation' => $this->validationType,
+            'reset' => false,
+            'force' => false
+        );
+        $r[] = array(
+            'element' => 'amount',
+            'message' => 'ERROR: Must be a number!',
+            'type' => 'numeric',
+            'format' => null,
+            'validation' => $this->validationType,
+            'reset' => false,
+            'force' => false
+        );
+        $r[] = array(
+            'element'    => 'transaction_date',
+            'message'    => 'ERROR: Invalid date!',
+            'type'       => 'callback',
+            'format'     => array(&$this, 'checkDate'),
+            'validation' => $this->validationType,
+            'reset'      => false,
+            'force'      => false
+        );
+        $r[] = array(
+            'element'    => array('inv_account_id', 'dynamic_amount'),
+            'message'    => 'ERROR: Amount Required for This Payment Type',
+            'type'       => 'callback',
+            'format'     => array(&$this, 'checkForReqAmount'),
+            'validation' => $this->validationType,
+            'reset'      => false,
+            'force'      => false
+        );
+        $this->setupRules($r);
+    }
+
+    /**
+     * create a transaction type from billing form submittion
+     * can be either payment,invoice or comment
+     * each is handled seperately
+     *
+     * @param array $values Form values
+     *
+     * @return void
+     * @access protected
+     */
+    protected function createTransactionRecord($values)
+    {
+        //var_dump($values);
+        //exit;
+        $statement = new Toolkit_Members_Billing_Statement();
+        $balanceDue = (float)$statement->getBalanceDue(
+            $this->dbh,
+            $_GET['id']
+        );
+        
+        var_dump($values);
+        var_dump($balanceDue);
+        exit;
+        if ($values['account_number'] && $values['id']) {         
+            // comment
+            
+        }
+    }
+
+    /**
+     * Get the billing types from the config object
+     *
+     * @return array
+     * @access protected
+     */
+    protected function getBillingTypes()
+    {
+        return $this->config2
+            ->getItem('section', 'billing')
+            ->getItem('directive', 'billingType')
+            ->getContent();
+    }
+
+    /**
+     * Descriptinon for setJscalScripts()
+     * 
+     * @return void
+     * @access public 
+     */
+    public function setJscalScripts()
+    {
+        $this->jsCalScripts = '
+<script type="text/javascript" src="http://app.gaslightmedia.com/libjs/Jscal/utils.js"></script>
+<script type="text/javascript" src="http://app.gaslightmedia.com/libjs/Jscal/calendar.js"></script>
+<script type="text/javascript" src="http://app.gaslightmedia.com/libjs/Jscal/calendar-en.js"></script>
+<script type="text/javascript" src="http://app.gaslightmedia.com/libjs/Jscal/calendar-setup.js"></script>
+';
+    }
+    
+    /**
+     * From the member_id get the member name
+     *
+     * @param integer $id Member_id field
+     *
+     * @return string
+     * @access protected
+     */
+    protected function getMemberData($id)
+    {
+        if (!$this->memberData) {
+            try {
+                $sql = "
+                SELECT m.member_id,m.member_name,m.account_number,
+                       ma.id as account_id,ma.payment_type
+                  FROM member m, member_account ma
+                 WHERE m.active = 't'
+                   AND m.member_id = ma.member_id
+                   AND m.member_id = :member_id";
+                $stmt = $this->dbh->prepare($sql);
+                $stmt->bindParam(
+                    ':member_id',
+                    $id,
+                    PDO::PARAM_INT
+                );
+                $stmt->execute();
+                $memberData = $stmt->fetchAll(PDO::FETCH_ASSOC);
+            } catch(PDOException $e) {
+                Toolkit_Common::handleError($e);
+            }
+            $this->memberData = $memberData;
+        }
+        return $this->memberData;
+    }
+
+    /**
+     * grab from the config object the payment methods
+     * take out for this client the credit card one they only do cash or check
+     *
+     * @return array
+     * @access protected
+     */
+    protected function getPaymentMethods()
+    {
+        $types = $this->config2
+            ->getItem('section', 'conf')
+            ->getItem('directive', 'paymentMethod')
+            ->getContent();
+        unset($types[2]);
+        return $types;
+    }
+
+    /**
+     * Get a prorated price for this memebr based on the date field from
+     * the form (billing) submit
+     * 
+     * @param array  $mData          Member Data
+     * @param string $date           The date for pro rating
+     * @param string $dynamic_amount The dynamic amount to use
+     * 
+     * @return float
+     */
+    protected function getProRatedPrice($mData, $date, $dynamic_amount)
+    {
+        $invoice         = new Toolkit_Members_Billing_Invoices();
+        $nextInvoiceDate = $invoice->getNextInvoiceDate();
+        $invDate = new Date(
+            date('c', $nextInvoiceDate)
+        );
+        $paymentType = Toolkit_Members_Billing_Factory::createDbObjectById(
+            $this->dbh,
+            'Toolkit_Members_Billing_PaymentTypes',
+            $mData['payment_type']
+        );
+        if (!($paymentType instanceof Toolkit_Members_Billing_PaymentTypes)) {
+            throw new Exception(
+                'Payment Type is not an instance of
+                Toolkit_Members_Billing_PaymentTypes'
+            );
+        }
+        if (   $paymentType->getDynamic_amount() == true
+            && is_numeric($dynamic_amount)
+        ) {
+            return (float)$dynamic_amount;
+        }
+        $transactionDate = new Date(
+            date('c', strtotime($date))
+        );
+        $cDate = Date::compare($invDate, $transactionDate);
+        if ($cDate == 0) {
+            return (float)$paymentType->getAmount();
+        } else if ($cDate == -1) {
+            // pro-rated for next year
+            // increase the invoice date by one year
+            $timestamp = $invDate->getTime();
+            $nextYear  = strtotime('+ 1 year', $timestamp);
+            if ($nextYear) {
+                $invDate = new Date($nextYear);
+            }
+        } else if ($cDate == 1) {
+            return $paymentType->getAmount();
+        }
+        $span = new Date_Span();
+        $span->setFromDateDiff($transactionDate, $invDate);
+        $days = round($span->toDays());  
+        $dailyPrice = (float)($paymentType->getAmount() / 365);
+        return (float)round(($days * $dailyPrice), 2);
+    }
+
+    /**
+     * build the $this->memberStatements string with the rows of billing table
+     * records and a line with balance due at end
+     *
+     * @return void
+     * @access protected
+     */
+    protected function getStatements()
+    {
+        if (!$_REQUEST['full']) {
+            $urlFormat
+                = "members.php?rt=Members&ac=editMember&id=%d&tab=invoices&full=1";
+            $url = sprintf(
+                $urlFormat,
+                $_REQUEST['id']
+            );
+            $format = '<a href="%s">Show Full Report</a>';
+                $this->memberStatements = sprintf(
+                    $format,
+                    $url
+                );
+        }
+        $statement = new Toolkit_Members_Billing_Statement();
+        $this->memberStatements .= $statement->createMemberStatements(
+            $this->dbh,
+            $_REQUEST['id']
+        );
+    }
+
+    /**
+     * Get all payment types for a select list
+     *
+     * @return array
+     * @access protected
+     */
+    protected function getPaymentTypes()
+    {
+        $paymentTypes = array();
+        try {
+            $sql = "
+               SELECT *
+                FROM payment_types
+            ORDER BY name";
+            $stmt = $this->dbh->prepare($sql);
+            $stmt->execute();
+            while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                $paymentTypes[$row['id']] = $row['name'] . ' ($'.$row['amount'].')';
+            }
+            return $paymentTypes;
+        } catch (PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+    }
+
+    /**
+     * Short description for function
+     * 
+     * Long description (if any) ...
+     * 
+     * @param unknown $sec Parameter description (if any) ...
+     *
+     * @return unknown Return description (if any) ...
+     * @access public 
+     */
+    public function isForm($sec)
+    {
+        return !$sec;
+    }
+
+    /**
+     * Short description for function
+     * 
+     * Long description (if any) ...
+     * 
+     * @param array $values Parameter description (if any) ...
+     *
+     * @return boolean 
+     * @access public
+     */
+    public function processData($values)
+    {
+        // section for creating invoices for member accounts
+        if ($values['transaction_date'] && $values['inv_account_id']) {
+            // need to find which mData to send to function based
+            // on the account_id
+            $mData
+                = ($this->memberData[0]['account_id'] == (int)$values['inv_account_id'])
+                ? $this->memberData[0]
+                : $this->memberData[1];
+            $this->addInvoice(
+                $mData,
+                $values['transaction_date'],
+                $values['dynamic_amount']
+            );
+        }
+        // section for adding payment or adjustments
+        if (   $values['account_id']
+            && $values['amount']
+            && $values['payment_method']
+        ) {
+            if ($values['payment_method'] == 5) {
+                // add adjustment
+                $adjustment = array(
+                    'account_id'     => $values['account_id'],
+                    'member_name'    => $this->memberData[0]['member_name'],
+                    'account_number' => $this->memberData[0]['account_number'],
+                    'amount'         => $values['amount'],
+                    'payment_method' => null,
+                    'payment_data'   => $values['payment_data']
+                );
+                $this->addAdjustment($adjustment);
+            } else {
+                $payment = array(
+                    'account_id'     => $values['account_id'],
+                    'member_name'    => $this->memberData[0]['member_name'],
+                    'account_number' => $this->memberData[0]['account_number'],
+                    'amount'         => $values['amount'],
+                    'payment_method' => $values['payment_method'],
+                    'payment_data'   => $values['payment_data']
+                );
+                $this->addPayment($payment);
+            }
+        }
+        
+        // section for adding notes
+        if ($values['notes']) {
+            $comment = array(
+                'account_id' => $values['account_id'],
+                'notes'      => $values['notes']
+            );
+            $this->addComment($comment);
+        }
+
+        // return page to jump to
+        $listPage = MEDIA_BASE_URL .
+            "admin/members.php?rt=Members&ac=editMember&tab=invoices&id={$_GET['id']}&formSubmitGood=1";
+        header('Location: '.$listPage);
+        return true;
+    }
+}
diff --git a/Toolkit/Members/Billing/EditPaymentType.php b/Toolkit/Members/Billing/EditPaymentType.php
new file mode 100644 (file)
index 0000000..fb0d234
--- /dev/null
@@ -0,0 +1,208 @@
+<?php
+
+/**
+ * Controls Amenity definitions for the member db
+ * 
+ * PHP version 5
+ * 
+ * @category  MembersDB
+ * @package   Toolkit_Members_Billing
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2011 Gaslight Media
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @link      http://demo.gaslightmedia.com
+ */
+
+/**
+ * Handles creating / editing amenities that the members will use
+ * 
+ * @category  MembersDB
+ * @package   Toolkit_Members_Billing
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2011 Gaslight Media
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @link      http://demo.gaslightmedia.com
+ */
+class Toolkit_Members_Billing_EditPaymentType
+    extends Toolkit_Members_Billing_Auxiliary
+{
+    /**
+     * class used for the database object
+     *
+     * @var    string
+     * @access public
+     */
+    public $className = 'Toolkit_Members_Billing_PaymentTypes';
+
+    /**
+     * template for edit form
+     *
+     * @var    string   
+     * @access protected
+     */
+    protected $formTemplate = 'editPaymentTypes.html';
+
+    /**
+     * Class constructor
+     *
+     * @param PDO    $pdo         PHP Data Object
+     * @param string $formName    Form's name.
+     * @param string $method      (optional)Form's method defaults to 'POST'
+     * @param string $action      (optional)Form's action
+     * @param string $target      (optional)Form's target defaults to '_self'
+     * @param mixed  $attributes  (optional)Extra attributes for <form> tag
+     * @param bool   $trackSubmit (optional)Whether to track if the form was
+     *                              submitted by adding a special hidden field
+     * 
+     * @access public
+     */
+    public function __construct(
+        PDO $pdo,
+        $formName,
+        $method = 'post',
+        $action = '',
+        $target = '',
+        $attributes = null,
+        $trackSubmit = false
+    ) {
+        parent::__construct(
+            $formName,
+            $method,
+            $action,
+            $target,
+            $attributes,
+            $trackSubmit
+        );
+
+        $this->dbh = $pdo;
+    }
+
+    /**
+     * Form element definitions
+     * 
+     * @return void     
+     * @access public
+     */
+    public function configureElements()
+    {
+        $e = array();
+        //    All Grouped Elements are created here.
+
+        //    All Elements are created here.
+        //    This includes group element definitions.
+        $e[] = array(
+            'type'    => 'header',
+            'req'     => false,
+            'name'    => 'PaymentTypeInfoHdr',
+            'display' => 'Payment Type'
+        );
+        $e[] = array(
+            'type'    => 'hidden',
+            'req'     => false,
+            'name'    => 'id'
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => true,
+            'name'    => 'name',
+            'display' => 'Name'
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => true,
+            'name'    => 'qcode',
+            'display' => 'Code'
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => true,
+            'name'    => 'category',
+            'display' => 'Category'
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => true,
+            'name'    => 'amount',
+            'display' => 'Amount'
+        );
+        $e[] = array(
+                       'type'    => 'advcheckbox',
+                       'req'     => true,
+                       'name'    => 'dynamic_amount',
+                       'display' => 'Dynamic Amount',
+            'opts'    => 'Ask for the amount on Invoice Forms',
+                       'val'     => array(0, 1),
+               );
+        $e[] = array(
+            'type'    => 'textarea',
+            'req'     => false,
+            'name'    => 'notes',
+            'display' => 'Notes'
+        );
+
+
+        $this->setupElements($e);
+    }
+
+    /**
+     * Helper function to configure an entire form
+     *
+     * @return void
+     * @access public
+     */
+    public function configureForm()
+    {
+        $this->configureElements();
+        $this->configureRules();
+        $this->configureDefaults();
+    }
+
+    /**
+     * Form rule definitions
+     * 
+     * @return void     
+     * @access public
+     */
+    public function configureRules()
+    {
+        $r = array();
+
+        $r[] = array(
+            'element' => 'amount',
+            'message' => 'ERROR: Invalid amount!',
+            'type' => 'numeric',
+            'format' => null,
+            'validation' => $this->validationType,
+            'reset' => true,
+            'force' => false
+        );
+        $r[] = array(
+            'element' => 'qcode',
+            'message' => 'ERROR: Invalid number!',
+            'type' => 'numeric',
+            'format' => null,
+            'validation' => $this->validationType,
+            'reset' => true,
+            'force' => false
+        );
+
+        $this->setupRules($r);
+    }
+
+    /**
+     * Renders the form for viewing
+     *
+     * This function validates the form if needed, and if it successfully
+     * validates attempts to insert or update the data record.
+     * If it is unsuccessful, it will return an error to the user
+     * informing them of what went wrong.
+     *
+     * @return string The compiled and filled form template.
+     * @access public
+     */
+    public function toHtml()
+    {
+        $listPage = MEDIA_BASE_URL . 'admin/members.php?rt=Billing';
+        return parent::toHtml($listPage);
+    }
+}
diff --git a/Toolkit/Members/Billing/EmailInvoices.php b/Toolkit/Members/Billing/EmailInvoices.php
new file mode 100644 (file)
index 0000000..a749ad5
--- /dev/null
@@ -0,0 +1,251 @@
+<?php
+/**
+ * EmailInvoices.php
+ *
+ * PHP version 5
+ *
+ * @category Toolkit_Members
+ * @package  Billing
+ * @author   Steve Sutton <steve@gaslightmedia.com>
+ * @license  http://www.gaslightmedia.com Gaslightmedia
+ * @link     <>
+ */
+
+/**
+ * Toolkit_Members_Billing_EmailInvoices
+ *
+ * @category Toolkit_Members
+ * @package  Billing
+ * @author   Steve Sutton <steve@gaslightmedia.com>
+ * @license  http://www.gaslightmedia.com Gaslightmedia
+ * @link     <>
+ */
+class Toolkit_Members_Billing_EmailInvoices
+    extends Toolkit_Members_Billing_InvoiceAbstract
+    implements Toolkit_Members_Billing_IInvoice
+{
+    /**
+     * Subject line for email
+     * 
+     * @var string
+     * @access protected
+     */
+    protected $subject = 'UPTRA Membership Invoice';
+
+    /**
+     * Billing storage array
+     *
+     * @var array
+     * @access protected
+     */
+    protected $billings = array();
+
+    /**
+     * Template file to use
+     * 
+     * @var string
+     * @access protected
+     */
+    protected $template = 'sendEmailInvoice.html';
+
+    /**
+     * Base of the billing template directory
+     *  
+     * @var string
+     * @access protected
+     */
+    protected $templateBase = 'Toolkit/Members/Billing/';
+    
+    /**
+     * The directory of the templates
+     *
+     * @var string
+     * @access protected
+     */
+    protected $templatesDir = 'templates';
+
+    /**
+     * The directory of the flexy-compiled templates
+     *
+     * @var string
+     * @access protected
+     */
+    protected $compiledDir = 'templates/compiled';
+    
+    /**
+     * Flexy Template Options array
+     * 
+     * @var array
+     * @access protected
+     */
+    protected $flexyOptions = array();
+
+    /**
+     * class Constructor
+     *
+     * @return void
+     * @access public
+     */
+    public function  __construct() 
+    {
+        $this->flexyOptions = array(
+            'templateDir'  => BASE . "{$this->templateBase}{$this->templatesDir}",
+            'compileDir'   => BASE . "{$this->templateBase}{$this->compiledDir}",
+            'forceCompile' => 1,
+            'debug'        => 0,
+            'locale'       => 'en',
+        );
+    }
+    /**
+     * get all members with the email type set and send them their invoices
+     *
+     * @param PDO $dbh Database Connection
+     * @todo     generate email from template
+     *
+     * @return void
+     */
+    public function emailInvoices(PDO $dbh)
+    {
+        $memberIds = array();
+        // get all member id's that have billing setup
+        $inv            = new Toolkit_Members_Billing_Invoices();
+        $members        = $inv->getMembersWithAccounts(
+            $dbh,
+            array(
+                'usmail_invoice' => 0,
+                'email_invoice'  => 1,
+                'fax_invoice'    => 0
+            )
+        );
+        $this->billings = $this->getLastInvoices($dbh, $members, 'emailed');
+        if ($this->billings) {
+            foreach ($this->billings as $bill) {
+                $this->send($dbh, $bill, $inv);
+            }
+        }
+        return $this->billings;
+    }
+
+    /**
+     * with an existing billing object set the billing feild emailed
+     * to true (boolean) and save it
+     *
+     * @param PDO                             $dbh     Database Connection
+     * @param Toolkit_Members_Billing_Billing $billing Billing object
+     *
+     * @return void
+     */
+    public function markSentToMember(
+        PDO $dbh,
+        Toolkit_Members_Billing_Billing $billing
+    ) {
+        $billing
+            ->setEmailed(1) // set to true
+            ->save($dbh); // save the object to database
+    }
+
+    /**
+     * Send the email to the member
+     *
+     * @param PDO                              $dbh  Database Connection
+     * @param Toolkit_Members_Billing_Billing  $bill Billing Object
+     * @param Toolkit_Members_Billing_Invoices $inv  Invoices Object
+     *
+     * @return string|mixed
+     */
+    protected function send(
+        PDO $dbh,
+        Toolkit_Members_Billing_Billing $bill,
+        Toolkit_Members_Billing_Invoices $inv
+    ) {
+        // felxy template
+        $template = new HTML_Template_Flexy($this->flexyOptions);
+        // member data
+        $memberData = $inv->getMemberData($dbh, $bill->getMember_id());
+        // object to merge in template
+        $page              = new stdClass;
+        $page->member_name = $bill->getMember_name();
+        $config = new Config();
+        $root = $config->parseConfig(
+            BASE . 'Toolkit/Members/Billing/config.ini',
+            'IniFile'
+        );
+        $page->companyName = $root
+            ->getItem('section', 'conf')
+            ->getItem('directive', 'companyName')
+            ->getContent();
+        $page->companyName2 = $root
+            ->getItem('section', 'conf')
+            ->getItem('directive', 'companyName2')
+            ->getContent();
+        $page->companyAddr1 = $root
+            ->getItem('section', 'conf')
+            ->getItem('directive', 'companyAddr1')
+            ->getContent();
+        $page->companyCity = $root
+            ->getItem('section', 'conf')
+            ->getItem('directive', 'companyCity')
+            ->getContent();
+        $page->companyState = $root
+            ->getItem('section', 'conf')
+            ->getItem('directive', 'companyState')
+            ->getContent();
+        $page->companyZip = $root
+            ->getItem('section', 'conf')
+            ->getItem('directive', 'companyZip')
+            ->getContent();
+        $page->companyPhone = $root
+            ->getItem('section', 'conf')
+            ->getItem('directive', 'companyPhone')
+            ->getContent();
+        $page->companyUrl = $root
+            ->getItem('section', 'conf')
+            ->getItem('directive', 'companyUrl')
+            ->getContent();
+        switch ($_ENV['GLM_HOST_ID']) {
+        case 'PRODUCTION' :
+            $email = $memberData['member_contact_email'];
+            break;
+        default :        
+            $email = 'steve@localhost';
+            break;
+        }
+        $template->compile($this->template);
+               $htmlMsg = $template->bufferedOutputObject($page);
+
+        $mimeMail = new Mail_mime("\n");
+               $mimeMail->setFrom("Billing <".OWNER_EMAIL.">");
+               $mimeMail->setSubject($this->subject);
+               $mimeMail->setHTMLBody($htmlMsg);
+        $res = $mimeMail->addAttachment(
+            $bill->getInvoiceAsPdf($dbh),
+            'application/pdf',
+            'Invoice-' . $bill->getInvoice_id(),
+            false,
+            'base64'
+        );
+        if (PEAR::isError($res)) {
+            var_dump($res->getMessage());
+            Toolkit_Common::handleError($res);
+        }
+               $mimeMail->setTXTBody($msg);
+
+               $mail =& Mail::factory('mail');
+               $body = $mimeMail->get();
+
+        $setHeader['Reply-To'] = OWNER_EMAIL;
+
+               $headers = $mimeMail->headers($setHeader);
+
+               $res = $mail->send($email, $headers, $body);
+        // mark the billings sent to member
+        //$this->markSentToMember($dbh, $bill);
+               if (PEAR::isError($res)) {
+                       return Toolkit_Common::handleError($res);
+               } else {
+                       return $res;
+               }
+        
+    }
+
+}
diff --git a/Toolkit/Members/Billing/Factory.php b/Toolkit/Members/Billing/Factory.php
new file mode 100644 (file)
index 0000000..9cf5a5a
--- /dev/null
@@ -0,0 +1,97 @@
+<?php
+/**
+ * Factory.php
+ *
+ * PHP version 5
+ *
+ * @category Toolkit
+ * @package  Toolkit
+ * @author   Steve Sutton <steve@gaslightmedia.com>
+ * @license  http://www.gaslightmedia.com Gaslightmedia
+ * @link     <>
+ */
+
+/**
+ * Create an object from string class name
+ * if passed values will also create the object and assign it values
+ *
+ * @category Toolkit
+ * @package  Toolkit
+ * @author   Steve Sutton <steve@gaslightmedia.com>
+ * @license  http://www.gaslightmedia.com Gaslightmedia
+ * @link     <>
+ */
+class Toolkit_Members_Billing_Factory
+{
+    /**
+     * Static function can be called to create billing object from an id
+     *
+     * @param PDO     $dbh       Database Connection
+     * @param string  $className Class name of object to create
+     * @param integer $id        id to use for record
+     *
+     * @return object
+     */
+    static function createDbObjectById(PDO $dbh, $className, $id)
+    {
+        if (!is_numeric($id)) {
+            throw new InvalidArgumentException('Id must be numeric');
+        }
+        switch ($className) {
+        case 'Toolkit_Members_Billing_PaymentTypes' :
+            $factory = new Toolkit_Members_Billing_PaymentTypes();
+            return $factory->fetchById($dbh, $id);
+            break;
+
+        case 'Toolkit_Members_Billing_Billing' :
+            $factory = new Toolkit_Members_Billing_Billing();
+            return $factory->fetchById($dbh, $id);
+            break;
+
+        case 'Toolkit_Members_Billing_MemberAccount' :
+            $factory = new Toolkit_Members_Billing_MemberAccount();
+            return $factory->fetchById($dbh, $id);
+            break;
+
+        default:
+            return null;
+            break;
+        }
+    }
+
+    /**
+     * Static function can be called to create billing object from an array
+     *
+     * @param string $className Class name of object to create
+     * @param array  $values    array of values for the object
+     *
+     * @return object
+     */
+    static function createDBObjectByValues($className, $values)
+    {
+        if (!is_array($values)) {
+            throw new InvalidArgumentException('Values must be Array');
+        }
+        switch ($className) {
+        case 'Toolkit_Members_Billing_PaymentTypes' :
+            $factory = new Toolkit_Members_Billing_PaymentTypes();
+            return $factory->createByValues($values);
+            break;
+
+        case 'Toolkit_Members_Billing_Billing' :
+            $factory = new Toolkit_Members_Billing_Billing();
+            return $factory->createByValues($values);
+            break;
+
+        case 'Toolkit_Members_Billing_MemberAccount' :
+            $factory = new Toolkit_Members_Billing_MemberAccount();
+            return $factory->createByValues($values);
+            break;
+
+        default:
+            return null;
+            break;
+        }
+    }
+
+}
diff --git a/Toolkit/Members/Billing/IInvoice.php b/Toolkit/Members/Billing/IInvoice.php
new file mode 100644 (file)
index 0000000..0363e47
--- /dev/null
@@ -0,0 +1,38 @@
+<?php
+/**
+ * IInvoices.php
+ *
+ * PHP version 5
+ *
+ * @category Toolkit_Members
+ * @package  Billing
+ * @author   Steve Sutton <steve@gaslightmedia.com>
+ * @license  http://www.gaslightmedia.com Gaslightmedia
+ * @link     <>
+ */
+
+/**
+ * Toolkit_Members_Billing_IInvoices
+ *
+ * @category Toolkit_Members
+ * @package  Billing
+ * @author   Steve Sutton <steve@gaslightmedia.com>
+ * @license  http://www.gaslightmedia.com Gaslightmedia
+ * @link     <>
+ */
+interface Toolkit_Members_Billing_IInvoice
+{
+    /**
+     * function to mark all billing records sent as sentToMember
+     * 
+     * @param PDO                             $dbh     Database handler
+     * @param Toolkit_Members_Billing_Billing $billing Description of $billing
+     * 
+     * @return void
+     * @access public
+     */
+    function markSentToMember(
+        PDO $dbh,
+        Toolkit_Members_Billing_Billing $billing
+    );
+}
diff --git a/Toolkit/Members/Billing/InvoiceAbstract.php b/Toolkit/Members/Billing/InvoiceAbstract.php
new file mode 100644 (file)
index 0000000..38d619b
--- /dev/null
@@ -0,0 +1,104 @@
+<?php
+/**
+ * InvoiceAbstract.php
+ *
+ * PHP version 5
+ *
+ * @category Toolkit_Members
+ * @package  Billing
+ * @author   Steve Sutton <steve@gaslightmedia.com>
+ * @license  http://www.gaslightmedia.com Gaslightmedia
+ * @link     <>
+ */
+
+/**
+ * Toolkit_Members_Billing_InvoiceAbstract
+ *
+ * @category Toolkit_Members
+ * @package  Billing
+ * @author   Steve Sutton <steve@gaslightmedia.com>
+ * @license  http://www.gaslightmedia.com Gaslightmedia
+ * @link     <>
+ */
+abstract class Toolkit_Members_Billing_InvoiceAbstract
+{
+    /**
+     * Get the last invoice from the array of members given
+     * creates Toolkit_Members_Billing_Billing object for each member.
+     * If the last one was already sent then don't create a billing object
+     * Furthur restrict this to the current billing invoice year
+     *
+     * @param PDO    $dbh     Database handler
+     * @param array  $members Members array
+     * @param string $type    Type
+     *
+     * @return array|boolean
+     * @access public
+     * @throws InvalidArgumentException
+     */
+    public function getLastInvoices(
+        PDO $dbh,
+        $members,
+        $type = 'emailed'
+    ) {
+        if (   !is_array($members)
+            || empty($members)
+        ) {
+            throw new InvalidArgumentException('$members must be an array');
+        }
+        $billings  = array();
+        $inv       = new Toolkit_Members_Billing_Invoices();
+        $startDate = new Date($inv->getCurrentInvoiceDate());
+        $endDate   = new Date($inv->getNextInvoiceDate());
+        try {
+            if (is_array($members)) {
+                foreach ($members as $member) {
+                    $memberIds[] = $member['member_id'];
+                }
+            }
+            if (!is_array($memberIds)) {
+                return false;
+            }
+            $sql = "
+              SELECT id, member_id
+                FROM billing
+               WHERE member_id IN (".implode(',', $memberIds).")
+                 AND (".$type." != 't'
+                     OR ".$type." IS NULL)
+                 AND billing_type = 1
+                 AND transaction_date BETWEEN :beginDate AND :endDate
+                -- AND (invoice != '' OR invoice IS NOT NULL)
+                 AND (paid = false OR paid IS NULL) 
+            ORDER BY member_name, id DESC";
+            $stmt = $dbh->prepare($sql);
+            $stmt->bindParam(':beginDate', $startDate->format("%m/%d/%Y"));
+            $stmt->bindParam(':endDate', $endDate->format("%m/%d/%Y"));
+            $stmt->execute();
+            $curMemberId = 0;
+            
+            while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                    //if ($curMemberId != $row['member_id']) {
+                    // only if the $type Field is not true
+                    $billing
+                        = Toolkit_Members_Billing_Factory::createDbObjectById(
+                            $dbh,
+                            'Toolkit_Members_Billing_Billing',
+                            $row['id']
+                        );
+                    //var_dump($billing);
+                    $callFunction = 'get' . ucfirst($type);
+                    $wasSent = $billing->$callFunction();
+                if (!$wasSent) {
+                    $billings[] = $billing;
+                }
+                //}
+                //$curMemberId = $row['member_id'];
+            }
+            return (!empty($billings))
+                ? $billings
+                : false;
+        } catch(PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+    }
+}
diff --git a/Toolkit/Members/Billing/InvoiceDate.php b/Toolkit/Members/Billing/InvoiceDate.php
new file mode 100644 (file)
index 0000000..550b64b
--- /dev/null
@@ -0,0 +1,68 @@
+<?php
+/**
+ * InvoiceDate.php
+ *
+ * PHP version 5
+ *
+ * @category Toolkit_Members
+ * @package  Billing
+ * @author   Steve Sutton <steve@gaslightmedia.com>
+ * @license  http://www.gaslightmedia.com Gaslightmedia
+ * @link     <>
+ */
+
+/**
+ * Toolkit_Members_Billing_InvoiceDate
+ *
+ * @category Toolkit_Members
+ * @package  Billing
+ * @author   Steve Sutton <steve@gaslightmedia.com>
+ * @license  http://www.gaslightmedia.com Gaslightmedia
+ * @link     <>
+ */
+class Toolkit_Members_Billing_InvoiceDate
+{
+    /**
+     * Start date for current billing invoice year
+     * @var date
+     * @access protected
+     */
+    protected $startDate;
+    
+    /**
+     * End date for current billing invoice year
+     * @var date 
+     * @access protected
+     */
+    protected $endDate;
+
+    /**
+     * invoice date object contains the start and end dates for the current
+     * billing invoice year.
+     * Start and End dates are PEAR::Date objects
+     *
+     * @param Date $startDate Start date for current billing invoice year
+     * @param Date $endDate   End date for current billing invoice year
+     *
+     * @return void
+     */
+    public function  __construct(Date $startDate, Date $endDate) 
+    {
+        $this->startDate = $startDate;
+        $this->endDate   = $endDate;
+    }
+
+    /**
+     * Return the starting date formatted or raw object
+     *
+     * @param mixed $format date format if not given the object is returned
+     *
+     * @return mixed
+     */
+    public function getStartDate($format = null)
+    {
+        if ($format) {
+            return $this->startDate->format($format);
+        }
+    }
+}
diff --git a/Toolkit/Members/Billing/InvoiceFilterForm.php b/Toolkit/Members/Billing/InvoiceFilterForm.php
new file mode 100644 (file)
index 0000000..65b2cf5
--- /dev/null
@@ -0,0 +1,410 @@
+<?php
+
+/**
+ * InvoiceFilterForm.php
+ *
+ * PHP version 5
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members_Billing
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2011 Gaslight Media
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @link      http://demo.gaslightmedia.com
+ */
+
+/**
+ * Toolkit_Members_Billing_InvoiceFilterForm
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members_Billing
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2011 Gaslight Media
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @link      http://demo.gaslightmedia.com
+ */
+class Toolkit_Members_Billing_InvoiceFilterForm
+    extends Toolkit_FormBuilder
+{
+
+    /**
+     * Base of the billing template directory
+     *  
+     * @var string
+     * @access protected
+     */
+    protected $templateBase = 'Toolkit/Members/Billing/';
+    /**
+     * The directory of the templates
+     *
+     * @var string
+     * @access protected
+     */
+    protected $templatesDir = 'templates';
+
+    /**
+     * The directory of the flexy-compiled templates
+     *
+     * @var string
+     * @access protected
+     */
+    protected $compiledDir = 'templates/compiled';
+
+    /**
+     * The name of the template used for the form
+     *
+     * @var string
+     * @access protected
+     */
+    protected $formTemplate = 'invoiceFilterForm.html';
+
+    /**
+     * The object to use inside the form
+     *
+     * This object which is to be populated by the $this object                
+     * is used inside the templates and allows access back into the
+     * calling class to call publicly available functions
+     *
+     * @var    string
+     * @access protected
+     */
+    protected $view;
+
+    /**
+     * Class constructor
+     *
+     * @param string $formName    Form's name.
+     * @param string $method      (optional)Form's method defaults to 'POST'
+     * @param string $action      (optional)Form's action
+     * @param string $target      (optional)Form's target defaults to '_self'
+     * @param mixed  $attributes  (optional)Extra attributes for <form> tag
+     * @param bool   $trackSubmit (optional)Whether to track if the form was
+     *                              submitted by adding a special hidden field
+     * 
+     * @access public
+     */
+    public function __construct(
+        $formName,
+        $method = 'post',
+        $action = '',
+        $target = '',
+        $attributes = null,
+        $trackSubmit = false
+    ) {
+        parent::__construct(
+            $formName,
+            $method,
+            $action,
+            $target,
+            $attributes,
+            $trackSubmit
+        );
+        $this->registeredRules = array();
+        switch ($_REQUEST['ac']) {
+        case 'createInvoices' :
+            $this->task = 'Create Invoices';
+            break;
+        case 'printInvoices' :
+            $this->task = 'Print Invoices';
+            break;
+        case 'createLabels' :
+            $this->task = 'Create Labels';
+            break;
+        case 'sendEmail' :
+            $this->task = 'Email Invoices';
+            break;
+        }
+
+        $options =& PEAR::getStaticProperty('HTML_Template_Flexy', 'options');
+        $options = array(
+            'templateDir'  => BASE . "{$this->templateBase}{$this->templatesDir}",
+            'compileDir'   => BASE . "{$this->templateBase}{$this->compiledDir}",
+            'forceCompile' => 1,
+            'debug'        => 0,
+            'locale'       => 'en',
+        );
+    }
+
+    /**
+     * Returns the MEDIA_BASE_URL
+     *
+     * This function is used inside the Flexy Templated forms
+     *
+     * @return string MEDIA_BASE_URL
+     * @access public
+     */
+    public function baseUrl()
+    {
+        return MEDIA_BASE_URL;
+    }
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @return void
+     * @access protected
+     */
+       protected function configureConstants()
+       {
+               $constants = array(
+                       'cat'    => $_REQUEST['rt'],
+                       'subCat' => $_REQUEST['ac'],
+            'tab'    => $_REQUEST['tab'],
+            'search' => 1
+               );
+
+               $this->setupConstants($constants);
+       }
+
+    /**
+     * Form element definitions
+     * 
+     * @return void     
+     * @access public
+     */
+    public function configureElements()
+    {
+        $e = array();
+        //    All Grouped Elements are created here.
+        $paymentTypes = $this->getPaymentTypes();
+        $counties     = $this->getCounties();  
+        
+        //    All Elements are created here.
+        //    This includes group element definitions.
+        $e[] = array(
+            'type'    => 'header',
+            'req'     => false,
+            'name'    => 'PaymentTypeInfoHdr',
+            'display' => 'Filters'
+        );
+        $e[] = array(
+            'type'    => 'hidden',
+            'req'     => false,
+            'name'    => 'search'
+        );
+        $e[] = array(
+            'type'    => 'hidden',
+            'req'     => false,
+            'name'    => 'rt'
+        );
+        $e[] = array(
+            'type'    => 'hidden',
+            'req'     => false,
+            'name'    => 'ac'
+        );
+        $e[] = array(
+            'type'    => 'hidden',
+            'req'     => false,
+            'name'    => 'tab'
+        );
+        $e[] = array(
+            'type'    => 'select',
+            'req'     => false,
+            'name'    => 'payment_type',
+            'display' => 'Payment Types',
+            'opts'    => $paymentTypes,
+            'att'    => array('multiple','size'=>10)
+        );
+        $e[] = array(
+            'type'    => 'select',
+            'req'     => false,
+            'name'    => 'county',
+            'display' => 'Regions',
+            'opts'    => $counties,
+            'att'    => array('multiple','size'=>10)
+        );
+        if ($_REQUEST['tab'] == '3') {
+            $e[] = array(
+                'type'    => 'advcheckbox',
+                'req'     => false,
+                'name'    => 'balance_due',
+                'display' => 'Only Payments Due',
+                'opts'    => 'Yes',
+                'val'     => array(0, 1)
+            );
+        }
+
+        $this->setupElements($e);
+    }
+
+    /**
+     * Defines all filters used on form elements when submitted
+     *
+     * Most times this function won't need to be overridden.
+     *
+     * @return void
+     * @access public
+     */
+    protected function configureFilters()
+    {
+        $filters[] = array('element' => '__ALL__', 'filter' => 'trim');
+
+        $this->setupFilters($filters);
+    }
+    
+    /**
+     * Helper function to configure an entire form
+     *
+     * @return void
+     * @access public
+     */
+    public function configureForm()
+    {
+        $this->configureElements();
+        $this->configureRules();
+               $this->configureFilters();
+               $this->configureConstants();
+    }
+    
+    /**
+     * Sets up the required / not-required rules for forms
+     *
+     * At the bare minimum, the required / not-required rules for form
+     * needs to be defined.  Since these rules are most easily defined
+     * while creating the element definitions themselves the base function
+     * is to call the setupRules function that will instantiate these
+     * rules in the quickform class.
+     *
+     * @return void
+     * @access public
+     */
+    protected function configureRules()
+    {
+        $this->setupRules();
+    }
+
+    /**
+     * Description for getCounties()
+     * 
+     * @return array
+     * @access protected
+     */
+    protected function getCounties()
+    {
+        $counties = array();
+        try {
+            $sql = "
+               SELECT *
+                FROM region
+            ORDER BY region_name";
+            $stmt = $this->dbh->prepare($sql);
+            $stmt->execute();
+            while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                $counties[$row['region_id']] = $row['region_name'];
+            }
+            return $counties;
+        } catch (PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+    }
+    
+    /**
+     * Description of getPaymentTypes()
+     * 
+     * @return array
+     * @access protected
+     */
+    protected function getPaymentTypes()
+    {
+        $paymentTypes = array();
+        try {
+            $sql = "
+               SELECT *
+                FROM payment_types
+            ORDER BY name";
+            $stmt = $this->dbh->prepare($sql);
+            $stmt->execute();
+            while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                $paymentTypes[$row['id']] = $row['name'];
+            }
+            return $paymentTypes;
+        } catch (PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+    }
+
+    /**
+     * Short description for processData()
+     * 
+     * Long description (if any) ...
+     * 
+     * @param array $values Parameter description (if any) ...
+     *
+     * @return boolean Return description (if any) ...
+     * @access protected
+     */
+    protected function processData($values)
+    {
+        var_dump($values);
+        exit;
+        return true;
+    }
+
+    /**
+     * Sets up the rendering engine for the form
+     *
+     * @return void
+     * @access protected
+     */
+    protected function setupRenderers()
+    {
+        $renderer =& new HTML_QuickForm_Renderer_Object(true);
+
+        $this->accept($renderer);
+
+        $this->template =& new HTML_Template_Flexy($this->options);
+
+        //    Make the view a copy of the $this object
+        //    That way we have access to call functions in
+        //    this class from within the template.
+        $this->view = $this;
+        $this->view->form = $renderer->toObject();
+        $this->template->compile($this->formTemplate);
+    }
+
+    /**
+     * Renders the form for viewing
+     *
+     * This function validates the form if needed, and if it successfully
+     * validates attempts to insert or update the data record.
+     * If it is unsuccessful, it will return an error to the user
+     * informing them of what went wrong.
+     *
+     * @param string $listPage The page the header should redirect
+     *                           to on successful insert or update.
+     *
+     * @return string The compiled and filled form template.
+     * @access public
+     */
+    public function toHTML($listPage = 'index.php')
+    { 
+        //    We need to validate (and freeze if needed)
+        //    before we render the form. That way the
+        //    template knows about any errors on the form.
+        $this->validated = $this->validate();
+        $this->setupRenderers();
+
+        //if ($this->validated) {            
+        //}
+
+        return $errorMsg . $this->template->bufferedOutputObject($this->view);
+    }
+
+    /**
+     * Returns form message from validation attempts
+     *
+     * @return string validation message
+     * @access public
+     */
+    public function validated()
+    {
+        if ($this->validated) {
+            return $this->successMsg;
+        } elseif ($this->isSubmitted()) {
+            return $this->errorMsg;
+        }
+    }
+
+}
diff --git a/Toolkit/Members/Billing/InvoicePdf.php b/Toolkit/Members/Billing/InvoicePdf.php
new file mode 100644 (file)
index 0000000..16785cc
--- /dev/null
@@ -0,0 +1,553 @@
+<?php
+/**
+ * InvoicePdf.php
+ *
+ * PHP version 5
+ *
+ * @category Toolkit_Members
+ * @package  Billing
+ * @author   Steve Sutton <steve@gaslightmedia.com>
+ * @license  http://www.gaslightmedia.com Gaslightmedia
+ * @link     <>
+ */
+require_once GLM_APP_BASE . 'pdf/pdf_lib_8/GlmPdf.php';
+/**
+ * Toolkit_Members_Billing_InvoicePdf
+ *
+ * @category Toolkit_Members
+ * @package  Billing
+ * @author   Steve Sutton <steve@gaslightmedia.com>
+ * @license  http://www.gaslightmedia.com Gaslightmedia
+ * @link     <>
+ */
+class Toolkit_Members_Billing_InvoicePdf
+    extends GlmPdf
+{
+    /**
+     * Class Constructor
+     * 
+     * @access public
+     */
+    public function  __construct()
+    {
+        $config = new Config();
+        $root = $config->parseConfig(
+            BASE . 'Toolkit/Members/Billing/config.ini',
+            'IniFile'
+        );
+        $this->config = $root;
+    }
+
+    /**
+     * Creates the array for forms then generates the string for a pdf
+     *
+     * @param PDO $dbh        Database Connection
+     * @param int $member_id  Member id
+     * @param int $invoice_id The invoice_id from the new invoice record
+     * @param int $account_id The Account ID.
+     *
+     * @return string
+     * @access public
+     * @throws InvalidArgumentException
+     */
+    public function createPdfInvoice(
+        PDO $dbh,
+        $member_id,
+        $invoice_id,
+        $account_id
+    ) {
+        if (!is_numeric($member_id)) {
+            throw new InvalidArgumentException('member_id must be numeric');
+        }
+        if (!is_numeric($invoice_id)) {
+            throw new InvalidArgumentException('invoice_id must be numeric');
+        }
+        if (!is_numeric($account_id)) {
+            throw new InvalidArgumentException('account_id must be numeric');
+        }
+        // grab all billing data for this member
+        $items = array();
+        $stmt = new Toolkit_Members_Billing_Statement();
+        $transactions = $stmt->getMemberBillings(
+            $dbh,
+            $member_id,
+            $account_id
+        );
+        
+        $memberAccount = Toolkit_Members_Billing_Factory::createDbObjectById(
+            $dbh,
+            'Toolkit_Members_Billing_MemberAccount',
+            $account_id
+        );
+        if ($memberAccount) {
+            $payment_type_id = $memberAccount->getPayment_type();
+        }
+        if ($payment_type_id) {
+            $paymentType = Toolkit_Members_Billing_Factory::createDbObjectById(
+                $dbh,
+                'Toolkit_Members_Billing_PaymentTypes',
+                $payment_type_id
+            );
+            $payment_type_name = $paymentType->getName();
+        }
+        if (is_array($transactions) && !empty($transactions)) {
+            foreach ($transactions as $transaction) {
+                $items[] = array(
+                    'type'    => $transaction->getBilling_type(),
+                    'date'    => $transaction->getTransaction_date(),
+                    'descr'   => 'Membership Dues ' . $payment_type_name,// @todo needs to say which payment_type
+                    'amount'  => $transaction->getAmount(),
+                    'balance' => $transaction->getBalance()
+                );
+            }
+        }
+
+        $memberData = $this->getMemberData($dbh, $member_id);
+        $companyLogo = $this->config
+            ->getItem('section', 'conf')
+            ->getItem('directive', 'companyLogo')
+            ->getContent();
+        $companyLogoHeight = $this->config
+            ->getItem('section', 'conf')
+            ->getItem('directive', 'companyLogoHeight')
+            ->getContent();
+        $companyName = $this->config
+            ->getItem('section', 'conf')
+            ->getItem('directive', 'companyName')
+            ->getContent();
+        $companyName2 = $this->config
+            ->getItem('section', 'conf')
+            ->getItem('directive', 'companyName2')
+            ->getContent();
+        $companyAddr1 = $this->config
+            ->getItem('section', 'conf')
+            ->getItem('directive', 'companyAddr1')
+            ->getContent();
+        $companyAddr2 = $this->config
+            ->getItem('section', 'conf')
+            ->getItem('directive', 'companyAddr2')
+            ->getContent();
+        $companyCity = $this->config
+            ->getItem('section', 'conf')
+            ->getItem('directive', 'companyCity')
+            ->getContent();
+        $companyState = $this->config
+            ->getItem('section', 'conf')
+            ->getItem('directive', 'companyState')
+            ->getContent();
+        $companyZip = $this->config
+            ->getItem('section', 'conf')
+            ->getItem('directive', 'companyZip')
+            ->getContent();
+        $companyPhone = $this->config
+            ->getItem('section', 'conf')
+            ->getItem('directive', 'companyPhone')
+            ->getContent();
+        $companyEmail = $this->config
+            ->getItem('section', 'conf')
+            ->getItem('directive', 'companyEmail')
+            ->getContent();
+        if ($memberData['mailing_address']) {
+            $streetAddress = $memberData['mailing_address'];
+            $cityName
+                = ($memberData['mailing_city_id'])
+            ? $this->getCityName($dbh, $memberData['mailing_city_id'])
+            : '';
+            $stateName
+                = ($memberData['mailing_state_id'])
+            ? $this->getStateName($dbh, $memberData['mailing_state_id'])
+            : '';
+            $zipCode = $memberData['mailing_zip'];
+        } else {
+            $streetAddress = $memberData['street'];
+            $cityName
+                = ($memberData['city_id'])
+            ? $this->getCityName($dbh, $memberData['city_id'])
+            : '';
+            $stateName
+                = ($memberData['state_id'])
+            ? $this->getStateName($dbh, $memberData['state_id'])
+            : '';
+            $zipCode = $memberData['zip'];
+        }
+        $primaryContactName
+            = $memberData['billing_contact'];
+        // For the invoice date we need to use either one of three dates depending on what 
+        // day she is printing the invoice
+        // if she prints this invoice two weeks before Aug 1st or Sep 1st then 
+        // use either of them that are within two weeks otherwise use July 1st
+        // After July 18th use Aug 1st
+        // After Aug 18th use Sep 1st
+        // else use July 1st
+        $currentDateDay   = date('j');
+        $currentDateMonth = date('n');
+        if ($currentDateMonth > 8) {
+            $monthForInvDate = 9;
+        } else if ($currentDateMonth == 8) {
+            $monthForInvDate
+                = ($currentDateDay >= 18)
+                ? 9
+                : 8;
+        } else if ($currentDateMonth == 7) {
+            $monthForInvDate
+                = ($currentDateDay >= 18)
+                ? 8
+                : 7;
+        } else if ($currentDateMonth < 7) {
+            $monthForInvDate = 7;
+        }
+        $invDate = date('n/d/Y');
+        $test_data = array(
+            'company_logo'        => BASE . $companyLogo,
+            'company_logo_height' => $companyLogoHeight,
+            'company_name'        => iconv('UTF-8', 'ISO-8859-1//IGNORE', $companyName),
+            'company_name2'       => iconv('UTF-8', 'ISO-8859-1//IGNORE', $companyName2),
+            'company_addr1'       => iconv('UTF-8', 'ISO-8859-1//IGNORE', $companyAddr1),
+            'company_addr2'       => iconv('UTF-8', 'ISO-8859-1//IGNORE', $companyAddr2),
+            'company_city'        => iconv('UTF-8', 'ISO-8859-1//IGNORE', $companyCity),
+            'company_state'       => iconv('UTF-8', 'ISO-8859-1//IGNORE', $companyState),
+            'company_zip'         => iconv('UTF-8', 'ISO-8859-1//IGNORE', $companyZip),
+            'company_phone'       => $companyPhone,
+            'company_email'       => iconv('UTF-8', 'ISO-8859-1//IGNORE', $companyEmail),
+
+            'invoice_date'        => $invDate,
+            'invoice_number'      => $invoice_id,
+            'invoice_balance'     => $stmt->getBalanceDue(
+                $dbh,
+                $member_id,
+                $account_id
+            ),
+            'invoice_file_name'   => "invoice_{$invoice_id}.pdf",
+
+            'member_billing_no'   => $memberData['account_number'],
+            'member_name'         => iconv('UTF-8', 'ISO-8859-1//IGNORE', $memberData['member_name']),
+            'member_addr1'        => iconv('UTF-8', 'ISO-8859-1//IGNORE', $streetAddress),
+            'member_city'         => $cityName,
+            'member_state'        => $stateName,
+            'member_zip'          => $zipCode,
+            'member_contact_name' => iconv('UTF-8', 'ISO-8859-1//IGNORE', $primaryContactName),
+
+            'payment_terms'       => iconv('UTF-8', 'ISO-8859-1//IGNORE', 'Membership dues need to be paid by October 1st'),
+
+            'items'               => $items
+        );
+        return $this->glmPdfInvoice($test_data);
+    }
+
+    /**
+     * Get the city_name from the city table given an id
+     *
+     * @param PDO $dbh Database Connection
+     * @param int $id  City id
+     *
+     * @return string
+     * @access public
+     */
+    public function getCityName(PDO $dbh, $id)
+    {
+        if (!is_numeric($id)) {
+            throw new InvalidArgumentException('id must be numeric');
+        }
+        try {
+            $sql = "
+            SELECT city_name
+              FROM city
+             WHERE city_id = :id";
+            $stmt = $dbh->prepare($sql);
+            $stmt->bindParam(
+                ':id',
+                $id,
+                PDO::PARAM_INT
+            );
+            $stmt->execute();
+            return $stmt->fetchColumn();
+        } catch(PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+    }
+
+    /**
+     * From the member_id get the member name
+     *
+     * @param PDO     $dbh Database Connection
+     * @param integer $id  Member_id field
+     *
+     * @return string
+     * @access public
+     */
+    public function getMemberData(PDO $dbh, $id)
+    {
+        try {
+            $sql = "
+              SELECT *
+                FROM member
+               WHERE member_id = :member_id
+            ORDER BY member_name";
+            $stmt = $dbh->prepare($sql);
+            $stmt->bindParam(
+                ':member_id',
+                $id,
+                PDO::PARAM_INT
+            );
+            $stmt->execute();
+            return $stmt->fetch(PDO::FETCH_ASSOC);
+        } catch(PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+    }
+    
+    /**
+     * Get the state_abb from the state table given an id
+     *
+     * @param PDO $dbh Database Connection
+     * @param int $id  State id
+     *
+     * @return string
+     * @access public
+     */
+    public function getStateName(PDO $dbh, $id)
+    {
+        if (!is_numeric($id)) {
+            throw new InvalidArgumentException('id must be numeric');
+        }
+        try {
+            $sql = "
+            SELECT state_abb
+              FROM state
+             WHERE state_id = :id";
+            $stmt = $dbh->prepare($sql);
+            $stmt->bindParam(
+                ':id',
+                $id,
+                PDO::PARAM_INT
+            );
+            $stmt->execute();
+            return $stmt->fetchColumn();
+        } catch(PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+    }
+
+    /**
+     * Create the pdf from the pdflib functions
+     *
+     * @param array $inv Array of form values for the pdf
+     *
+     * @return string
+     * @access public
+     * @throws Exception
+     */
+    function glmPdfInvoice($inv)
+    {
+        $invoiceForms = array(
+            1 => array( 'x' => 0, 'y' => 0, 'xs' => 612,  'ys' => 792 )
+        );
+
+        // Initialize Forms
+        $r = $this->glmpdfSetForms(
+            count($invoiceForms),
+            $invoiceForms,
+            $this->glmpdfStandardPage[PAPER_SIZE]['x'],
+            $this->glmpdfStandardPage[PAPER_SIZE]['y']
+        );
+        if (!$r) {
+            echo "ERROR: Unable to set PDF forms layout.";
+            exit;
+        }
+
+        // Start PDF Generation - (Creator, Author, Title)
+        $this->glmpdfStart("GLM PDF", "Gaslight Media", "Standard Invoice with Payment Form");
+
+        // Load barcode font
+        if (!$this->glmpdfAddFont('barcode', 'barcode/FREE3OF9.TTF')) {
+            throw new Exception(
+                'ERROR: Unable to add barcode font.<P>
+                Did you place the barcode font into a system fonts directory
+                (/usr/share/fonts/ttf) and set the fonts directory correctly in the
+                pdf_functions.inc file?'
+            );
+            exit;
+        }
+
+        // Create new form
+        $this->glmpdfNextForm();
+
+        // Create Company Header
+        if (trim($inv['company_logo']) != '') {
+            $logo_y = 760 - $inv['company_logo_height'];
+            $logoImage = $this->glmpdfOpenImage($inv['company_logo']);
+            $this->glmpdfPlaceImage($logoImage, 5, $logo_y, .45);
+        }
+        $this->glmpdfSetFont("Helvetica-Bold", 12.0);
+        $this->glmpdfPlaceText($inv['company_name'], 20, 660, "left", 'black');
+        $this->glmpdfSetFont("Helvetica", 12.0);
+        $this->glmpdfPlaceText($inv['company_addr1'], 20, $this->glmpdfCurrentY, "left", 'black');
+        if (trim($inv['company_addr2']) != '') {
+            $this->glmpdfPlaceText($inv['company_addr2'], 20, $this->glmpdfCurrentY, "left", 'black');
+        }
+        $city_state = $inv['company_city'].', '.$inv['company_state'].' '.$inv['company_zip'];
+        $this->glmpdfPlaceText($city_state, 20, $this->glmpdfCurrentY, "left", 'black');
+        $this->glmpdfPlaceText($inv['company_phone'], 20, $this->glmpdfCurrentY, "left", 'black');
+        $this->glmpdfPlaceText($inv['company_email'], 20, $this->glmpdfCurrentY, "left", 'black');
+
+        // Invoice Header
+        $this->glmpdfSetFont("Helvetica-Bold", 26.0);
+        $this->glmpdfPlaceText('INVOICE', 570, 710, "right", 'black');
+        $this->glmpdfSetFont("Helvetica", 12.0);
+        $this->glmpdfPlaceText('Date:', 350, 650, "left", 'black');
+        $this->glmpdfPlaceText($inv['invoice_date'], 450, $this->glmpdfLastY, "left", 'black');
+//        $this->glmpdfPlaceText('Invoice #:', 350, $this->glmpdfCurrentY, "left", 'black');
+//        $this->glmpdfPlaceText($inv['invoice_number'], 450, $this->glmpdfLastY, "left", 'black');
+        $this->glmpdfPlaceText('Member Billing #:', 350, $this->glmpdfCurrentY, "left", 'black');
+        $this->glmpdfPlaceText($inv['member_billing_no'], 450, $this->glmpdfLastY, "left", 'black');
+
+        // Member Info
+        $this->glmpdfSetFont("Helvetica-Bold", 22.0);
+        $this->glmpdfPlaceText('Bill To:', 20, 570, "left", 'black');
+        $this->glmpdfSetFont("Helvetica", 12.0);
+        $this->glmpdfPlaceText($inv['member_name'], 20, $this->glmpdfCurrentY, "left", 'black');
+        if ($inv['member_contact_name']) {
+            $this->glmpdfPlaceText($inv['member_contact_name'], 20, $this->glmpdfCurrentY, "left", 'black');
+        }
+        $this->glmpdfPlaceText($inv['member_addr1'], 20, $this->glmpdfCurrentY, "left", 'black');
+        if (trim($inv['member_addr2']) != '') {
+            $this->glmpdfPlaceText($inv['member_addr2'], 20, $this->glmpdfCurrentY, "left", 'black');
+        }
+        $city_state = $inv['member_city'].', '.$inv['member_state'].' '.$inv['member_zip'];
+        $this->glmpdfPlaceText($city_state, 20, $this->glmpdfCurrentY, "left", 'black');
+        //$this->glmpdfPlaceText($inv['member_email'], 20, $this->glmpdfCurrentY, "left", 'black');
+
+        // Items Header
+        //$this->glmpdfPlaceBox(1, 20, 470, 550, 20, 'white', 'white', 'round');
+        $this->glmpdfSetFont("Helvetica-Bold", 12.0);
+        $this->glmpdfPlaceText('Date', 30, 470, "left", 'black');
+        $this->glmpdfPlaceText('Description', 110, $this->glmpdfLastY, "left", 'black');
+        $this->glmpdfPlaceText('Amount', 490, $this->glmpdfLastY, "right", 'black');
+        $this->glmpdfPlaceText('Balance', 560, $this->glmpdfLastY, "right", 'black');
+        $this->glmpdfPlaceLine(1, 20, $this->glmpdfLastY - 3, 570, $this->glmpdfLastY - 3, 'black');
+
+        // Items
+        $this->glmpdfSetFont("Helvetica", 12.0);
+        $this->glmpdfPlaceText('', 30, $this->glmpdfLastY, "left", 'black');
+        foreach ($inv['items'] as $item) {
+            switch ($item['type']) {
+            // Type 1 - Invoice
+            case 1;
+                $this->glmpdfPlaceText($item['date'], 30, $this->glmpdfCurrentY, "left", 'black');
+                $this->glmpdfPlaceText($item['descr'], 110, $this->glmpdfLastY, "left", 'black');
+                $this->glmpdfPlaceText($this->money($item['amount']), 490, $this->glmpdfLastY, "right", 'black');
+                $this->glmpdfPlaceText($this->money($item['balance']), 560, $this->glmpdfLastY, "right", 'black');
+                break;
+
+            // Type 2 - Payment
+            case 2:
+                $this->glmpdfPlaceText($item['date'], 30, $this->glmpdfCurrentY, "left", 'black');
+                $this->glmpdfPlaceText('Payment - Thank You!', 110, $this->glmpdfLastY, "left", 'black');
+                $this->glmpdfPlaceText($this->money($item['amount']*-1), 490, $this->glmpdfLastY, "right", 'black');
+                $this->glmpdfPlaceText($this->money($item['balance']), 560, $this->glmpdfLastY, "right", 'black');
+                break;
+
+            }
+        }
+
+        // Terms
+        $this->glmpdfSetFont("Helvetica-Bold", 12.0);
+        $this->glmpdfPlaceText('Payment Terms', 30, 200, "left", 'black');
+        $this->glmpdfPlaceLine(1, 20, 197, 570, 197, 'black');
+        $this->glmpdfSetFont("Helvetica", 12.0);
+        $this->glmpdfPlaceText($inv['payment_terms'], 30, 180, "left", 'black');
+        
+
+        // Tear-off Payment Form
+        $this->glmpdfPlaceLine(1, 20, 150, 570, 150, 'black', true, 4, 14);
+        $this->glmpdfSetFont("Helvetica", 10.0);
+        $this->glmpdfPlaceText('Please return this coupon below with your payment.', 300, 154, 'center', 'black');
+        $this->glmpdfPlaceText('Make checks payable to:', 20, 140, 'left', 'red');
+        $this->glmpdfSetFont("Helvetica", 12.0);
+
+        $this->glmpdfPlaceText($inv['company_name2'], 20, 128, "left", 'black');
+        $addr = $inv['company_addr1'];
+        if (trim($inv['company_addr2']) != '') {
+            $addr .= ', '.$inv['company_addr2'];
+        }
+        $addr .= ', '.$inv['company_city'].', '.$inv['company_state'].' '.$inv['company_zip'];
+        $this->glmpdfPlaceText($addr, 20, $this->glmpdfCurrentY, "left", 'black');
+        $this->glmpdfPlaceText($inv['company_phone'].'  -  '.$inv['company_email'], 20, $this->glmpdfCurrentY, "left", 'black');
+
+        $this->glmpdfPlaceText($inv['member_name'], 20, 80, "left", 'black');
+        $this->glmpdfPlaceText($inv['member_addr1'], 20, $this->glmpdfCurrentY, "left", 'black');
+        if (trim($inv['member_addr2']) != '') {
+            $this->glmpdfPlaceText($inv['member_addr2'], 20, $this->glmpdfCurrentY, "left", 'black');
+        }
+        $city_state = $inv['member_city'].', '.$inv['member_state'].' '.$inv['member_zip'];
+        $this->glmpdfPlaceText($city_state, 20, $this->glmpdfCurrentY, "left", 'black');
+        $this->glmpdfPlaceText($inv['member_phone'], 20, $this->glmpdfCurrentY, "left", 'black');
+        $this->glmpdfPlaceText($inv['member_email'], 20, $this->glmpdfCurrentY, "left", 'black');
+
+        $this->glmpdfSetFont("Helvetica-Bold", 12.0);
+        $this->glmpdfPlaceText('Please Pay:', 450, 80, "right", 'black');
+        $this->glmpdfPlaceText($this->money($inv['invoice_balance']), 560, $this->glmpdfLastY, "right", 'black');
+
+        $this->glmpdfSetFont("Helvetica-Bold", 12.0);
+        if (($inv['invoice_balance']-0) > 0) {
+            $this->glmpdfPlaceText('Payment Amount:', 450, 60, "right", 'black');
+            $this->glmpdfPlaceBox(1, 460, 75, 100, 20, 'black', 'white');
+        } else {
+            $this->glmpdfPlaceText('NO PAYMENT REQUIRED', 560, 60, "right", 'black');
+        }
+
+        $this->glmpdfSetFont("Helvetica", 12.0);
+        $this->glmpdfPlaceText('Member Billing #:', 450, 30, "right", 'black');
+        $this->glmpdfPlaceText($inv['member_billing_no'], 560, $this->glmpdfLastY, "right", 'black');
+
+        $barcode = $inv['member_billing_no'].'-'.$inv['invoice_number'];
+        $this->glmpdfSetFont('barcode', 40);
+        $this->glmpdfPlaceText("*$barcode*", 560, 110, 'right', 'black');
+        return $this->glmpdfEnd();
+    }
+
+    /**
+     * Format to money
+     *
+     * @param integer $value  Value
+     * @param string  $option Option
+     *
+     * @return string
+     * @access public
+     */
+    function money($value, $option = "")
+    {
+        // Check if the "NOPREFIX" option is specified
+        if (strstr($option, 'NOPREFIX')) {
+            $prefix = "";
+        } else {
+            $prefix = "$";
+        }
+
+        // Check if it's a negative value. If so, save that and make it positive.
+        $neg = false;
+        if ($value < 0) {
+            $neg = true;
+            $value *= -1;
+        }
+
+        // Do value sanity check
+        if (!is_numeric($value)) {
+            return $prefix . "0.00";
+        }
+
+        // Format number and add prefix
+        $r = $prefix.number_format($value, 2, ".", ",");
+
+        // Check if the value was negative.
+        if ($neg) {
+            if (strstr($option, 'NEG_PAREN')) {
+                $r = '('.$r.')';
+            } else {
+                $r = '-'.$r;
+            }
+        }
+
+        return $r;
+    }
+}
diff --git a/Toolkit/Members/Billing/Invoices.php b/Toolkit/Members/Billing/Invoices.php
new file mode 100644 (file)
index 0000000..5de9c39
--- /dev/null
@@ -0,0 +1,439 @@
+<?php
+/**
+ * Invoices.php
+ *
+ * PHP version 5
+ *
+ * @category Toolkit_Members
+ * @package  Billing
+ * @author   Steve Sutton <steve@gaslightmedia.com>
+ * @license  http://www.gaslightmedia.com Gaslightmedia
+ * @link     <>
+ */
+require_once GLM_APP_BASE . 'pdf/pdf_lib_8/GlmPdf.php';
+/**
+ * Toolkit_Members_Billing_Invoices
+ *
+ * @category Toolkit_Members
+ * @package  Billing
+ * @author   Steve Sutton <steve@gaslightmedia.com>
+ * @license  http://www.gaslightmedia.com Gaslightmedia
+ * @link     <>
+ */
+class Toolkit_Members_Billing_Invoices
+{
+
+    /**
+     * Class Constructor
+     * 
+     * @access public
+     */
+    public function  __construct()
+    {
+        $config = new Config();
+        $root = $config->parseConfig(
+            BASE . 'Toolkit/Members/Billing/config.ini',
+            'IniFile'
+        );
+        $this->config = $root;
+    }
+
+    /**
+     * Add invoice for the member (full price for the year)
+     * this will need to create a PDF using php pdf pdflib functions
+     *
+     * @param PDO   $dbh Database Connection
+     * @param array $row Member data array
+     *
+     * @return void
+     * @access public
+     * @throws Exception
+     */
+    public function addInvoice(PDO $dbh, $row)
+    {
+        $paymentType = Toolkit_Members_Billing_Factory::createDbObjectById(
+            $dbh,
+            'Toolkit_Members_Billing_PaymentTypes',
+            $row['payment_type']
+        );
+        if (!($paymentType instanceof Toolkit_Members_Billing_PaymentTypes)) {
+            throw new Exception(
+                'Payment Type is not an instance of
+                Toolkit_Members_Billing_PaymentTypes'
+            );
+        }
+
+        $currentInvoiceDate = $this->getCurrentInvoiceDate(true);
+
+        $amount = $paymentType->getAmount();
+        if ($amount > 0) {
+            $memberData = $this->getMemberData($dbh, $row['member_id']);
+            //var_dump($memberData);exit;
+            $stmt = new Toolkit_Members_Billing_Statement();
+            $transactionDate = date(
+                'm/d/Y',
+                $currentInvoiceDate
+            );
+            $invoice = Toolkit_Members_Billing_Factory::createDBObjectByValues(
+                'Toolkit_Members_Billing_Billing',
+                array(
+                    'member_name'      => $memberData['member_name'],
+                    'member_id'        => $row['member_id'],
+                    'transaction_date' => $transactionDate,
+                    'transaction_time' => date('m/d/Y H:i:s'),
+                    'account_number'   => $row['account_number'],
+                    'account_id'       => $row['account_id'],
+                    'billing_type'     => 1,
+                    'amount'           => (float)$amount,
+                    'emailed'          => 0,
+                    'printed'          => 0,
+                    'notes'            => '',
+                    'balance'          => (float)($stmt->getBalanceDue(
+                        $dbh,
+                        $row['member_id'],
+                        $row['account_id']
+                    ) + $amount)
+                 )
+            );
+
+            $invoice->save($dbh);
+        }
+    }
+
+    /**
+     * creating invoices
+     * 1) get only active members
+     * 2) get only members that do not have current invoice for this year
+     * 3) year
+     *
+     * @param PDO $dbh Database Connection
+     *
+     * @return string
+     * @access public
+     */
+    public function createInvoices(PDO $dbh)
+    {
+        $membersWithAccounts = $this->getMembersWithAccounts($dbh);
+        if (!empty($membersWithAccounts)) {
+            foreach ($membersWithAccounts as $member) {
+                $hasInvoice = $this->hasInvoice($dbh, $member);
+                if (!$hasInvoice) {
+                    $this->addInvoice($dbh, $member);
+                }
+            }
+        }
+        return '<div id="">Created Invoices</div>';
+    }
+    
+    /**
+     * Description of listCreateInvoices
+     * 
+     * @param PDO $dbh Database Handler
+     * 
+     * @return array
+     * @access public 
+     */
+    public function listCreateInvoices(PDO $dbh)
+    {
+        $membersWithAccounts = $this->getMembersWithAccounts($dbh);
+        if (!empty($membersWithAccounts)) {
+            foreach ($membersWithAccounts as $member) {
+                $hasInvoice = $this->hasInvoice($dbh, $member);
+                if (!$hasInvoice) {
+                    $newMembers[] = $member;
+                }
+            }
+        }
+        return $newMembers;
+    }
+    
+    /**
+     * From the member_id get the member name
+     *
+     * @param PDO     $dbh Database Connection
+     * @param integer $id  Member_id field
+     * 
+     * @return string
+     */
+    public function getMemberData(PDO $dbh, $id)
+    {
+        try {
+            $sql = "
+            SELECT *
+              FROM member
+             WHERE member_id = :member_id";
+            $stmt = $dbh->prepare($sql);
+            $stmt->bindParam(
+                ':member_id',
+                $id,
+                PDO::PARAM_INT
+            );
+            $stmt->execute();
+            return $stmt->fetch(PDO::FETCH_ASSOC);
+        } catch(PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+    }
+
+    /**
+     * get an array of members with account data
+     * 
+     * @param PDO $dbh   Database Connetion
+     * @param int $types email,usmail,fax pref
+     * 
+     * @return array
+     * @access public
+     */
+    public function getMembersWithAccounts(PDO $dbh, $types = array())
+    {
+        $members = array();
+        if (empty($types)) {
+            $types = array(
+                'email_invoice'  => 0,
+                'usmail_invoice' => 0,
+                'fax_invoice'    => 0
+            );
+        }
+        try {
+            $params = array();
+            $sql = "
+            SELECT m.member_id,m.member_name,
+                   ma.id as account_id,m.account_number,ma.payment_type
+              FROM member m, member_account ma
+             WHERE m.active = 't'
+               AND m.member_id = ma.member_id";
+            if ($types['email_invoice']) {
+                $params[] = "ma.email_invoice = 't'";
+            }
+            if ($types['usmail_invoice']) {
+                $params[] = "ma.usmail_invoice = 't'";
+            }
+            if ($types['fax_invoice']) {
+                $params[] = "ma.fax_invoice = 't'";
+            }
+            $payment_types = (is_array($_POST['payment_type']))
+                ? array_filter($_POST['payment_type'])
+                : null;
+            if ($payment_types) {
+                $params2[]
+                    = "ma.payment_type in ("
+                    . implode(",", $payment_types)
+                    . ")";
+            }
+            $counties = (is_array($_POST['county']))
+                ? array_filter($_POST['county'])
+                : null;
+            if ($counties) {
+                $params2[]
+                    = "m.region in ("
+                    . implode(",", $counties)
+                    . ")";
+            }
+            if (!empty($params)) {
+                $sql .= " AND (" . implode(" OR ", $params) . ")";
+            }
+            if (!empty($params2)) {
+                $sql .= " AND (" . implode(" AND ", $params2) . ")";
+            }
+            $sql .= " ORDER BY m.member_name";
+            $stmt = $dbh->prepare($sql);
+            $stmt->execute();
+            while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                $members[] = $row;
+            }
+        } catch (PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+        return $members;
+    }
+
+    /**
+     * get array of member without account data
+     *
+     * @param PDO $dbh Database Connection
+     *
+     * @return array
+     * @access public
+     */
+    public function getMembersWithOutAccounts(PDO $dbh)
+    {
+        $members = array();
+        try {
+            $sql = "
+            SELECT member_id,member_name
+              FROM member
+             WHERE active = 't'
+               AND (member_id NOT IN (
+                   SELECT DISTINCT member_id
+                   FROM member_account
+                   )
+                   OR (account_number is null OR account_number = '')
+                   )";
+            $sql .= " ORDER BY member_name";
+            $stmt = $dbh->prepare($sql);
+            $stmt->execute();
+            while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                $members[] = $row;
+            }
+        } catch (PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+        return $members;
+    }
+
+    /**
+     * get the current invoice date for members
+     * 
+     * Grabs the invoiceDay and invoiceMonth from config.ini and creates a
+     * timestamp using current year. If this timestamp is greater than current
+     * date then it will substract a year and use that timestamp.
+     * 
+     * @param boolean $all Description for $all
+     * 
+     * @return integer
+     * @access public
+     */
+    public function getCurrentInvoiceDate($all = false)
+    {
+        // invoice date
+        $invoiceDay = $this->config
+            ->getItem('section', 'conf')
+            ->getItem('directive', 'invoiceDay')
+            ->getContent();
+        $invoiceMonth = $this->config
+            ->getItem('section', 'conf')
+            ->getItem('directive', 'invoiceMonth')
+            ->getContent();
+        $currentInvoiceDate = mktime(
+            0,
+            0,
+            0,
+            $invoiceMonth,
+            $invoiceDay,
+            date('Y')
+        );
+        if ($currentInvoiceDate > mktime() && !$all) {
+            $currentInvoiceDate = mktime(
+                0,
+                0,
+                0,
+                $invoiceMonth,
+                $invoiceDay,
+                date('Y') - 1
+            );
+        }
+        return $currentInvoiceDate;
+    }
+    
+    /**
+     * get the next invoice date for prorated members
+     * that hasn't passed yet this year.
+     *
+     * @return integer
+     * @access public
+     */
+    public function getNextInvoiceDate()
+    {
+        // invoice date
+        $invoiceDay = $this->config
+            ->getItem('section', 'conf')
+            ->getItem('directive', 'invoiceDay')
+            ->getContent();
+        $invoiceMonth = $this->config
+            ->getItem('section', 'conf')
+            ->getItem('directive', 'invoiceMonth')
+            ->getContent();
+        $nextInvoiceDate = mktime(
+            0,
+            0,
+            0,
+            $invoiceMonth,
+            $invoiceDay,
+            date('Y')
+        );
+        if ($nextInvoiceDate <= mktime()) {
+            $nextInvoiceDate = mktime(
+                0,
+                0,
+                0,
+                $invoiceMonth,
+                $invoiceDay,
+                date('Y') + 1
+            );
+        }
+        return $nextInvoiceDate;
+    }
+
+    /**
+     * Check to see if the member has a current invoice (this year) or not
+     * Need to check based on the record in member_account table
+     *
+     * @param PDO   $dbh    Database Connection
+     * @param array $member Member array
+     *
+     * @return boolean
+     * @access public
+     */
+    public function hasInvoice(PDO $dbh, $member)
+    {
+        $nextInvoiceDate    = $this->getNextInvoiceDate();
+        $currentInvoiceDate = $this->getCurrentInvoiceDate();
+        try {
+            $sql = "
+            SELECT *
+              FROM billing
+             WHERE billing_type = 1
+               AND transaction_date BETWEEN
+                   '".date('m/d/Y', $currentInvoiceDate)."'
+                   AND
+                   '".date('m/d/Y', $nextInvoiceDate)."'
+               AND member_id = :member_id
+               AND account_id = :account_id";
+            $stmt = $dbh->prepare($sql);
+            $stmt->bindParam(
+                ':member_id',
+                $member['member_id'],
+                PDO::PARAM_INT
+            );
+            $stmt->bindParam(
+                ':account_id',
+                $member['account_id'],
+                PDO::PARAM_STR
+            );
+            $stmt->execute();
+            return (bool)$stmt->rowCount();
+        } catch (PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+    }
+
+    /**
+     * Output the billing invoice field to the browser
+     * 
+     * Billing invoice field contains the pdf base64encoded
+     *
+     * @param PDO    $dbh        Database Connection
+     * @param int    $invoice_id Billing table id
+     * @param string $name       Filename Out
+     *
+     * @return mixed
+     */
+    public function pdfToBrowser(PDO $dbh, $invoice_id, $name = 'output.pdf')
+    {
+        $billing = Toolkit_Members_Billing_Factory::createDbObjectById(
+            $dbh,
+            'Toolkit_Members_Billing_Billing',
+            $invoice_id
+        );
+        $buf = $billing->getInvoiceAsPdf($dbh);
+        $pdf = new Toolkit_Members_Billing_InvoicePdf();
+        if (!$buf) {
+            return false;
+        }
+        $len = strlen($buf);
+        header("Content-type: application/pdf");
+        header("Content-Length: $len");
+        header("Content-Disposition: inline; filename=$name");
+        echo $buf;
+    }
+}
diff --git a/Toolkit/Members/Billing/ListPaymentTypes.php b/Toolkit/Members/Billing/ListPaymentTypes.php
new file mode 100644 (file)
index 0000000..864551d
--- /dev/null
@@ -0,0 +1,112 @@
+<?php
+/**
+ * ListPaymentTypes.php
+ *
+ * PHP version 5
+ *
+ * @category Toolkit_Members
+ * @package  Billing
+ * @author   Steve Sutton <steve@gaslightmedia.com>
+ * @license  http://www.gaslightmedia.com Gaslightmedia
+ * @link     <>
+ */
+
+/**
+ * Toolkit_Members_Billing_ListPaymentTypes
+ *
+ * @category Toolkit_Members
+ * @package  Billing
+ * @author   Steve Sutton <steve@gaslightmedia.com>
+ * @license  http://www.gaslightmedia.com Gaslightmedia
+ * @link     <>
+ */
+class Toolkit_Members_Billing_ListPaymentTypes
+    extends Toolkit_DataGridBuilder
+{
+    /**
+     * Class constructor
+     * 
+     * Instantiates a DataGrid and sets up when to make the grid sortable
+     * 
+        * @param PDO     $pdo          PDO object
+     * @param integer $limit        The number of records to display per page.
+     * @param integer $page         The current page view. In most cases,
+        *                                                              this is useless. Note: if you specify
+        *                                                              this, the "page"GET variable will be ignored.
+     * @param string  $rendererType The type of renderer to use. You may
+        *                                                              prefer to use the $type argument of
+        *                                                              render, fill or getOutput.
+        *
+     * @return void   
+     * @access public 
+     */
+    public function __construct(
+        PDO $pdo,
+        $limit = null,
+        $page = null,
+        $rendererType = null
+    ) {
+               parent::__construct($pdo, $limit, $page, $rendererType);
+        $sql = "
+        SELECT *
+          FROM payment_types";
+        $this->setQuery($sql);
+               $this->options = array('dbc' => $pdo);
+        $defaultSort = array('name' => 'ASC');
+               $this->setDefaultSort($defaultSort);
+               if (!is_null($limit)) {
+                       $this->sortableAfter = $limit;
+               }
+       }
+
+    /**
+     * configure retrieved columns
+     *
+     * Tells the DataGrid how to render the retrieved columns
+     * 
+        * @return void
+     * @access protected
+     */
+    protected function configureColumns() 
+    {
+        $this->addColumn(
+            new Structures_DataGrid_Column(
+                'Function',
+                null,
+                null,
+                null,
+                null,
+                array($this, 'editButton')
+            )
+        );
+        $this->addColumn(
+            new Structures_DataGrid_Column(
+                'Name',
+                'name',
+                'name'
+            )
+        );
+        $this->addColumn(
+            new Structures_DataGrid_Column(
+                'Amount',
+                'amount',
+                'amount'
+            )
+        );
+    }
+
+    /**
+     * Description of editButton()
+     * 
+     * @param array $data Data
+     * 
+     * @return string
+     * @access public 
+     */
+    public function editButton($data)
+    {
+        $format = '<a href="'.MEDIA_BASE_URL.'admin/members.php?rt=Billing&ac=EditPaymentType&id=%s">Edit</a>';
+        return sprintf($format, $data['record']['id']);
+    }
+}
+?>
diff --git a/Toolkit/Members/Billing/MailingLabelPdf.php b/Toolkit/Members/Billing/MailingLabelPdf.php
new file mode 100644 (file)
index 0000000..5100703
--- /dev/null
@@ -0,0 +1,308 @@
+<?php
+/**
+ * MailingLabelPdf.php
+ *
+ * PHP version 5
+ *
+ * @category Toolkit_Members
+ * @package  Billing
+ * @author   Steve Sutton <steve@gaslightmedia.com>
+ * @license  http://www.gaslightmedia.com Gaslightmedia
+ * @link     <>
+ */
+require_once GLM_APP_BASE . 'pdf/pdf_lib_8/GlmPdf.php';
+/**
+ * Toolkit_Members_Billing_MailingLabelPdf
+ *
+ * @category Toolkit_Members
+ * @package  Billing
+ * @author   Steve Sutton <steve@gaslightmedia.com>
+ * @license  http://www.gaslightmedia.com Gaslightmedia
+ * @link     <>
+ */
+class Toolkit_Members_Billing_MailingLabelPdf
+    extends GlmPdf
+{
+    /**
+     * Description of getCities()
+     * 
+     * @param PDO $dbh Database handler
+     * 
+     * @return array
+     * @access protected 
+     */
+    protected function getCities(PDO $dbh)
+    {
+        $cities = array();
+        try {
+            $sql = "
+              SELECT *
+                FROM city
+            ORDER BY city_id";
+            $stmt = $dbh->query($sql);
+            while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                $cities[$row['city_id']] = $row['city_name'];
+            }
+        } catch(PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+        return $cities;
+    }
+    
+    /**
+     * Description of getStates
+     * 
+     * @param PDO $dbh database handler
+     * 
+     * @return array
+     * @access protected 
+     */
+    protected function getStates(PDO $dbh)
+    {
+        $states = array();
+        try {
+            $sql = "
+              SELECT *
+                FROM state
+            ORDER BY state_id";
+            $stmt = $dbh->query($sql);
+            while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                $states[$row['state_id']] = $row['state_abb'];
+            }
+        } catch(PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+        return $states;
+    }
+
+    /**
+     * Get all mailing addresses that have invoices
+     *
+     * @param PDO   $dbh     Database Connection
+     * @param array $members Array of member ids
+     *
+     * @return string
+     */
+    protected function getMemberAddresses(PDO $dbh, $members = array())
+    {
+        if (empty($members)) {
+            return false;
+        }
+        $memberIds = array();
+        $addresses = array();
+        $statement = new Toolkit_Members_Billing_Statement();
+        foreach ($members as $row => &$member) {
+            $balanceDue = (float)$statement->getBalanceDue(
+                $dbh,
+                $member['member_id'],
+                $member['account_id']
+            );
+            if ($_REQUEST['balance_due'] && $balanceDue <= 0.00) {
+                unset($members[$row]);
+            } else {
+                $memberIds[] = $member['member_id'];
+            }
+        }
+        if (empty($memberIds)) {
+            return false;
+        }
+        $states = $this->getStates($dbh);
+        $cities = $this->getCities($dbh);
+        try {
+            $sql = "
+              SELECT member_name,mailing_address,mailing_city_id,
+                     mailing_state_id,mailing_zip,street,city_id,
+                     state_id,zip,
+                     billing_contact as contact
+                FROM member
+               WHERE member_id IN (".implode(",", $memberIds).")
+                -- AND member_id IN (SELECT DISTINCT member_id FROM billing)
+            ORDER BY member_name";
+            $stmt = $dbh->prepare($sql);
+            $stmt->execute();
+            while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                if (strlen($row['member_name']) > 40) {
+                    $splitPos = strpos($row['member_name'], ' ', 40);
+                    if ($splitPos) {
+                        $text1    = substr($row['member_name'], 0, $splitPos);
+                        ++$splitPos;
+                        $text2    = substr(
+                            $row['member_name'],
+                            $splitPos,
+                            strlen($row['member_name'])
+                        );
+                    } else {
+                        $text1 = $row['member_name'];
+                    }
+                    
+                } else {
+                    $text1 = $row['member_name'];
+                    $text2 = null;
+                }
+                $contact = $row['contact'];
+                if ($row['mailing_address']) {
+                    $address = $row['mailing_address'];
+                    $city    = $cities[$row['mailing_city_id']];
+                    $state   = $states[$row['mailing_state_id']];
+                    $zip     = $row['mailing_zip'];
+                } else {
+                    $address = $row['street'];
+                    $city    = $cities[$row['city_id']];
+                    $state   = $states[$row['state_id']];
+                    $zip     = $row['zip'];
+                }
+                $addresses[] = array(
+                    'name'    => iconv('UTF-8', 'ISO-8859-1//IGNORE', $row['member_name']),
+                    'name1'   => iconv('UTF-8', 'ISO-8859-1//IGNORE', $text1),
+                    'name2'   => iconv('UTF-8', 'ISO-8859-1//IGNORE', $text2),
+                    'contact' => iconv('UTF-8', 'ISO-8859-1//IGNORE', $contact),
+                    'address' => iconv('UTF-8', 'ISO-8859-1//IGNORE', $address),
+                    'city'    => iconv('UTF-8', 'ISO-8859-1//IGNORE', $city),
+                    'state'   => iconv('UTF-8', 'ISO-8859-1//IGNORE', $state),
+                    'zip'     => iconv('UTF-8', 'ISO-8859-1//IGNORE', $zip)
+                );
+            }
+        } catch(PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+        return $addresses;
+    }
+
+    /**
+     * creates a pdf of labels for members
+     *
+     * @param array $addresses Address array of member data
+     *
+     * @return mixed
+     */
+    protected function glmPdfInvoice($addresses = array())
+    {
+        if (empty($addresses)) {
+            return false;
+        }
+        // setup the forms
+        $i = 1;
+        $j = 0;
+
+        for ($y = 750; $y >= 5; $y = $y - 75) {
+            for ($x = 30; $x <= 340; $x = $x + 300) {
+                $invoiceForms[$i] = array(
+                    'x'  => $x,
+                    'y'  => $y,
+                    'xs' => 300,
+                    'ys' => 75
+                );
+                ++$i;
+            }
+            ++$j;
+        }
+        // Initialize Forms
+        $r = $this->glmpdfSetForms(
+            count($invoiceForms),
+            $invoiceForms,
+            $this->glmpdfStandardPage[PAPER_SIZE]['x'],
+            $this->glmpdfStandardPage[PAPER_SIZE]['y']
+        );
+        if (!$r) {
+            echo "ERROR: Unable to set PDF forms layout.";
+            exit;
+        }
+
+        // Start PDF Generation - (Creator, Author, Title)
+        $this->glmpdfStart(
+            "GLM PDF",
+            "Gaslight Media",
+            "Standard Invoice with Payment Form"
+        );
+        
+        foreach ($addresses as $add) {
+            // Create new form
+            $this->glmpdfNextForm();
+            $this->glmpdfSetFont("Helvetica-Bold", 12.0);
+            $this->glmpdfPlaceText(
+                $add['name1'],
+                0,
+                0,
+                "left",
+                'black'
+            );
+            if ($add['name2']) {
+                $this->glmpdfPlaceText(
+                    $add['name2'],
+                    0,
+                    $this->glmpdfCurrentY,
+                    "left",
+                    'black'
+                );
+            }
+            $this->glmpdfSetFont("Helvetica", 12.0);
+            $this->glmpdfPlaceText(
+                $add['contact'],
+                0,
+                $this->glmpdfCurrentY,
+                "left",
+                'black'
+            );
+            
+            $this->glmpdfPlaceText(
+                $add['address'],
+                0,
+                $this->glmpdfCurrentY,
+                "left",
+                'black'
+            );
+            $city_state
+                = $add['city'] . ', '
+                . $add['state'] . ' '
+                . $add['zip'];
+            $this->glmpdfPlaceText(
+                $city_state,
+                0,
+                $this->glmpdfCurrentY,
+                "left",
+                'black'
+            );
+        }
+
+        $buf = $this->glmpdfEnd();
+        $len = strlen($buf);
+        if (ini_get('zlib.output_compression')) {
+            ini_set('zlib.output_compression', 'Off');
+        }
+        header("Content-type: application/force-download\n");
+        header("Content-type: application/pdf");
+        header("Content-Length: $len");
+        /* Correction for the stupid MSIE thing */
+        if (strstr(getenv('HTTP_USER_AGENT'), 'MSIE')) {
+            header("Content-Disposition: inline; filename=\"MemberLabel.pdf\"");
+        } else {
+            header("Content-Disposition: attachment; filename=\"MemberLabel.pdf\"");
+        }
+        echo $buf; 
+    }
+
+    /**
+     * gather all members with a usmail set that have invoices
+     * and call the pdf creation function
+     *
+     * @param PDO $dbh Database Connection
+     *
+     * @return void
+     */
+    public function printMailingLabels(PDO $dbh)
+    {
+        // get all member id's that have billing setup
+        $inv     = new Toolkit_Members_Billing_Invoices();
+        $members = $inv->getMembersWithAccounts(
+            $dbh,
+            array(
+                'usmail_invoice' => 1,
+                'email_invoice'  => 0,
+                'fax_invoice'    => 1
+            )
+        );
+        $addresses = $this->getMemberAddresses($dbh, $members);
+        $this->glmPdfInvoice($addresses);
+    }
+}
diff --git a/Toolkit/Members/Billing/MemberAccount.php b/Toolkit/Members/Billing/MemberAccount.php
new file mode 100644 (file)
index 0000000..a2e0d4e
--- /dev/null
@@ -0,0 +1,155 @@
+<?php
+/**
+ * Billing.php
+ *
+ * PHP version 5
+ *
+ * @category Toolkit
+ * @package  Members_Billing
+ * @author   Steve Sutton <steve@gaslightmedia.com>
+ * @license  http://www.gaslightmedia.com Gaslightmedia
+ * @link     <>
+ */
+
+/**
+ * Toolkit_Members_Billing_Billing
+ * 
+ * Member Billing Module Billing table class
+ *
+ * @category Toolkit
+ * @package  Members_Billing
+ * @author   Steve Sutton <steve@gaslightmedia.com>
+ * @license  http://www.gaslightmedia.com Gaslightmedia
+ * @link     <>
+ */
+class Toolkit_Members_Billing_MemberAccount
+    extends Toolkit_Table
+{
+    /**
+     * Description of $tableName
+     * @var string
+     * @access public
+     */
+    public $tableName = 'member_account';
+    
+    /**
+     * Description of $id
+     * @var int
+     * @access protected
+     */
+    protected $id;
+    
+    /**
+     * Description of $member_id
+     * @var int
+     * @access protected
+     */
+    protected $member_id;
+    
+    /**
+     * Description of $payment_type
+     * @var int
+     * @access protected
+     */
+    protected $payment_type;
+    
+    /**
+     * Description of $email_invoice
+     * @var boolean
+     * @access protected
+     */
+    protected $email_invoice;
+    
+    /**
+     * Description of $usmail_invoice
+     * @var boolean
+     * @access protected
+     */
+    protected $usmail_invoice;
+    
+    /**
+     * Description of $fax_invoice
+     * @var boolean
+     * @access protected
+     */
+    protected $fax_invoice;
+
+    /**
+     * Make sure this is a boolean value
+     *
+     * @param boolean $email_invoice Emailed to the member
+     *
+     * @return Toolkit_Members_Billing_Billing
+     * @access public
+     */
+    public function setEmailInvoice($email_invoice)
+    {
+        $this->email_invoice = ($email_invoice);
+        return $this;
+    }
+
+    /**
+     * Make sure this is a boolean value
+     *
+     * @param boolean $usmail_invoice Emailed to the member
+     *
+     * @return Toolkit_Members_Billing_Billing
+     * @access public
+     */
+    public function setUsmailInvoice($usmail_invoice)
+    {
+        $this->usmail_invoice = ($usmail_invoice);
+        return $this;
+    }
+
+    /**
+     * Make sure this is a boolean value
+     *
+     * @param boolean $fax_invoice Emailed to the member
+     *
+     * @return Toolkit_Members_Billing_Billing
+     * @access public
+     */
+    public function setFaxInvoice($fax_invoice)
+    {
+        $this->fax_invoice = ($fax_invoice);
+        return $this;
+    }
+    
+    /**
+     * Check to see if given an id that it is numeric
+     *
+     * @param int $type payment_type field
+     *
+     * @return Toolkit_Members_Billing_Billing
+     * @access public
+     * @throws InvalidArgumentException 
+     */
+    public function setPaymentType($type)
+    {
+        if ( !is_numeric($type)) {
+            throw new InvalidArgumentException('payment_type must be numeric');
+        }
+        $this->payment_type = $type;
+        return $this;
+    }
+
+    /**
+     * Check to see if given an id that it is numeric
+     *
+     * @param int $id member_id field
+     *
+     * @return Toolkit_Members_Billing_Billing
+     * @access public
+     * @throws InvalidArgumentException 
+     */
+    public function setMemberId($id)
+    {
+        if (!is_numeric($id)) {
+            throw new InvalidArgumentException('member_id must be numeric');
+        }
+        $this->member_id = $id;
+        return $this;
+    }
+}
+
diff --git a/Toolkit/Members/Billing/MemberLists.php b/Toolkit/Members/Billing/MemberLists.php
new file mode 100644 (file)
index 0000000..07be7f1
--- /dev/null
@@ -0,0 +1,468 @@
+<?php
+/**
+ * MemberLists.php
+ *
+ * PHP version 5
+ *
+ * @category Toolkit_Members
+ * @package  Billing
+ * @author   Steve Sutton <steve@gaslightmedia.com>
+ * @license  http://www.gaslightmedia.com Gaslightmedia
+ * @link     <>
+ */
+
+/**
+ * Toolkit_Members_Billing_MemberLists
+ *
+ * @category Toolkit_Members
+ * @package  Billing
+ * @author   Steve Sutton <steve@gaslightmedia.com>
+ * @license  http://www.gaslightmedia.com Gaslightmedia
+ * @link     <>
+ */
+class Toolkit_Members_Billing_MemberLists
+{
+    /**
+     * Database handler
+     * @var PDO
+     * @access protected
+     */
+    protected $dbh;
+    
+    /**
+     * Money Format
+     * @var string
+     * @access protected
+     */
+    protected $moneyFormat = '%01.2f';
+
+    /**
+     * Class constructor
+     * 
+     * @param PDO $pdo Database handler
+     * 
+     * @access public
+     */
+    public function __construct(PDO $pdo)
+    {
+        $this->dbh = $pdo;
+    }
+    
+    /**
+     * Pull all accounts that have a balance
+     * 
+     * @return string
+     * @access public
+     */
+    public function getOpenAccounts()
+    {
+        $out = '<p>Open Accounts</p>';
+        $mc      = new Toolkit_Members_Billing_Invoices();
+        $members = $mc->getMembersWithAccounts($this->dbh);
+        $statement = new Toolkit_Members_Billing_Statement();
+        if ($members && is_array($members)) {
+            foreach ($members as $row => &$member) {
+                $balanceDue = (float)$statement->getBalanceDue(
+                    $this->dbh,
+                    $member['member_id'],
+                    $member['account_id']
+                );
+                if ($balanceDue <= 0.00) {
+                    unset($members[$row]);
+                } else {
+                    $member['balanceDue'] = sprintf(
+                        $this->moneyFormat,
+                        (float)$balanceDue
+                    );
+                }
+            }
+            $out .= $this->displayMemberList($members);
+        }
+        return $out;
+    }
+    
+    /**
+     * Pull all accounts past due sorted by past date ranges
+     * 
+     * Using an array to sort them out by how far past the due date is
+     * probely won't have but one table to output as they may all have same 
+     * over due date range since we're using October 1st of each year as due date
+     * 
+     * @return string
+     * @access public
+     */
+    public function getAccountsByAge()
+    {
+        $out       = '';
+        $tableByAge = array(
+            0 => array('title' => 'past due'),
+            1 => array('title' => 'over 30 days'),
+            2 => array('title' => 'over 60 days'),
+            3 => array('title' => 'over 90 days'),
+            4 => array('title' => 'over 120 days')
+        );
+        $mc        = new Toolkit_Members_Billing_Invoices();
+        $members   = $mc->getMembersWithAccounts($this->dbh);
+        $statement = new Toolkit_Members_Billing_Statement();
+        
+        if ($members && is_array($members)) {
+            foreach ($members as $row => &$member) {
+                
+                $balanceDue = (float)$statement->getBalanceDue(
+                    $this->dbh,
+                    $member['member_id'],
+                    $member['account_id']
+                );
+                if ($balanceDue <= 0.00) {
+                    unset($members[$row]);
+                } else {
+                    $member['balanceDue'] = sprintf(
+                        $this->moneyFormat,
+                        (float)$balanceDue
+                    );
+                    // get original invoice
+                    $originalInvoice = $statement->getOriginalInvoice(
+                        $this->dbh,
+                        $member['member_id'],
+                        $member['account_id']
+                    );
+                    // get original invoice date
+                    $originalInvoiceDate = $originalInvoice->getTransaction_date();
+                    // calulate time span 
+                    list($month, $day, $year) = explode("/", $originalInvoiceDate);
+                    // the due date is always Oct 1st of the said year
+                    $dueDate     = new Date(mktime(0, 0, 1, 10, 1, $year));
+                    $currentDate = new Date();
+                    $span        = new Date_Span();
+                    $span->setFromDateDiff($dueDate, $currentDate);
+                    $daysPastDue = (int)$span->toDays();
+                    $format = '<p>
+                        Member Name: %s<br>
+                        Due Date: %s<br>
+                        Current Date: %s<br>
+                        Days Past Due: %s
+                        </p>';
+                    $testDates = Date::compare($currentDate, $dueDate);
+                    if ($testDates >= 0) {
+                        if ($daysPastDue <= 30) {
+                            $tableByAge[0]['data'][] = $member;
+                        } else if ($daysPastDue >= 30 && $daysPastDue < 60) {
+                            $tableByAge[1]['data'][] = $member;
+                        } else if ($daysPastDue >= 60 && $daysPastDue < 90) {
+                            $tableByAge[2]['data'][] = $member;
+                        } else if ($daysPastDue >= 90 && $daysPastDue < 120) {
+                            $tableByAge[3]['data'][] = $member;
+                        } else if ($daysPastDue >= 120) {
+                            $tableByAge[4]['data'][] = $member;
+                        }
+                    }
+                }
+            }
+        }
+        foreach ($tableByAge as $tableNumber => $tArray) {
+            if (is_array($tArray['data']) && !empty($tArray['data'])) {
+                $out .= '<p><b>'.$tArray['title'].'</b></p>';
+                $out .= $this->displayMemberList($tArray['data']);
+            }
+        }
+        return $out;
+    }
+    
+    /**
+     * Description of getClosedAccounts()
+     * 
+     * @return string
+     * @access public 
+     */
+    public function getClosedAccounts()
+    {
+        $out = '<p>Closed Accounts</p>';
+        $mc      = new Toolkit_Members_Billing_Invoices();
+        $members = $mc->getMembersWithAccounts($this->dbh);
+        $statement = new Toolkit_Members_Billing_Statement();
+        if ($members && is_array($members)) {
+            foreach ($members as $row => &$member) {
+                $balanceDue = (float)$statement->getBalanceDue(
+                    $this->dbh,
+                    $member['member_id'],
+                    $member['account_id']
+                );
+                if ($balanceDue <= 0.00) {
+                    $member['balanceDue'] = sprintf(
+                        $this->moneyFormat,
+                        (float)$balanceDue
+                    );
+                } else {
+                    unset($members[$row]);
+                }
+            }
+            $out .= $this->displayMemberList($members);
+        }
+        return $out;
+    }
+    
+    /**
+     * Description for getAllAccounts()
+     * 
+     * @return string
+     * @access public
+     */
+    public function getAllAccounts()
+    {
+        $out = '<p>All Accounts</p>';
+        $mc      = new Toolkit_Members_Billing_Invoices();
+        $members = $mc->getMembersWithAccounts($this->dbh);
+        $statement = new Toolkit_Members_Billing_Statement();
+        if ($members && is_array($members)) {
+            foreach ($members as $row => &$member) {
+                $balanceDue = (float)$statement->getBalanceDue(
+                    $this->dbh,
+                    $member['member_id'],
+                    $member['account_id']
+                );
+                if ($balanceDue <= 0.00) {
+                    $member['balanceDue'] = sprintf(
+                        $this->moneyFormat,
+                        (float)$balanceDue
+                    );
+                } else {
+                    $member['balanceDue'] = 'N/A';
+                }
+            }
+            $out .= $this->displayMemberList($members, 7);
+        }
+        return $out;
+    }
+    
+    /**
+     * Description for getAccountsNeedingData
+     * 
+     * @return string
+     * @access publics
+     */
+    public function getAccountsNeedingData()
+    {
+        $out = '<p>Accounts Needing Data</p>';
+        $mc      = new Toolkit_Members_Billing_Invoices();
+        $members = $mc->getMembersWithOutAccounts($this->dbh);
+        $statement = new Toolkit_Members_Billing_Statement();
+        if ($members && is_array($members)) {
+            foreach ($members as $row => &$member) {
+                $member['balanceDue'] = 'N/A';
+                
+            }
+            $out .= $this->displayMemberList($members, 7);
+        }
+        return $out;
+    }
+    
+    /** 
+     * Description for getPrintableInvoices()
+     * 
+     * @return string
+     * @access public 
+     */
+    public function getPrintableInvoices()
+    {
+        $out     = '<p>Printable Invoices</p>';
+        $inv     = new Toolkit_Members_Billing_Invoices();
+        $members = $inv->getMembersWithAccounts(
+            $this->dbh,
+            array(
+                'usmail_invoice' => 1,
+                'email_invoice'  => 0,
+                'fax_invoice'    => 1
+            ),
+            $_POST
+        );
+        $statement = new Toolkit_Members_Billing_Statement();
+        if ($members && is_array($members)) {
+            foreach ($members as $row => &$member) {
+                $balanceDue = (float)$statement->getBalanceDue(
+                    $this->dbh,
+                    $member['member_id'],
+                    $member['account_id']
+                );
+                if ($balanceDue <= 0.00) {
+                    unset($members[$row]);
+                } else {
+                    $member['balanceDue'] = sprintf(
+                        $this->moneyFormat,
+                        (float)$balanceDue
+                    );
+                }
+            }
+            $out = $this->displayMemberList($members);
+        }
+        return $out;
+    }
+    
+    /**
+     * Description for getEmailableInvoices()
+     * 
+     * @return string
+     * @access public 
+     */
+    public function getEmailableInvoices()
+    {
+        $out     = '<p>Emails Invoices</p>';
+        $inv     = new Toolkit_Members_Billing_Invoices();
+        $members = $inv->getMembersWithAccounts(
+            $this->dbh,
+            array(
+                'usmail_invoice' => 0,
+                'email_invoice'  => 1,
+                'fax_invoice'    => 0
+            )
+        );
+        $statement = new Toolkit_Members_Billing_Statement();
+        if ($members && is_array($members)) {
+            foreach ($members as $row => &$member) {
+                $balanceDue = (float)$statement->getBalanceDue(
+                    $this->dbh,
+                    $member['member_id'],
+                    $member['account_id']
+                );
+                if ($balanceDue <= 0.00) {
+                    unset($members[$row]);
+                } else {
+                    $member['balanceDue'] = sprintf(
+                        $this->moneyFormat,
+                        (float)$balanceDue
+                    );
+                }
+            }
+            $out = $this->displayMemberList($members);
+        }
+        return $out;
+    }
+    
+    /**
+     * Description for getMailableInvoices()
+     * 
+     * @return string
+     * @access public
+     */
+    public function getMailableInvoices()
+    {
+        $out     = '<p>Labels for Invoices</p>';
+        $inv     = new Toolkit_Members_Billing_Invoices();
+        $members = $inv->getMembersWithAccounts(
+            $this->dbh,
+            array(
+                'usmail_invoice' => 1,
+                'email_invoice'  => 0,
+                'fax_invoice'    => 1
+            )
+        );
+        $statement = new Toolkit_Members_Billing_Statement();
+        if ($members && is_array($members)) {
+            foreach ($members as $row => &$member) {
+                $balanceDue = (float)$statement->getBalanceDue(
+                    $this->dbh,
+                    $member['member_id'],
+                    $member['account_id']
+                );
+                if ($_REQUEST['balance_due'] && $balanceDue <= 0.00) {
+                    unset($members[$row]);
+                } else {
+                    $member['balanceDue'] = sprintf(
+                        $this->moneyFormat,
+                        (float)$balanceDue
+                    );
+                }
+            }
+            $out = $this->displayMemberList($members);
+        }
+        return $out;
+    }
+    
+    /**
+     * Description for getNeededInvoices()
+     * 
+     * @return string
+     * @access public 
+     */
+    public function getNeededInvoices()
+    {
+        $out = '<p>No Records Matching</p>';
+        $mc = new Toolkit_Members_Billing_Invoices();
+        $members = $mc->listCreateInvoices($this->dbh);
+        
+        if (!empty($members)) {
+            foreach ($members as $row => &$member) {
+                if (in_array($member['payment_type'], array(1))) {
+                    unset($members[$row]);
+                }
+            }
+            $out = $this->displayMemberList($members);
+        }
+        return $out;
+    }
+
+    /**
+     * Description for displayMemberList
+     * 
+     * @param array  $members Members array
+     * @param string $tab     Description for $tab
+     * 
+     * @return boolean|string
+     * @access protected
+     */
+    protected function displayMemberList($members, $tab = 'invoices')
+    {
+        if (is_array($members) && !empty($members)) {
+            $paymentTypes = $this->getPaymentTypes();
+            $counter = 1;
+            foreach ($members as $row => &$member) {
+                $member['classStyle']
+                    = ($counter % 2)
+                    ? 'even'
+                    : 'odd';
+                ++$counter;
+                $member['member_url']
+                    = MEDIA_BASE_URL . 'admin/members.php?rt=Members&ac=editMember&id='
+                    . $member['member_id'] . '&tab=' . $tab;
+                $member['payment_type']
+                    = $paymentTypes[$member['payment_type']];
+            }
+            $tplOptions = $GLOBALS['flexyOptions'];
+            $tplOptions['templateDir']
+                = BASE . 'Toolkit/Members/Billing/templates';
+            $tplOptions['compileDir']
+                = BASE . 'Toolkit/Members/Billing/templates/compiled';
+            $tpl = new HTML_Template_Flexy($tplOptions);
+            $page = new stdClass();
+            $page->members = $members;
+            $tpl->compile('memberList.html');
+
+            return $tpl->bufferedOutputObject($page);
+        } else {
+            return false;
+        }
+    }
+    
+    /**
+     * Description of getPaymentTypes
+     * 
+     * @return array
+     * @access protected
+     */
+    protected function getPaymentTypes()
+    {
+        $types = array();
+        $sql = "
+          SELECT id, name
+            FROM payment_types
+        ORDER BY id";
+        try {
+            $stmt = $this->dbh->query($sql);
+            while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                $types[$row['id']] = $row['name'];
+            }
+            return $types;
+        } catch(PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+    }
+}
diff --git a/Toolkit/Members/Billing/PaymentForm.php b/Toolkit/Members/Billing/PaymentForm.php
new file mode 100644 (file)
index 0000000..6bf1a0a
--- /dev/null
@@ -0,0 +1,548 @@
+<?php
+
+/**
+ * Controls Amenity definitions for the member db
+ * 
+ * PHP version 5
+ * 
+ * @category  MembersDB
+ * @package   Toolkit_Members_Billing
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2011 Gaslight Media
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @link      http://demo.gaslightmedia.com
+ */
+
+/**
+ * Handles creating / editing amenities that the members will use
+ * 
+ * @category  MembersDB
+ * @package   Toolkit_Members_Billing
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2011 Gaslight Media
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @link      http://demo.gaslightmedia.com
+ */
+class Toolkit_Members_Billing_PaymentForm
+    extends Toolkit_Members_Billing_Auxiliary
+{
+    /**
+     * template for edit form
+     *
+     * @var    string   
+     * @access protected
+     */
+    protected $formTemplate = 'paymentForm.html';
+
+    /**
+     * Class constructor
+     *
+     * @param PDO    $pdo         PHP Data Object
+     * @param string $formName    Form's name.
+     * @param string $method      (optional)Form's method defaults to 'POST'
+     * @param string $action      (optional)Form's action
+     * @param string $target      (optional)Form's target defaults to '_self'
+     * @param mixed  $attributes  (optional)Extra attributes for <form> tag
+     * @param bool   $trackSubmit (optional)Whether to track if the form was
+     *                              submitted by adding a special hidden field
+     * 
+     * @access public
+     */
+    public function __construct(
+        PDO $pdo,
+        $formName,
+        $method = 'post',
+        $action = '',
+        $target = '',
+        $attributes = null,
+        $trackSubmit = false
+    ) {
+        parent::__construct(
+            $formName,
+            $method,
+            $action,
+            $target,
+            $attributes,
+            $trackSubmit
+        );
+
+        $this->dbh = $pdo;
+        
+        $config = new Config();
+        $root = $config->parseConfig(
+            BASE . 'Toolkit/Members/Billing/config.ini',
+            'IniFile'
+        );
+        $this->config = $root;
+    }
+    
+    /**
+     * Description for addAdjustment
+     * 
+     * @param array $values Values array
+     *
+     * @return void
+     * @access protected
+     */
+    protected function addAdjustment($values)
+    {
+        $statement = new Toolkit_Members_Billing_Statement();
+        $balanceDue = (float)$statement->getBalanceDue(
+            $this->dbh,
+            $values['member_id'],
+            $values['account_id']
+        );
+        $balance = (float)($balanceDue + (float)$values['amount']);
+        $paid
+            = ($balance == (float)'0.00')
+            ? true
+            : false;
+        // need to get the original invoice id
+        $originalInvoice = $statement->getOriginalInvoice(
+            $this->dbh,
+            $values['member_id'],
+            $values['account_id']
+        );
+        $invoiceId = $originalInvoice->getInvoice_id();
+        if ($values['amount']) {
+            $invoice = Toolkit_Members_Billing_Factory::createDBObjectByValues(
+                'Toolkit_Members_Billing_Billing',
+                array(
+                    'member_name'      => $values['member_name'],
+                    'invoice_id'       => $invoiceId,
+                    'member_id'        => $values['member_id'],
+                    'transaction_date' => date('m/d/Y'),
+                    'transaction_time' => date('m/d/Y H:i:s'),
+                    'account_number'   => $values['account_number'],
+                    'account_id'       => $values['account_id'],
+                    'billing_type'     => 3,
+                    'emailed'          => 0,
+                    'printed'          => 0,
+                    'amount'           => (float)$values['amount'],
+                    'balance'          => $balance,
+                    'paid'             => $paid,
+                    'payment_method'   => $values['payment_method'],
+                    'payment_data'     => $values['payment_data']
+                )
+            )->save($this->dbh);
+        }
+    }
+    
+    /**
+     * Description for addPayment()
+     * 
+     * @param array $values Values Array
+     * 
+     * @return void
+     * @access protected 
+     */
+    protected function addPayment($values)
+    {
+        //var_dump($values);exit;
+        $statement = new Toolkit_Members_Billing_Statement();
+        $balanceDue = (float)$statement->getBalanceDue(
+            $this->dbh,
+            $values['member_id'],
+            $values['account_id']
+        );
+        //var_dump($balanceDue);exit;
+        if ($values['amount']) {
+            $balanceDue = (float)($balanceDue - (float)$values['amount']);
+            $paid
+                = ($balanceDue == (float)'0.00')
+                ? true
+                : false;
+            // need to get the original invoice id
+            $originalInvoice = $statement->getOriginalInvoice(
+                $this->dbh,
+                $values['member_id'],
+                $values['account_id']
+            );
+            $invoiceId = $originalInvoice->getInvoice_id();
+            if ($paid) {
+                $originalInvoice
+                    ->setPaid(true)
+                    ->save($this->dbh);
+                
+            }
+            $invoice = Toolkit_Members_Billing_Factory::createDBObjectByValues(
+                'Toolkit_Members_Billing_Billing',
+                array(
+                    'member_name'      => $values['member_name'],
+                    'invoice_id'       => $invoiceId,
+                    'member_id'        => $values['member_id'],
+                    'transaction_date' => date('m/d/Y'),
+                    'transaction_time' => date('m/d/Y H:i:s'),
+                    'account_number'   => $values['account_number'],
+                    'account_id'       => $values['account_id'],
+                    'billing_type'     => 2,
+                    'emailed'          => 0,
+                    'printed'          => 0,
+                    'amount'           => (float)$values['amount'],
+                    'balance'          => $balanceDue,
+                    'paid'             => $paid,
+                    'payment_method'   => $values['payment_method'],
+                    'payment_data'     => $values['payment_data']
+                )
+            )->save($this->dbh);
+        }
+    }
+
+    /**
+     * Description for checkForAccountNumber()
+     * 
+     * @param string $accountNumber Account number
+     * 
+     * @return boolean
+     * @access public
+     */
+    public function checkForAccountNumber($accountNumber)
+    {
+        try {
+            $sql = "
+            SELECT count(member_id) as count
+              FROM member
+             WHERE account_number = :account_number";
+            $stmt = $this->dbh->prepare($sql);
+            $stmt->bindParam(
+                ':account_number',
+                $accountNumber,
+                PDO::PARAM_STR
+            );
+            $stmt->execute();
+            $valid = $stmt->fetchColumn();
+            //var_dump($accountNumber);
+            //var_dump($valid);
+            //var_dump((bool)$valid);exit;
+            return (bool) $valid;
+        } catch(PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+    }
+    
+    /**
+     * Form element definitions
+     * 
+     * @return void     
+     * @access public
+     */
+    public function configureElements()
+    {
+        $e = array();
+        $paymentTypes   = $this->getPaymentTypes();
+        $paymentMethods = $this->getPaymentMethods();
+        unset($paymentMethods[0]);
+        $paymentMethods['5'] = 'Adjustment';
+        //    All Grouped Elements are created here.
+
+        //    All Elements are created here.
+        //    This includes group element definitions.
+        $e[] = array(
+            'type'    => 'header',
+            'req'     => false,
+            'name'    => 'PaymentTypeInfoHdr',
+            'display' => 'Make Payment'
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => true,
+            'name'    => 'account_number',
+            'display' => 'Account Number'
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => true,
+            'name'    => 'amount',
+            'display' => 'Amount'
+        );
+        $e[] = array(
+            'type'    => 'select',
+            'req'     => true,
+            'name'    => 'payment_type',
+            'display' => 'Payment Type',
+            'opts'    => array('' => '-- Select --') + $paymentTypes
+        );
+        $e[] = array(
+            'type'    => 'select',
+            'req'     => true,
+            'name'    => 'payment_method',
+            'display' => 'Payment Method',
+            'opts'    => array('' => '-- Select --') + $paymentMethods
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => 'payment_data',
+            'display' => 'Payment Details',
+            'opts'    => array('class' => 'text')
+        );
+
+
+        $this->setupElements($e);
+    }
+
+    /**
+     * Descriptionfor configureFilters()
+     * 
+     * @return void
+     * @access public 
+     */
+    public function configureFilters()
+       {
+               $f = array();
+               $f[] = array(
+                       'element' => '__ALL__',
+                       'filter' => 'trim'
+               );
+
+               $this->setupFilters($f);
+       }
+    
+    /**
+     * Helper function to configure an entire form
+     *
+     * @return void
+     * @access public
+     */
+    public function configureForm()
+    {
+        $this->configureElements();
+        $this->configureFilters();
+        $this->configureRules();
+    }
+
+    /**
+     * Form rule definitions
+     * 
+     * @return void     
+     * @access public
+     */
+    public function configureRules()
+    {
+        $r = array();
+
+        $r[] = array(
+            'element' => 'amount',
+            'message' => 'ERROR: Invalid amount!',
+            'type' => 'numeric',
+            'format' => null,
+            'validation' => $this->validationType,
+            'reset' => true,
+            'force' => false
+        );
+        $r[] = array(
+            'element'    => 'account_number',
+            'message'    => 'ERROR: No Member with that Account Number',
+            'type'       => 'callback',
+            'format'     => array(&$this, 'checkForAccountNumber'),
+            'validation' => $this->validationType,
+            'reset'      => false,
+            'force'      => false
+        );
+
+        $this->setupRules($r);
+    }
+    
+    /**
+     * Description for getMemberDataByAccountNumber()
+     * 
+     * @param array $values Values array
+     * 
+     * @return mixed 
+     * @access protected
+     */
+    protected function getMemberDataByAccountNumber($values)
+    {
+        try {
+            $sql = "
+            SELECT m.member_id,m.member_name,m.account_number,
+                   ma.id as account_id,ma.payment_type
+              FROM member m, member_account ma
+             WHERE m.active = 't'
+               AND m.member_id = ma.member_id
+               AND m.account_number = :account_number
+               AND ma.payment_type = :payment_type";
+            $stmt = $this->dbh->prepare($sql);
+            $stmt->bindParam(
+                ':account_number',
+                $values['account_number'],
+                PDO::PARAM_INT
+            );
+            $stmt->bindParam(
+                ':payment_type',
+                $values['payment_type'],
+                PDO::PARAM_INT
+            );
+            $stmt->execute();
+            return $stmt->fetch(PDO::FETCH_ASSOC);
+        } catch(PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+    }
+
+    /**
+     * Description for getMemberDataByInvoiceId()
+     * 
+     * @param array $values Values array
+     * 
+     * @return boolean|array
+     * @access protected
+     */
+    protected function getMemberDataByInvoiceId($values)
+    {
+        try {
+            // first need to get member_id from the billing table
+            $sql = "
+            SELECT member_id,account_id
+              FROM billing
+             WHERE billing_type = 1
+               AND invoice_id = :invoice_id";
+            $stmt = $this->dbh->prepare($sql);
+            $stmt->bindParam(
+                ':invoice_id',
+                $values['invoice_id'],
+                PDO::PARAM_INT
+            );
+            $stmt->execute();
+            $member     = $stmt->fetch(PDO::FETCH_ASSOC);
+            $member_id  = $member['member_id'];
+            $account_id = $member['account_id'];
+            if (!$member_id) {
+                return false;
+            } else {
+                $sql = "
+                SELECT m.member_id,m.member_name,m.account_number,
+                       ma.id as account_id,ma.payment_type
+                  FROM member m, member_account ma
+                 WHERE m.active = 't'
+                   AND m.member_id = ma.member_id
+                   AND m.member_id = :member_id";
+                $stmt = $this->dbh->prepare($sql);
+                $stmt->bindParam(
+                    ':member_id',
+                    $member_id,
+                    PDO::PARAM_INT
+                );
+                $stmt->execute();
+                $memberData = $stmt->fetchAll(PDO::FETCH_ASSOC);
+                if (is_array($memberData) && !empty($memberData)) {
+                    foreach ($memberData as $memberD) {
+                        if ($memberD['account_id'] == $account_id) {
+                            return $memberD;
+                        }
+                    }
+                }
+            }
+        } catch(PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+    }
+    
+    /**
+     * Description of getPaymentMethods()
+     * 
+     * @return array 
+     * @access protected
+     */
+    protected function getPaymentMethods()
+    {
+        $types = $this->config
+            ->getItem('section', 'conf')
+            ->getItem('directive', 'paymentMethod')
+            ->getContent();
+        unset($types[2]);
+        return $types;
+    }
+    
+    /**
+     * Description of getPaymentTypes()
+     * 
+     * @return array
+     * @access protected
+     */
+    protected function getPaymentTypes()
+    {
+        $paymentTypes = array();
+        try {
+            $sql = "
+               SELECT *
+                FROM payment_types
+               WHERE dynamic_amount = true
+                  OR amount > 0.00
+            ORDER BY name";
+            $stmt = $this->dbh->prepare($sql);
+            $stmt->execute();
+            while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                $paymentTypes[$row['id']] = $row['name'];
+            }
+            return $paymentTypes;
+        } catch (PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+    }
+
+    /**
+     * Description of processData()
+     * 
+     * @param array $values Values array
+     * 
+     * @return boolean 
+     * @access protected
+     */
+    protected function processData($values)
+    {
+        $memberData = $this->getMemberDataByAccountNumber($values);
+        //var_dump($memberData);
+        //exit;
+        if (!$memberData) {
+            return false;
+        }
+        if (   $memberData['account_id']
+            && $values['amount']
+            && $values['payment_method']
+        ) {
+            if ($values['payment_method'] == 5) {
+                // add adjustment
+                $adjustment = array(
+                    'member_id'      => $memberData['member_id'],
+                    'account_id'     => $memberData['account_id'],
+                    'member_name'    => $memberData['member_name'],
+                    'account_number' => $memberData['account_number'],
+                    'amount'         => $values['amount'],
+                    'payment_method' => null,
+                    'payment_data'   => $values['payment_data']
+                );
+                $this->addAdjustment($adjustment);
+            } else {
+                $payment = array(
+                    'member_id'      => $memberData['member_id'],
+                    'account_id'     => $memberData['account_id'],
+                    'member_name'    => $memberData['member_name'],
+                    'account_number' => $memberData['account_number'],
+                    'amount'         => $values['amount'],
+                    'payment_method' => $values['payment_method'],
+                    'payment_data'   => $values['payment_data']
+                );
+                $this->addPayment($payment);
+            }
+        }
+        return true;
+    }
+    
+    /**
+     * Renders the form for viewing
+     *
+     * This function validates the form if needed, and if it successfully
+     * validates attempts to insert or update the data record.
+     * If it is unsuccessful, it will return an error to the user
+     * informing them of what went wrong.
+     *
+     * @return string The compiled and filled form template.
+     * @access public
+     */
+    public function toHtml()
+    {
+        $listPage = MEDIA_BASE_URL . 'admin/members.php?rt=Payment&formSubmitGood=1';
+        return parent::toHtml($listPage);
+    }
+}
diff --git a/Toolkit/Members/Billing/PaymentTypes.php b/Toolkit/Members/Billing/PaymentTypes.php
new file mode 100644 (file)
index 0000000..938f642
--- /dev/null
@@ -0,0 +1,115 @@
+<?php
+/**
+ * PaymentTypes.php
+ *
+ * PHP version 5
+ *
+ * @category Toolkit
+ * @package  Members_Billing
+ * @author   Steve Sutton <steve@gaslightmedia.com>
+ * @license  http://www.gaslightmedia.com Gaslightmedia
+ * @link     <>
+ */
+
+/**
+ * Toolkit_Members_Billing_PaymentTypes
+ *
+ * Member Billing Module Payment Type table class
+ *
+ * @category Toolkit
+ * @package  Members_Billing
+ * @author   Steve Sutton <steve@gaslightmedia.com>
+ * @license  http://www.gaslightmedia.com Gaslightmedia
+ * @link     <>
+ */
+class Toolkit_Members_Billing_PaymentTypes
+    extends Toolkit_Table
+{
+    /**
+     * Description of $tableName
+     * @var string
+     * @access public
+     */
+    public $tableName = 'payment_types';
+    
+    /**
+     * Description of $id
+     * @var int
+     * @access protected
+     */
+    protected $id;
+    
+    /**
+     * Description of $name
+     * @var string
+     * @access protected
+     */
+    protected $name;
+    
+    /**
+     * Description of $qcode
+     * @var int
+     * @access protected
+     */
+    protected $qcode;
+    
+    /**
+     * Description of $category
+     * @var text
+     * @access protected
+     */
+    protected $category;
+    
+    /**
+     * Description of $amount
+     * @var int
+     * @access protected
+     */
+    protected $amount;
+    
+    /**
+     * Description of $notes
+     * @var string
+     * @access protected
+     */
+    protected $notes;
+    
+    /**
+     * Description of $dynamic_amount
+     * @var boolean
+     * @access protected
+     */
+    protected $dynamic_amount;
+
+    /**
+     * Description of setDynamicAmount
+     * 
+     * @param boolean $dynamic_amount Dynamic Amount
+     * 
+     * @return \Toolkit_Members_Billing_PaymentTypes 
+     * @access public
+     */
+    public function setDynamicAmount($dynamic_amount)
+    {
+        $this->dynamic_amount = ($dynamic_amount);
+        return $this;
+    }
+
+    /**
+     * Description of setQcode
+     * 
+     * @param int $id Qcode ID
+     * 
+     * @return \Toolkit_Members_Billing_PaymentTypes
+     * @throws InvalidArgumentException 
+     * @access public
+     */
+    public function setQcode($id)
+    {
+        if (!is_numeric($id)) {
+            throw new InvalidArgumentException('qcode must be numeric');
+        }
+        $this->qcode = $id;
+        return $this;
+    }
+}
diff --git a/Toolkit/Members/Billing/PrintInvoices.php b/Toolkit/Members/Billing/PrintInvoices.php
new file mode 100644 (file)
index 0000000..e38210a
--- /dev/null
@@ -0,0 +1,232 @@
+<?php
+/**
+ * EmailInvoices.php
+ *
+ * PHP version 5
+ *
+ * @category Toolkit_Members
+ * @package  Billing
+ * @author   Steve Sutton <steve@gaslightmedia.com>
+ * @license  http://www.gaslightmedia.com Gaslightmedia
+ * @link     <>
+ */
+require_once GLM_APP_BASE . 'pdf/pdf_lib_8/GlmPdf.php';
+/**
+ * Toolkit_Members_Billing_EmailInvoices
+ *
+ * @category Toolkit_Members
+ * @package  Billing
+ * @author   Steve Sutton <steve@gaslightmedia.com>
+ * @license  http://www.gaslightmedia.com Gaslightmedia
+ * @link     <>
+ */
+class Toolkit_Members_Billing_PrintInvoices
+    extends Toolkit_Members_Billing_InvoiceAbstract
+    implements Toolkit_Members_Billing_IInvoice
+{
+    /**
+     * Subject line for email
+     * 
+     * @var string
+     */
+    protected $subject = 'Member Invoice';
+
+    /**
+     * Billing storage array
+     *
+     * @var array
+     * @access protected
+     */
+    protected $billings = array();
+
+    /**
+     * Template file to use
+     * 
+     * @var string
+     */
+    protected $template = 'sendEmailInvoice.html';
+
+    /**
+     * Base of the billing template directory
+     *  
+     * @var string
+     * @access protected
+     */
+    protected $templateBase = 'Toolkit/Members/Billing/';
+    
+    /**
+     * The directory of the templates
+     *
+     * @var string
+     * @access protected
+     */
+    protected $templatesDir = 'templates';
+
+    /**
+     * The directory of the flexy-compiled templates
+     *
+     * @var string
+     * @access protected
+     */
+    protected $compiledDir = 'templates/compiled';
+    
+    /**
+     * Flexy Template Options array
+     * 
+     * @var array
+     */
+    protected $flexyOptions = array();
+
+    /**
+     * class Constructor
+     *
+     * @return void
+     */
+    public function  __construct() {
+        $this->flexyOptions = array(
+            'templateDir'  => BASE . "{$this->templateBase}{$this->templatesDir}",
+            'compileDir'   => BASE . "{$this->templateBase}{$this->compiledDir}",
+            'forceCompile' => 1,
+            'debug'        => 0,
+            'locale'       => 'en',
+        );
+    }
+    /**
+     * get all members with the usmail,fax type set and print their invoices
+     *
+     * @todo     generate email from template
+     * @param PDO $dbh Database Connection
+     *
+     * @return void
+     */
+    public function printInvoices(PDO $dbh)
+    {
+        // get all member id's that have billing setup
+        $inv     = new Toolkit_Members_Billing_Invoices();
+        $members = $inv->getMembersWithAccounts(
+            $dbh,
+            array(
+                'usmail_invoice' => 1,
+                'email_invoice'  => 0,
+                'fax_invoice'    => 1
+            )
+        );
+        if (empty($members)) {
+            return false;
+        }
+        $this->billings = $this->getLastInvoices($dbh, $members, 'printed');
+        if (empty($this->billings)) {
+            return false;
+        }
+        try {
+            $pdf = PDF_new();
+            $i   = 1;
+            if (PDF_begin_document($pdf, '', "") == 0) {
+               die("Error: " . PDF_get_errmsg($pdf));
+            }
+
+            foreach ($this->billings as $bill) {
+                if ($bill->getAmount() == 0) {
+                    continue;
+                }
+                if ($bill->getBalance() == 0) {
+                    continue;
+                }
+                $pdfName     = "/pvf/invoice{$i}.pdf";
+                $out        .= '<p>pdfName: '.$pdfName.'</p>';
+                $out        .= '<p>pdf_create_pvf</p>';
+                $invoiceText = $bill->getInvoiceAsPdf($dbh);
+                $pvf         = pdf_create_pvf(
+                    $pdf,
+                    $pdfName,
+                    $invoiceText,
+                    ""
+                );
+                if ($pvf == 0) {
+                    $out .= "<p>Error: " . PDF_get_errmsg($pdf).'</p>';
+                }
+                $out   .= '<p>pdf_open_pdi_document</p>';
+                $srcDoc = pdf_open_pdi_document($pdf,
+                    $pdfName,
+                    ''
+                );
+                if ($srcDoc == 0) {
+                    $out .= "<p>Error: " . PDF_get_errmsg($pdf).'</p>';
+                }
+                $out     .= '<p>pdf_open_pdi_page</p>';
+                $srcPage  = pdf_open_pdi_page($pdf, $srcDoc, 1, '');
+                if ($srcPage == 0) {
+                    $out .= "<p>Error: " . PDF_get_errmsg($pdf).'</p>';
+                }
+                
+                $srcWidth = pdf_get_pdi_value(
+                    $pdf,
+                    'width',
+                    $srcDoc,
+                    $srcPage,
+                    0
+                );
+                $out      .= '<p>pdf_get_pdi_value: width = '.$srcWidth.'</p>';
+                $srcHeight = pdf_get_pdi_value(
+                    $pdf,
+                    'height',
+                    $srcDoc,
+                    $srcPage,
+                    0
+                );
+                $out .= '<p>pdf_get_pdi_value: height = '.$srcHeight.'</p>';
+                $out .= '<p>PDF_begin_page_ext</p>';
+                PDF_begin_page_ext($pdf, $srcWidth, $srcHeight, '');
+                $out .= '<p>pdf_fit_pdi_page</p>';
+                pdf_fit_pdi_page($pdf, $srcPage, 0, 0, '');
+                $out .= '<p>pdf_close_pdi_page</p>';
+                pdf_close_pdi_page($pdf, $srcPage);
+                $out .= '<p>PDF_end_page_ext</p>';
+                PDF_end_page_ext($pdf, '');
+                $out .= '<p>PDF_delete_pvf</p>';
+                PDF_delete_pvf($pdf, $pdfName);
+                // Don't mark sent to member at this time
+                //$this->markSentToMember($dbh, $bill);
+                ++$i;
+            }
+
+            
+        } catch(PDFlibException $e) {
+            Toolkit_Common::handleError($e);
+            exit;
+        }
+        PDF_end_document($pdf, '');
+        $buf = PDF_get_buffer($pdf);
+        PDF_delete($pdf);
+        $len = strlen($buf);
+        header("Content-type: application/force-download\n");
+        header("Content-type: application/pdf");
+        header("Content-Length: $len");
+        /* Correction for the stupid MSIE thing */
+        if (strstr(getenv('HTTP_USER_AGENT'), 'MSIE')) {
+            header("Content-Disposition: inline; filename=\"MemberInvoices.pdf\"");
+        } else {
+            header("Content-Disposition: attachment; filename=\"MemberInvoices.pdf\"");
+        }
+        print $buf;
+        exit;
+    }
+
+    /**
+     * with an existing billing object set the billing feild printed
+     * to true (boolean) and save it
+     *
+     * @param PDO                             $dbh     Database Connection
+     * @param Toolkit_Members_Billing_Billing $billing Billing object
+     *
+     * @return void
+     */
+    public function markSentToMember(
+        PDO $dbh,
+        Toolkit_Members_Billing_Billing $billing
+    ) {
+        $billing
+            ->setPrinted(1) // set to true
+            ->save($dbh); // save the object to database
+    }
+}
diff --git a/Toolkit/Members/Billing/QifExport.php b/Toolkit/Members/Billing/QifExport.php
new file mode 100644 (file)
index 0000000..f59e0c8
--- /dev/null
@@ -0,0 +1,201 @@
+<?php
+
+/**
+ * QifExport.php
+ *
+ * PHP version 5
+ *
+ * @category Toolkit_Members
+ * @package  Billing
+ * @author   Steve Sutton <steve@gaslightmedia.com>
+ * @license  http://www.gaslightmedia.com Gaslightmedia
+ * @link     <>
+ */
+
+/**
+ * Toolkit_Members_Billing_QifExport
+ *
+ * @category Toolkit_Members
+ * @package  Billing
+ * @author   Steve Sutton <steve@gaslightmedia.com>
+ * @license  http://www.gaslightmedia.com Gaslightmedia
+ * @link     <>
+ */
+class Toolkit_Members_Billing_QifExport
+{
+    
+    /**
+     * Description of $fileData
+     * @var array
+     * @access protected 
+     */
+    protected $fileData = array();
+    const HEADERFORMAT = "!Type:Bank\nD%s\nT%s\nP%s\nL[%s]\n^\n";
+    const INVOICEFORMAT = "!D%s\nT%s\nP%s\nL%s\n^\n";
+    const PAYMENTFORMAT = "D%s\nT%s\nP%s\nN%s\nM%s\nL%s\n^\n";
+    const MAINCATEGORY  = 'Member Services:';
+    const INVCATEGORY   = 'Other Inc';
+    
+    /**
+     * Description of $paymentTypes
+     * @var array
+     */
+    protected $paymentTypes = array();
+
+    /**
+     * Class constructor
+     * 
+     * @return void
+     * @access public 
+     */
+    public function __construct()
+    {
+        // nothing yet
+    }
+    
+    /**
+     * Description for setQUery
+     * 
+     * @param PDO   $dbh    Database handler
+     * @param array $params Parameter array
+     * 
+     * @return void
+     * @access public
+     */
+    public function setQuery(PDO $dbh, $params)
+    {
+        
+        //$params[] = "billing_type = 2";
+        
+        $sql = "
+        SELECT id,member_id
+          FROM billing ";
+        if (!empty($params) && is_array($params)) {
+            foreach ($params as $key => &$param) {
+                if (preg_match('/billing_type IN \((.*)\)/i', $param, $matches)) {
+                    $billing_types = explode(",", $matches[1]);
+                    $billing_types[] = 2;
+                    $param = "billing_type IN (" . implode(',', $billing_types) . ")";
+                }
+            }
+            $sql .= " WHERE ".implode(" AND ", $params);
+        }
+        $sql .= " ORDER BY account_number,transaction_date,billing_type";
+        
+        try {
+            $stmt = $dbh->prepare($sql);
+            $stmt->execute();
+            while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                $billings[$row['member_id']][]
+                    = Toolkit_Members_Billing_Factory::createDbObjectById(
+                        $dbh, 'Toolkit_Members_Billing_Billing', $row['id']
+                    );
+            }
+        } catch(PDOException $e) {
+            var_dump($e);
+            Toolkit_Common::handleError($e);
+        }
+        $this->getPaymentTypes($dbh);
+        $this->createExportFile($dbh, $billings);
+    }
+    
+    /**
+     * Description of createExportFile()
+     * 
+     * @param PDO   $dbh      Database handler
+     * @param array $billings Billings array
+     * 
+     * @return void
+     * @access protected
+     */
+    protected function createExportFile(PDO $dbh, $billings)
+    {
+        $this->fileData[] = "!Type:Bank\n";
+        foreach ($billings as $member_id => $records) {
+            foreach ($records as $record) {
+                $billingType = $record->getBilling_type();
+                switch ($billingType) {
+                case 2 :
+                    $paymentTypeId = $this->getPaymentTypeFromAccountId(
+                        $dbh,
+                        $record->getAccount_id()
+                    );
+                    $paymentType = $this->paymentTypes[$paymentTypeId];
+                    $this->fileData[] = sprintf(
+                        self::PAYMENTFORMAT,
+                        $record->getTransaction_date(), // D
+                        '-' . $record->getAmount(), // T
+                        $record->getMember_name(), // P
+                        preg_replace(
+                            '[0-9]',
+                            '',
+                            $record->getPayment_data()
+                        ), // N
+                        $paymentType->getCategory(), // M
+                        $paymentType->getQcode() // L
+                    );
+                    break;
+                }
+            }
+        }
+        $buf = implode('', $this->fileData);
+        $len = strlen($buf);
+        header("Content-type: application/force-download");
+        header("Content-Length: $len");
+        header("Content-Disposition: inline; filename=testImport.qif");
+        echo $buf;
+    }
+    
+    /**
+     * Description of getPaymentTypeFromAccountId
+     * 
+     * @param PDO $dbh        Database handler
+     * @param int $account_id Account ID
+     * 
+     * @return string
+     * @access protected 
+     */
+    protected function getPaymentTypeFromAccountId(PDO $dbh, $account_id)
+    {
+        try {
+            $sql = "
+            SELECT payment_type
+              FROM member_account
+             WHERE id = :account_id";
+            $stmt = $dbh->prepare($sql);
+            $stmt->bindParam(':account_id', $account_id, PDO::PARAM_INT);
+            $stmt->execute();
+            return $stmt->fetchColumn();
+        } catch (PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+    }
+    
+    /**
+     * Description for getPaymentTypes
+     * 
+     * @param PDO $dbh Database handler
+     * 
+     * @return void
+     * @access protected
+     */
+    protected function getPaymentTypes(PDO $dbh)
+    {
+        try {
+            $sql = "
+               SELECT id
+                 FROM payment_types
+             ORDER BY id";
+            $stmt = $dbh->prepare($sql);
+            $stmt->execute();
+            while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                $this->paymentTypes[$row['id']]
+                    = Toolkit_Members_Billing_Factory::createDbObjectById(
+                        $dbh, 'Toolkit_Members_Billing_PaymentTypes', $row['id']
+                    );
+            }
+        } catch(PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+    }
+}
diff --git a/Toolkit/Members/Billing/RecordNavigation.php b/Toolkit/Members/Billing/RecordNavigation.php
new file mode 100644 (file)
index 0000000..19d4e7a
--- /dev/null
@@ -0,0 +1,86 @@
+<?php
+/**
+ * RecordNavigation.php
+ *
+ * PHP version 5
+ *
+ * @category Toolkit_Members
+ * @package  Billing
+ * @author   Steve Sutton <steve@gaslightmedia.com>
+ * @license  http://www.gaslightmedia.com Gaslightmedia
+ * @link     <>
+ */
+
+/**
+ * Toolkit_Members_Billing_RecordNavigation
+ *
+ * @category Toolkit_Members
+ * @package  Billing
+ * @author   Steve Sutton <steve@gaslightmedia.com>
+ * @license  http://www.gaslightmedia.com Gaslightmedia
+ * @link     <>
+ */
+class Toolkit_Members_Billing_RecordNavigation
+{
+    /**
+     * Description of getNavigationArray()
+     * 
+     * @param string $params Description for $params
+     * @param array  $nav    Description for $nav
+     * 
+     * @return string 
+     * @access public
+     */
+    public function getNavigationArray($params, $nav)
+    {
+        $nav['billingInfo'] = array(
+            'title' => 'Billing Info',
+            'url'   => "/members.php?$params",
+            'desc'  => 'View and edit billing details'
+        );
+        // this should only show if they have account data and member account 
+        // with amounts or dynamic amounts assigned to them
+        $hasPaymentAccounts = $this->hasPaymentAccounts();
+        if ($hasPaymentAccounts) {
+            $nav['invoices'] = array(
+                'title' => 'Payments/Statements',
+                'url'   => "/members.php?$params",
+                'desc'  => 'View and edit Payment details'
+            );
+        }
+        return $nav;
+    }
+    
+    /**
+     * Description for hasPaymentAccounts()
+     * 
+     * @return boolean 
+     * @access protected
+     */
+    protected function hasPaymentAccounts()
+    {
+        $hasPaymentAccount = false;
+        try {
+            $dbh = Toolkit_Database::getInstance();
+            $sql = "
+            SELECT pt.*
+              FROM member_account ma
+                   LEFT OUTER JOIN payment_types pt
+                   ON (pt.id = ma.payment_type)
+             WHERE ma.member_id = :member_id";
+            $stmt = $dbh->prepare($sql);
+            $stmt->bindParam(':member_id', $_GET['id'], PDO::PARAM_INT);
+            $stmt->execute();
+            while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                if ($row['amount'] > 0 && $row['dynamic_amount'] == false) {
+                    $hasPaymentAccount = true;
+                } else if ($row['dynamic_amount'] == true) {
+                    $hasPaymentAccount = true;
+                }
+            }
+        } catch(PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+        return $hasPaymentAccount;
+    }
+}
diff --git a/Toolkit/Members/Billing/Report.php b/Toolkit/Members/Billing/Report.php
new file mode 100644 (file)
index 0000000..4fcf065
--- /dev/null
@@ -0,0 +1,390 @@
+<?php
+/**
+ * Report.php
+ *
+ * PHP version 5
+ *
+ * @category Toolkit_Members
+ * @package  Billing
+ * @author   Steve Sutton <steve@gaslightmedia.com>
+ * @license  http://www.gaslightmedia.com Gaslightmedia
+ * @link     <>
+ */
+
+/**
+ * Toolkit_Members_Billing_Report
+ *
+ * @category Toolkit_Members
+ * @package  Billing
+ * @author   Steve Sutton <steve@gaslightmedia.com>
+ * @license  http://www.gaslightmedia.com Gaslightmedia
+ * @link     <>
+ */
+class Toolkit_Members_Billing_Report
+    extends Toolkit_DataGridBuilder
+{
+    /**
+     * Description of $config
+     * @var Config
+     * @access public 
+     */
+    public $config;
+    
+    /**
+     * Description of $sortableAfter
+     * @var integer
+     * @access protected
+     */
+    protected $sortableAfter = 100000;
+    
+    /**
+     * Description of configureColumns()
+     * 
+     * @return void
+     * @access public 
+     */
+    protected function configureColumns()
+    {
+        $config = new Config();
+        $root = $config->parseConfig(
+            BASE . 'Toolkit/Members/Billing/config.ini',
+            'IniFile'
+        );
+        $this->config = $root;
+        
+        $this->addColumn(
+            new Structures_DataGrid_Column(
+                'Transaction Time',
+                'transaction_time',
+                'transaction_time',
+                array(
+                    'nowrap' => 'nowrap',
+                    'width'   => '120'
+                )
+            )
+        );
+        $this->addColumn(
+            new Structures_DataGrid_Column(
+                'Type',
+                'billing_type',
+                'billing_type',
+                array(
+                    'nowrap' => 'nowrap',
+                    'width'   => '70'
+                ),
+                null,
+                array($this, 'renderBillingType')
+            )
+        );
+        $this->addColumn(
+            new Structures_DataGrid_Column(
+                'Member Name',
+                'member_name',
+                'member_name',
+                null,
+                null,
+                array($this, 'renderMemberName')
+            )
+        );
+        $this->addColumn(
+            new Structures_DataGrid_Column(
+                'Description',
+                'member_id',
+                null,
+                null,
+                null,
+                array($this, 'renderDescription')
+            )
+        ); 
+
+        $this->addColumn(
+            new Structures_DataGrid_Column(
+                'Amount',
+                'amount',
+                null,
+                array(
+                    'nowrap' => 'nowrap',
+                    'width'   => '50'
+                ),
+                null,
+                array($this, 'renderAmount')
+            )
+        );
+    }
+
+    /**
+     * Description of exportFile
+     * 
+     * @param PDO   $dbh    Database Handler
+     * @param array $params Parameter array
+     * 
+     * @return void
+     * @access public
+     */
+    public function exportFile(PDO $dbh, $params)
+    {
+        $QifExport = new Toolkit_Members_Billing_QifExport();
+        $QifExport->setQuery($dbh, $params);
+    }
+    
+    /**
+     * Description of getGrandTotals()
+     * 
+     * @param PDO   $dbh    Database handler
+     * @param array $params Parameter array
+     * 
+     * @return string 
+     * @access public
+     */
+    public function getGrandTotals(PDO $dbh, $params)
+    {
+        $out = '<table class="dataGrid"><tbody>';
+        $params[] = "billing_type in (1,2)";
+        $sql = "
+        SELECT sum(amount) as sum,billing_type
+          FROM billing";
+        if (!empty($params)) {
+            $sql .= " WHERE " . implode(" AND ", $params);
+        }
+        $sql .= " GROUP BY billing_type";
+        try {
+            $stmt = $dbh->prepare($sql);
+            $stmt->execute();
+            $format = '
+            <tr class="odd" align="right">
+              <td>Total %s:</td>
+              <td width="50" align="right">%01.2f</td>
+            </tr>';
+            while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                $name
+                    = ($row['billing_type'] == 1)
+                    ? 'Invoices'
+                    : 'Payments';
+                $out .= sprintf(
+                    $format,
+                    $name,
+                    $row['sum']
+                );
+            }
+        } catch(PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+        $out .= '</tbody></table>';
+        return $out;
+    }
+
+    /**
+     * Description of renderAmount()
+     * 
+     * @param array $data Data array
+     * 
+     * @return string Descript
+     * @access public
+     */
+    public function renderAmount($data)
+    {
+        extract($data['record']);
+        switch ($billing_type) {
+        case 1 :
+            $amount = (float)$amount;
+            break;
+        case 2 :
+            $amount = (float)-$amount;
+            break;
+        }
+        $format = "%01.2f";
+        return sprintf(
+            $format,
+            (float)$amount
+        );
+    }
+    
+    /**
+     * Description of renderBillingType()
+     * 
+     * @param array $data Data array
+     * 
+     * @return string
+     * @access public 
+     */
+    public function renderBillingType($data)
+    {
+        $billingTypes = $this->config
+            ->getItem('section', 'conf')
+            ->getItem('directive', 'billingType')
+            ->getContent();
+        extract($data['record']);
+        return $billingTypes[$billing_type];
+    }
+
+    /**
+     * Description of renderDescription()
+     * 
+     * @param array $data Data array
+     * 
+     * @return string
+     * @access public 
+     */
+    public function renderDescription($data)
+    {
+        extract($data['record']);
+        switch ($billing_type) {
+        case 1 :
+            $urlFormat
+                = "members.php?rt=Members&ac=editMember&id=%d&invoice_id=%d"
+                . "&tab=invoices&returnPdf=1";
+            $url = sprintf(
+                $urlFormat,
+                $member_id,
+                $id
+            );
+            $format = '<a target="_blank" href="%s">Invoice: %s</a> for %s';
+            $out = sprintf(
+                $format,
+                $url,
+                $invoice_id,
+                $transaction_date
+            );
+            break;
+        case 2 :
+            $paymentMethods = $this->config
+                ->getItem('section', 'conf')
+                ->getItem('directive', 'paymentMethod')
+                ->getContent();
+            $out = $paymentMethods[$payment_method].': '.$payment_data;
+            break;
+        case 3 :
+            $out = 'Adjustment: '.$payment_data;
+            break;
+        case 4:
+            $out = $note;
+            break;
+        }
+        return $out;
+    }
+   
+    /**
+     * Description of renderMemberName()
+     * 
+     * @param array $data Data array
+     * 
+     * @return string
+     * @access public 
+     */
+    public function renderMemberName($data)
+    {
+        extract($data['record']);
+        $urlFormat
+            = "members.php?rt=Members&ac=editMember&id=%d&tab=invoices";
+        $url = sprintf(
+            $urlFormat,
+            $member_id
+        );
+        $format = '<a href="%s">%s</a>';
+        return sprintf(
+            $format,
+            $url,
+            $member_name
+        );
+    }
+
+    /**
+     * Description of setQuery()
+     * 
+     * @return array 
+     * @access public
+     */
+    public function  setQuery()
+    {
+        $params = array();
+        $sql = "
+        SELECT id,invoice_id,
+               to_char(transaction_time, 'MM/DD/YYY HH24:MI:SS') as transaction_time,
+               transaction_date,member_name,member_id,account_number,billing_type,
+               amount,balance,payment_method,payment_data,emailed,printed,paid,
+               invoice,notes
+          FROM billing";
+        if (   $_REQUEST['transaction_date_beg']['m']
+            && $_REQUEST['transaction_date_end']['m']
+        ) {
+            $startDate = implode("/", $_REQUEST['transaction_date_beg']);
+            $endDate = implode("/", $_REQUEST['transaction_date_end']);
+            $params[] = "transaction_date BETWEEN '{$startDate}' AND '{$endDate}'";
+        }
+        if ($_REQUEST['member_name']) {
+            $params[] = "member_name ilike '%{$_REQUEST['member_name']}%'";
+        }
+        if ($_REQUEST['account_number']) {
+            $params[] = "account_number = '{$_REQUEST['account_number']}'";
+        }
+        if (!empty($_REQUEST['payment_types'])) {
+            $params[] = "account_id IN (
+                SELECT id
+                  FROM member_account
+                 WHERE payment_type IN (" . implode(",", $_REQUEST['payment_types']) . ")
+                 )";
+        }
+        if (!empty($_REQUEST['counties'])) {
+            $params[] = "member_id IN (
+                SELECT member_id
+                  FROM member
+                 WHERE region IN (" . implode(",", $_REQUEST['counties']) . "))";
+        }
+        if (!empty($_REQUEST['billing_types'])) {
+            $params[] = "billing_type IN (" . implode(",", $_REQUEST['billing_types']) . ")";
+        }
+        if (!empty($params)) {
+            $sql .= " WHERE " . implode(" AND ", $params);
+        }
+        parent::setQuery($sql);
+        return $params;
+    }
+    
+    /**
+     * Description of toCSV()
+     * 
+     * @return boolean|mixed
+     * @access protected
+     */
+    protected function toCSV()
+    {
+        $this->generateColumns(
+            array(
+                'id' => 'Id',
+                'invoice_id' => 'Invoice Id',
+                'transaction_date' => 'Date',
+                'member_name' => 'Member Name',
+                'member_id' => 'Member Id',
+                'account_number' => 'Account Number',
+                'billing_type' => 'Billing Type',
+                'amount' => 'Amount',
+                'balance' => 'Balance',
+                'payment_method' => 'Payment Method',
+                'payment_data' => 'Payment Data'
+                
+            )
+        );
+        try {
+            $bind = $this->bind($this->sql, $this->options, 'PDO');
+            if (PEAR::isError($bind)) {
+                return Toolkit_Common::handleError($bind);
+            } elseif (($recCount = $this->getRecordCount()) > 0) {
+                $this->setRendererOptions($this->rendererOptions);
+                $csv = $this->getOutput(
+                    DATAGRID_RENDER_CSV,
+                    $this->rendererOptions
+                );
+                if (PEAR::isError($gridBody)) {
+                    return Toolkit_Common::handleError($gridBody);
+                }
+
+                return $csv;
+            } else {
+                return false;
+            }
+        } catch(PDOException $e) {
+            return Toolkit_Common::handleError($e);
+        }
+    }
+}
+
diff --git a/Toolkit/Members/Billing/ReportSearch.php b/Toolkit/Members/Billing/ReportSearch.php
new file mode 100644 (file)
index 0000000..9c68ac9
--- /dev/null
@@ -0,0 +1,364 @@
+<?php
+/**
+ * ReportSearch.php
+ *
+ * PHP version 5
+ *
+ * @category Toolkit_Members
+ * @package  Billing
+ * @author   Steve Sutton <steve@gaslightmedia.com>
+ * @license  http://www.gaslightmedia.com Gaslightmedia
+ * @link     <>
+ */
+
+/**
+ * Toolkit_Members_Billing_ReportSearch
+ *
+ * @category Toolkit_Members
+ * @package  Billing
+ * @author   Steve Sutton <steve@gaslightmedia.com>
+ * @license  http://www.gaslightmedia.com Gaslightmedia
+ * @link     <>
+ */
+class Toolkit_Members_Billing_ReportSearch
+    extends Toolkit_FormBuilder
+{
+    /**
+        * Flexy options used in the renderer
+        *
+        * @var array
+        * @access protected
+        */
+       protected $flexyOptions;
+
+       /**
+        * The directory that holds the templates
+        *
+        * Keep this setting in relation to this file.
+        *
+        * @var string
+        * @access protected
+        */
+       protected $templatesDir = 'templates';
+
+       /**
+        * The directory that holds the compiled templates
+        *
+        * Keep this setting in relation to this file.
+        *
+        * @var string
+        * @access protected
+        */
+       protected $compiledTemplatesDir = 'templates/compiled';
+
+       /**
+        * The name of the template used to render the business info form
+        *
+        * @var string
+        * @access protected
+        */
+       protected $formTemplate = 'reportSearch.html';
+    /**
+        * Constructor
+        *
+     * @param string $formName    Form's name.
+     * @param string $method      (optional)Form's method defaults to 'POST'
+     * @param string $action      (optional)Form's action
+     * @param string $target      (optional)Form's target defaults to '_self'
+     * @param mixed  $attributes  (optional)Extra attributes for <form> tag
+     * @param bool   $trackSubmit (optional)Whether to track if the form was
+        *                                                        submitted by adding a special hidden field
+        * 
+        * @see    HTML_QuickForm
+        * @access public
+        */
+       public function __construct(
+        $formName,
+        $method = 'post',
+        $action = '',
+        $target = '',
+        $attributes = null,
+        $trackSubmit = false
+    ) {
+               //      T_VARIABLE error when passing this server var in on the constructors params.
+               $action = empty($action) ? $_SERVER['REQUEST_URI'] : $action;
+               parent::__construct(
+            $formName,
+            $method,
+            $action,
+            $target,
+            $attributes,
+            $trackSubmit
+        );
+        $this->flexyOptions                = $GLOBALS['flexyOptions'];
+               $this->flexyOptions['templateDir'] = dirname(__FILE__) . "/{$this->templatesDir}";
+               $this->flexyOptions['compileDir']  = dirname(__FILE__) . "/{$this->compiledTemplatesDir}";
+        
+               $this->template = BASE . 'Toolkit/Members/templates/currentTables/';
+       }
+
+    /**
+     * Setup form fields
+     *
+     * @return void
+     * @access protected
+     */
+       protected function configureElements()
+       {
+        $paymentTypes = $this->getPaymentTypes();
+        $counties     = $this->getCounties();
+        $config       = new Config();
+        $root         = $config->parseConfig(
+            BASE . 'Toolkit/Members/Billing/config.ini',
+            'IniFile'
+        );
+        $billingTypes = $root
+            ->getItem('section', 'conf')
+            ->getItem('directive', 'billingType')
+            ->getContent();
+        unset($billingTypes[0]);
+        $dateStartYear = 2011;
+               //      All Elements are created here.  This includes group element definitions.
+        $e[] = array(
+            'type' => 'header',
+            'name' => 'Search'
+        );
+               $e[] = array(
+            'type' => 'hidden',
+            'req'  => false,
+            'name' => 'rt'
+        );
+               $e[] = array(
+            'type' => 'hidden',
+            'req'  => false,
+            'name' => 'ac'
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => 'member_name',
+            'display' => 'Member Name'
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => 'account_number',
+            'display' => 'Account Number'
+        );
+        $transactionDate = array();
+               $transactionDate[] = array(
+                       'type'    => 'date',
+                       'req'     => false,
+                       'name'    => 'transaction_date_beg',
+                       'opts'    => array(
+                               'format'           => 'm / d / Y',
+                               'minYear'          => $dateStartYear,
+                               'maxYear'          => date('Y'),
+                               'addEmptyOption'   => true,
+                               'emptyOptionValue' => '',
+                               'emptyOptionText'  => array(
+                                       'm' => 'mm',
+                                       'd' => 'dd',
+                                       'Y' => 'yyyy',
+                               )
+                       )
+               );
+               $transactionDate[] = array(
+                       'type'    => 'date',
+                       'req'     => false,
+                       'name'    => 'transaction_date_end',
+                       'opts'    => array(
+                               'format'           => 'm / d / Y',
+                               'minYear'          => $dateStartYear,
+                               'maxYear'          => date('Y'),
+                               'addEmptyOption'   => true,
+                               'emptyOptionValue' => '',
+                               'emptyOptionText'  => array(
+                                       'm' => 'mm',
+                                       'd' => 'dd',
+                                       'Y' => 'yyyy',
+                               )
+                       )
+               );
+        $e[] = array(
+                       'type'       => 'group',
+                       'req'        => false,
+                       'name'       => 'transaction_date',
+                       'group'      => $transactionDate,
+                       'seperator'  => '<br>',
+                       'label'      => "Date Between",
+                       'appendName' => false
+               );
+        $e[] = array(
+            'type'    => 'select',
+            'req'     => false,
+            'name'    => 'payment_types',
+            'display' => 'Payment Types',
+            'opts'    => $paymentTypes,
+            'att'     => array('multiple' => 'multiple', 'size' => count($paymentTypes))
+        );
+        $e[] = array(
+            'type'    => 'select',
+            'req'     => false,
+            'name'    => 'billing_types',
+            'display' => 'Transaction Types',
+            'opts'    => $billingTypes,
+            'att'     => array('multiple' => 'multiple', 'size' => count($billingTypes))
+        );
+        $e[] = array(
+            'type'    => 'select',
+            'req'     => false,
+            'name'    => 'counties',
+            'display' => 'Counties',
+            'opts'    => $counties,
+            'att'     => array('multiple' => 'multiple', 'size' => 5)
+        );
+        $e[] = array(
+            'type'    => 'select',
+            'req'     => false,
+            'name'    => 'report_type',
+            'display' => 'Report Type',
+            'opts'    => array(
+                'html' => 'html',
+                'file' => 'file'
+            )
+        );
+        
+               $e[] = array(
+            'type'    => 'submit',
+            'req'     => false,
+            'name'    => 'submit',
+            'display' => 'Search'
+        );
+
+               $this->setupElements($e);
+       }
+    
+    /**
+     * Description of configureForm()
+     * 
+     * @return void
+     * @access public 
+     */
+    public function configureForm()
+    {
+        $this->configureElements();
+               $this->configureFilters();
+               $this->configureConstants();
+    }
+    
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @return void
+     * @access protected
+     */
+       protected function configureFilters()
+       {
+               $filters[] = array(
+            'element' => '__ALL__',
+            'filter' => 'trim'
+        );
+
+               $this->setupFilters($filters);
+       }
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @return void
+     * @access protected
+     */
+       protected function configureConstants()
+       {
+               $constants = array(
+                       'rt'    => 'Reports',
+                       'ac' => 'searchReports'
+               );
+
+               $this->setupConstants($constants);
+       }
+    
+    /**
+     * Description of getCounties()
+     * 
+     * @return array
+     * @access public 
+     */
+    public function getCounties()
+    {
+        $counties = array();
+        try {
+            $sql = "
+               SELECT *
+                FROM region
+            ORDER BY region_name";
+            $stmt = $this->dbh->prepare($sql);
+            $stmt->execute();
+            while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                $counties[$row['region_id']] = $row['region_name'];
+            }
+        } catch (PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+        return $counties;
+    }
+
+    /**
+     * Get all payment types for a select list
+     *
+     * @return array
+     * @access protected
+     */
+    protected function getPaymentTypes()
+    {
+        $paymentTypes = array();
+        try {
+            $sql = "
+               SELECT *
+                FROM payment_types
+            ORDER BY name";
+            $stmt = $this->dbh->prepare($sql);
+            $stmt->execute();
+            while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                $paymentTypes[$row['id']] = $row['name'] . ' ($'.$row['amount'].')';
+            }
+            return $paymentTypes;
+        } catch (PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+    }
+
+    /**
+     * Description of setupRenderers()
+     * 
+     * @return void
+     * @access protected 
+     */
+    protected function setupRenderers()
+       {
+               $renderer = new HTML_QuickForm_Renderer_Object(true);
+               $this->accept($renderer);
+               $this->template = new HTML_Template_Flexy($this->flexyOptions);
+               $this->view = $this;
+               $this->view->baseUrl = MEDIA_BASE_URL;
+               $this->view->form = $renderer->toObject();
+               $this->template->compile($this->formTemplate);
+       }
+
+    /**
+     * Description of toHTML()
+     * 
+     * @return string
+     * @access public
+     */
+    public function toHTML()
+       {
+        $this->setupRenderers();
+        return $this->template->bufferedOutputObject($this->view);
+       }
+}
diff --git a/Toolkit/Members/Billing/Statement.php b/Toolkit/Members/Billing/Statement.php
new file mode 100644 (file)
index 0000000..85fa439
--- /dev/null
@@ -0,0 +1,515 @@
+<?php
+/**
+ * Statement.php
+ *
+ * PHP version 5
+ *
+ * @category Toolkit_Members
+ * @package  Billing
+ * @author   Steve Sutton <steve@gaslightmedia.com>
+ * @license  http://www.gaslightmedia.com Gaslightmedia
+ * @link     <>
+ */
+
+/**
+ * Toolkit_Members_Billing_Statement
+ *
+ * @category Toolkit_Members
+ * @package  Billing
+ * @author   Steve Sutton <steve@gaslightmedia.com>
+ * @license  http://www.gaslightmedia.com Gaslightmedia
+ * @link     <>
+ */
+class Toolkit_Members_Billing_Statement
+{
+    /**
+     * Template file to use
+     * 
+     * @var string
+     * @access protected
+     */
+    protected $template = 'memberStatements.html';
+
+    /**
+     * Base of the billing template directory
+     *  
+     * @var string
+     * @access protected
+     */
+    protected $templateBase = 'Toolkit/Members/Billing/';
+    
+    /**
+     * The directory of the templates
+     *
+     * @var string
+     * @access protected
+     */
+    protected $templatesDir = 'templates';
+
+    /**
+     * The directory of the flexy-compiled templates
+     *
+     * @var string
+     * @access protected
+     */
+    protected $compiledDir = 'templates/compiled';
+    
+    /**
+     * Flexy Template Options array
+     * 
+     * @var array
+     * @access protected
+     */
+    protected $flexyOptions = array();
+    /**
+     * class Constructor
+     *
+     * @return void
+     * @access public
+     */
+    public function  __construct()
+    {
+        $config = new Config();
+        $root = $config->parseConfig(
+            BASE . 'Toolkit/Members/Billing/config.ini',
+            'IniFile'
+        );
+        $this->config = $root;
+        $this->flexyOptions = array(
+            'templateDir'  => BASE . "{$this->templateBase}{$this->templatesDir}",
+            'compileDir'   => BASE . "{$this->templateBase}{$this->compiledDir}",
+            'forceCompile' => 1,
+            'debug'        => 0,
+            'locale'       => 'en',
+        );
+    }
+
+    /**
+     * Generate a statement for a member
+     * This should be for the current year only
+     *
+     * @param PDO     $dbh       Database Connection
+     * @param mixed   $member_id member id
+     * @param boolean $all       Description of $all
+     * 
+     * @todo need to work this out for multilpe accounts
+     * 
+     * @return string
+     * @access public
+     */
+    public function createMemberStatements(
+        PDO $dbh,
+        $member_id,
+        $all = false
+    ) {
+        $template = new HTML_Template_Flexy($this->flexyOptions);
+        $page = new stdClass();
+        if (!is_numeric($member_id)) {
+            throw new InvalidArgumentException('member_id must be numeric');
+        }
+        $billingTypes
+            = $this->config
+                ->getItem('section', 'conf')
+                ->getItem('directive', 'billingType')
+                ->getContent();
+        $paymentMethods
+            = $this->config
+                ->getItem('section', 'conf')
+                ->getItem('directive', 'paymentMethod')
+                ->getContent();
+        $page->canEditBillings
+            = $this->config
+                ->getItem('section', 'conf')
+                ->getItem('directive', 'canEditBillings')
+                ->getContent();
+        unset($paymentMethods[2]);
+        $paymentTypes = $this->getPaymentTypes($dbh);
+        try {
+            $sql = "
+              SELECT id,payment_type
+                FROM member_account
+               WHERE member_id = :member_id
+            ORDER BY id";
+            $stmt = $dbh->prepare($sql);
+            $stmt->bindParam(
+                ':member_id',
+                $member_id,
+                PDO::PARAM_INT
+            );
+            $stmt->execute();
+        } catch(PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+        $accounts = array();
+        while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
+            $accounts[$row['id']]['paymentType']
+                = $paymentTypes[$row['payment_type']];
+            if ($all) {
+                $transactions = $this->getAllMemberBillings(
+                    $dbh,
+                    $member_id,
+                    $row['id']
+                );
+            } else {
+                $transactions = $this->getMemberBillings(
+                    $dbh,
+                    $member_id,
+                    $row['id']
+                );
+            }
+            
+            $count = 1;
+            if (is_array($transactions)) {
+                $records = array();
+                foreach ($transactions as $transaction) {
+                    $invoiceLink
+                        = MEDIA_BASE_URL . "admin/members.php"
+                        . "?rt=Members"
+                        . "&ac=editMember"
+                        . "&id={$member_id}"
+                        . "&tab=invoices"
+                        . "&invoice_id=" . $transaction->getId()
+                        . "&returnPdf=1";
+                    $editLink = MEDIA_BASE_URL . 'admin/members.php'
+                        . "?rt=Members"
+                        . "&ac=editMember"
+                        . "&id={$member_id}"
+                        . "&tab=invoices"
+                        . "&invoice_id=" . $transaction->getId()
+                        . "&editBilling=1";
+                    $deleteLink = MEDIA_BASE_URL . 'Toolkit/Members/Billing/'
+                        . "deleteBilling.php"
+                        . "?member_id=" . $member_id
+                        . "&billing_id=" . $transaction->getId();
+                    $resetUrl = MEDIA_BASE_URL . 'admin/members.php'
+                        . "?rt=Members"
+                        . "&ac=editMember"
+                        . "&id={$member_id}"
+                        . "&tab=invoices";
+                    $records[] = array(
+                        'id' => $transaction->getId(),
+                        'deleteLink' => $deleteLink,
+                        'editUrl' => (($transaction->getBilling_type() == 1)
+                            ? $editLink : ''),
+                        'resetUrl' => $resetUrl,
+                        'transaction_time' => date(
+                            "m/d/Y H:i:s",
+                            strtotime($transaction->getTransaction_time())
+                        ),
+                        'transaction_date'
+                            => $transaction->getTransaction_date(),
+                        'billing_type'
+                            => $billingTypes[$transaction->getBilling_type()],
+                        'payment_method'
+                            => $paymentMethods[$transaction->getPayment_method()],
+                        'payment_data' => $transaction->getPayment_data(),
+                        'notes' => $transaction->getNotes(),
+                        'amount' => $transaction->getAmount(),
+                        'balance' => $transaction->getBalance(),
+                        'className' => ((++$count % 2 == 0) ? 'even': 'odd'),
+                        'invoice' =>
+                            (($transaction->getBilling_type() == 1)
+                             ? $invoiceLink
+                             : ''
+                            )
+                    );
+                }
+                $accounts[$row['id']]['records'] = $records;
+            }
+            $accounts[$row['id']]['balanceDue']
+                = $this->getBalanceDue(
+                    $dbh,
+                    $_REQUEST['id'],
+                    $row['id']
+                );
+        }
+        $page->accounts = $accounts;
+        $template->compile($this->template);
+               $html = $template->bufferedOutputObject($page);
+        return $html;
+    }
+
+    /**
+     * Get the member balance due.
+     * Taken from the last billing record inserted into the billing table
+     *
+     * @param PDO $dbh        Database Connection
+     * @param int $member_id  Member id
+     * @param int $account_id Account id
+     *
+     * @return float
+     * @access public
+     */
+    public function getBalanceDue(
+        PDO $dbh,
+        $member_id,
+        $account_id
+    ) {
+        if (!is_numeric($member_id)) {
+            throw new InvalidArgumentException('member id must be numeric');
+        }
+        try {
+            $sql = "
+              SELECT id
+                FROM billing
+               WHERE member_id = :member_id
+                 AND account_id = :account_id
+            ORDER BY id DESC
+               LIMIT 1
+              OFFSET 0";
+            $stmt = $dbh->prepare($sql);
+            $stmt->bindParam(
+                ':member_id',
+                $member_id,
+                PDO::PARAM_INT
+            );
+            $stmt->bindParam(
+                ':account_id',
+                $account_id,
+                PDO::PARAM_INT
+            );
+            $stmt->execute();
+            $row = $stmt->fetch(PDO::FETCH_ASSOC);
+            if ($row) {
+                $balance = Toolkit_Members_Billing_Factory::createDbObjectById(
+                    $dbh,
+                    'Toolkit_Members_Billing_Billing',
+                    $row['id']
+                )->getBalance();
+            } else {
+                $balance = (float)'0.00';
+            }
+            return (float)((float)$balance + (float)'0.00');
+        } catch(PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+    }
+    
+    /**
+     * Description of getOriginalInvoice
+     * 
+     * @param PDO $dbh        Database handler
+     * @param int $member_id  Member ID
+     * @param int $account_id Account ID
+     * 
+     * @return object|boolean
+     * @access public
+     * @throws InvalidArgumentException
+     */
+    public function getOriginalInvoice(
+        PDO $dbh,
+        $member_id,
+        $account_id
+    ) {
+        if (!is_numeric($member_id)) {
+            throw new InvalidArgumentException('member id must be numeric');
+        }
+        if (!is_numeric($account_id)) {
+            throw new InvalidArgumentException('account id must be numeric');
+        }
+        try {
+            $sql = "
+              SELECT id
+                FROM billing
+               WHERE member_id = :member_id
+                 AND account_id = :account_id
+                 AND billing_type = 1
+            ORDER BY id DESC
+               LIMIT 1
+              OFFSET 0";
+            $stmt = $dbh->prepare($sql);
+            $stmt->bindParam(
+                ':member_id',
+                $member_id,
+                PDO::PARAM_INT
+            );
+            $stmt->bindParam(
+                ':account_id',
+                $account_id,
+                PDO::PARAM_INT
+            );
+            $stmt->execute();
+            $row = $stmt->fetch(PDO::FETCH_ASSOC);
+            if ($row) {
+                return Toolkit_Members_Billing_Factory::createDbObjectById(
+                    $dbh,
+                    'Toolkit_Members_Billing_Billing',
+                    $row['id']
+                );
+            }
+            return false;
+        } catch(PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+    }
+
+    /**
+     * Description of getAllMemberBillings()
+     * 
+     * @param PDO $dbh        Database Handler
+     * @param int $member_id  Member ID
+     * @param int $account_id Account ID
+     * 
+     * @return array
+     * @access public
+     */
+    public function getAllMemberBillings(
+        PDO $dbh,
+        $member_id,
+        $account_id
+    ) {
+        //$invoice            = new Toolkit_Members_Billing_Invoices();
+        //$nextInvoiceDate    = date('m/d/Y', $invoice->getNextInvoiceDate());
+        //$currentInvoiceDate = date('m/d/Y', $invoice->getCurrentInvoiceDate());
+        try {
+            // all statements
+            $sql = "
+              SELECT id
+                FROM billing
+               WHERE member_id = :member_id
+                 AND account_id = :account_id
+            ORDER BY id";
+            $stmt = $dbh->prepare($sql);
+            //$stmt->bindParam(':startDate', $currentInvoiceDate);
+            //$stmt->bindParam(':endDate', $nextInvoiceDate);
+            $stmt->bindParam(
+                ':member_id',
+                $member_id,
+                PDO::PARAM_INT
+            );
+            $stmt->bindParam(
+                ':account_id',
+                $account_id,
+                PDO::PARAM_INT
+            );
+            $stmt->execute();
+            while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                $transactions[]
+                    = Toolkit_Members_Billing_Factory::createDbObjectById(
+                        $dbh,
+                        'Toolkit_Members_Billing_Billing',
+                        $row['id']
+                    );
+            }
+            return $transactions;
+        } catch(PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+    }
+    /**
+     * return all member billing records for the current year.
+     * plus any unpaid ones from previous years
+     *
+     * @param PDO $dbh        Database Connection
+     * @param int $member_id  Member id
+     * @param int $account_id Account id
+     *
+     * @return array
+     * @access public
+     */
+    public function getMemberBillings(
+        PDO $dbh,
+        $member_id,
+        $account_id
+    ) {
+        $invoice            = new Toolkit_Members_Billing_Invoices();
+        $nextInvoiceDate    = date('m/d/Y', $invoice->getNextInvoiceDate());
+        $currentInvoiceDate = date('m/d/Y', $invoice->getCurrentInvoiceDate());
+        try {
+            // see if this member has a balance due from previous year
+            // previous billing statement
+            $sql = "
+              SELECT id
+                FROM billing
+               WHERE member_id = :member_id
+                 AND account_id = :account_id
+                 AND transaction_date < :date
+                 AND billing_type IN (1, 2, 3)
+            ORDER BY id DESC
+               LIMIT 1
+              OFFSET 0";
+            $stmtB = $dbh->prepare($sql);
+            $stmtB->bindParam(
+                ':member_id',
+                $member_id,
+                PDO::PARAM_INT
+            );
+            $stmtB->bindParam(
+                ':account_id',
+                $account_id,
+                PDO::PARAM_INT
+            );
+            $stmtB->bindParam(':date', $currentInvoiceDate);
+            $stmtB->execute();
+            while ($row = $stmtB->fetch(PDO::FETCH_ASSOC)) {
+                $transactions[]
+                    = Toolkit_Members_Billing_Factory::createDbObjectById(
+                        $dbh,
+                        'Toolkit_Members_Billing_Billing',
+                        $row['id']
+                    );
+            }
+            // current year statements
+            $sql = "
+              SELECT id
+                FROM billing
+               WHERE member_id = :member_id
+                 AND account_id = :account_id
+                 AND transaction_date BETWEEN :startDate AND :endDate
+            ORDER BY id";
+            $stmt = $dbh->prepare($sql);
+            $stmt->bindParam(':startDate', $currentInvoiceDate);
+            $stmt->bindParam(':endDate', $nextInvoiceDate);
+            $stmt->bindParam(
+                ':member_id',
+                $member_id,
+                PDO::PARAM_INT
+            );
+            $stmt->bindParam(
+                ':account_id',
+                $account_id,
+                PDO::PARAM_INT
+            );
+            $stmt->execute();
+            while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                $transactions[]
+                    = Toolkit_Members_Billing_Factory::createDbObjectById(
+                        $dbh,
+                        'Toolkit_Members_Billing_Billing',
+                        $row['id']
+                    );
+            }
+            return $transactions;
+        } catch(PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+    }
+
+    /**
+     * Get all payment types for a select list
+     * 
+     * @param PDO $dbh Database Handler
+     *
+     * @return array
+     * @access protected
+     */
+    protected function getPaymentTypes(PDO $dbh)
+    {
+        $paymentTypes = array();
+        try {
+            $sql = "
+               SELECT *
+                FROM payment_types
+            ORDER BY name";
+            $stmt = $dbh->prepare($sql);
+            $stmt->execute();
+            while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                $paymentTypes[$row['id']] = $row['name'] . ' ($'.$row['amount'].')';
+            }
+            return $paymentTypes;
+        } catch (PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+    }
+}
+
diff --git a/Toolkit/Members/Billing/billing.css b/Toolkit/Members/Billing/billing.css
new file mode 100644 (file)
index 0000000..a77cb8b
--- /dev/null
@@ -0,0 +1,21 @@
+fieldset {margin:0;padding:0;width:100%;}
+legend {margin-left: 100px;}
+table.statement {width:100%;background-color: #D6DFC3;}
+table.statement thead tr {background-color: white;}
+table.statement * td {padding: 3px 1px;}
+.even {background-color: #D6DFC3;}
+.odd {background-color: white;}
+.balanceline {background-color: white;border: solid 1px #D6DFC3;}
+#form-payment_types {width:220px;float: left;}
+#form-billing_types {width:150px;float: right;}
+#form-counties {width:150px;float: left;clear: both;}
+#form-report_type {width:100px;float: right;padding-right: 50px;}
+#button-submit {width:50px;margin-left: auto;margin-right: auto;clear: both;}
+table.dataGrid {width:740px;background-color: #D6DFC3;}
+table.dataGrid thead tr {background-color: white;}
+table.dataGrid * td {padding: 3px 1px;}
+#search-reports * img {display:inline;}
+#search-billing-form {width:580px;margin-left: auto;margin-right: auto;}
+#form-member_name {width:580px;margin-left: auto;margin-right: auto;}
+#form-account_number {width:580px;margin-left: auto;margin-right: auto;}
+#form-transaction_date {width:580px;margin-left: auto;margin-right: auto;padding-bottom: 20px;}
diff --git a/Toolkit/Members/Billing/checkInvestmentType.php b/Toolkit/Members/Billing/checkInvestmentType.php
new file mode 100644 (file)
index 0000000..e31844c
--- /dev/null
@@ -0,0 +1,35 @@
+<?php
+/**
+ * Billing.php
+ *
+ * PHP version 5
+ *
+ * @category Toolkit
+ * @package  Members_Billing
+ * @author   Steve Sutton <steve@gaslightmedia.com>
+ * @license  http://www.gaslightmedia.com Gaslightmedia
+ * @link     <>
+ */
+
+require_once '../../../setup.phtml';
+
+$dbh = Toolkit_Database::getInstance();
+if (is_numeric($_GET['account_id'])) {
+    try {
+        $sql = "
+        SELECT pt.dynamic_amount
+          FROM payment_types pt 
+               LEFT OUTER JOIN member_account ma
+               ON (ma.payment_type = pt.id)
+         WHERE ma.id = :account_id";
+        $stmt = $dbh->prepare($sql);
+        $stmt->bindParam(':account_id', $_GET['account_id'], PDO::PARAM_INT);
+        $stmt->execute();
+        $dynamicAmount = $stmt->fetchColumn();
+    } catch(PDOException $e) {
+        Toolkit_Common::handleError($e);
+    }
+    echo (bool)$dynamicAmount;
+} else {
+    echo (bool)false;
+}
\ No newline at end of file
diff --git a/Toolkit/Members/Billing/config.ini b/Toolkit/Members/Billing/config.ini
new file mode 100644 (file)
index 0000000..298d492
--- /dev/null
@@ -0,0 +1,38 @@
+; Member Billing configuration file
+[conf]
+; invoice day
+invoiceDay = 1
+; invoice month
+invoiceMonth = 7
+
+; if admin can edit the billings
+canEditBillings = Off
+
+; billingTypes
+billingType[] = ""
+billingType[] = "Invoice"   ; 1
+billingType[] = "Payment"   ; 2
+billingType[] = "Adjustment"; 3
+billingType[] = "Comment"   ; 4
+
+; payment Methods
+paymentMethod[] = ""
+paymentMethod[] = "Check"      ; 1
+paymentMethod[] = "Credit Card"; 2
+paymentMethod[] = "Cash"       ; 3
+paymentMethod[] = "Other"      ; 4
+
+; Company and PDF Info
+companyLogo       = "images/logo.gif"
+companyLogoHeight = "50"
+companyName       = SITENAME
+companyName2      = "Demo"
+companyAddr1      = "demo address"
+companyAddr2      = "P.O. Box 150"
+companyCity       = "Petoskey"
+companyState      = "MI"
+companyZip        = "49770"
+companyPhone      = "1-800-OUR-DEMO"
+companyEmail      = "info@gaslightmedia.com"
+; url do not put in http://
+companyUrl        = "demo.gaslightmedia.com"
diff --git a/Toolkit/Members/Billing/deleteBilling.php b/Toolkit/Members/Billing/deleteBilling.php
new file mode 100644 (file)
index 0000000..2548f84
--- /dev/null
@@ -0,0 +1,87 @@
+<?php
+/**
+ * deleteBilling.php
+ *
+ * PHP version 5
+ *
+ * @category Toolkit_Members
+ * @package  Billing
+ * @author   Steve Sutton <steve@gaslightmedia.com>
+ * @license  http://www.gaslightmedia.com Gaslightmedia
+ * @link     <>
+ */
+require_once '../../../setup.phtml';
+
+$memberId = filter_input(
+    INPUT_GET,
+    'member_id',
+    FILTER_SANITIZE_NUMBER_INT
+);
+$billingId = filter_input(
+    INPUT_GET,
+    'billing_id',
+    FILTER_SANITIZE_NUMBER_INT
+);
+if ($billingId) {
+    $dbh = Toolkit_Database::getInstance();
+    try {
+        //$dbh->beginTransaction();
+        $factory = new Toolkit_Members_Billing_Factory();
+        $billing = $factory->createDBObjectById(
+            $dbh,
+            'Toolkit_Members_Billing_Billing',
+            $billingId
+        );
+
+        // need to also delete any items
+        // related to the billing item after
+        // say like payments adjustment
+        $sql = "
+        DELETE
+          FROM billing
+         WHERE invoice_id = :invoice_id
+           AND member_id = :member_id";
+        $stmt1 = $dbh->prepare($sql);
+        $stmt1->bindParam(
+            ':invoice_id',
+            $billing->getInvoice_id(),
+            PDO::PARAM_INT
+        );
+        $stmt1->bindParam(
+            ':member_id',
+            $memberId,
+            PDO::PARAM_INT
+        );
+        $stmt1->execute();
+
+        // new delete record
+        $sql = "
+        DELETE
+          FROM billing
+         WHERE id = :billing_id
+           AND member_id = :member_id";
+        $stmt2 = $dbh->prepare($sql);
+        $stmt2->bindParam(
+            ':billing_id',
+            $billingId,
+            PDO::PARAM_INT
+        );
+        $stmt2->bindParam(
+            ':member_id',
+            $memberId,
+            PDO::PARAM_INT
+        );
+        $stmt2->execute();
+
+        // and commit transaction
+        //$dbh->commit();
+    } catch(PDOException $e) {
+        Toolkit_Common::handleError($e);
+    }
+}
+$resetUrl = MEDIA_BASE_URL . 'admin/members.php'
+    . "?rt=Members"
+    . "&ac=editMember"
+    . "&id={$memberId}"
+    . "&tab=invoices";
+header('Location: ' . $resetUrl);
\ No newline at end of file
diff --git a/Toolkit/Members/Billing/js/edit-billing.js b/Toolkit/Members/Billing/js/edit-billing.js
new file mode 100644 (file)
index 0000000..8fb2a99
--- /dev/null
@@ -0,0 +1,53 @@
+/**
+ * When we are adding an region we want to focus on the first text field
+ * of the form.
+ */
+var MemberBilling =
+{
+    calImg: '//app.gaslightmedia.com/assets/icons/calendar.png',
+    today: new Date(),
+       init: function()
+       {
+               if ($("#transDate").length > 0) {
+            var transImg = '&nbsp;<img id="transDateCal"' +
+                ' height="16"' +
+                ' style="verticle-align:middle;"' +
+                ' src="' + MemberBilling.calImg + '">';
+            $("#transDate").after(transImg);
+            var month = (MemberBilling.today.getMonth()) + 1;
+            var day = MemberBilling.today.getDate();
+            var year = MemberBilling.today.getFullYear();
+            var transCal = new Zapatec.Calendar.setup({
+               weekNumbers : false,
+               ifFormat    : '%m/%d/%Y',
+               button      : 'transDateCal',
+               onUpdate    : MemberBilling.updateTransactionDateField,
+               showsTime   : false,
+               range       : [MemberBilling.today.getFullYear(), MemberBilling.today.getFullYear() + 1],
+               date        :  '7/1/' + year
+            });
+        }
+        if ($(".delete-billing").length > 0) {
+            $(".delete-billing").click(
+                function () {
+                    var ret = confirm(
+                        'This action cannot be undone!\nAre you sure?'
+                    );
+                    return ret;
+                }
+            );
+        }
+       },
+    updateTransactionDateField: function(cal)
+    {
+        var date = cal.date;
+        var month = date.getMonth() + 1;
+        var day = date.getDate();
+        var year = date.getFullYear();
+
+        $("#transDate").val(month + '/' + day + '/' + year);
+    }
+
+}
+
+$(document).ready(MemberBilling.init);
diff --git a/Toolkit/Members/Billing/pdfInvoice.php b/Toolkit/Members/Billing/pdfInvoice.php
new file mode 100644 (file)
index 0000000..ea518f8
--- /dev/null
@@ -0,0 +1,318 @@
+<?php
+/**
+ *  GLM PDF Forms
+ *  Invoice #1 - Invoice with payment return
+ *
+ *  PHP version 5
+ *
+ * @category  GLM_PDF_Forms
+ * @package   None
+ * @author    Chuck Scott <cscott@gaslightmedia.com>
+ * @copyright 2010-2010 Gaslight Media
+ * @license   http://app.gaslightmedia.com/license.php Gaslight Media
+ * @link      http://www.gaslightmedia.com www.gaslightmedia.com
+ * @since     $Date: 2011/03/18 12:33:47 $
+ */
+
+define('GLMPDF_DEBUG', false);
+
+// Setup border parameters
+define('BORDER_WIDTH', 0);
+define('BORDER_COLOR', 'black');
+
+// Set paper size
+define('PAPER_SIZE', 'US Letter');
+
+// Set system parameters
+define('GLM_PDF_ROOT', GLM_APP_BASE . "pdf/pdf_lib_8/");
+
+// putenv("PDFLIBSERIAL=L40300-102733-2200XX-5B12E1");    // PDFLib License Number
+
+require_once GLM_PDF_ROOT . "glmPdf.php";
+
+/**
+ * money()
+ *
+ * Format a number using conventions for money.
+ *
+ * @param float  $value  Value to be formatted
+ * @param string $option Comma or space separated options controlling format
+ *
+ * Options:
+ *     NOPREFIX  Do not prefix the number with a $.
+ *     NEG_PAREN Use parenthesis instead of "-" to designate a negative number.
+ *
+ * @return string
+ * @access public
+ */
+function money($value, $option = "")
+{
+
+    // Check if the "NOPREFIX" option is specified
+    if (strstr($option, 'NOPREFIX')) {
+        $prefix = "";
+    } else {
+        $prefix = "$";
+    }
+
+    // Check if it's a negative value. If so, save that and make it positive.
+    $neg = false;
+    if ($value < 0) {
+        $neg = true;
+        $value *= -1;
+    }
+
+    // Do value sanity check
+    if (!is_numeric($value)) {
+        return( $prefix."0.00" );
+    }
+
+    // Format number and add prefix
+    $r = $prefix.number_format($value, 2, ".", ",");
+
+    // Check if the value was negative.
+    if ($neg) {
+        if (strstr($option, 'NEG_PAREN')) {
+            $r = '('.$r.')';
+        } else {
+            $r = '-'.$r;
+        }
+    }
+
+    return $r;
+}
+
+
+/**
+ * glmpdf_invoice()
+ *
+ * Create an invoice (style 1) and return as a PDF.
+ *
+ * @param array $inv An array specifying invoice contents
+ *
+ * Format of $inv. A sample follows...
+ *
+ * array(
+ *    'company_logo'          => GLM_PDF_ROOT."forms/invoices/gaslight.gif",
+ *    'company_logo_height'   => 70,
+ *    'comany_name_in_header' => false,
+ *    'company_name'          => 'Gaslight Media',
+ *    'company_addr1'         => '120 E. Lake St.',
+ *    'company_addr2'         => '',
+ *    'company_city'          => 'Petoskey',
+ *    'company_state'         => 'MI',
+ *    'company_zip'           => '49770',
+ *    'company_phone'         => '231-487-0692',
+ *    'company_email'         => 'info@gaslightmedia.com',
+ *    'invoice_date'          => 'February 25, 2011',
+ *    'invoice_number'        => '1001',
+ *    'invoice_balance'       => 123.42,
+ *    'invoice_file_name'     => 'invoice_1001.pdf',
+ *    'member_billing_no'     => '123456',
+ *    'member_name'           => 'Fred Jones Digging',
+ *    'member_addr1'          => '123 E. West St.',
+ *    'member_addr2'          => 'Suite 35A',
+ *    'member_city'           => 'Eagle Mountain',
+ *    'member_state'          => 'MI',
+ *    'member_zip'            => '49110',
+ *    'member_phone'          => '123-123-1234',
+ *    'member_email'          => 'info@fjdig.com',
+ *    'payment_terms'         => 'Payable upon receipt',
+ *    'items' => array(
+ *        array(
+ *            'type' => 1,        // Invoice
+ *            'date' => '01/01/2011',
+ *            'descr' => 'Yearly Member Billing',
+ *            'amount' => 123.12,
+ *            'balance' => 123.12
+ *        ),
+ *        array(
+ *            'type' => 2,        // Payment
+ *            'date' => '01/01/2011',
+ *            'descr' => 'Check # 12345',
+ *            'amount' => 123.12,
+ *            'balance' => 0
+ *        )
+ *    )
+ * );
+ *
+ * @return string (pdf)
+ * @access public
+ */
+function glmpdf_invoice_1($inv)
+{
+    $glmPdf = new glmPdf();
+    /*
+     * Setup Forms Form layout
+     *
+     * x = bottom, y = left, xs = horz size, ys = vert size
+     *
+     * Invoice form is single form per page full page
+     */
+    $invoice_forms = array(
+        1 => array( 'x' => 0, 'y' => 0, 'xs' => 612,  'ys' => 792 )
+    );
+
+    // Initialize Forms
+    $r = $glmPdf->glmpdf_set_forms(
+        count($invoice_forms),
+        $voucher_forms,
+        $glmPdf->glmpdf_standard_page[PAPER_SIZE]['x'],
+        $glmPdf->glmpdf_standard_page[PAPER_SIZE]['y']
+    );
+    if (!$r) {
+        echo "ERROR: Unable to set PDF forms layout.";
+        exit;
+    }
+
+    // Start PDF Generation - (Creator, Author, Title)
+    $glmPdf->glmpdf_start("GLM PDF", "Gaslight Media", "Standard Invoice with Payment Form");
+//var_dump($glmPdf->glmpdf_pdf);
+//    exit;
+    // Load barcode font
+    if (!$glmPdf->glmpdf_add_font('barcode', 'barcode/FREE3OF9.TTF')) {
+        throw new Exception('ERROR: Unable to add barcode font.<P>
+            Did you place the barcode font into a system fonts directory
+            (/usr/share/fonts/ttf) and set the fonts directory correctly in the
+            pdf_functions.inc file?');
+        exit;
+    }
+
+
+
+    // Create new form
+    $glmPdf->glmpdf_next_form();
+
+    // Create Company Header
+    if (trim($inv['company_logo']) != '') {
+        $logo_y = 760 - $inv['company_logo_height'];
+        $logoImage = $glmPdf->glmpdf_open_image($inv['company_logo']);
+        $glmPdf->glmpdf_place_image($logoImage, 20, $logo_y, 1);
+    }
+    $glmPdf->glmpdf_set_font("Helvetica-Bold", 12.0);
+    $glmPdf->glmpdf_place_text($inv['company_name'], 20, 660, "left", 'black');
+    $glmPdf->glmpdf_set_font("Helvetica", 12.0);
+    $glmPdf->glmpdf_place_text($inv['company_addr1'], 20, $glmPdf->glmpdf_current_y, "left", 'black');
+    if (trim($inv['company_addr2']) != '') {
+        $glmPdf->glmpdf_place_text($inv['company_addr2'], 20, $glmPdf->glmpdf_current_y, "left", 'black');
+    }
+    $city_state = $inv['company_city'].', '.$inv['company_state'].' '.$inv['company_zip'];
+    $glmPdf->glmpdf_place_text($city_state, 20, $glmPdf->glmpdf_current_y, "left", 'black');
+    $glmPdf->glmpdf_place_text($inv['company_phone'], 20, $glmPdf->glmpdf_current_y, "left", 'black');
+    $glmPdf->glmpdf_place_text($inv['company_email'], 20, $glmPdf->glmpdf_current_y, "left", 'black');
+
+    // Invoice Header
+    $glmPdf->glmpdf_set_font("Helvetica-Bold", 26.0);
+    $glmPdf->glmpdf_place_text('INVOICE', 570, 710, "right", 'black');
+    $glmPdf->glmpdf_set_font("Helvetica", 12.0);
+    $glmPdf->glmpdf_place_text('Date:', 350, 650, "left", 'black');
+    $glmPdf->glmpdf_place_text($inv['invoice_date'], 450, $glmPdf->glmpdf_last_y, "left", 'black');
+    $glmPdf->glmpdf_place_text('Invoice #:', 350, $glmPdf->glmpdf_current_y, "left", 'black');
+    $glmPdf->glmpdf_place_text($inv['invoice_number'], 450, $glmPdf->glmpdf_last_y, "left", 'black');
+    $glmPdf->glmpdf_place_text('Member Billing #:', 350, $glmPdf->glmpdf_current_y, "left", 'black');
+    $glmPdf->glmpdf_place_text($inv['member_billing_no'], 450, $glmPdf->glmpdf_last_y, "left", 'black');
+
+    // Member Info
+    $glmPdf->glmpdf_set_font("Helvetica-Bold", 22.0);
+    $glmPdf->glmpdf_place_text('Bill To:', 20, 570, "left", 'black');
+    $glmPdf->glmpdf_set_font("Helvetica", 12.0);
+    $glmPdf->glmpdf_place_text($inv['member_name'], 20, $glmPdf->glmpdf_current_y, "left", 'black');
+    $glmPdf->glmpdf_place_text($inv['member_addr1'], 20, $glmPdf->glmpdf_current_y, "left", 'black');
+    if (trim($inv['member_addr2']) != '') {
+        $glmPdf->glmpdf_place_text($inv['member_addr2'], 20, $glmPdf->glmpdf_current_y, "left", 'black');
+    }
+    $city_state = $inv['member_city'].', '.$inv['member_state'].' '.$inv['member_zip'];
+    $glmPdf->glmpdf_place_text($city_state, 20, $glmPdf->glmpdf_current_y, "left", 'black');
+    $glmPdf->glmpdf_place_text($inv['member_phone'], 20, $glmPdf->glmpdf_current_y, "left", 'black');
+    $glmPdf->glmpdf_place_text($inv['member_email'], 20, $glmPdf->glmpdf_current_y, "left", 'black');
+
+    // Terms
+    $glmPdf->glmpdf_place_box(1, 20, 470, 550, 20, 'gray', 'gray', 'round');
+    $glmPdf->glmpdf_set_font("Helvetica-Bold", 12.0);
+    $glmPdf->glmpdf_place_text('Payment Terms', 30, 455, "left", 'white');
+    $glmPdf->glmpdf_set_font("Helvetica", 12.0);
+    $glmPdf->glmpdf_place_text($inv['payment_terms'], 30, 430, "left", 'black');
+
+    // Items Header
+    $glmPdf->glmpdf_place_box(1, 20, 420, 550, 20, 'gray', 'gray', 'round');
+    $glmPdf->glmpdf_set_font("Helvetica-Bold", 12.0);
+    $glmPdf->glmpdf_place_text('Date', 30, 405, "left", 'white');
+    $glmPdf->glmpdf_place_text('Description', 110, $glmPdf->glmpdf_last_y, "left", 'white');
+    $glmPdf->glmpdf_place_text('Amount', 490, $glmPdf->glmpdf_last_y, "right", 'white');
+    $glmPdf->glmpdf_place_text('Balance', 560, $glmPdf->glmpdf_last_y, "right", 'white');
+
+    // Items
+    $glmPdf->glmpdf_set_font("Helvetica", 12.0);
+    $glmPdf->glmpdf_place_text('', 30, 380, "left", 'black');
+    foreach ($inv['items'] as $item) {
+        switch ($item['type']) {
+        // Type 1 - Invoice
+        case 1;
+            $glmPdf->glmpdf_place_text($item['date'], 30, $glmPdf->glmpdf_current_y, "left", 'black');
+            $glmPdf->glmpdf_place_text($item['descr'], 110, $glmPdf->glmpdf_last_y, "left", 'black');
+            $glmPdf->glmpdf_place_text(money($item['amount']), 490, $glmPdf->glmpdf_last_y, "right", 'black');
+            $glmPdf->glmpdf_place_text(money($item['balance']), 560, $glmPdf->glmpdf_last_y, "right", 'black');
+            break;
+
+        // Type 2 - Payment
+        case 2:
+            $glmPdf->glmpdf_place_text($item['date'], 30, $glmPdf->glmpdf_current_y, "left", 'black');
+            $glmPdf->glmpdf_place_text('Payment - Thank You!', 110, $glmPdf->glmpdf_last_y, "left", 'black');
+            $glmPdf->glmpdf_place_text(money($item['amount']*-1), 490, $glmPdf->glmpdf_last_y, "right", 'black');
+            $glmPdf->glmpdf_place_text(money($item['balance']), 560, $glmPdf->glmpdf_last_y, "right", 'black');
+            break;
+
+        }
+    }
+
+
+    // Tear-off Payment Form
+    $glmPdf->glmpdf_place_line(1, 20, 150, 570, 150, 'black', true, 4, 14);
+    $glmPdf->glmpdf_set_font("Helvetica", 10.0);
+    $glmPdf->glmpdf_place_text('Please return this coupon below with your payment.', 300, 154, 'center', 'black');
+    $glmPdf->glmpdf_place_text('Make checks payable to:', 20, 140, 'left', 'red');
+    $glmPdf->glmpdf_set_font("Helvetica", 12.0);
+
+    $glmPdf->glmpdf_place_text($inv['company_name'], 20, 128, "left", 'black');
+    $addr = $inv['company_addr1'];
+    if (trim($inv['company_addr2']) != '') {
+        $addr .= ', '.$inv['company_addr2'];
+    }
+    $addr .= ', '.$inv['company_city'].', '.$inv['company_state'].' '.$inv['company_zip'];
+    $glmPdf->glmpdf_place_text($addr, 20, $glmPdf->glmpdf_current_y, "left", 'black');
+    $glmPdf->glmpdf_place_text($inv['company_phone'].'  -  '.$inv['company_email'], 20, $glmPdf->glmpdf_current_y, "left", 'black');
+
+    $glmPdf->glmpdf_place_text($inv['member_name'], 20, 80, "left", 'black');
+    $glmPdf->glmpdf_place_text($inv['member_addr1'], 20, $glmPdf->glmpdf_current_y, "left", 'black');
+    if (trim($inv['member_addr2']) != '') {
+        $glmPdf->glmpdf_place_text($inv['member_addr2'], 20, $glmPdf->glmpdf_current_y, "left", 'black');
+    }
+    $city_state = $inv['member_city'].', '.$inv['member_state'].' '.$inv['member_zip'];
+    $glmPdf->glmpdf_place_text($city_state, 20, $glmPdf->glmpdf_current_y, "left", 'black');
+    $glmPdf->glmpdf_place_text($inv['member_phone'], 20, $glmPdf->glmpdf_current_y, "left", 'black');
+    $glmPdf->glmpdf_place_text($inv['member_email'], 20, $glmPdf->glmpdf_current_y, "left", 'black');
+
+    $glmPdf->glmpdf_set_font("Helvetica-Bold", 12.0);
+    $glmPdf->glmpdf_place_text('Please Pay:', 450, 80, "right", 'black');
+    $glmPdf->glmpdf_place_text(money($inv['invoice_balance']), 560, $glmPdf->glmpdf_last_y, "right", 'black');
+
+    $glmPdf->glmpdf_set_font("Helvetica-Bold", 12.0);
+    if (($inv['invoice_balance']-0) > 0) {
+        $glmPdf->glmpdf_place_text('Payment Amount:', 450, 60, "right", 'black');
+        $glmPdf->glmpdf_place_box(1, 460, 75, 100, 20, 'black', 'white');
+    } else {
+        $glmPdf->glmpdf_place_text('NO PAYMENT REQUIRED', 560, 60, "right", 'black');
+    }
+
+    $glmPdf->glmpdf_set_font("Helvetica", 12.0);
+    $glmPdf->glmpdf_place_text('Member Billing #:', 450, 30, "right", 'black');
+    $glmPdf->glmpdf_place_text($inv['member_billing_no'], 560, $glmPdf->glmpdf_last_y, "right", 'black');
+
+    $barcode = $inv['member_billing_no'].'-'.$inv['invoice_number'];
+    $glmPdf->glmpdf_set_font('barcode', 40);
+    $glmPdf->glmpdf_place_text("*$barcode*", 560, 110, 'right', 'black');
+    $glmPdf->glmpdfDrawGrid(1, 1);
+
+        // Close PDF setup and send to user's browser
+    $glmPdf->glmpdf_send_to_browser('', $inv['invoice_file_name']);
+}
diff --git a/Toolkit/Members/Billing/recreateInvoices.php b/Toolkit/Members/Billing/recreateInvoices.php
new file mode 100644 (file)
index 0000000..d874eea
--- /dev/null
@@ -0,0 +1,44 @@
+<?php
+/**
+ * recreateInvoices.php
+ *
+ * PHP version 5
+ *
+ * @category Toolkit_Members
+ * @package  Billing
+ * @author   Steve Sutton <steve@gaslightmedia.com>
+ * @license  http://www.gaslightmedia.com Gaslightmedia
+ * @link     <>
+ */
+require_once '../../../setup.phtml';
+
+$dbh = Toolkit_Database::getInstance();
+
+try {
+    $sql = "
+    SELECT *
+      FROM billing
+     WHERE invoice != ''";
+    $stmt = $dbh->query($sql);
+    while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
+        $invoice = Toolkit_Members_Billing_Factory::createDbObjectById(
+            $dbh,
+            'Toolkit_Members_Billing_Billing',
+            $row['id']
+        );
+        //var_dump($invoice);
+        $pdf = new Toolkit_Members_Billing_InvoicePdf();
+        $pdfInvoice = $pdf->createPdfInvoice(
+            $dbh,
+            $row['member_id'],
+            $row['invoice_id'],
+            $row['account_id']
+        );
+
+        $invoice->setInvoice(base64_encode($pdfInvoice));
+        $invoice->save($dbh);
+    }
+} catch(PDOException $e) {
+    Toolkit_Common::handleError($e);
+}
+echo 'done';
\ No newline at end of file
diff --git a/Toolkit/Members/Billing/templates/editBilling.html b/Toolkit/Members/Billing/templates/editBilling.html
new file mode 100644 (file)
index 0000000..675cb4c
--- /dev/null
@@ -0,0 +1,42 @@
+
+<div id="member-info">
+       {form.javascript:h}
+       {form.outputHeader():h}
+       {form.hidden:h}
+       <!-- Error or Success Message -->
+       {validated():h}
+       <div id="mRow1">
+               <fieldset flexy:foreach="form.sections,sec" class="form">
+                       <legend>{sec.header}</legend>
+                       <table>
+                <tr flexy:foreach="sec.elements,elem">
+                    <!-- CheckBoxes go here. -->
+                    {if:elem.isType(#checkbox#)}                        
+                        <td class="labelcell"> {elem.label:h} </td>
+                        <td class="fieldcell"> {elem.html:h} </td>
+                    {else:}
+                        <!-- All regular elements go here. -->
+                        <td class="labelcell">
+                            <span flexy:if="elem.required" class="req">*</span>
+                            {if:elem.error}<span class="req">{end:}
+                                {elem.label}
+                            {if:elem.error}</span>{end:}
+                        </td>
+                        <td class="fieldcell">
+                            <div flexy:if="elem.error" class="req">
+                                {elem.error}
+                            </div>
+                            {elem.html:h}
+                        </td>
+                    {end:}
+                </tr>
+                       </table>
+               </fieldset>
+       </div>
+       <div class="submitArea" style="height:30px;width:400px;">
+        <b style="width:200px;float:left;">
+            <input type="submit" class="submit" value="Save">
+        </b>
+       </div>
+       </form>
+</div>
diff --git a/Toolkit/Members/Billing/templates/editPaymentTypes.html b/Toolkit/Members/Billing/templates/editPaymentTypes.html
new file mode 100644 (file)
index 0000000..f5f1b46
--- /dev/null
@@ -0,0 +1,48 @@
+
+<div id="member-info">
+       {form.javascript:h}
+       {form.outputHeader():h}
+       {form.hidden:h}
+       <!-- Error or Success Message -->
+       {validated():h}
+       <div id="mRow1">
+               <fieldset flexy:foreach="form.sections,sec" class="form">
+                       <legend>{sec.header}</legend>
+                       <table>
+                <tr flexy:foreach="sec.elements,elem">
+                    <!-- CheckBoxes go here. -->
+                    {if:elem.isType(#checkbox#)}                        
+                        <td class="labelcell"> {elem.label:h} </td>
+                        <td class="fieldcell"> {elem.html:h} </td>
+                    {else:}
+                        <!-- All regular elements go here. -->
+                        <td class="labelcell">
+                            <span flexy:if="elem.required" class="req">*</span>
+                            {if:elem.error}<span class="req">{end:}
+                                {elem.label}
+                            {if:elem.error}</span>{end:}
+                        </td>
+                        <td class="fieldcell">
+                            <div flexy:if="elem.error" class="req">
+                                {elem.error}
+                            </div>
+                            {elem.html:h}
+                        </td>
+                    {end:}
+                </tr>
+                       </table>
+               </fieldset>
+       </div>
+       <div class="submitArea" style="height:30px;width:400px;">
+        <b style="width:200px;float:left;">
+            <input type="submit" class="submit" value="Save Payment Type">
+        </b>
+               {if:isEdit()}
+        <b style="width:200px;float:left;">
+                       <input type="submit" name="delete" class="submit" value="Remove Payment Type"
+                   onClick="return(confirm('This cannot be undone! Are You Sure?'));">
+        </b>
+               {end:}
+       </div>
+       </form>
+</div>
diff --git a/Toolkit/Members/Billing/templates/invoiceFilterForm.html b/Toolkit/Members/Billing/templates/invoiceFilterForm.html
new file mode 100644 (file)
index 0000000..01e0987
--- /dev/null
@@ -0,0 +1,39 @@
+
+<div>
+       {form.javascript:h}
+       {form.outputHeader():h}
+       {form.hidden:h}
+       <div>
+               <fieldset flexy:foreach="form.sections,sec">
+                       <legend>{sec.header}</legend>
+                <div flexy:foreach="sec.elements,elem" style="float:left;width:200px;">
+                    <!-- CheckBoxes go here. -->
+                    {if:elem.isType(#checkbox#)}                        
+                        {elem.label:h}
+                        {elem.html:h}
+                    {else:}
+                        <!-- All regular elements go here. -->
+                            <span flexy:if="elem.required" class="req">*</span>
+                            {if:elem.error}<span class="req">{end:}
+                                <div>{elem.label}</div>
+                            {if:elem.error}</span>{end:}
+                            <div flexy:if="elem.error" class="req">
+                                {elem.error}
+                            </div>
+                            <div>{elem.html:h}</div>
+                    {end:}
+                </div>
+               </fieldset>
+       </div>
+       <div style="height:30px;width:400px;">
+        <b style="width:100px;float:left;">
+            <input type="submit" class="submit" value="Filter">
+        </b>
+        {if:task}
+        <b style="width:100px;float:left;">
+                       <input type="submit" name="submit" class="submit" value="{task:h}">
+        </b>
+        {end:}
+       </div>
+       </form>
+</div>
diff --git a/Toolkit/Members/Billing/templates/memberList.html b/Toolkit/Members/Billing/templates/memberList.html
new file mode 100644 (file)
index 0000000..5636f73
--- /dev/null
@@ -0,0 +1,26 @@
+<table id="dataGrid" class="dataGrid">
+  <thead>
+    <tr>
+      <th>Member Name</th>
+      <th>Account Number</th>
+      <th>Payment Type</th>
+      <th>Balance Due</th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr flexy:foreach="members,member" class="{member[classStyle]}">
+      <td>
+          <a href="{member[member_url]:h}">{member[member_name]:h}</a>
+      </td>
+      <td>
+          {member[account_number]:h}
+      </td>
+      <td>
+          {member[payment_type]:h}
+      </td>
+      <td>
+          {member[balanceDue]:h}
+      </td>
+    </tr>
+  </tbody>
+</table>
\ No newline at end of file
diff --git a/Toolkit/Members/Billing/templates/memberStatements.html b/Toolkit/Members/Billing/templates/memberStatements.html
new file mode 100644 (file)
index 0000000..caaeecd
--- /dev/null
@@ -0,0 +1,44 @@
+
+<fieldset flexy:foreach="accounts,accountData">
+  <legend>{accountData[paymentType]:h} Statement</legend>
+    <table class="statement">
+      <thead>
+        <tr>
+          <td flexy:if="canEditBillings">&nbsp;</td>
+          <td>Date</td>
+          <td nowrap="nowrap">Transaction Time</td>
+          <td>Type</td>
+          <td>Method</td>
+          <td>Details</td>
+          <td>Notes</td>
+          <td>Invoice</td>
+          <td>Amount</td>
+          <td>Balance</td>
+          <td flexy:if="canEditBillings">&nbsp;</td>
+        </tr>
+      </thead>
+      <tr flexy:foreach="accountData[records],row" class="{row[className]:h}">
+          <td flexy:if="canEditBillings"><a flexy:if="row[editUrl]" class="edit-billing" rel="{row[id]}" href="{row[editUrl]:h}">Edit</a></td>
+        <td>{row[transaction_date]:h}</td>
+        <td nowrap="nowrap">{row[transaction_time]:h}</td>
+        <td>{row[billing_type]:h}</td>
+        <td>{row[payment_method]:h}</td>
+        <td>{row[payment_data]:h}</td>
+        <td>{row[notes]:h}</td>
+        <td><a flexy:if="row[invoice]" target="_blank" href="{row[invoice]:h}">Invoice</a></td>
+        <td align="right">${row[amount]:h}</td>
+        <td align="right">${row[balance]:h}</td>
+        <td flexy:if="canEditBillings"><a flexy:if="row[editUrl]" class="delete-billing" href="{row[deleteLink]:h}">Delete</a></td>
+      </tr>
+      <tr class="balanceline">
+        {if:canEditBillings}
+        <td colspan="9" align="right">Balance Due:</td>
+        {else:}
+        <td colspan="8" align="right">Balance Due:</td>
+        {end:}
+        
+        <td align="right">${accountData[balanceDue]:h}</td>
+        <td flexy:if="canEditBillings">&nbsp;</td>
+      </tr>
+  </table>
+</fieldset>
\ No newline at end of file
diff --git a/Toolkit/Members/Billing/templates/paymentForm.html b/Toolkit/Members/Billing/templates/paymentForm.html
new file mode 100644 (file)
index 0000000..04eef40
--- /dev/null
@@ -0,0 +1,39 @@
+<div id="member-info">
+       {form.javascript:h}
+       {form.outputHeader():h}
+       {form.hidden:h}
+       <!-- Error or Success Message -->
+       {validated():h}
+       <div id="mRow1">
+               <fieldset flexy:foreach="form.sections,sec" class="form">
+                       <legend>{sec.header}</legend>
+                       <table>
+                <tr flexy:foreach="sec.elements,elem">
+                    <!-- CheckBoxes go here. -->
+                    {if:elem.isType(#checkbox#)}
+                        <td class="labelcell"> {elem.html:h} </td>
+                        <td class="fieldcell"> {elem.label:h} </td>
+                    {else:}
+                        <!-- All regular elements go here. -->
+                        <td class="labelcell">
+                            <span flexy:if="elem.required" class="req">*</span>
+                            {if:elem.error}<span class="req">{end:}
+                                {elem.label}
+                            {if:elem.error}</span>{end:}
+                        </td>
+                        <td class="fieldcell">
+                            <div flexy:if="elem.error" class="req">
+                                {elem.error}
+                            </div>
+                            {elem.html:h}
+                        </td>
+                    {end:}
+                </tr>
+                       </table>
+               </fieldset>
+       </div>
+       <div class="submitArea">
+               <input type="submit" class="submit" value="Make Payment">
+       </div>
+       </form>
+</div>
\ No newline at end of file
diff --git a/Toolkit/Members/Billing/templates/reportSearch.html b/Toolkit/Members/Billing/templates/reportSearch.html
new file mode 100644 (file)
index 0000000..9aa971c
--- /dev/null
@@ -0,0 +1,54 @@
+
+{form.javascript:h}
+{form.outputHeader():h}
+{form.hidden:h}
+<!-- Error or Success Message -->
+{validated():h}
+<div id="search-billing-form">
+{foreach:form.sections,sec} 
+        <h2>Report Generator</h2>
+
+        {foreach:sec.elements,elem} 
+            {if:elem.style}
+              {elem.outputStyle():h}
+            {else:}
+                {if:elem.isButton()}
+                    {if:elem.notFrozen()}
+                        <div id="button-submit">{elem.html:h}</div>
+                    {end:}
+                {else:}
+                    <div id="form-{elem.name}">
+                    {if:elem.isType(#textarea#)}
+                        <!-- <td colspan="2"> -->
+                            {if:elem.required}<span class="error">*</span>{end:}
+                            {if:elem.error}<span class="error">{end:}
+                            <b>{elem.label:h}:</b><br />
+                            {if:elem.error}</span>{end:}
+                    {else:}
+                        <!-- <td align="right" valign="top"> -->
+                            {if:elem.required}<span class="error">*</span>{end:}
+                            {if:elem.error}<span class="error">{end:}
+                            <b>{elem.label:h}:</b>
+                            {if:elem.error}</span>{end:}
+                        <!-- </td>
+                        <td> -->
+                    {end:}
+                    {if:elem.error}<div class="error">{elem.error}</div>{end:}
+                    {if:elem.isType(#group#)}
+                        {foreach:elem.elements,gitem}
+                            {gitem.label:h}
+                            {gitem.html:h}{if:gitem.required}<span class="error">*</span>*</span>{end:}
+                            {if:elem.separator}{elem.separator:h}{end:}
+                        {end:}
+                    {else:}
+                        {elem.html:h}
+                    {end:}
+                        <!-- </td> -->
+                    </div>
+                {end:}
+            {end:}
+        {end:}
+ {end:}
+        
+</div>
+</form>
diff --git a/Toolkit/Members/Billing/templates/sendEmailInvoice.html b/Toolkit/Members/Billing/templates/sendEmailInvoice.html
new file mode 100644 (file)
index 0000000..1d955e6
--- /dev/null
@@ -0,0 +1,21 @@
+
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+  <head>
+    <title></title>
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+  </head>
+  <body>      
+      <p>
+          Attached is your membership invoice for {member_name:h}.
+      </p>
+      <p>
+        {companyName}<br>
+        {companyName2}<br>
+        {companyAddr1}<br>
+        {companyCity}, {companyState} {companyZip}<br>
+        {companyPhone}<br>
+        <a href="http://{companyUrl}">{companyUrl}</a><br>
+      </p>
+  </body>
+</html>
diff --git a/Toolkit/Members/Billing/testMember.php b/Toolkit/Members/Billing/testMember.php
new file mode 100644 (file)
index 0000000..79e70f3
--- /dev/null
@@ -0,0 +1,170 @@
+<?php
+/**
+ * testMember.php
+ * 
+ * PHP version 5.2
+ * 
+ * @category  Toolkit
+ * @package   Members_Billing
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @version   SVN: $id$
+ * @link      <>
+ */
+
+require_once '../../../setup.phtml';
+
+/**
+ * memberObj
+ * 
+ * Description of memberObj
+ * 
+ * @category  Toolkit
+ * @package   Members_Billing
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @release   Release: $id$
+ * @link      <>
+ */
+class memberObj extends Toolkit_Common
+{
+
+    /**
+     * Table name
+     * 
+     * @var string
+     * @access protected
+     */
+    protected $tableName = 'member';
+    
+    /**
+     * Class constructor
+     * 
+     * @param PDO $dbh Database handler
+     * 
+     * @access public
+     */
+    public function __construct(PDO $dbh)
+    {
+        $this->dbh = $dbh;
+    }
+}
+$members = array();
+$dbh = Toolkit_Database::getInstance();
+$dbh2 = new PDO(
+    'pgsql:host=ds3 user=nobody dbname=uptravel',
+    null,
+    null,
+    array(
+        PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
+    )
+);
+$sql = "
+SELECT member_name,type
+  FROM member
+ WHERE member_id = :member_id";
+$matchStmt = $dbh2->prepare($sql);
+$newMemberFields = array(
+    'member_id',
+    'member_name',
+    'member_login',
+    'member_passwd',
+    'active',
+    'street',
+    'lat',
+    'lon',
+    'phone',
+    'process_email',
+    'url',
+    'city_id',
+    'state_id',
+    'description',
+    'create_date',
+    'zip',
+    'toll_free',
+    'member_contact_email',
+    'state_id',
+    'mailing_address',
+    'mailing_city_id',
+    'mailing_state_id',
+    'mailing_zip',
+    'region',
+    'type',
+    'account_number'
+);
+$sql = "
+SELECT ".implode(',', $newMemberFields)."
+  FROM member
+ WHERE account_number IS NOT NULL
+ORDER BY account_number";
+$stmt = $dbh->query($sql);
+$sql = "
+UPDATE member
+   SET account_number = :account_number,
+       type = :type
+ WHERE member_id = :member_id";
+$updateMember = $dbh2->prepare($sql);
+$format = "<tr>
+        <td>%s</td>
+        <td>%s</td>
+        <td>%s</td>
+        <td>%s</td>
+        <td>%s</td>
+        <td>%s</td>
+      </tr>";
+echo '<table border="1">';
+$memberDat = new memberObj($dbh2);
+
+while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
+    $matchStmt->bindParam(
+        ':member_id',
+        $row['member_id'],
+        PDO::PARAM_INT
+    );
+    $matchStmt->execute();
+    $matchData = $matchStmt->fetch();
+    if ($matchData) {
+        printf(
+            $format,
+            $row['member_id'],
+            $row['member_name'],
+            (($matchData['member_name'])
+                ? $matchData['member_name']
+                : '<b style="color:red;">Not Found</b>'),
+            $row['type'],
+            $matchData['type'],
+            $row['account_number']
+        );
+        $dbh2->beginTransaction();
+        $updateMember->bindParam(
+            ':account_number',
+            $row['account_number'],
+            PDO::PARAM_STR
+        );
+        $updateMember->bindParam(
+            ':type',
+            $row['type'],
+            PDO::PARAM_STR
+        );
+        $updateMember->bindParam(
+            ':member_id',
+            $row['member_id'],
+            PDO::PARAM_INT
+        );
+        $updateMember->execute();
+        $dbh2->rollBack();
+    } else {
+        $notFound[] = $row;
+        $sql = $memberDat->createSQLInsert('member', array_keys($row));
+        var_dump($sql);
+        $dbh2->beginTransaction();
+        $insertStmt = $memberDat->prepareQuery($dbh2, $sql, $row);
+        $insertStmt->execute();
+        $dbh2->rollBack();
+    }
+}
+
+echo '</table>';
+var_dump($notFound);
diff --git a/Toolkit/Members/BreadCrumbs.php b/Toolkit/Members/BreadCrumbs.php
new file mode 100644 (file)
index 0000000..538d4a9
--- /dev/null
@@ -0,0 +1,147 @@
+<?php
+/**
+ * NewsletterController.php
+ *
+ * PHP version 5.2
+ *
+ * @category  Toolkit
+ * @package   Members_Admin
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @version   SVN: $id$
+ * @link      <>
+ */
+
+/**
+ * Toolkit_Members_Admin_NewsletterController
+ *
+ * Description of Toolkit_Members_Admin_NewsletterController
+ *
+ * @category  Toolkit
+ * @package   Members_Admin
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @release   Release: $id$
+ * @link      <>
+ */
+class Toolkit_Members_BreadCrumbs
+    extends Toolkit_Template_BreadCrumbs
+{
+    //  {{{ properties
+
+    /**
+     * PDO
+     * @var PDO
+     * @access private
+     */
+    private $_dbh;
+
+    //  }}}
+    //  {{{ __construct()
+
+    /**
+     * Class constructor
+     *
+     * @param Toolkit_Toolbox_PageGatewayAbstract $gateway Gateway
+     * @param PDO                                 $dbh     Database Handler
+     *
+     * @access public
+     */
+    public function __construct(
+        Toolkit_Toolbox_PageGatewayAbstract $gateway,
+        PDO $dbh
+    ) {
+        $this->pageGateway = $gateway;
+        $this->_dbh        = $dbh;
+    }
+
+    //  }}}
+    //  {{{ getBreadCrumbsArray()
+
+    /**
+     * Description of getBreadCrumbsArray()
+     *
+     * @param int $id Description for $id
+     *
+     * @return array
+     * @access protected
+     */
+    protected function getBreadCrumbsArray($id)
+    {
+        if ($id == HOME_ID) {
+            return array();
+        }
+
+        $stack = array();
+        $memberId = filter_var($_REQUEST['member_id'], FILTER_VALIDATE_INT);
+        if ($memberId) {
+            $memberName = $this->_getMemberName($memberId);
+            array_unshift($stack, $memberName);
+
+            //$anchor = '<a href="' . BASE_URL . 'memberProfiles/">Member Profiles</a>';
+            //array_unshift($stack, $anchor);
+
+            $anchor = '<a href="' . MEDIA_BASE_URL . 'index.php">Home</a>';
+            array_unshift($stack, $anchor);
+
+            return $stack;
+        }
+        $memberName = $this->_getMemberName($_GET['member_id']);
+        array_unshift($stack, $memberName);
+        $searchId = $id;
+        do {
+            $page = $this->pageGateway->find($searchId);
+            $seoUrl = Toolkit_Template_Page::getSeoUrl(
+                $this->pageGateway,
+                $searchId
+            );
+            $anchor = '<a href="'.$seoUrl.'">'.$page['navigation_name'].'</a>';
+            array_unshift($stack, $anchor);
+            $searchId = $page['parent'];
+        } while ($page['parent'] != 0);
+
+        $anchor = '<a href="' . MEDIA_BASE_URL . 'index.php">Home</a>';
+        array_unshift($stack, $anchor);
+
+        return $stack;
+    }
+
+    //  }}}
+
+    //  {{{ _getMemberName()
+
+    /**
+     * Get the name of a member via memberId
+     *
+     * @param integer $mid Member id
+     *
+     * @return string member name
+     * @access private
+     * @throws Toolkit_Members_Exception upon error retrieving member name
+     */
+    private function _getMemberName($mid)
+    {
+        try {
+            $sql = "
+                SELECT member_name
+                  FROM member
+                 WHERE member_id = :mid";
+
+            $stmt = $this->_dbh->prepare($sql);
+            $stmt->bindParam(':mid', $mid, PDO::PARAM_INT);
+            $stmt->execute();
+            $row = $stmt->fetch(PDO::FETCH_ASSOC);
+
+            return $row['member_name'];
+        } catch (PDOException $e) {
+            Toolkit_Logger::logException('DB Error', $e);
+            throw new Toolkit_Members_Exception(
+                "Unable to fetch member name for breadcrumbs for member `$mid`"
+            );
+        }
+    }
+
+    //  }}}
+}
diff --git a/Toolkit/Members/CategoriesIterator.php b/Toolkit/Members/CategoriesIterator.php
new file mode 100644 (file)
index 0000000..db9e54d
--- /dev/null
@@ -0,0 +1,215 @@
+<?php
+
+/**
+ * Member categories to iterate
+ *
+ * PHP version 5
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <jamie@gaslightmedia.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com/ Gaslightmedia
+ * @version   CVS: $Id: CategoriesIterator.php,v 1.7 2010/06/29 19:15:23 jamie Exp $
+ * @link      <>
+ */
+
+
+/**
+ * Member categories to iterate
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <jamie@gaslightmedia.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com/ Gaslightmedia
+ * @link      <>
+ */
+class Toolkit_Members_CategoriesIterator implements IteratorAggregate
+{
+    //  {{{ properties
+
+
+    /**
+     * Categories to iterate
+     * @var    array
+     * @access private
+     */
+       private $_categories;
+
+    //  }}}
+    //  {{{ __construct()
+
+
+    /**
+     * Constructor
+     *
+     * @return void
+     * @access private
+     */
+       private function __construct()
+       {
+               $this->_categories = array();
+       }
+
+    //  }}}
+
+    //  {{{ add()
+
+
+    /**
+     * Add a category to the collection
+     *
+     * @param Toolkit_Members_Category $c Category
+     *
+     * @return void
+     * @access public
+     */
+       public function add(Toolkit_Members_Category $c)
+       {
+               $this->_categories[] = $c;
+       }
+
+    //  }}}
+
+    //  {{{ create()
+
+
+    /**
+     * Create a new category iterator
+     *
+     * @return Toolkit_Members_CategoriesIterator An instance of an iteratorable object containing member categories
+     * @access public
+     * @static
+     */
+       public static function create()
+       {
+               return new self();
+       }
+
+    //  }}}
+
+    //  {{{ fetchAll()
+
+
+    /**
+     * Fetch all the categories
+     *
+     * @param PDO $pdo Database handler
+     *
+     * @return void
+     * @access public
+     */
+       public function fetchAll(PDO $pdo)
+       {
+               $this->_categories = array();
+
+               try {
+                       $sql = "
+                               SELECT *
+                                 FROM category";
+
+                       $this->_categories = $pdo->query($sql)->fetchAll(PDO::FETCH_ASSOC);
+               } catch (PDOException $e) {
+                       return Toolkit_Common::handleError($e);
+               }
+       }
+
+    //  }}}
+
+    //  {{{ getAllIdsAsArray()
+
+
+    /**
+     * Gets all the categories ids as an array
+     *
+     * @return array  Member category ids
+     * @access public
+     */
+       public function getAllIdsAsArray()
+       {
+               $catIds = array();
+
+               foreach ($this->_categories as $i) {
+                       $catIds[] = $i->getId();
+               }
+
+               return $catIds;
+       }
+
+    //  }}}
+    //  {{{ getCategory()
+
+
+    /**
+     * Get a category object based on its database id
+     *
+     * @param integer $catid Member category database id
+     *
+     * @return Toolkit_Members_Category member category
+     * @access public
+     * @throws InvalidArgumentException if $catid is not an integer
+     */
+       public function getCategory($catid)
+       {
+               if (!ctype_digit((string)$catid)) {
+                       throw new InvalidArgumentException(
+                               'Invalid Argument: $catid must be an integer.')
+                       ;
+               }
+
+               foreach ($this->_categories as $i) {
+                       if ($i->getId() == $catid) {
+                               return $i;
+                       }
+               }
+
+               return false;
+       }
+
+    //  }}}
+    //  {{{ getIterator()
+
+
+    /**
+     * Set an external iterator
+     *
+     * @return Traversable An instance of an object implementing Iterator or Traversable
+     * @access public
+     */
+       public function getIterator()
+       {
+               return new ArrayIterator($this->_categories);
+       }
+
+    //  }}}
+    //  {{{ getTree()
+
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @param PDO        $pdo  Database handler
+     * @param integer $root root of tree
+     *
+     * @return array Nested set keys from tree
+     * @access public
+     */
+       public function getTree(PDO $pdo, $root)
+       {
+               $tree = Toolkit_Common::getHierarchicalTreeStructure(
+                       $pdo,
+                       'category',
+                       'category_id',
+                       'parent_id',
+                       'pos',
+                       $root
+               );
+
+               return array_keys($tree);
+       }
+
+    //  }}}
+}
diff --git a/Toolkit/Members/Category.php b/Toolkit/Members/Category.php
new file mode 100644 (file)
index 0000000..17175e3
--- /dev/null
@@ -0,0 +1,521 @@
+<?php
+
+/**
+ * Member Category object
+ *
+ * PHP version 5
+ *
+ * The license text...
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <jamie@gaslightmedia.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com/ Gaslightmedia
+ * @version   CVS: $Id: Category.php,v 1.4 2010/02/12 21:22:17 jamie Exp $
+ * @link      <>
+ * @see       References to other sections (if any)...
+ */
+
+
+/**
+ * Member Category object
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <jamie@gaslightmedia.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com/ Gaslightmedia
+ * @version   Release: @package_version@
+ * @link      <>
+ * @see       References to other sections (if any)...
+ */
+class Toolkit_Members_Category
+{
+       //      {{{     properties
+
+    /**
+     * Category id
+     * @var    integer
+     * @access private
+     */
+       private $_id;
+
+    /**
+     * Category name
+     * @var    string
+     * @access private
+     */
+       private $_name;
+
+    /**
+     * Parent Id
+     * @var    integer
+     * @access private
+     */
+       private $_parentId;
+
+    /**
+     * Category is an accommodations category
+     * @var    boolean
+     * @access private
+     */
+       private $_accommodations;
+
+    /**
+     * Category is a restaurant category
+     * @var    boolean
+     * @access private
+     */
+       private $_restaurant;
+
+    /**
+     * Category is a golf category
+     * @var    boolean
+     * @access private
+     */
+       private $_golf;
+
+    /**
+     * Category code
+     * @var    integer
+     * @access private
+     */
+       private $_code;
+
+       //      }}}
+       //      {{{     __construct()
+
+    /**
+     * constructor
+     *
+     * @param string  $name           Category name
+     * @param integer $parent         Parent Id
+     * @param boolean $accommodations is accommodations category
+     * @param boolean $restaurant     is restaurant category
+     * @param boolean $golf           is golf category
+     * @param integer $code           category code
+     * @param integer $id             category id
+        *
+     * @return void
+     * @access private
+     */
+       private function __construct(
+               $name,
+               $parent,
+               $accommodations,
+               $restaurant,
+               $golf,
+               $code,
+               $id = null
+       ) {
+               $this->_name = $name;
+               $this->_parentId = $parent;
+               $this->_accommodations = $accommodations;
+               $this->_restaurant = $restaurant;
+               $this->_golf = $golf;
+               $this->_code = $code;
+               $this->_id = $id;
+       }
+
+       //      }}}
+       //      {{{     create()
+
+    /**
+     * Create a new category
+     *
+     * @param string  $name           Category name
+     * @param integer $parent         Parent Id
+     * @param boolean $accommodations is accommodations category
+     * @param boolean $restaurant     is restaurant category
+     * @param boolean $golf           is golf category
+     * @param integer $code           category code
+        *
+     * @return Toolkit_Members_Category new category object
+     * @access public
+     * @throws InvalidArgumentException if $parent is not an integer
+     * @throws InvalidArgumentException if $accommodations is not boolean
+     * @throws InvalidArgumentException if $restaurant is not boolean
+     * @throws InvalidArgumentException if $golf is not boolean
+     * @throws InvalidArgumentException if $code is not an integer
+     * @static
+     */
+       public static function create(
+               $name,
+               $parent = 0,
+               $accommodations = false,
+               $restaurant = false,
+               $golf = false,
+               $code = null
+       ) {
+               if (!ctype_digit((string)$parent)) {
+                       throw new InvalidArgumentException('Invalid Argument: $parent must be an integer.');
+               }
+               if (!is_bool($accommodations)) {
+                       throw new InvalidArgumentException('Invalid Argument: $accommodations must be boolean.');
+               }
+               if (!is_bool($restaurant)) {
+                       throw new InvalidArgumentException('Invalid Argument: $restaurant must be boolean.');
+               }
+               if (!is_bool($golf)) {
+                       throw new InvalidArgumentException('Invalid Argument: $golf must be boolean.');
+               }
+               if (!is_null($code) && !ctype_digit((string) $code)) {
+                       throw new InvalidArgumentException('Invalid Argument: $code must be an integer.');
+               }
+
+               return new self(
+                       $name,
+                       $parent,
+                       $accommodations,
+                       $restaurant,
+                       $golf,
+                       $code
+               );
+       }
+
+       //      }}}
+       //      {{{     fetch()
+
+    /**
+     * Fetch a category from the database
+     *
+     * @param PDO     $pdo Database handler
+     * @param integer $id  Category id to fetch
+        *
+     * @return Toolkit_Members_Category new object
+     * @access public
+     * @throws InvalidArgumentException if $id is not an integer
+     */
+       public function fetch(PDO $pdo, $id)
+       {
+               if (!ctype_digit((string)$id)) {
+                       throw new InvalidArgumentException(
+                               'Invalid Argument: $categoryId must be an integer.'
+                       );
+               }
+
+               try {
+                       $sql = "
+                               SELECT *
+                                 FROM category
+                                WHERE category_id = :id";
+
+                       $stmt = $pdo->prepare($sql);
+                       $stmt->bindParam(':id', $id, PDO::PARAM_INT);
+                       $stmt->execute();
+                       $row = $stmt->fetch(PDO::FETCH_ASSOC);
+
+                       return new self(
+                               $row['name'],
+                               $row['parent_id'],
+                               (bool) $row['accommodations'],
+                               (bool) $row['restaurant'],
+                               (bool) $row['golf'],
+                               (bool) $row['code'],
+                               $row['category_id']
+                       );
+               } catch (PDOException $e) {
+                       return Toolkit_Common::handleError($e);
+               }
+       }
+
+       //      }}}
+       //      {{{     getAccommodations()
+
+    /**
+     * Get accommodations
+     *
+     * @return boolean
+     * @access public
+     */
+       public function getAccommodations()
+       {
+               return $this->_accommodations;
+       }
+
+       //      }}}
+       //      {{{     getCode()
+
+    /**
+     * Get code
+     *
+     * @return integer
+     * @access public
+     */
+       public function getCode()
+       {
+               return $this->_code;
+       }
+
+       //      }}}
+       //      {{{     getGolf()
+
+    /**
+     * Get golf
+     *
+     * @return boolean
+     * @access public
+     */
+       public function getGolf()
+       {
+               return $this->_golf;
+       }
+
+       //      }}}
+       //      {{{     getId()
+
+    /**
+     * Get category id
+     *
+     * @return integer
+     * @access public
+     */
+       public function getId()
+       {
+               return $this->_id;
+       }
+
+       //      }}}
+       //      {{{     getName()
+
+    /**
+     * Get category name
+     *
+     * @return string
+     * @access public
+     */
+       public function getName()
+       {
+               return $this->_name;
+       }
+
+       //      }}}
+       //      {{{     getParentId()
+
+    /**
+     * Get categories parent id
+     *
+     * @return integer
+     * @access public
+     */
+       public function getParentId()
+       {
+               return $this->_parentId;
+       }
+
+       //      }}}
+       //      {{{     getRestaurant()
+
+    /**
+     * Get restaurant
+     *
+     * @return boolean
+     * @access public
+     */
+       public function getRestaurant()
+       {
+               return $this->_restaurant;
+       }
+
+       //      }}}
+       //      {{{     save()
+
+    /**
+     * Save a category to the database
+     *
+     * @param PDO $dbh Database handler
+        *
+     * @return boolean result of inserting the category to the db
+     * @access public
+     */
+       public function save(PDO $dbh)
+       {
+               $data = array(
+                       'name' => $this->_name,
+                       'parent_id' => $this->_parentId,
+                       'accommodations' => $this->_accommodations,
+                       'restaurant' => $this->_restaurant,
+                       'golf' => $this->_golf,
+                       'code' => $this->_code
+               );
+               try {
+                       $sql = Toolkit_Common::createSQLInsert(
+                               'category',
+                               array_keys($data)
+                       );
+
+                       $dbh->beginTransaction();
+
+                       $res = Toolkit_Common::processQuery(
+                               $dbh,
+                               'category',
+                               $sql,
+                               $data
+                       );
+
+                       $sql = "
+                               SELECT *
+                                 FROM category
+                                ORDER BY category_id DESC
+                                LIMIT 1";
+
+                       $row = $dbh->query($sql)->fetch();
+
+                       $this->_id = $row['category_id'];
+                       $dbh->commit();
+
+                       return $res;
+               } catch (PDOException $e) {
+                       $dbh->rollback();
+                       return Toolkit_Common::handleError($e);
+               }
+       }
+
+       //      }}}
+       //      {{{     setname()
+
+    /**
+     * Set the category name
+     *
+     * @param string $name new category name
+        *
+     * @return void
+     * @access public
+     */
+       public function setName($name)
+       {
+               $this->_name = $name;
+       }
+
+       //      }}}
+       //      {{{     setAccommodations()
+
+    /**
+     * Set if category is an accommodation
+     *
+     * @param boolean $accommodations if category is an accommodations
+        *
+     * @return void
+     * @access public
+     * @throws InvalidArgumentException if $accommodations is not a boolean
+     */
+       public function setAccommodations($accommodations)
+       {
+               if (!is_bool($accommodations)) {
+                       throw new InvalidArgumentException(
+                               'Invalid Argument: $accommodations must be boolean.'
+                       );
+               }
+
+               $this->_accommodations = $accommodations;
+       }
+
+       //      }}}
+       //      {{{     setRestaurant()
+
+    /**
+     * Set if a category is a restaurant
+     *
+     * @param boolean $restaurant if category is a restaurant
+        *
+     * @return void
+     * @access public
+     * @throws InvalidArgumentException if $restuarant is not a boolean
+     */
+       public function setRestaurant($restaurant)
+       {
+               if (!is_bool($restaurant)) {
+                       throw new InvalidArgumentException(
+                               'Invalid Argument: $restaurant must be boolean.'
+                       );
+               }
+
+               $this->_restaurant = $restaurant;
+       }
+
+       //      }}}
+       //      {{{     setGolf()
+
+    /**
+     * Set if category is a golf category
+     *
+     * @param boolean $golf if category is a golf category
+        *
+     * @return void
+     * @access public
+     * @throws InvalidArgumentException if $golf is not a boolean
+     */
+       public function setGolf($golf)
+       {
+               if (!is_bool($golf)) {
+                       throw new InvalidArgumentException(
+                               'Invalid Argument: $golf must be boolean.'
+                       );
+               }
+
+               $this->_golf = $golf;
+       }
+
+       //      }}}
+       //      {{{     setCode()
+
+    /**
+     * Set the category code
+     *
+     * @param integer $code categories new code
+        *
+     * @return void
+     * @access public
+     * @throws InvalidArgumentException if $code is not an integer
+     */
+       public function setCode($code)
+       {
+               if (!ctype_digit((string)$code)) {
+                       throw new InvalidArgumentException(
+                               'Invalid Argument: $code must be an integer.'
+                       );
+               }
+
+               $this->_code = $code;
+       }
+
+       //      }}}
+       //      {{{     update()
+
+    /**
+     * Sync the database w/ the category object
+     *
+     * @param PDO $pdo Database handler
+        *
+     * @return boolean true on success, false on error
+     * @access public
+     */
+       public function update(PDO $pdo)
+       {
+               try {
+                       $data = array(
+                               'name' => $this->_name,
+                               'parent_id' => $this->_parentId,
+                               'accommodations' => $this->_accommodations,
+                               'restaurant' => $this->_restaurant,
+                               'golf' => $this->_golf,
+                               'code' => $this->_code
+                       );
+
+                       $sql = Toolkit_Common::createSQLUpdate(
+                               'category',
+                               array_keys($data),
+                               array('category_id = :category_id')
+                       );
+                       $data['category_id'] = $this->_id;
+
+                       return Toolkit_Common::processQuery($pdo, 'category', $sql, $data);
+               } catch (PDOException $e) {
+                       return Toolkit_Common::handleError($e);
+               }
+       }
+
+       //      }}}
+}
+?>
diff --git a/Toolkit/Members/CategoryTree.php b/Toolkit/Members/CategoryTree.php
new file mode 100644 (file)
index 0000000..19c0c60
--- /dev/null
@@ -0,0 +1,71 @@
+<?php
+//     vim:set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker syntax=php:
+
+/**
+ * Short description for file
+ *
+ * Long description (if any) ...
+ *
+ * PHP version 5
+ *
+ * The license text...
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @version   CVS: $Id: CategoryTree.php,v 1.6 2010/05/25 14:01:16 jamie Exp $
+ * @link      http://demo.gaslightmedia.com
+ */
+
+/**
+ * Short description for class
+ *
+ * Long description (if any) ...
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @link      http://demo.gaslightmedia.com
+ */
+class Toolkit_Members_CategoryTree extends Toolkit_Tree
+{
+       //      {{{ addChildren()
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @return void
+     * @access protected
+     */
+    protected function addChildren()
+    {
+               try {
+                       $sql = "
+                               SELECT *
+                                 FROM category
+                                WHERE parent_id = :parent
+                                ORDER BY name";
+                       $stmt = $this->dbh->prepare($sql);
+                       $stmt->bindParam(':parent', $this->catid, PDO::PARAM_INT);
+                       $stmt->execute();
+
+                       while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                               $this->children[] = new Toolkit_Members_CategoryTree($row['category_id'],
+                                                                                                                                        $row['name'],
+                                                                                                                                        $this->dbh,
+                                                                                                                                        $row['parent_id']);
+                       }
+               } catch (PDOException $e) {
+                       Toolkit_Common::handleError($e);
+               }
+       }
+
+       //      }}}
+}
+?>
diff --git a/Toolkit/Members/City.php b/Toolkit/Members/City.php
new file mode 100644 (file)
index 0000000..01b4df0
--- /dev/null
@@ -0,0 +1,557 @@
+<?php
+//     vim:set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker syntax=php:
+
+/**
+ * City Object
+ *
+ * PHP version 5
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <steve@gaslightmedia.comm>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @version   CVS: $Id: City.php,v 1.5 2009/12/29 14:17:48 jamie Exp $
+ * @link      http://demo.gaslightmedia.com
+ */
+
+/**
+ * Handle City functionality
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2008 Gaslight Media
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @version   CVS: $Id: City.php,v 1.5 2009/12/29 14:17:48 jamie Exp $
+ * @link      http://devsys2.gaslightmedia.com/demo.gaslightmedia.com/admin/members.php?page=citiesList&module=listCities
+ */
+class Toolkit_Members_City
+{
+       //      {{{      properties
+
+    /**
+     * Description of $name
+     * @var string
+     * @access protected
+     */
+    protected $name;
+
+    /**
+     * Description of $region
+     * @var int
+     * @access protected
+     */
+    protected $region;
+
+    /**
+     * Description of $state
+     * @var int
+     * @access protected
+     */
+    protected $state;
+
+    /**
+     * Description of $image
+     * @var string
+     * @access protected
+     */
+    protected $image;
+
+    /**
+     * Description of $description
+     * @var string
+     * @access protected
+     */
+    protected $description;
+
+    /**
+     * Description of $lat
+     * @var double
+     * @access protected
+     */
+    protected $lat;
+
+    /**
+     * Description of $lon
+     * @var double
+     * @access protected
+     */
+    protected $lon;
+
+    /**
+     * Table name in DB
+     * @var    string
+     * @access public
+     */
+       public $tableName = 'city';
+
+    /**
+     * Image Server
+     * @var    object
+     * @access protected
+     */
+    protected $is;
+
+       //      }}}
+       //      {{{ __construct()
+
+       /**
+        * Class constructor
+        *
+     * @param PDO $pdo PHP Data Object
+        *
+        * @access public
+        */
+       public function __construct(PDO $pdo)
+       {
+        $this->dbh = $pdo;
+       }
+
+       //      }}}
+
+    //  {{{ createNewCity()
+
+    /**
+     * Creates a new city in the DB
+     *
+     * @return mixed
+     * @access public
+     */
+    public function createNewCity()
+    {
+        //  Minimum required fields to have an array
+        if (!is_numeric($this->state) || empty($this->name)) {
+            return false;
+        }
+
+        if ($this->_isDuplicate()) {
+            return PEAR::raiseError('This city already exists');
+        }
+        
+        $this->setLatLonCoordinates();
+
+        $values = array(
+            'city_name'   => $this->name,
+            'state_id'    => $this->state,
+            'region_id'   => $this->region,
+            'description' => $this->description,
+            'image'       => $this->image,
+            'lat'         => $this->lat,
+            'lon'         => $this->lon
+        );
+
+        $sql = Toolkit_Common::createSQLInsert(
+            $this->tableName,
+            array_keys($values)
+        );
+
+        try {
+            return Toolkit_Common::processQuery(
+                $this->dbh,
+                $this->tableName,
+                $sql,
+                $values
+            );
+        } catch (PDOException $e) {
+            return Toolkit_Common::handleError($e);
+        }
+    }
+
+    //  }}}
+
+    //  {{{ deleteCity()
+
+    /**
+     * delete a city from the DB
+     *
+     * @param integer $id city record to delete
+     *
+     * @return mixed
+     * @access public
+     */
+    public function deleteCity($id)
+    {
+        if (!is_numeric($id)) {
+            return false;
+        }
+
+        $sql = "
+            DELETE FROM {$this->tableName}
+             WHERE city_id = :id";
+
+        try {
+            $stmt = $this->dbh->prepare($sql);
+            $stmt->bindParam(':id', $id, PDO::PARAM_INT);
+            return $stmt->execute();
+        } catch (PDOException $e) {
+            return Toolkit_Common::handleError($e);
+        }
+    }
+
+    //  }}}
+
+    //  {{{ getName()
+
+    /**
+     * get the name of the city
+     *
+     * @return string
+     * @access public
+     */
+    public function getName()
+    {
+        return $this->name;
+    }
+
+    //  }}}
+    /**
+     * Description for setLatLonCoordinates()
+     * 
+     * @access protected
+     * @return void 
+     */
+    protected function setLatLonCoordinates()
+    {
+        $cityName = $this->getName();
+        try {
+            $sql = "
+            SELECT state_abb
+              FROM state
+             WHERE state_id = :state_id";
+            $stmt = $this->dbh->prepare($sql);
+            $stmt->bindParam(
+                ':state_id',
+                $this->getState(),
+                PDO::PARAM_INT
+            );
+            $stmt->execute();
+            $stateName = $stmt->fetchColumn();
+        } catch(PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+        $geocoder = new GeocodeYahoo();
+        $address = array(
+                       'city'  => $cityName,
+                       'state' => $stateName
+               );
+        try {
+            $response = $geocoder->geocodeAddress($address);
+            $responseArray = unserialize($response);
+            if ($responseArray['ResultSet']['Result'][0]['Latitude']) {
+                $lat = $responseArray['ResultSet']['Result'][0]['Latitude'];
+                $lon = $responseArray['ResultSet']['Result'][0]['Longitude'];
+            } else {
+                $lat = $responseArray['ResultSet']['Result']['Latitude'];
+                $lon = $responseArray['ResultSet']['Result']['Longitude'];
+            }
+        } catch (BadMethodCallException $e) {
+            Toolkit_Logger::logException('Invalid Arg', $e);
+        } catch (Exception $e) {
+            Toolkit_Logger::logException('Yahoo GeoCode', $e);
+        }
+        if ($lat) {
+            $this->setLat($lat);
+        }
+        if ($lon) {
+            $this->setLon($lon);
+        }
+    }
+    //  {{{ getState()
+
+    /**
+     * get the state the city belongs in
+     *
+     * @return string
+     * @access public
+     */
+    public function getState()
+    {
+        return $this->state;
+    }
+
+    //  }}}
+    //  {{{ getRegion()
+
+    /**
+     * get the Region the city belongs in
+     *
+     * @return string
+     * @access public
+     */
+    public function getRegion()
+    {
+        return $this->region;
+    }
+
+    //  }}}
+    //  {{{ getImage()
+
+    /**
+     * get the city image
+     *
+     * @param string $uri URI location to find the image at
+     *
+     * @return string file name of image, or html img tag of image
+     * @access public
+     */
+    public function getImage($uri = null)
+    {
+        if (is_null($uri)) {
+            return $this->image;
+        } else {
+            $path = $uri . $this->image;
+            $imageSize = $this->is->getImageSize($path);
+            $img = "<img {$imageSize[2]} src=\"{$path}\">";
+            return $img;
+        }
+    }
+
+    //  }}}
+    //  {{{ getDesc()
+
+    /**
+     * get the description of the city
+     *
+     * @return string city description
+     * @access public
+     */
+    public function getDescription()
+    {
+        return $this->description;
+    }
+
+    //  }}}
+
+    //  {{{ _isDuplicate()
+
+    /**
+     * get the description of the city
+     *
+     * @param integer $ignore city records to not include in count
+     *
+     * @return mixed city description, error, bool
+     * @access private
+     */
+    private function _isDuplicate($ignore = null)
+    {
+        $sql = "
+            SELECT count(*) AS total
+              FROM {$this->tableName}
+             WHERE city_name = :name
+               AND state_id = :state";
+
+        if (is_numeric($ignore)) {
+            $sql .= " AND city_id <> $ignore";
+        }
+
+        try {
+            $stmt = $this->dbh->prepare($sql);
+            $stmt->bindParam(':name', $this->name, PDO::PARAM_STR);
+            $stmt->bindParam(':state', $this->state, PDO::PARAM_INT);
+            $stmt->execute();
+            $stmt->bindColumn('total', $total);
+            $stmt->fetch();
+
+            return (bool) $total;
+        } catch (PDOException $e) {
+            return Toolkit_Common::handleError($e);
+        }
+    }
+
+    //  }}}
+
+    //  {{{ setName()
+
+    /**
+     * set the name of the city
+     *
+     * @param string $name City Name
+     *
+     * @return string
+     * @access public
+     */
+    public function setName($name)
+    {
+        $this->name = $name;
+    }
+
+    //  }}}
+    /**
+     * Set latitude of the city
+     * 
+     * @param int $lat City latitude
+     * 
+     * @return void
+     * @access public
+     */
+    public function setLat($lat)
+    {
+        $newLat = (float) $lat;
+               if ($newLat >= -90.0 && $newLat <= 90.0) {
+            $this->lat = $newLat;
+        }
+    }
+    
+    /**
+     * Set longitude of the city
+     * 
+     * @param int $lng City longitude
+     * 
+     * @return void
+     * @access public
+     */
+    public function setLon($lng)
+    {
+        $newLng = (float) $lng;
+               if ($newLng >= -180.0 && $newLng <= 180.0) {
+            $this->lon = $newLng;
+        }
+    }
+    //  {{{ setState()
+
+    /**
+     * set the state the city belongs in
+     *
+     * @param integer $state state id from db
+     *
+     * @return void
+     * @access public
+     * @throws PEAR_Error
+     */
+    public function setState($state)
+    {
+        if (!is_numeric($state)) {
+            return PEAR::raiseError('Invalid state id');
+        }
+
+        $this->state = $state;
+    }
+
+    //  }}}
+    //  {{{ setRegion()
+
+    /**
+     * set the Region the city belongs in
+     *
+     * @param integer $region region id from db
+     *
+     * @return void
+     * @access public
+     * @throws PEAR_Error
+     */
+    public function setRegion($region)
+    {
+        if (!is_numeric($region)) {
+            return PEAR::raiseError('Invalid region id');
+        }
+        $this->region = $region;
+    }
+
+    //  }}}
+    //  {{{ setImage()
+
+    /**
+     * set the city image
+     *
+     * @param string $image the city image
+     *
+     * @return void
+     * @access public
+     */
+    public function setImage($image)
+    {
+        $this->image = $image;
+    }
+
+    //  }}}
+    //  {{{ setImageServer()
+
+    /**
+     * set the image server to use
+     *
+     * @param Toolkit_Image_Server $is Image Server Instance
+     *
+     * @return void
+     * @access public
+     */
+    public function setImageServer(Toolkit_Image_Server $is)
+    {
+        $this->is = $is;
+    }
+
+    //  }}}
+    //  {{{ setDesc()
+
+    /**
+     * set the description of the city
+     *
+     * @param string $desc City Description
+     *
+     * @return void
+     * @access public
+     */
+    public function setDescription($desc)
+    {
+        $this->description = $desc;
+    }
+
+    //  }}}
+
+    //  {{{ updateCity()
+
+    /**
+     * update a city in the DB
+     *
+     * @param integer $id record id to update
+     *
+     * @return mixed
+     * @access public
+     * @throws PEAR_Error
+     */
+    public function updateCity($id)
+    {
+        //  Minimum required fields to have an array
+        if (!is_numeric($this->state) || empty($this->name)) {
+            return false;
+        }
+
+        if ($this->_isDuplicate($id)) {
+            return PEAR::raiseError('This city already exists');
+        }
+        
+        $this->setLatLonCoordinates();
+        
+        $values = array(
+            'city_name'   => $this->name,
+            'state_id'    => $this->state,
+            'region_id'   => $this->region,
+            'description' => $this->description,
+            'image'       => $this->image,
+            'lat'         => $this->lat,
+            'lon'         => $this->lon
+        );
+
+        $sql = Toolkit_Common::createSQLUpdate(
+            $this->tableName,
+            array_keys($values),
+            array('city_id = :city_id')
+        );
+
+        $values['city_id'] = $id;
+
+        try {
+            return Toolkit_Common::processQuery(
+                $this->dbh,
+                $this->tableName,
+                $sql,
+                $values
+            );
+        } catch (PDOException $e) {
+            return Toolkit_Common::handleError($e);
+        }
+    }
+
+    //  }}}
+}
+?>
diff --git a/Toolkit/Members/Contact.php b/Toolkit/Members/Contact.php
new file mode 100644 (file)
index 0000000..9616e32
--- /dev/null
@@ -0,0 +1,508 @@
+<?php
+/**
+ * Contact.php
+ *
+ * PHP Version 5
+ *
+ * @category Toolkit
+ * @package  Members
+ * @author   Steve Sutton <steve@gaslightmedia.com>
+ * @license  Gaslight Media
+ * @link     <>
+ */
+/**
+ * Toolkit_Members_Contact
+ *
+ * Description for Toolkit_Members_Contact
+ *
+ * @category Toolkit
+ * @package  Members
+ * @author   Steve Sutton <steve@gaslightmedia.com>
+ * @license  Gaslight Media
+ * @link     <>
+ */
+class Toolkit_Members_Contact
+{
+       //      {{{     properties
+
+    /**
+     * Contact title
+     * @var string
+     * @access private
+     */
+       private $_title;
+
+    /**
+     * Contact first name
+     * @var string
+     * @access private
+     */
+       private $_fname;
+
+    /**
+     * Contact last name
+     * @var string
+     * @access private
+     */
+       private $_lname;
+
+    /**
+     * Contact email
+     * @var string
+     * @access private
+     */
+       private $_email;
+
+    /**
+     * Contact phone number
+     * @var string
+     * @access private
+     */
+       private $_phone;
+
+    /**
+     * Whether contact can receive mail
+     * @var boolean
+     * @access private
+     */
+       private $_canReceiveMail;
+
+       //      }}}
+       //      {{{     __construct()
+
+    /**
+     * Class constructor
+     * 
+     * @param string  $fname          Contact first name
+     * @param string  $lname          Contact last name
+     * @param string  $title          Contact title
+     * @param string  $email          Contact email
+     * @param string  $phone          Contact phone number
+     * @param boolean $canReceiveMail Contact can Receive Mail
+     * 
+     * @access public
+     */
+       public function __construct(
+               $fname,
+               $lname,
+               $title = null,
+               $email = null,
+               $phone = null,
+               $canReceiveMail = false
+       ) {
+               $this->_fname = $fname;
+               $this->_lname = $lname;
+               $this->_title = $title;
+               $this->_email = $email;
+               $this->_phone = $phone;
+               $this->_canReceiveMail = (bool) $canReceiveMail;
+       }
+
+       //      }}}
+       //      {{{     __toString()
+
+    /**
+     * Description of __toString()
+     * 
+     * @return string
+     * @access public 
+     */
+       public function __toString()
+       {
+               if (!is_null($this->_email)) {
+                       $format = '<a href="mailto:%s">%s %s</a>';
+                       $name = sprintf(
+                               $format,
+                               $this->_email,
+                               $this->_fname,
+                               $this->_lname
+                       );
+               } else {
+                       $name = "{$this->_fname} {$this->_lname}";
+               }
+
+               $string = $name;
+               if (!is_null($this->_title)) {
+                       $string .= " {$this->_title}";
+
+                       if (!is_null($this->_phone)) {
+                               $string .= ", ";
+                       }
+               }
+
+               if (!is_null($this->_phone)) {
+                       $string .= " {$this->_phone}";
+               }
+
+               return $string;
+       }
+
+       //      }}}
+
+       //      {{{     delete()
+
+       /**
+        * Delete a contact
+        *
+        * @param PDO     $pdo Database handler
+        * @param integer $cid Contact id to delete
+        *
+        * @return Toolkit_Members_Contact instance of contact that was deleted
+        * @access public
+        * @static
+        * @throws InvalidArgumentException if $cid is not an integer
+        */
+       public static function delete(PDO $pdo, $cid)
+       {
+               if (!ctype_digit((string)$cid)) {
+                       throw new InvalidArgumentException(
+                               'InvalidArgument: $cid must be an integer.'
+                       );
+               }
+
+               $contact = self::fetch($pdo, $cid);
+
+               try {
+                       $sql = "
+                               DELETE FROM member_contacts
+                                WHERE id = :id";
+                       $stmt = $pdo->prepare($sql);
+                       $stmt->bindParam(':id', $cid, PDO::PARAM_INT);
+                       $stmt->execute();
+               } catch (PDOException $e) {
+                       return Toolkit_Common::handleError($e);
+               }
+
+               return $contact;
+       }
+
+       //      }}}
+       //      {{{     fetch()
+
+       /**
+        * Fetch a contact from the database
+        *
+        * @param PDO     $pdo Database handler
+        * @param integer $cid Contact id
+        *
+        * @return Toolkit_Members_Contact|false contact object or error
+        * @access public
+        * @static
+        * @throws InvalidArgumentException if $cid is not an integer
+        */
+       public static function fetch(PDO $pdo, $cid)
+       {
+               if (!ctype_digit((string)$cid)) {
+                       throw new InvalidArgumentException(
+                               'Invalid Argument: $cid must be an integer.'
+                       );
+               }
+
+               try {
+                       $sql = "
+                               SELECT *
+                                 FROM member_contacts
+                                WHERE id = :id";
+                       $stmt = $pdo->prepare($sql);
+                       $stmt->bindParam(':id', $cid, PDO::PARAM_INT);
+                       $stmt->execute();
+                       $row = $stmt->fetch(PDO::FETCH_ASSOC);
+                       return new self(
+                               $row['fname'],
+                               $row['lname'],
+                               $row['title'],
+                               $row['email'],
+                               $row['phone'],
+                               (bool) $row['send_mail']
+                       );
+               } catch (PDOException $e) {
+                       return Toolkit_Common::handleError($e);
+               }
+       }
+
+       //      }}}
+
+       //      {{{     getTitle()
+
+       /**
+        * Get the contact title
+        *
+        * @return string title
+        * @access public
+        */
+       public function getTitle()
+       {
+               return $this->_title;
+       }
+
+       //      }}}
+       //      {{{     getFname()
+
+       /**
+        * Get the contact first name
+        *
+        * @return string fname
+        * @access public
+        */
+       public function getFname()
+       {
+               return $this->_fname;
+       }
+
+       //      }}}
+       //      {{{     getLname()
+
+       /**
+        * Get the contact last name
+        *
+        * @return string lname
+        * @access public
+        */
+       public function getLname()
+       {
+               return $this->_lname;
+       }
+
+       //      }}}
+       //      {{{     getEmail()
+
+       /**
+        * Get the contact email address
+        *
+        * @return string email
+        * @access public
+        */
+       public function getEmail()
+       {
+               return $this->_email;
+       }
+
+       //      }}}
+       //      {{{     getPhone()
+
+       /**
+        * Get the contact phone number
+        *
+        * @return string phone
+        * @access public
+        */
+       public function getPhone()
+       {
+               return $this->_phone;
+       }
+
+       //      }}}
+       //      {{{     getCanReceiveMail()
+
+       /**
+        * Get if the contact can receive mail
+        *
+        * @return boolean canReceiveMail
+        * @access public
+        */
+       public function getCanReceiveMail()
+       {
+               return $this->_canReceiveMail;
+       }
+
+       //      }}}
+       //      {{{     setFname()
+
+       /**
+        * Set the contact first name
+        *
+        * @param string $fname first name
+        *
+        * @return void
+        * @access public
+        */
+       public function setFname($fname)
+       {
+               $this->_fname = $fname;
+       }
+
+       //      }}}
+       //      {{{     setLname()
+
+       /**
+        * Set the contact last name
+        *
+        * @param string $lname last name
+        *
+        * @return void
+        * @access public
+        */
+       public function setLname($lname)
+       {
+               $this->_lname = $lname;
+       }
+
+       //      }}}
+       //      {{{     setTitle()
+
+       /**
+        * Set the contact title
+        *
+        * @param string $title contact title
+        *
+        * @return void
+        * @access public
+        */
+       public function setTitle($title)
+       {
+               $this->_title = $title;
+       }
+
+       //      }}}
+       //      {{{     setEmail()
+
+       /**
+        * Set the contact email address
+        *
+        * @param string $email email address
+        *
+        * @return void
+        * @access public
+        */
+       public function setEmail($email)
+       {
+               $this->_email = $email;
+       }
+
+       //      }}}
+       //      {{{     setPhone()
+
+       /**
+        * Set the contact phone
+        *
+        * @param string $phone phone number
+        *
+        * @return void
+        * @access public
+        */
+       public function setPhone($phone)
+       {
+               $this->_phone = $phone;
+       }
+
+       //      }}}
+       //      {{{     setCanReceiveMail()
+
+       /**
+        * Set if the contact can receive mail or not
+        *
+        * @param boolean $mail if the contact can receive mail or not
+        *
+        * @return void
+        * @access public
+        * @throws InvalidArgumentException if $mail is not a boolean value
+        */
+       public function setCanReceiveMail($mail)
+       {
+               if (!is_bool($mail)) {
+                       throw new InvalidArgumentException(
+                               'Invalid Argument: $mail must be a boolean.'
+                       );
+               }
+
+               $this->_canReceiveMail = $mail;
+       }
+
+       //      }}}
+
+       //      {{{     save()
+
+       /**
+        * Save a contact to a member in the database
+        *
+        * @param PDO     $pdo    Database handler
+        * @param integer $member Member to assign contact to
+        *
+        * @return boolean Result of save query
+        * @access public
+        * @throws InvalidArgumentException if $member is not an integer
+        */
+       public function save(PDO $pdo, $member)
+       {
+               if (!ctype_digit((string)$member)) {
+                       throw new InvalidArgumentException(
+                               'Invalid Argument: $member must be an integer.'
+                       );
+               }
+
+               $data = array(
+                       'member_id' => $member,
+                       'fname'         => $this->_fname,
+                       'lname'         => $this->_lname,
+                       'title'         => $this->_title,
+                       'email'         => $this->_email,
+                       'phone'         => $this->_phone,
+                       'send_mail' => $this->_canReceiveMail
+               );
+               $sql = Toolkit_Common::createSQLInsert(
+                       'member_contacts',
+                       array_keys($data)
+               );
+
+               try {
+                       return Toolkit_Common::processQuery(
+                               $pdo,
+                               'member_contacts',
+                               $sql,
+                               $data
+                       );
+               } catch (PDOException $e) {
+                       return Toolkit_Common::handleError($e);
+               }
+       }
+
+       //      }}}
+       //      {{{     update()
+
+       /**
+        * Update a contact in the databse
+        *
+        * @param PDO     $pdo Database handler
+        * @param integer $cid Contact id to update in the database
+        *
+        * @return boolean Result of update query
+        * @access public
+        * @throws InvalidArgumentException if $cid is not an integer
+        */
+       public function update(PDO $pdo, $cid)
+       {
+               if (!ctype_digit((string)$cid)) {
+                       throw new InvalidArgumentException(
+                               'Invalid Argument: $cid must be an integer.'
+                       );
+               }
+
+               $data = array(
+                       'fname' => $this->_fname,
+                       'lname' => $this->_lname,
+                       'email' => $this->_email,
+                       'title' => $this->_title,
+                       'phone' => $this->_phone,
+                       'send_mail' => $this->_canReceiveMail
+               );
+
+               $sql = Toolkit_Common::createSQLUpdate(
+                       'member_contacts',
+                       array_keys($data),
+                       array('id = :id')
+               );
+               $data['id'] = $cid;
+
+               try {
+                       return Toolkit_Common::processQuery(
+                               $pdo,
+                               'member_contacts',
+                               $sql,
+                               $data
+                       );
+               } catch (PDOException $e) {
+                       return Toolkit_Common::handleError($e);
+               }
+       }
+}
+?>
diff --git a/Toolkit/Members/ContactsDataGrid.php b/Toolkit/Members/ContactsDataGrid.php
new file mode 100644 (file)
index 0000000..f035ee3
--- /dev/null
@@ -0,0 +1,174 @@
+<?php
+//     vim:set expandtab tabstop=4 shiftwidth=4 softtabsstop=4 foldmethod=marker syntax=php:
+
+/**
+ * File Doc Comment
+ *
+ * PHP version 5
+ *
+ * @category MembersDB
+ * @package  Toolkit_Members
+ * @author   Jamie Kahgee <steve@gaslightmedia.com>
+ * @license     http://www.gaslightmedia.com Gaslightmedia
+ * @release  CVS: $Id: ContactsDataGrid.php,v 1.2 2010/07/18 16:44:33 jamie Exp $
+ * @link        http://demo.gaslightmedia.com
+ */
+
+/**
+ * Datagrid of search results of member db
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @link      http://demo.gaslightmedia.com
+ */
+class Toolkit_Members_ContactsDataGrid extends Toolkit_FlexyDataGridBuilder
+{
+       //      {{{     properties
+
+    /**
+     * Description for protected
+     * @var    array
+     * @access protected
+     */
+       private $queryParams = array();
+
+    /**
+     * Description for $template
+     * @var string
+     * @access protected
+     */
+       protected $template = 'listContacts.html';
+
+       //      }}}
+
+       //      {{{ configureColumns()
+
+       /**
+        * Configures the columns that will be used in our datagrid renderer.
+        *
+        * @return void
+        * @access protected
+        */
+       protected function configureColumns()
+       {
+               $id = new Structures_DataGrid_Column(
+                       'Contact Id',
+                       'id',
+                       'id'
+               );
+               $this->addColumn($id);
+
+               $fname = new Structures_DataGrid_Column(
+                       'First Name',
+                       'fname',
+                       'fname'
+               );
+               $this->addColumn($fname);
+
+               $lname = new Structures_DataGrid_Column(
+                       'Last Name',
+                       'lname',
+                       'lname'
+               );
+               $this->addColumn($lname);
+
+               $title = new Structures_DataGrid_Column(
+                       'Member Name',
+            'title',
+            'title'
+               );
+               $this->addColumn($title);
+
+               $phone = new Structures_DataGrid_Column(
+                       'Phone',
+            'phone',
+            'phone'
+        );
+               $this->addColumn($phone);
+
+               $email = new Structures_DataGrid_Column(
+                       'Email',
+            'email',
+            'email'
+               );
+               $this->addColumn($email);
+
+               $canReceiveMail = new Structures_DataGrid_Column(
+                       'Can Mail',
+            'send_mail',
+            'send_mail'
+               );
+               $this->addColumn($canReceiveMail);
+    }
+
+    //    }}}
+
+       //      {{{ phone()
+
+       /**
+        * Returns the phone # for a member.
+        *
+        * Used when configuring the columns for the data grid. This
+        * function generates the phone for the member
+        *
+     * @param array $data tuple record from db
+     *
+        * @return mixed false when empty, member phone number if not empty
+        * @access public
+        */
+       public function email($data)
+       {
+        extract($data['record']);
+               return empty($phone) ? false : $phone;
+       }
+
+       //      }}}
+
+    //  {{{ setQuery()
+
+    /**
+     * Sets the query to use to fetch the datagrid results
+     *
+     * @return void
+     * @access public
+     */
+    public function setQuery()
+    {
+               $sql = "
+                       SELECT *
+                         FROM member_contacts";
+
+               if ($memberId = filter_input(INPUT_GET, 'id', FILTER_VALIDATE_INT)) {
+                       $this->queryParams = array("member_id = $memberId");
+               }
+
+               if (!empty($this->queryParams)) {
+                       $sql .= ' WHERE ' . implode(' AND ', $this->queryParams);
+               }
+
+        parent::setQuery($sql);
+    }
+
+    //  }}}
+       //      {{{     setControlObject()
+
+    /**
+     * These are the objects that will be inserted into the template.
+     *
+     * @return void
+     * @access protected
+     */
+       protected function setControlObject()
+       {
+               $this->ctrlObj['baseUrl'] = MEDIA_BASE_URL;
+               if ($memberId = filter_input(INPUT_GET, 'id', FILTER_VALIDATE_INT)) {
+                       $this->ctrlObj['member'] = $memberId;
+               }
+       }
+
+       //      }}}
+}
+?>
diff --git a/Toolkit/Members/Coupons/EditCouponForm.php b/Toolkit/Members/Coupons/EditCouponForm.php
new file mode 100644 (file)
index 0000000..70c3177
--- /dev/null
@@ -0,0 +1,786 @@
+<?php
+//     vim:set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker syntax=php:
+
+/**
+ * EditCouponForm.php
+ *
+ * PHP version 5
+ *
+ * @category Toolkit
+ * @package  Members_Coupons
+ * @author   Jamie Kahgee <steve@gaslightmedia.com>
+ * @license     http://www.gaslightmedia.com Gaslightmedia
+ * @version  CVS: $Id: EditCouponForm.php,v 1.8 2010/08/12 18:07:21 matrix Exp $
+ * @link        http://demo.gaslightmedia.com
+ */
+
+require_once BASE . 'Toolkit/Forms/Rules/Date.php';
+
+/**
+ * Banner Ad Application
+ *
+ * Description of Toolkit_Members_Coupons_EditCouponForm
+ *
+ * @category  Toolkit
+ * @package   Members_Coupons
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2008 Gaslight Media
+ * @license      http://www.gaslightmedia.com Gaslightmedia
+ * @link         http://demo.gaslightmedia.com
+ */
+class Toolkit_Members_Coupons_EditCouponForm extends Toolkit_FormBuilder
+{
+    //  {{{ properties
+
+       /**
+        * What do you want the success msg to be if the form validates successfully
+        *
+        * @var string
+        * @access protected
+        */
+       protected $successMsg
+               = '<div id="form-success-top">Your new coupon or coupon update has been
+            submitted and is pending approval by the Web site administrator.</div>';
+
+       /**
+        * The default rules to register for validating
+        *
+        * We have to register these rules, or any others we want, before
+        * we are able to use them in our forms.
+        *
+        * @var array
+        * @access protected
+        */
+       protected $registeredRules = array(
+               array(
+                       'checkURI',
+                       'callback',
+                       'uri',
+                       'Validate'
+               )
+       );
+
+    //  }}}
+
+       //      {{{     checkDates()
+
+       /**
+        * Check that start date does not come before end date
+        *
+        * @param array $dates Multi-Dimensional array of dates.
+        *
+        * @return boolean false if start is after end, else true
+        * @access public
+        */
+       public function checkDates(array $dates)
+       {
+               extract($dates[0], EXTR_PREFIX_ALL, 'start');
+               extract($dates[1], EXTR_PREFIX_ALL, 'end');
+
+
+               $sdate = new Date();
+               $sdate->setDate("$start_Y-$start_m-$start_d");
+               $edate = new Date();
+               $edate->setDate("$end_Y-$end_m-$end_d");
+
+               return (Date::compare($sdate, $edate) < 1);
+       }
+
+       //      }}}
+    //  {{{ configureConstants()
+
+       /**
+        * Configure form constants
+        *
+        * @return void
+        * @access public
+        */
+    public function configureConstants()
+    {
+        $c = array();
+
+        //     If we are adding a new banner
+        if (!ctype_digit($_GET['id'])) {
+               $c['current_image_rmv'] = 'Coupon not yet uploaded';
+               $c['pending'] = true;
+        }
+
+        //     If the form has been submitted and a new image was uploaded
+        $currImg = $this->getSubmitValue('image');
+        if ($this->isSubmitted() && !empty($currImg)) {
+               $img = '<img src="%s%s">';
+                       $path = THUMB;
+               $c['current_image_rmv'] = sprintf($img, $path, $currImg);
+        }
+
+        $this->setupConstants($c);
+    }
+
+    //  }}}
+    //  {{{ configureDefaults()
+
+       /**
+        * Configure the initial default values for the form
+        *
+        * @param PDO $dbh Database handler
+        *
+     * @return void
+        * @access protected
+        */
+    public function configureDefaults(PDO $dbh)
+    {
+               if (ctype_digit($_GET['id'])) {
+                       try {
+                               $sql = "
+                               SELECT *
+                                 FROM coupons
+                                WHERE id = :id";
+                               $stmt = $dbh->prepare($sql);
+                               $stmt->bindParam(":id", $_GET['id'], PDO::PARAM_INT);
+                               $stmt->execute();
+                               $d = $stmt->fetch(PDO::FETCH_ASSOC);
+                if ($d['image']) {
+                    $d['current_image_rmv'] = '<img src="'
+                        . THUMB
+                        . $d['image']
+                        . '"><br><label>'
+                        . '<input type="hidden" name="old_image_rmv" value="'
+                        . $d['image']
+                        . '">'
+                        . '<input type="checkbox" name="del_image_rmv" value="1">'
+                        . 'Delete Image?'
+                        . '</label>';
+                } else {
+                    $d['current_image_rmv'] = 'Nothing uploaded yet';
+                }
+                               //echo '<pre>'.print_r($d, true).'</pre>';
+                       } catch(PDOException $e) {
+                               Toolkit_Common::handleError($e);
+                       }
+                       //die('fix configure defaults');
+               } else {
+                       $date = new Date();
+                       $sdateMonth = $date->getMonth();
+                       $sdateDay   = $date->getDay();
+                       $sdateYear  = $date->getYear();
+
+                       $date->addMonths(6);
+
+                       $edateMonth = $date->getMonth();
+                       $edateDay   = $date->getDay();
+                       $edateYear  = $date->getYear();
+                       $d = array(
+                               'sdate' => array(
+                    'm' => $sdateMonth,
+                    'd' => $sdateDay,
+                    'Y' => $sdateYear
+                ),
+                               'edate' => array(
+                    'm' => $edateMonth,
+                    'd' => $edateDay,
+                    'Y' => $edateYear
+                ),
+                               'expiration' => array(
+                    'm' => $edateMonth,
+                    'd' => $edateDay,
+                    'Y' => $edateYear
+                ),
+                       );
+               }
+
+               $this->setupDefaults($d);
+       }
+
+       //      }}}
+    //  {{{ configureElements()
+
+       /**
+        * Configure how the form elements should look
+        *
+     * @param PDO              $dbh Database handler
+     * @param Config_Container $c   Configuration object
+     *
+     * @return void
+        * @access public
+        */
+       public function configureElements(PDO $dbh, Config_Container $c)
+       {
+        $e = array();
+
+               $categories = $this->_getCategories($dbh);
+
+        //  Get reference to [conf] section of config file
+        $conf =& $c->getItem('section', 'conf');
+
+               $minYear = $c->getItem('section', 'conf')
+                       ->getItem('directive', 'dateStartYear')
+                       ->getContent();
+
+               //      All Grouped Elements are created here.
+
+               //      All Elements are created here.  This includes group element definitions.
+               $e[] = array(
+            'type' => 'html',
+            'req' => false,
+            'name' => '<tr><td colspan="2"><b>Creating a Coupon:</b>The Start date is when you expect the coupon to appear on our website. The End date is when it will automatically be removed from display to visitors. The expiration date is printed with the coupon, so be sure to set your expiration date correctly.</td></tr>',
+        );
+               $e[] = array(
+            'type' => 'hidden',
+            'req' => false,
+            'name' => 'active',
+        );
+               $e[] = array(
+            'type' => 'hidden',
+            'req' => false,
+            'name' => 'pending',
+        );
+        $e[] = array(
+               'type' => 'hidden',
+               'req' => false,
+               'name' => 'member',
+        );
+        $e[] = array(
+               'type' => 'select',
+               'req' => true,
+               'name' => 'category',
+               'display' => 'Category',
+                       'opts' => array('' => '-- Select --') + $categories
+        );
+        $e[] = array(
+               'type' => 'text',
+               'req' => true,
+               'name' => 'title',
+               'display' => 'Title',
+                       'opts' => array('class' => 'text')
+        );
+        $e[] = array(
+               'type' => 'textarea',
+               'req' => false,
+               'name' => 'description',
+               'display' => 'Description <span id="charleft">600</span> characters left',
+            'opts' => array('id' => 'description')
+        );
+        $e[] = array(
+               'type' => 'text',
+               'req' => false,
+               'name' => 'url',
+               'display' => 'Website Link',
+        );
+               $e[] = array(
+                'type' => 'static',
+                'req' => false,
+                'name' => 'current_image_rmv',
+                'display' => 'Current Coupon Image'
+        );
+               $e[] = array(
+                'type' => 'hidden',
+                'req' => false,
+                'name' => 'image'
+        );
+               $e[] = array(
+            'type' => 'file',
+            'req' => false,
+            'name' => 'file_rmv',
+            'display' => 'New Coupon Image'
+        );
+               $e[] = array(
+            'type' => 'static',
+            'req' => false,
+            'name' => 'img_instructions_rmv',
+            'opts' => '.jpg or .gif images only'
+        );
+               $e[] = array(
+            'type' => 'date',
+            'req' => true,
+            'name' => 'sdate',
+            'display' => 'Start Date',
+            'opts' => array(
+               'format' => 'm / d / Y',
+               'minYear' => $minYear,
+               'maxYear' => date('Y') + 5,
+               'addEmptyOption' => true,
+               'emptyOptionText' => array(
+                       'm' => 'mm',
+                       'd' => 'dd',
+                       'Y' => 'yyyy'
+                               )
+                       ),
+            'att' => array('id' => 'sdate')
+        );
+               $e[] = array(
+            'type' => 'date',
+            'req' => true,
+            'name' => 'edate',
+            'display' => 'End Date',
+            'opts' => array(
+               'format' => 'm / d / Y',
+               'minYear' => $minYear,
+               'maxYear' => date('Y') + 5,
+               'addEmptyOption' => true,
+               'emptyOptionText' => array(
+                       'm' => 'mm',
+                       'd' => 'dd',
+                       'Y' => 'yyyy'
+                               )
+                       ),
+            'att' => array('id' => 'edate')
+        );
+               $e[] = array(
+            'type' => 'date',
+            'req' => true,
+            'name' => 'expiration',
+            'display' => 'Expire Date',
+            'opts' => array(
+               'format' => 'm / d / Y',
+               'minYear' => $minYear,
+               'maxYear' => date('Y') + 5,
+               'addEmptyOption' => true,
+               'emptyOptionText' => array(
+                       'm' => 'mm',
+                       'd' => 'dd',
+                       'Y' => 'yyyy'
+                               )
+                       ),
+            'att' => array('id' => 'expire')
+        );
+               $e[] = array(
+            'type' => 'submit',
+            'req' => false,
+            'name' => 'submit_rmv',
+            'display' => 'Submit Coupon',
+            'opts' => array('id' => 'submit')
+        );
+        if (ctype_digit($_GET['id'])) {
+            $e[] = array(
+                'type'    => 'submit',
+                'req'     => false,
+                'name'    => 'delete_rmv',
+                'display' => 'Delete Coupon',
+                'opts'    => array(
+                    'id'      => 'delete',
+                    'onClick' => 'return confirm(\'This will delete the Coupon\nAre You Sure?\');'
+                )
+            );
+        }
+
+               $this->setupElements($e);
+       }
+
+       //      }}}
+       //      {{{     configureFilters()
+
+       /**
+        * Configure how the form elements should act when being submitted
+        *
+     * @return void
+        * @access protected
+        */
+       public function configureFilters()
+       {
+        $f   = array();
+               $f[] = array(
+            'element' => '__ALL__',
+            'filter' => 'trim'
+        );
+        $f[] = array(
+            'element' => 'url',
+                       'filter' => array('Toolkit_Common', 'filterURI')
+        );
+
+               $this->setupFilters($f);
+       }
+
+       //      }}}
+       //      {{{     configureForm()
+
+       /**
+        * Configure a form so we can use it
+        *
+        * @param PDO              $dbh Database handler
+        * @param Config_Container $c   Application configuration settings
+        *
+        * @return void
+        * @access public
+        */
+       public function configureForm(
+               PDO $dbh,
+               Config_Container $c
+       ) {
+               $this->configureElements($dbh, $c);
+               $this->configureFilters();
+               $this->configureRules();
+               $this->configureDefaults($dbh);
+               $this->configureConstants();
+       }
+
+       //      }}}
+       //      {{{     configureRules()
+
+       /**
+        * Configure how the form elements should act
+        *
+     * @return void
+        * @access public
+        */
+       public function configureRules()
+       {
+        $r = array();
+
+               $mimeTypes = array(
+                       'image/jpe',
+                       'image/jpeg',
+                       'image/jpg',
+                       'image/jfif',
+                       'image/pjpeg',
+                       'image/pjp',
+                       'image/gif',
+                       'image/png',
+               );
+
+               $r[] = array(
+                       'element'    => 'url',
+                       'message'    => 'ERROR: Invalid URL format (http, https only)',
+                       'type'       => 'checkURI',
+                       'format'     => array(
+                               'allowed_schemes' => array('http', 'https'),
+                               'strict' => true
+                       ),
+                       'validation' => $this->validationType,
+                       'reset'      => false,
+                       'force'      => false
+               );
+               $r[] = array(
+            'element' => 'sdate',
+            'message' => 'ERROR: Invalid Date!',
+            'type' => 'Date',
+            'format' => array('format' => '%m-%d-%Y'),
+            'validation' => $this->validationType,
+            'reset' => true,
+            'force' => false
+        );
+               $r[] = array(
+            'element' => 'edate',
+            'message' => 'ERROR: Invalid Date!',
+            'type' => 'Date',
+            'format' => array('format' => '%m-%d-%Y', 'allowEmpty' => true),
+            'validation' => $this->validationType,
+            'reset' => true,
+            'force' => false
+        );
+               $r[] = array(
+            'element' => 'expire',
+            'message' => 'ERROR: Invalid Date!',
+            'type' => 'Date',
+            'format' => array('format' => '%m-%d-%Y', 'allowEmpty' => true),
+            'validation' => $this->validationType,
+            'reset' => true,
+            'force' => false
+        );
+               $r[] = array(
+            'element' => array('sdate', 'edate'),
+            'message' => 'ERROR: Invalid Start Date!',
+            'type' => 'callback',
+            'format' => array(&$this, 'checkDates'),
+            'validation' => $this->validationType,
+            'reset' => false,
+            'force' => false
+        );
+               $r[] = array(
+            'element' => array('edate', 'expire'),
+            'message' => 'ERROR: Expire date ends before end date!',
+            'type' => 'callback',
+            'format' => array(&$this, 'checkDates'),
+            'validation' => $this->validationType,
+            'reset' => false,
+            'force' => false
+        );
+               if (is_uploaded_file($_FILES['file_rmv']['tmp_name'])) {
+                       $r[] = array(
+                'element' => 'file_rmv',
+                'message' => 'ERROR: Incorrect File Type (.gif, .png, .jpg) only!',
+                'type' => 'mimetype',
+                'format' => $mimeTypes,
+                'validation' => $this->validationType,
+                'reset' => false,
+                'force' => false
+            );
+               }
+
+               $this->setupRules($r);
+       }
+
+       //      }}}
+
+    //  {{{ _getCategories()
+
+       /**
+        * Get the array of coupon categories used for the select list
+        *
+        * @param PDO $dbh Database handler
+        *
+        * @return array categories used to populate a select list element
+        * @access protected
+        */
+       private function _getCategories(PDO $dbh)
+       {
+               //  Get only the active categories from
+               //  the nav structure for our select list.
+               $sql = "
+                       SELECT *
+                         FROM coupon_category
+                        ORDER BY name";
+
+               $categories = array();
+               foreach ($dbh->query($sql, PDO::FETCH_ASSOC) as $row) {
+                       $categories[$row['name']] = $row['name'];
+               }
+               return $categories;
+       }
+
+    //  }}}
+
+       //      {{{     _processData()
+
+       /**
+        * Determine how the form should be handled (insert new data or update old)
+        *
+     * @param PDO                           $dbh                       Database handler
+     * @param array                         $values                    Submitted form values
+     * @param Toolkit_Coupons_CouponFactory $cFactory                  Coupon
+     * @param stdClass                      &$couponMailerTemplateData Coupon mailer template data
+     *
+     * @return boolean Result of insert or update function
+        * @access private
+        */
+    private function _processData(
+        PDO $dbh,
+        array $values,
+               Toolkit_Coupons_CouponFactory $cFactory,
+               stdClass &$couponMailerTemplateData
+    ) {
+               unset($values['MAX_FILE_SIZE']);
+
+        $deleteImage = ($_POST['del_image_rmv']) ? true: false;
+               foreach ($values as $k => $v) {
+            if (   $k != 'uploaded_file_rmv'
+                && preg_match('/^.+_rmv$/', $k)
+            ) {
+                               unset($values[$k]);
+                       }
+               }
+
+               $sdateStr = implode('/', $values['sdate']);
+               $sdateTS = strtotime($sdateStr);
+               $sdate = new Date();
+               $sdate->setDate($sdateTS, DATE_FORMAT_TIMESTAMP);
+               $values['sdate'] = $sdate;
+
+               $edateStr = implode('/', $values['edate']);
+               $edateTS = strtotime($edateStr);
+               $edate = new Date();
+               $edate->setDate($edateTS, DATE_FORMAT_TIMESTAMP);
+               $values['edate'] = $edate;
+
+               $exdateStr = implode('/', $values['expiration']);
+               $exdateTS = strtotime($exdateStr);
+               $exdate = new Date();
+               $exdate->setDate($exdateTS, DATE_FORMAT_TIMESTAMP);
+               $values['expiration'] = $exdate;
+        if ($deleteImage) {
+                   $values['image'] = null;
+        } else {
+                   $values['image'] = Toolkit_Coupons_ImageFactory::getImage($values);
+        }
+        $values['member'] = $_SESSION['_authsession']['data']['member_id'];
+        $values['active'] = 0;
+               if (ctype_digit((string)$_GET['id'])) {
+                       //      Editing a banner
+                       $coupon =& $cFactory->fetch($dbh, $_GET['id']);
+               } else {
+                       $coupon =& $cFactory->createCoupon($values);
+               }
+
+               if (filter_input(INPUT_GET, 'id', FILTER_VALIDATE_INT)) {
+                       $coupon->setPending(true);
+                       $res = $coupon->update($dbh, $values);
+               } else {
+                       $res = $coupon->save($dbh);
+               }
+               $couponMailerTemplateData->couponId = $coupon->getId();
+
+               return $res;
+       }
+
+    //  }}}
+
+    //  {{{ sendCouponToImageServer()
+
+    /**
+     * Send a coupon image to the image server
+     *
+     * Sets the image name in the submit values, so when saving
+     * the banner, we keep the image that was uploaded.
+     *
+     * Injects the thumbnail image of this uploaded coupon into
+     * the form, this way if validation fails, the thumbnail will
+     * be shown on the form so the user knows they don't have to
+     * re-upload the coupon.
+     *
+     * @param Toolkit_Image_Server $is   Image server
+     * @param string                      $file key of upload in $_FILES super array
+     *
+     * @return void
+     * @access protected
+     */
+       protected function sendCouponToImageServer(
+        Toolkit_Image_Server $is,
+        $file
+    ) {
+               $oldCoupon =& $this->getSubmitValue('image');
+               if (!empty($oldCoupon)) {
+                       $is->imageDelete($oldCoupon);
+               }
+
+               $imgTag = '<img src="%s%s">';
+               $name = $is->imageUpload($file);
+
+               $htmlImg = sprintf($imgTag, COUPON_THUMB, $name);
+
+               $currImg =& $this->getElement('current_image_rmv');
+               $currImg->setValue($htmlImg);
+
+               $fileName =& $this->getElement('image');
+               $fileName->setValue($name);
+               $this->_submitValues['image'] = $name;
+       }
+
+    //  }}}
+       //      {{{     setupRenderers()
+    //  @codeCoverageIgnoreStart
+
+    /**
+     * Custom rendering templates for special fields on the form
+     *
+     * @return void
+     * @access protected
+     */
+       protected function setupRenderers()
+       {
+               parent::setupRenderers();
+               $renderer =& $this->defaultRenderer();
+               $required
+                       = "<!-- BEGIN required -->\n
+                       <span class=\"req\"> * </span>\n
+                       <!-- END required -->";
+               $error
+                       = "<!-- BEGIN error -->\n
+                       <div class=\"req\"> {error} </div>\n
+                       <!-- END error -->";
+               $renderer->setElementTemplate(
+                       "<tr>\n
+                               <td colspan=\"2\">$required{label}$error{element}</td>\n
+                       </tr>",
+                       'description'
+               );
+               $renderer->setElementTemplate(
+                       "<tr align=\"center\">\n
+                               <td colspan=\"2\">$required{label}$error{element}</td>\n
+                       </tr>",
+                       'submit_rmv'
+               );
+               $renderer->setElementTemplate(
+                       "<tr align=\"center\">\n
+                               <td colspan=\"2\">$required{label}$error{element}</td>\n
+                       </tr>",
+                       'delete_rmv'
+               );
+
+       }
+
+    //  @codeCoverageIgnoreEnd
+       //      }}}
+
+    //  {{{ toHtml()
+
+       /**
+        * Call the rendering function to get the form in a string
+        *
+        * @param PDO                            $dbh                       Database handler
+        * @param Toolkit_Image_Server           $is                        Image Server
+     * @param Toolkit_Coupons_CouponFactory  $cFactory                  Coupon
+     * @param Toolkit_Members_Coupons_Mailer $mailer                    Mailer
+     * @param stdClass                       &$couponMailerTemplateData Coupon mailer template data
+        *
+        * @return string $output The Form to be rendered or success msg.
+        * @access protected
+        */
+       public function toHtml(
+               PDO $dbh,
+               Toolkit_Image_Server $is,
+               Toolkit_Coupons_CouponFactory $cFactory,
+               Toolkit_Members_Coupons_Mailer $mailer,
+               stdClass &$couponMailerTemplateData
+       ) {
+               //$GLOBALS['styleSheets'][] = MEDIA_BASE_URL . 'css/contactform.css';
+               $GLOBALS['bottomScripts'][] = MEDIA_APP_BASE_URL . 'libjs/textlimit.js';
+               $GLOBALS['bottomScripts'][] = MEDIA_BASE_URL . 'libjs/couponLimitText.js';
+
+        //  Handle Deleting coupon.
+        if (   $this->isSubmitted()
+            && ctype_digit($_GET['id'])
+        ) {
+            if ($this->getSubmitValue('delete_rmv')) {
+                $coupon = $cFactory->fetch($dbh, $_GET['id']);
+                if ($coupon instanceof Toolkit_Coupons_Coupon) {
+                    if ($coupon->delete($dbh, $is)) {
+                        return 'Coupon successfully deleted.';
+                    }
+                } else {
+                    //  the coupon has already been deleted or doesn't exist.
+                    return "The coupon has already been deleted or doesn't exists.";
+                }
+            }
+        }
+
+               $this->setupRenderers();
+               $uploadedNewCouponImg
+                       = (   $this->isSubmitted()
+                          && is_uploaded_file($_FILES['file_rmv']['tmp_name'])
+               );
+        if ($_POST['del_image_rmv'] && $_POST['old_image_rmv']) {
+            $is->imageDelete($_POST['old_image_rmv']);
+        }
+               if ($uploadedNewCouponImg) {
+                       $this->sendCouponToImageServer($is, 'file_rmv');
+               }
+               if ($this->validate()) {
+                       $this->cleanForm();
+
+                       $submitValues = $this->getSubmitValues();
+                       if ($this->_processData(
+                               $dbh,
+                               $submitValues,
+                               $cFactory,
+                               $couponMailerTemplateData
+                       )
+                       ) {
+                               if ('' != MEMBERS_COUPON_NOTIFICATION_EMAIL) {
+                                       $mailer->sendNotification(
+                                               array(MEMBERS_COUPON_NOTIFICATION_EMAIL),
+                                               $couponMailerTemplateData
+                                       );
+                               }
+                               $this->freeze();
+                               $output = $this->successMsg;
+                       }
+               } elseif ($this->isSubmitted()) {
+                       $output  = $this->errorMsg;
+                       $output .= parent::toHTML();
+               } else {
+                       $output = parent::toHTML();
+               }
+               return $output;
+       }
+
+    //  }}}
+}
diff --git a/Toolkit/Members/Coupons/Mailer.php b/Toolkit/Members/Coupons/Mailer.php
new file mode 100644 (file)
index 0000000..2621a33
--- /dev/null
@@ -0,0 +1,129 @@
+<?php
+
+/**
+ * Updated member coupon notification mailer
+ *
+ * Sends email to site admin when a member add / updates a coupon
+ *
+ * PHP version 5
+ *
+ * The license text...
+ *
+ * @category  Members
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <jamie@gaslightmedia.com>
+ * @copyright 2010 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com/ Gaslightmedia
+ * @version   CVS: $Id:$
+ * @link      <>
+ * @see       References to other sections (if any)...
+ */
+
+/**
+ * Updated member coupon notification mailer
+ *
+ * @category  Members
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <jamie@gaslightmedia.com>
+ * @copyright 2010 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com/ Gaslightmedia
+ * @version   Release: @package_version@
+ * @link      <>
+ * @see       References to other sections (if any)...
+ */
+class Toolkit_Members_Coupons_Mailer
+{
+       //      {{{     properties
+
+    /**
+     * Templating engine
+     * @var    HTML_Template_Flexy
+     * @access private
+     */
+       private $_tEngine;
+
+    /**
+     * Mail Mime object
+     * @var    Mail_mime
+     * @access private
+     */
+       private $_mailMime;
+
+    /**
+     * mailer
+     * @var    Mail
+     * @access private
+     */
+       private $_mailer;
+
+       //      }}}
+       //      {{{     __construct()
+
+    /**
+     * constructor
+     *
+     * @param HTML_Template_Flexy $tEngine   Templating engine to generate html email
+     * @param Mail_mime           &$mailMime class to create MIME messages
+     * @param Mail                &$mailer   internal PHP-mail() implementation of the PEAR Mail:: interface.
+        *
+     * @return void
+     * @access public
+     */
+       public function __construct(
+               HTML_Template_Flexy $tEngine,
+               Mail_mime &$mailMime,
+               Mail &$mailer
+       ) {
+               $this->_tEngine  = $tEngine;
+               $this->_mailMime = $mailMime;
+               $this->_mailer   = $mailer;
+       }
+
+       //      }}}
+    /**
+     * Sends notification of member coupon updates
+     *
+     * @param array    $recipients                array of recipients to send mail to
+     * @param stdClass &$couponMailerTemplateData template data object used to populate template variables
+        *
+     * @return boolean True or False depending on result of mailing
+     * @access public
+     * @throws RuntimeException if no recipients present
+     */
+       public function sendNotification(
+               array $recipients,
+               stdClass &$couponMailerTemplateData
+       ) {
+               if (empty($recipients)) {
+                       throw new RuntimeException('Array of recipients cannot be empty');
+               }
+               $page = new stdClass();
+
+               $page->email_from = DO_NOT_REPLY_EMAIL;
+               $page->subject = 'Member Coupon Notification from ' . SITENAME;
+               $page->member_name = $couponMailerTemplateData->memberName;
+               $page->coupon_id = $couponMailerTemplateData->couponId;
+               $page->edit = $couponMailerTemplateData->editingCoupon;
+               $page->baseUrl = MEDIA_BASE_URL;
+
+               $this->_mailMime->setHTMLBody($this->_tEngine->bufferedOutputObject($page));
+
+               $body = $this->_mailMime->get();
+               $hdrs = $this->_mailMime->headers(
+                       array(
+                               'From' => DO_NOT_REPLY_EMAIL,
+                               'Subject' => 'Member Coupon from ' . SITENAME,
+                               'Reply-To' => DO_NOT_REPLY_EMAIL
+                       )
+               );
+
+               $res = $this->_mailer->send($recipients, $hdrs, $body);
+               if (!PEAR::isError($res)) {
+                       return true;
+               } else {
+                       $logger =& Toolkit_Logger::getLogger();
+                       $logger->warning("Unable to mail coupon notification {$res->getMessage()}");
+                       return false;
+               }
+       }
+}
diff --git a/Toolkit/Members/Database/application.sql b/Toolkit/Members/Database/application.sql
new file mode 100644 (file)
index 0000000..5152ffb
--- /dev/null
@@ -0,0 +1,55 @@
+CREATE SCHEMA members;
+GRANT ALL ON SCHEMA members TO nobody;
+
+--
+-- Tables
+--
+\i ./tables/state.sql
+\i ./tables/region.sql
+\i ./tables/county.sql
+\i ./tables/city.sql
+\i ./tables/ccard_type.sql
+\i ./tables/member.sql
+\i ./tables/member_accommodations.sql
+\i ./tables/amenity.sql
+\i ./tables/member_amenity.sql
+\i ./tables/category.sql
+\i ./tables/category_amenities.sql
+\i ./tables/bus_cat_member.sql
+\i ./tables/member_category.sql
+\i ./tables/member_ccard_type.sql
+\i ./tables/member_contacts.sql
+\i ./tables/member_files.sql
+\i ./tables/member_golf.sql
+\i ./tables/member_packages.sql
+\i ./tables/member_photos.sql
+\i ./tables/member_restaurants.sql
+\i ./tables/member_updates.sql
+\i ./tables/member_session.sql
+\i ./tables/exploded_members_name.sql
+\i ./tables/member_newsletters.sql
+\i ./tables/member_last_updates.sql
+\i ./tables/exposure.sql
+\i ./tables/member_categories2toolbox_pages.sql
+\i ./tables/member_categories2toolbox_pages_draft.sql
+\i ./tables/banners2membercategories.sql
+\i ./tables/member_leads.sql
+\i ./tables/member_regions2toolbox_pages.sql
+\i ./tables/member_regions2toolbox_pages_draft.sql
+\i ./tables/streamsend.sql
+
+--
+-- Utilities
+--
+\i ./utilities/unnest.sql
+--
+-- Procedures
+--
+\i ./procedures/last_record_update_timestamp.sql
+\i ./procedures/explode_members_name_PRE_8.3.sql
+
+--
+-- Modules
+--
+\i /usr/share/postgresql/8.4/contrib/tablefunc.sql
+\i /usr/share/postgresql/8.4/contrib/fuzzystrmatch.sql
diff --git a/Toolkit/Members/Database/procedures/explode_members_name_POST_8.3.sql b/Toolkit/Members/Database/procedures/explode_members_name_POST_8.3.sql
new file mode 100644 (file)
index 0000000..a13e23d
--- /dev/null
@@ -0,0 +1,49 @@
+--DROP LANGUALGE plpgsql;
+CREATE LANGUAGE plpgsql;
+
+DROP TRIGGER IF EXISTS explode_members_name ON members.member;
+
+--
+-- Function and trigger to split the member names on INSERT and UPDATE
+-- so we can do reverse name search lookups ie("john doe" vs "doe john").
+--
+CREATE OR REPLACE FUNCTION members.explode_members_name() RETURNS TRIGGER AS $explode_members_name$
+       DECLARE
+               noPunct    text;
+               alNumSpace text;
+       BEGIN
+
+               noPunct = regexp_replace(NEW.member_name, '[[:punct:]]', '', 'g');
+               alNumSpace = regexp_replace(noPunct, '[^[:alnum:] ]', '', 'g');
+
+               IF (TG_OP = 'INSERT') THEN
+
+                       INSERT INTO members.exploded_members_name (part, mid)
+                       SELECT regexp_split_to_table(alNumSpace, E'\\s+'), NEW.member_id;
+
+               ELSEIF (TG_OP = 'UPDATE') THEN
+
+                       --
+                       -- Only update the member name if they are not the same.
+                       --
+                       IF NEW.member_name != OLD.member_name THEN
+
+                               --
+                               -- Remove old name parts first so we can load a fresh copy.
+                               --
+                               DELETE FROM members.exploded_members_name
+                                WHERE mid = NEW.member_id;
+
+                               INSERT INTO members.exploded_members_name (part, mid)
+                               SELECT regexp_split_to_table(alNumSpace, E'\\s+'), NEW.member_id;
+
+                       END IF;
+
+               END IF;
+               RETURN NULL;
+       END;
+$explode_members_name$ LANGUAGE plpgsql;
+
+CREATE TRIGGER explode_members_name
+AFTER INSERT OR UPDATE ON members.member
+       FOR EACH ROW EXECUTE PROCEDURE members.explode_members_name();
diff --git a/Toolkit/Members/Database/procedures/explode_members_name_PRE_8.3.sql b/Toolkit/Members/Database/procedures/explode_members_name_PRE_8.3.sql
new file mode 100644 (file)
index 0000000..f96641a
--- /dev/null
@@ -0,0 +1,56 @@
+--
+-- This function requires the unnest utility function.
+-- That function can be found in the Members/Database/utilities folder
+--
+
+--DROP LANGUALGE plpgsql;
+CREATE LANGUAGE plpgsql;
+
+DROP TRIGGER IF EXISTS explode_members_name ON members.member;
+
+--
+-- Function and trigger to split the member names on INSERT and UPDATE
+-- so we can do reverse name search lookups ie("john doe" vs "doe john").
+--
+CREATE OR REPLACE FUNCTION members.explode_members_name() RETURNS TRIGGER AS $explode_members_name$
+       DECLARE
+               noPunct       text;
+               noMultiSpace text;
+               alNumSpace    text;
+       BEGIN
+
+               noPunct      = regexp_replace(NEW.member_name, '[[:punct:]]', '', 'g');
+               noMultiSpace = regexp_replace(noPunct, '[[:space:]]+', ' ', 'g');
+               alNumSpace   = regexp_replace(noMultiSpace, '[^[:alnum:] ]', '', 'g');
+
+               IF (TG_OP = 'INSERT') THEN
+
+                       INSERT INTO members.exploded_members_name (part, mid)
+                       SELECT unnest(string_to_array(alNumSpace, ' ')), NEW.member_id;
+
+               ELSEIF (TG_OP = 'UPDATE') THEN
+
+                       --
+                       -- Only update the member name if they are not the same.
+                       --
+                       IF NEW.member_name != OLD.member_name THEN
+
+                               --
+                               -- Remove old name parts first so we can load a fresh copy.
+                               --
+                               DELETE FROM members.exploded_members_name
+                                WHERE mid = NEW.member_id;
+
+                               INSERT INTO members.exploded_members_name (part, mid)
+                               SELECT unnest(string_to_array(alNumSpace, ' ')), NEW.member_id;
+
+                       END IF;
+
+               END IF;
+               RETURN NULL;
+       END;
+$explode_members_name$ LANGUAGE plpgsql;
+
+CREATE TRIGGER explode_members_name
+AFTER INSERT OR UPDATE ON members.member
+       FOR EACH ROW EXECUTE PROCEDURE members.explode_members_name();
diff --git a/Toolkit/Members/Database/procedures/last_record_update_timestamp.sql b/Toolkit/Members/Database/procedures/last_record_update_timestamp.sql
new file mode 100644 (file)
index 0000000..3c785b5
--- /dev/null
@@ -0,0 +1,77 @@
+--DROP LANGUAGE plpgsql;
+CREATE LANGUAGE plpgsql;
+
+DROP TRIGGER IF EXISTS last_record_update_timestamp ON members.member;
+DROP TRIGGER IF EXISTS last_record_update_timestamp ON members.member_photos;
+DROP TRIGGER IF EXISTS last_record_update_timestamp ON members.member_files;
+DROP TRIGGER IF EXISTS last_record_update_timestamp ON members.member_contacts;
+
+--
+-- Function and trigger adding a timestamp to the member record
+-- everytime it is updated.
+--
+CREATE OR REPLACE FUNCTION members.last_record_update_timestamp() RETURNS TRIGGER AS $last_record_update_timestamp$
+       DECLARE
+               mid INTEGER; -- Member ID
+               rid INTEGER; -- Record ID
+       BEGIN
+               -- Get the right member_id from the correct record variable OLD/NEW
+
+               IF (TG_OP = 'DELETE') THEN
+                       -- The variable NEW does not exist on DELETE operations
+                       -- need a special control structure to obtain the member_id
+                       mid := OLD.member_id;
+                       IF (TG_TABLE_NAME <> 'member') THEN
+                               rid := OLD.id;
+                       ELSE
+                               rid := NULL;
+                       END IF;
+               ELSE
+                       -- The variable OLD does not exist on INSERT operations
+                       -- need a special control structure to obtain the member_id
+                       mid := NEW.member_id;
+                       IF (TG_TABLE_NAME <> 'member') THEN
+                               rid := NEW.id;
+                       ELSE
+                               rid := NULL;
+                       END IF;
+               END IF;
+
+               INSERT INTO members.member_last_updates (
+                                       member_id,
+                                       timestamp,
+                                       "table",
+                                       operation,
+                                       record_id)
+             VALUES (
+                                        mid,
+                                        CURRENT_TIMESTAMP,
+                                        TG_TABLE_NAME,
+                                        TG_OP,
+                                        rid
+                                       );
+
+               -- Return the correct record variable
+               IF (TG_OP = 'DELETE') THEN
+                       RETURN OLD;
+               ELSE
+                       RETURN NEW;
+               END IF;
+       END;
+$last_record_update_timestamp$ LANGUAGE plpgsql;
+
+CREATE TRIGGER last_record_update_timestamp 
+AFTER INSERT OR UPDATE ON members.member
+       FOR EACH ROW EXECUTE PROCEDURE members.last_record_update_timestamp();
+
+CREATE TRIGGER last_record_update_timestamp
+AFTER INSERT OR UPDATE ON members.member_photos
+       FOR EACH ROW EXECUTE PROCEDURE members.last_record_update_timestamp();
+
+CREATE TRIGGER last_record_update_timestamp
+AFTER INSERT OR UPDATE ON members.member_files
+       FOR EACH ROW EXECUTE PROCEDURE members.last_record_update_timestamp();
+
+CREATE TRIGGER last_record_update_timestamp
+AFTER INSERT OR UPDATE ON members.member_contacts
+       FOR EACH ROW EXECUTE PROCEDURE members.last_record_update_timestamp();
diff --git a/Toolkit/Members/Database/removeApplication.sql b/Toolkit/Members/Database/removeApplication.sql
new file mode 100644 (file)
index 0000000..cd8b76d
--- /dev/null
@@ -0,0 +1,11 @@
+--
+--     This will drop everything in the members schema.
+--     Nothing better be in here except members related objects
+--     or it will be dropped
+--
+--     The force is strong w/ this one, use it wisely.
+--
+DROP SCHEMA IF EXISTS members CASCADE;
+
+DROP TABLE IF EXISTS toolbox.member_categories2toolbox_pages;
+DROP TABLE IF EXISTS toolbox.member_categories2toolbox_pages_draft;
diff --git a/Toolkit/Members/Database/tables/amenity.sql b/Toolkit/Members/Database/tables/amenity.sql
new file mode 100644 (file)
index 0000000..486833f
--- /dev/null
@@ -0,0 +1,12 @@
+DROP TABLE IF EXISTS members.amenity CASCADE;
+
+CREATE TABLE members.amenity
+(amenity_id SERIAL,
+ amenity_name TEXT,
+ amenity_icon TEXT,
+ display_form BOOLEAN,
+ PRIMARY KEY (amenity_id)
+);
+
+GRANT ALL ON members.amenity_amenity_id_seq TO nobody;
+GRANT ALL ON members.amenity TO nobody;
diff --git a/Toolkit/Members/Database/tables/banners2membercategories.sql b/Toolkit/Members/Database/tables/banners2membercategories.sql
new file mode 100644 (file)
index 0000000..09f6d2c
--- /dev/null
@@ -0,0 +1,13 @@
+DROP TABLE IF EXISTS members.banners2membercategories CASCADE;
+
+CREATE TABLE members.banners2membercategories
+(bid INTEGER NOT NULL
+       REFERENCES banners.banners (id)
+       ON UPDATE CASCADE
+       ON DELETE CASCADE,
+ catid INTEGER NOT NULL
+       REFERENCES members.category (category_id)
+       ON UPDATE CASCADE
+       ON DELETE CASCADE);
+
+GRANT ALL ON members.banners2membercategories TO nobody;
diff --git a/Toolkit/Members/Database/tables/category.sql b/Toolkit/Members/Database/tables/category.sql
new file mode 100644 (file)
index 0000000..b6d5eeb
--- /dev/null
@@ -0,0 +1,26 @@
+DROP TABLE IF EXISTS members.category CASCADE;
+--
+-- This table requires the connectby module found in the
+-- /usr/share/postgresql/*/contrib/tablefunc.sql file
+--
+
+CREATE TABLE members.category
+(category_id SERIAL,
+ name TEXT NOT NULL,
+ parent_id INTEGER DEFAULT 0,
+ pos INTEGER DEFAULT 1,
+ accommodations BOOLEAN DEFAULT FALSE,
+ restaurant BOOLEAN DEFAULT FALSE,
+ golf BOOLEAN DEFAULT FALSE,
+ code INTEGER,
+ PRIMARY KEY (category_id));
+
+DELETE FROM members.category;
+ALTER SEQUENCE members.category_category_id_seq RESTART WITH 1;
+INSERT INTO members.category (name, parent_id, pos, accommodations, restaurant, golf) VALUES ('Parent', 0, 1, true, true, true);
+INSERT INTO members.category (name, parent_id, pos, accommodations, restaurant, golf) VALUES ('Child', 1, 1, false, false, false);
+INSERT INTO members.category (name, parent_id, pos, accommodations, restaurant, golf) VALUES ('Sibling', 1, 2, false, true, false);
+INSERT INTO members.category (name, parent_id, pos, accommodations, restaurant, golf) VALUES ('Grand Child', 2, 1, false, false, true);
+
+GRANT ALL ON members.category_category_id_seq TO nobody;
+GRANT ALL ON members.category TO nobody;
diff --git a/Toolkit/Members/Database/tables/category_amenities.sql b/Toolkit/Members/Database/tables/category_amenities.sql
new file mode 100644 (file)
index 0000000..b7d2a70
--- /dev/null
@@ -0,0 +1,14 @@
+DROP TABLE IF EXISTS members.category_amenities CASCADE;
+
+CREATE TABLE members.category_amenities
+(category_id INTEGER NOT NULL
+       REFERENCES members.category(category_id)
+       ON UPDATE CASCADE
+       ON DELETE CASCADE,
+ amenity_id INTEGER NOT NULL
+       REFERENCES members.amenity(amenity_id)
+       ON UPDATE CASCADE
+       ON DELETE CASCADE,
+ PRIMARY KEY (category_id, amenity_id));
+
+GRANT ALL ON members.category_amenities TO nobody;
diff --git a/Toolkit/Members/Database/tables/ccard_type.sql b/Toolkit/Members/Database/tables/ccard_type.sql
new file mode 100644 (file)
index 0000000..905c37b
--- /dev/null
@@ -0,0 +1,18 @@
+DROP TABLE IF EXISTS members.ccard_type CASCADE;
+
+CREATE TABLE members.ccard_type
+(ccard_type_id SERIAL,
+ ccard_type_name TEXT,
+ ccard_type_icon TEXT,
+ PRIMARY KEY (ccard_type_id));
+
+DELETE FROM members.ccard_type;
+ALTER SEQUENCE members.ccard_type_ccard_type_id_seq RESTART WITH 1;
+INSERT INTO members.ccard_type(ccard_type_name, ccard_type_icon) VALUES ('Visa', 'cardvisa.gif');
+INSERT INTO members.ccard_type(ccard_type_name, ccard_type_icon) VALUES ('Master Card', 'cardmaster.gif');
+INSERT INTO members.ccard_type(ccard_type_name, ccard_type_icon) VALUES ('Discover', 'carddiscover.gif');
+INSERT INTO members.ccard_type(ccard_type_name, ccard_type_icon) VALUES ('American Express', 'cardamex.gif');
+INSERT INTO members.ccard_type(ccard_type_name, ccard_type_icon) VALUES ('Diners', 'carddiner.gif');
+
+GRANT ALL ON members.ccard_type_ccard_type_id_seq TO nobody;
+GRANT ALL ON members.ccard_type TO nobody;
diff --git a/Toolkit/Members/Database/tables/city.sql b/Toolkit/Members/Database/tables/city.sql
new file mode 100644 (file)
index 0000000..3192c42
--- /dev/null
@@ -0,0 +1,30 @@
+DROP TABLE IF EXISTS members.city CASCADE;
+
+CREATE TABLE members.city
+(city_id SERIAL,
+ state_id INTEGER NOT NULL
+       REFERENCES members.state(state_id)
+       ON UPDATE CASCADE
+       ON DELETE CASCADE,
+ city_name TEXT,
+ county_id INTEGER
+       REFERENCES members.county(county_id)
+       ON UPDATE CASCADE
+       ON DELETE CASCADE,
+ region_id INTEGER
+       REFERENCES members.region(region_id)
+       ON UPDATE CASCADE
+       ON DELETE CASCADE,
+ description TEXT,
+ image TEXT,
+ lat DOUBLE PRECISION,
+ lon DOUBLE PRECISION,
+ UNIQUE (state_id, city_name),
+ PRIMARY KEY (city_id));
+
+DELETE FROM members.city;
+ALTER SEQUENCE members.city_city_id_seq RESTART WITH 1;
+INSERT INTO members.city (state_id, city_name, county_id, region_id) VALUES (1, 'Default Temp City', 1, 1);
+
+GRANT ALL ON members.city_city_id_seq TO nobody;
+GRANT ALL ON members.city TO nobody;
diff --git a/Toolkit/Members/Database/tables/county.sql b/Toolkit/Members/Database/tables/county.sql
new file mode 100644 (file)
index 0000000..8d87f53
--- /dev/null
@@ -0,0 +1,22 @@
+DROP TABLE IF EXISTS members.county CASCADE;
+
+CREATE TABLE members.county
+(county_id SERIAL,
+ state_id INTEGER NOT NULL
+       REFERENCES members.state(state_id)
+       ON UPDATE CASCADE
+       ON DELETE CASCADE,
+ county_name TEXT,
+ region_id INTEGER
+       REFERENCES members.region(region_id)
+       ON UPDATE CASCADE
+       ON DELETE CASCADE,
+ PRIMARY KEY (county_id)
+);
+
+DELETE FROM members.county;
+ALTER SEQUENCE members.county_county_id_seq RESTART WITH 1;
+INSERT INTO members.county(state_id, county_name, region_id) VALUES (1, 'Default Temp County', 1);
+
+GRANT ALL ON members.county_county_id_seq TO nobody;
+GRANT ALL ON members.county TO nobody;
diff --git a/Toolkit/Members/Database/tables/exploded_members_name.sql b/Toolkit/Members/Database/tables/exploded_members_name.sql
new file mode 100644 (file)
index 0000000..e3c7177
--- /dev/null
@@ -0,0 +1,20 @@
+DROP TABLE IF EXISTS members.exploded_members_name CASCADE;
+
+--
+-- This table requires the procedures/explode_members_name.sql function
+-- and trigger as well as the metaphone and soundex module found in
+-- /usr/share/postgres/*/contrib/fuzzystrmatch.sql
+--
+
+DROP TABLE IF EXISTS members.exploded_members_name;
+
+CREATE TABLE members.exploded_members_name
+(part TEXT NOT NULL,
+ mid INTEGER NOT NULL
+       REFERENCES members.member(member_id)
+       ON DELETE CASCADE
+       ON UPDATE CASCADE);
+
+CREATE INDEX member_name_part_idx ON members.exploded_members_name (part);
+
+GRANT ALL ON members.exploded_members_name TO nobody;
diff --git a/Toolkit/Members/Database/tables/exposure.sql b/Toolkit/Members/Database/tables/exposure.sql
new file mode 100644 (file)
index 0000000..3b16b55
--- /dev/null
@@ -0,0 +1,18 @@
+DROP TABLE IF EXISTS members.exposure CASCADE;
+
+CREATE TABLE members.exposure
+(exposure_id SERIAL,
+ list INTEGER DEFAULT 0,
+ click INTEGER DEFAULT 0,
+ detail INTEGER DEFAULT 0,
+ edate DATE DEFAULT CURRENT_DATE,
+ member_id INTEGER,
+ PRIMARY KEY (exposure_id));
+
+CREATE INDEX exposure_edate_idx ON members.exposure (edate);
+CREATE INDEX exposure_edate_month_idx ON members.exposure (date_part('month', edate));
+CREATE INDEX exposure_edate_year_idx ON members.exposure (date_part('year', edate));
+CREATE INDEX exposure_member_id_idx ON members.exposure (member_id);
+
+GRANT ALL ON members.exposure_exposure_id_seq TO nobody;
+GRANT ALL ON members.exposure TO nobody;
diff --git a/Toolkit/Members/Database/tables/member.sql b/Toolkit/Members/Database/tables/member.sql
new file mode 100644 (file)
index 0000000..ca51f6f
--- /dev/null
@@ -0,0 +1,77 @@
+DROP TABLE IF EXISTS members.member CASCADE;
+
+--
+-- This table requires the procedures/last_record_update_timestamp.sql
+-- function and trigger
+--
+
+CREATE TABLE members.member
+(member_id SERIAL,
+ member_name TEXT UNIQUE,
+ member_login TEXT UNIQUE,
+ member_passwd TEXT,
+ street TEXT,
+ lat DOUBLE PRECISION,
+ lon DOUBLE PRECISION,
+ country TEXT,
+ phone TEXT,
+ fax TEXT,
+ process_email TEXT,
+ url TEXT,
+ city TEXT,
+ city_id INTEGER
+    REFERENCES members.city(city_id)
+    ON UPDATE CASCADE
+    ON DELETE SET NULL,
+ state TEXT,
+ state_id INTEGER
+    REFERENCES members.state(state_id)
+    ON UPDATE CASCADE
+    ON DELETE SET NULL,
+ description TEXT,
+ create_date DATE DEFAULT CURRENT_DATE,
+ zip TEXT,
+ toll_free TEXT,
+ member_contact_email TEXT,
+ spotlight BOOLEAN,
+ logo TEXT,
+ mailing_address TEXT,
+ primary_contact TEXT,
+ primary_contact_fname TEXT,
+ primary_contact_lname TEXT,
+ active BOOLEAN DEFAULT FALSE,
+ mailing_city TEXT,
+ mailing_city_id INTEGER
+    REFERENCES members.city(city_id)
+    ON UPDATE CASCADE
+    ON DELETE SET NULL,
+ mailing_state_id INTEGER
+    REFERENCES members.state(state_id)
+    ON UPDATE CASCADE
+    ON DELETE SET NULL,
+ mailing_zip TEXT,
+ join_date DATE,
+ region INTEGER
+    REFERENCES members.region(region_id)
+    ON UPDATE CASCADE
+    ON DELETE SET NULL,
+ new_member BOOLEAN DEFAULT FALSE,
+ non_member BOOLEAN DEFAULT FALSE,
+ facebook TEXT,
+ twitter TEXT,
+ myspace TEXT,
+ linkedin TEXT,
+ blog TEXT,
+ youtube TEXT,
+ pinterest TEXT,
+ instagram TEXT,
+ google_plus TEXT,
+ last_update TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
+ PRIMARY KEY (member_id)
+);
+
+CREATE INDEX member_state_id ON members.member (state_id);
+CREATE INDEX member_city_id ON members.member (city_id);
+
+GRANT ALL ON members.member_member_id_seq TO nobody;
+GRANT ALL ON members.member TO nobody;
diff --git a/Toolkit/Members/Database/tables/member_accommodations.sql b/Toolkit/Members/Database/tables/member_accommodations.sql
new file mode 100644 (file)
index 0000000..b651ff9
--- /dev/null
@@ -0,0 +1,17 @@
+DROP TABLE IF EXISTS members.member_accommodations CASCADE;
+
+CREATE TABLE members.member_accommodations
+(id SERIAL,
+ reservation_url TEXT,
+ reservation_id        TEXT,
+ num_rooms INTEGER,
+ year_round BOOLEAN    DEFAULT FALSE,
+ member_id INTEGER NOT NULL
+       REFERENCES members.member(member_id)
+       ON UPDATE CASCADE
+       ON DELETE CASCADE,
+ PRIMARY KEY (id)
+);
+
+GRANT ALL ON members.member_accommodations_id_seq TO nobody;
+GRANT ALL ON members.member_accommodations TO nobody;
diff --git a/Toolkit/Members/Database/tables/member_amenity.sql b/Toolkit/Members/Database/tables/member_amenity.sql
new file mode 100644 (file)
index 0000000..5b8031d
--- /dev/null
@@ -0,0 +1,16 @@
+DROP TABLE IF EXISTS members.member_amenity CASCADE;
+
+CREATE TABLE members.member_amenity
+(member_amenity_id SERIAL,
+ member_id INTEGER NOT NULL
+       REFERENCES members.member(member_id)
+       ON UPDATE CASCADE
+       ON DELETE CASCADE,
+ amenity_id INTEGER NOT NULL
+       REFERENCES members.amenity(amenity_id)
+       ON UPDATE CASCADE
+       ON DELETE CASCADE,
+ PRIMARY KEY (member_amenity_id));
+
+GRANT ALL ON members.member_amenity_member_amenity_id_seq TO nobody;
+GRANT ALL ON members.member_amenity TO nobody;
diff --git a/Toolkit/Members/Database/tables/member_categories2toolbox_pages.sql b/Toolkit/Members/Database/tables/member_categories2toolbox_pages.sql
new file mode 100644 (file)
index 0000000..b2b274a
--- /dev/null
@@ -0,0 +1,13 @@
+DROP TABLE IF EXISTS toolbox.member_categories2toolbox_pages CASCADE;
+
+CREATE TABLE toolbox.member_categories2toolbox_pages
+(page INTEGER NOT NULL
+       REFERENCES toolbox.pages (id)
+       ON UPDATE CASCADE
+       ON DELETE CASCADE,
+ category INTEGER NOT NULL
+       REFERENCES members.category (category_id)
+       ON UPDATE CASCADE
+       ON DELETE CASCADE);
+
+GRANT ALL ON toolbox.member_categories2toolbox_pages TO nobody;
diff --git a/Toolkit/Members/Database/tables/member_categories2toolbox_pages_draft.sql b/Toolkit/Members/Database/tables/member_categories2toolbox_pages_draft.sql
new file mode 100644 (file)
index 0000000..8b8cd98
--- /dev/null
@@ -0,0 +1,13 @@
+DROP TABLE IF EXISTS toolbox.member_categories2toolbox_pages_draft CASCADE;
+
+CREATE TABLE toolbox.member_categories2toolbox_pages_draft
+(page INTEGER NOT NULL
+       REFERENCES toolbox.pages_draft (id)
+       ON UPDATE CASCADE
+       ON DELETE CASCADE,
+ category INTEGER NOT NULL
+       REFERENCES members.category (category_id)
+       ON UPDATE CASCADE
+       ON DELETE CASCADE);
+
+GRANT ALL ON toolbox.member_categories2toolbox_pages_draft TO nobody;
diff --git a/Toolkit/Members/Database/tables/member_category.sql b/Toolkit/Members/Database/tables/member_category.sql
new file mode 100644 (file)
index 0000000..d5f99d5
--- /dev/null
@@ -0,0 +1,16 @@
+DROP TABLE IF EXISTS members.member_category CASCADE;
+
+CREATE TABLE members.member_category
+(member_category_id    SERIAL,
+ member_id INTEGER NOT NULL
+       REFERENCES members.member(member_id)
+       ON UPDATE CASCADE
+       ON DELETE CASCADE,
+ category_id INTEGER NOT NULL
+       REFERENCES members.category(category_id)
+       ON UPDATE CASCADE
+       ON DELETE CASCADE,
+ PRIMARY KEY (member_category_id));
+
+GRANT ALL ON members.member_category_member_category_id_seq TO nobody;
+GRANT ALL ON members.member_category TO nobody;
diff --git a/Toolkit/Members/Database/tables/member_ccard_type.sql b/Toolkit/Members/Database/tables/member_ccard_type.sql
new file mode 100644 (file)
index 0000000..1758637
--- /dev/null
@@ -0,0 +1,16 @@
+DROP TABLE IF EXISTS members.member_ccard_type CASCADE;
+
+CREATE TABLE members.member_ccard_type
+(member_ccard_type_id SERIAL,
+ member_id INTEGER NOT NULL
+       REFERENCES members.member(member_id)
+       ON UPDATE CASCADE
+       ON DELETE CASCADE,
+ ccard_type_id INTEGER NOT NULL
+       REFERENCES members.ccard_type(ccard_type_id)
+       ON UPDATE CASCADE
+       ON DELETE CASCADE,
+ PRIMARY KEY (member_ccard_type_id));
+
+GRANT ALL ON members.member_ccard_type_member_ccard_type_id_seq TO nobody;
+GRANT ALL ON members.member_ccard_type TO nobody;
diff --git a/Toolkit/Members/Database/tables/member_contacts.sql b/Toolkit/Members/Database/tables/member_contacts.sql
new file mode 100644 (file)
index 0000000..150be14
--- /dev/null
@@ -0,0 +1,18 @@
+DROP TABLE IF EXISTS members.member_contacts CASCADE;
+
+CREATE TABLE members.member_contacts
+(id SERIAL,
+ title TEXT,
+ fname TEXT NOT NULL,
+ lname TEXT NOT NULL,
+ email TEXT,
+ phone TEXT,
+ send_mail BOOLEAN DEFAULT FALSE,
+ member_id INTEGER NOT NULL
+       REFERENCES members.member(member_id)
+       ON UPDATE CASCADE
+       ON DELETE CASCADE,
+ PRIMARY KEY (id));
+
+GRANT ALL ON members.member_contacts_id_seq TO nobody;
+GRANT ALL ON members.member_contacts TO nobody;
diff --git a/Toolkit/Members/Database/tables/member_files.sql b/Toolkit/Members/Database/tables/member_files.sql
new file mode 100644 (file)
index 0000000..5f8367f
--- /dev/null
@@ -0,0 +1,19 @@
+DROP TABLE IF EXISTS members.member_files CASCADE;
+
+CREATE TABLE members.member_files
+(id SERIAL,
+ file_contents TEXT,
+ create_date DATE DEFAULT CURRENT_DATE,
+ original_name TEXT NOT NULl,
+ "size" INTEGER NOT NULL,
+ file_name TEXT NOT NULL,
+ name_on_disk TEXT NOT NULL,
+ pending BOOLEAN DEFAULT true,
+ member_id INTEGER NOT NULL
+       REFERENCES members.member(member_id)
+       ON UPDATE CASCADE
+       ON DELETE CASCADE,
+ PRIMARY KEY (id));
+
+GRANT ALL ON members.member_files_id_seq TO nobody;
+GRANT ALL ON members.member_files TO nobody;
diff --git a/Toolkit/Members/Database/tables/member_golf.sql b/Toolkit/Members/Database/tables/member_golf.sql
new file mode 100644 (file)
index 0000000..a6591ee
--- /dev/null
@@ -0,0 +1,20 @@
+DROP TABLE IF EXISTS members.member_golf CASCADE;
+
+CREATE TABLE members.member_golf
+(id SERIAL,
+ par TEXT,
+ yardage TEXT,
+ course_rating TEXT,
+ slope_rating TEXT,
+ walking_course BOOLEAN DEFAULT FALSE,
+ holes18 TEXT,
+ holes9 TEXT,
+ res_url TEXT,
+ member_id INTEGER NOT NULL
+    REFERENCES members.member(member_id)
+    ON UPDATE CASCADE
+    ON DELETE CASCADE,
+ PRIMARY KEY (id));
+
+GRANT ALL ON members.member_golf_id_seq TO nobody;
+GRANT ALL ON members.member_golf TO nobody;
diff --git a/Toolkit/Members/Database/tables/member_last_updates.sql b/Toolkit/Members/Database/tables/member_last_updates.sql
new file mode 100644 (file)
index 0000000..5ea8541
--- /dev/null
@@ -0,0 +1,16 @@
+DROP TABLE IF EXISTS members.member_last_updates CASCADE;
+
+CREATE TABLE members.member_last_updates
+(member_id INTEGER NOT NULL
+       REFERENCES members.member(member_id)
+       ON UPDATE CASCADE
+       ON DELETE CASCADE,
+ "timestamp" TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
+ "table"     TEXT NOT NULL,
+ operation TEXT NOT NULL,
+ record_id INTEGER);
+
+CREATE INDEX member_last_updates_member_id_idx ON members.member_last_updates (member_id);
+CREATE INDEX member_last_updates_timestamp_idx ON members.member_last_updates (timestamp);
+
+GRANT ALL ON members.member_last_updates TO nobody;
diff --git a/Toolkit/Members/Database/tables/member_leads.sql b/Toolkit/Members/Database/tables/member_leads.sql
new file mode 100644 (file)
index 0000000..ddc903f
--- /dev/null
@@ -0,0 +1,10 @@
+CREATE TABLE members.member_leads (
+    id SERIAL,
+    member_id INT
+      REFERENCES members.member(member_id)
+      ON DELETE CASCADE,
+    query text
+);
+CREATE UNIQUE INDEX members.member_leads_member_id ON member_leads(member_id);
+GRANT ALL ON members.member_leads TO nobody;
+GRANT ALL ON members.member_leads_id_seq TO nobody;
diff --git a/Toolkit/Members/Database/tables/member_newsletters.sql b/Toolkit/Members/Database/tables/member_newsletters.sql
new file mode 100644 (file)
index 0000000..3527932
--- /dev/null
@@ -0,0 +1,15 @@
+DROP TABLE IF EXISTS members.member_newsletters CASCADE;
+
+CREATE TABLE members.member_newsletters
+(id SERIAL,
+ subject TEXT,
+ response TEXT,
+ mailout date 
+    DEFAULT current_date,
+ last_update DATE 
+    DEFAULT current_date,
+ archived BOOLEAN,
+PRIMARY KEY (id));
+
+GRANT ALL ON members.member_newsletters TO nobody;
+GRANT ALL ON members.member_newsletters_id_seq TO nobody;
diff --git a/Toolkit/Members/Database/tables/member_packages.sql b/Toolkit/Members/Database/tables/member_packages.sql
new file mode 100644 (file)
index 0000000..3103415
--- /dev/null
@@ -0,0 +1,20 @@
+DROP TABLE IF EXISTS members.member_packages CASCADE;
+
+CREATE TABLE members.member_packages
+(id SERIAL,
+ title TEXT,
+ description TEXT,
+ image TEXT,
+ "type" TEXT,
+ pending BOOLEAN DEFAULT TRUE,
+ pos INTEGER,
+ sdate DATE,
+ edate DATE,
+ member_id INTEGER NOT NULL
+       REFERENCES members.member(member_id)
+       ON UPDATE CASCADE
+       ON DELETE CASCADE,
+ PRIMARY KEY (id));
+
+GRANT ALL ON members.member_packages_id_seq TO nobody;
+GRANT ALL ON members.member_packages TO nobody;
diff --git a/Toolkit/Members/Database/tables/member_photos.sql b/Toolkit/Members/Database/tables/member_photos.sql
new file mode 100644 (file)
index 0000000..08cc3cd
--- /dev/null
@@ -0,0 +1,16 @@
+DROP TABLE IF EXISTS members.member_photos CASCADE;
+
+CREATE TABLE members.member_photos
+(id SERIAL,
+ image TEXT NOT NULL,
+ caption TEXT,
+ pending BOOLEAN DEFAULT TRUE,
+ pos INTEGER DEFAULT 1,
+ member_id INTEGER NOT NULL
+       REFERENCES members.member(member_id)
+       ON UPDATE CASCADE
+       ON DELETE CASCADE,
+ PRIMARY KEY (id));
+
+GRANT ALL ON members.member_photos_id_seq TO nobody;
+GRANT ALL ON members.member_photos TO nobody;
diff --git a/Toolkit/Members/Database/tables/member_regions2toolbox_pages.sql b/Toolkit/Members/Database/tables/member_regions2toolbox_pages.sql
new file mode 100644 (file)
index 0000000..0a9e5dc
--- /dev/null
@@ -0,0 +1,13 @@
+DROP TABLE IF EXISTS toolbox.member_regions2toolbox_pages CASCADE;
+
+CREATE TABLE toolbox.member_regions2toolbox_pages
+(page INTEGER NOT NULL
+       REFERENCES toolbox.pages (id)
+       ON UPDATE CASCADE
+       ON DELETE CASCADE,
+ region INTEGER NOT NULL
+       REFERENCES members.region (region_id)
+       ON UPDATE CASCADE
+       ON DELETE CASCADE);
+
+GRANT ALL ON toolbox.member_regions2toolbox_pages TO nobody;
diff --git a/Toolkit/Members/Database/tables/member_regions2toolbox_pages_draft.sql b/Toolkit/Members/Database/tables/member_regions2toolbox_pages_draft.sql
new file mode 100644 (file)
index 0000000..5696183
--- /dev/null
@@ -0,0 +1,13 @@
+DROP TABLE IF EXISTS toolbox.member_regions2toolbox_pages_draft CASCADE;
+
+CREATE TABLE toolbox.member_regions2toolbox_pages_draft
+(page INTEGER NOT NULL
+       REFERENCES toolbox.pages (id)
+       ON UPDATE CASCADE
+       ON DELETE CASCADE,
+ region INTEGER NOT NULL
+       REFERENCES members.region (region_id)
+       ON UPDATE CASCADE
+       ON DELETE CASCADE);
+
+GRANT ALL ON toolbox.member_regions2toolbox_pages_draft TO nobody;
diff --git a/Toolkit/Members/Database/tables/member_restaurants.sql b/Toolkit/Members/Database/tables/member_restaurants.sql
new file mode 100644 (file)
index 0000000..40f40dc
--- /dev/null
@@ -0,0 +1,26 @@
+DROP TABLE IF EXISTS members.member_restaurants CASCADE;
+
+CREATE TABLE members.member_restaurants
+(id SERIAL,
+ breakfast BOOLEAN DEFAULT FALSE,
+ breakfast_from TEXT,
+ breakfast_to TEXT,
+ brunch BOOLEAN DEFAULT FALSE,
+ brunch_from TEXT,
+ brunch_to TEXT,
+ lunch BOOLEAN DEFAULT FALSE,
+ lunch_from TEXT,
+ lunch_to TEXT,
+ dinner BOOLEAN        DEFAULT FALSE,
+ dinner_from TEXT,
+ dinner_to TEXT,
+ alcohol BOOLEAN DEFAULT FALSE,
+ non_smoking BOOLEAN DEFAULT FALSE,
+ member_id INTEGER NOT NULL
+       REFERENCES members.member(member_id)
+       ON UPDATE CASCADE
+       ON DELETE CASCADE,
+ PRIMARY KEY (id));
+
+GRANT ALL ON members.member_restaurants_id_seq TO nobody;
+GRANT ALL ON members.member_restaurants TO nobody;
diff --git a/Toolkit/Members/Database/tables/member_session.sql b/Toolkit/Members/Database/tables/member_session.sql
new file mode 100644 (file)
index 0000000..b42d491
--- /dev/null
@@ -0,0 +1,21 @@
+DROP TABLE IF EXISTS members.member_session CASCADE;
+
+CREATE TABLE members.member_session
+(id SERIAL,
+ member_id INTEGER NOT NULL
+       REFERENCES members.member (member_id)
+       ON UPDATE CASCADE
+       ON DELETE CASCADE,
+ contact_id INTEGER NOT NULL
+       REFERENCES contacts.contact (id)
+       ON UPDATE CASCADE
+       ON DELETE CASCADE,
+ catid INTEGER NOT NULL
+       REFERENCES toolbox.pages (id)
+       ON UPDATE CASCADE
+       ON DELETE CASCADE,
+ PRIMARY KEY (id)
+);
+
+GRANT ALL ON members.member_session_id_seq TO nobody;
+GRANT ALL ON members.member_session TO nobody;
diff --git a/Toolkit/Members/Database/tables/member_updates.sql b/Toolkit/Members/Database/tables/member_updates.sql
new file mode 100644 (file)
index 0000000..78ed736
--- /dev/null
@@ -0,0 +1,20 @@
+DROP TABLE IF EXISTS members.member_updates CASCADE;
+
+CREATE TABLE members.member_updates
+(id SERIAL,
+ field TEXT,
+ "update" TEXT,
+ alter_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ db_table TEXT NOT NULL,
+ data_type TEXT,
+ label TEXT,
+ foreign_key INTEGER,
+ member_id INTEGER NOT NULL
+       REFERENCES members.member(member_id)
+       ON UPDATE CASCADE
+       ON DELETE CASCADE,
+ field_type TEXT,
+ PRIMARY KEY (id));
+
+GRANT ALL ON members.member_updates_id_seq TO nobody;
+GRANT ALL ON members.member_updates TO nobody;
diff --git a/Toolkit/Members/Database/tables/region.sql b/Toolkit/Members/Database/tables/region.sql
new file mode 100644 (file)
index 0000000..b0e4ea4
--- /dev/null
@@ -0,0 +1,13 @@
+DROP TABLE IF EXISTS members.region CASCADE;
+
+CREATE TABLE members.region
+(region_id SERIAL,
+ region_name TEXT UNIQUE,
+ PRIMARY KEY (region_id));
+
+DELETE FROM members.region;
+ALTER SEQUENCE members.region_region_id_seq RESTART WITH 1;
+INSERT INTO members.region (region_name) VALUES ('Default Temp Region');
+
+GRANT ALL ON members.region_region_id_seq TO nobody;
+GRANT ALL ON members.region TO nobody;
diff --git a/Toolkit/Members/Database/tables/state.sql b/Toolkit/Members/Database/tables/state.sql
new file mode 100644 (file)
index 0000000..209a31f
--- /dev/null
@@ -0,0 +1,85 @@
+DROP TABLE IF EXISTS members.state CASCADE;
+
+CREATE TABLE members.state
+(state_id SERIAL,
+ state_name TEXT,
+ state_abb VARCHAR(2),
+ us_state BOOLEAN DEFAULT FALSE,
+ PRIMARY KEY (state_id));
+
+DELETE FROM members.state;
+ALTER SEQUENCE members.state_state_id_seq RESTART WITH 1;
+INSERT INTO members.state (us_state, state_name, state_abb) VALUES (true, 'Alabama', 'AL');
+INSERT INTO members.state (us_state, state_name, state_abb) VALUES (true, 'Alaska', 'AK');
+INSERT INTO members.state (us_state, state_name, state_abb) VALUES (true, 'Arizona', 'AZ');
+INSERT INTO members.state (us_state, state_name, state_abb) VALUES (true, 'Arkansas', 'AR');
+INSERT INTO members.state (us_state, state_name, state_abb) VALUES (true, 'California', 'CA');
+INSERT INTO members.state (us_state, state_name, state_abb) VALUES (true, 'Colorado', 'CO');
+INSERT INTO members.state (us_state, state_name, state_abb) VALUES (true, 'Connecticut', 'CT');
+INSERT INTO members.state (us_state, state_name, state_abb) VALUES (true, 'Delaware', 'DE');
+INSERT INTO members.state (us_state, state_name, state_abb) VALUES (true, 'District of Columbia', 'DC');
+INSERT INTO members.state (us_state, state_name, state_abb) VALUES (true, 'Florida', 'FL');
+INSERT INTO members.state (us_state, state_name, state_abb) VALUES (true, 'Georgia', 'GA');
+INSERT INTO members.state (us_state, state_name, state_abb) VALUES (true, 'Hawaii', 'HI');
+INSERT INTO members.state (us_state, state_name, state_abb) VALUES (true, 'Idaho', 'ID');
+INSERT INTO members.state (us_state, state_name, state_abb) VALUES (true, 'Illinois', 'IL');
+INSERT INTO members.state (us_state, state_name, state_abb) VALUES (true, 'Indiana', 'IN');
+INSERT INTO members.state (us_state, state_name, state_abb) VALUES (true, 'Iowa', 'IA');
+INSERT INTO members.state (us_state, state_name, state_abb) VALUES (true, 'Kansas', 'KS');
+INSERT INTO members.state (us_state, state_name, state_abb) VALUES (true, 'Kentucky', 'KY');
+INSERT INTO members.state (us_state, state_name, state_abb) VALUES (true, 'Louisiana', 'LA');
+INSERT INTO members.state (us_state, state_name, state_abb) VALUES (true, 'Maine', 'ME');
+INSERT INTO members.state (us_state, state_name, state_abb) VALUES (true, 'Maryland', 'MD');
+INSERT INTO members.state (us_state, state_name, state_abb) VALUES (true, 'Massachusetts', 'MA');
+INSERT INTO members.state (us_state, state_name, state_abb) VALUES (true, 'Michigan', 'MI');
+INSERT INTO members.state (us_state, state_name, state_abb) VALUES (true, 'Minnesota', 'MN');
+INSERT INTO members.state (us_state, state_name, state_abb) VALUES (true, 'Mississppi', 'MS');
+INSERT INTO members.state (us_state, state_name, state_abb) VALUES (true, 'Missouri', 'MO');
+INSERT INTO members.state (us_state, state_name, state_abb) VALUES (true, 'Montana', 'MT');
+INSERT INTO members.state (us_state, state_name, state_abb) VALUES (true, 'Nebraska', 'NE');
+INSERT INTO members.state (us_state, state_name, state_abb) VALUES (true, 'Nevada', 'NV');
+INSERT INTO members.state (us_state, state_name, state_abb) VALUES (true, 'New Hampshire', 'NH');
+INSERT INTO members.state (us_state, state_name, state_abb) VALUES (true, 'New Jersey', 'NJ');
+INSERT INTO members.state (us_state, state_name, state_abb) VALUES (true, 'New Mexico', 'NM');
+INSERT INTO members.state (us_state, state_name, state_abb) VALUES (true, 'New York', 'NY');
+INSERT INTO members.state (us_state, state_name, state_abb) VALUES (true, 'North Carolina', 'NC');
+INSERT INTO members.state (us_state, state_name, state_abb) VALUES (true, 'North Dakota', 'ND');
+INSERT INTO members.state (us_state, state_name, state_abb) VALUES (true, 'Ohio', 'OH');
+INSERT INTO members.state (us_state, state_name, state_abb) VALUES (true, 'Oklahoma', 'OK');
+INSERT INTO members.state (us_state, state_name, state_abb) VALUES (true, 'Oregon', 'OR');
+INSERT INTO members.state (us_state, state_name, state_abb) VALUES (true, 'Pennsylvania', 'PA');
+INSERT INTO members.state (us_state, state_name, state_abb) VALUES (true, 'Rhode Island', 'RI');
+INSERT INTO members.state (us_state, state_name, state_abb) VALUES (true, 'South Carolina', 'SC');
+INSERT INTO members.state (us_state, state_name, state_abb) VALUES (true, 'South Dakota', 'SD');
+INSERT INTO members.state (us_state, state_name, state_abb) VALUES (true, 'Tennessee', 'TN');
+INSERT INTO members.state (us_state, state_name, state_abb) VALUES (true, 'Texas', 'TX');
+INSERT INTO members.state (us_state, state_name, state_abb) VALUES (true, 'Utah', 'UT');
+INSERT INTO members.state (us_state, state_name, state_abb) VALUES (true, 'Vermont', 'VT');
+INSERT INTO members.state (us_state, state_name, state_abb) VALUES (true, 'Virginia', 'VA');
+INSERT INTO members.state (us_state, state_name, state_abb) VALUES (true, 'Washington', 'WA');
+INSERT INTO members.state (us_state, state_name, state_abb) VALUES (true, 'West Virginia', 'WV');
+INSERT INTO members.state (us_state, state_name, state_abb) VALUES (true, 'Wisconsin', 'WI');
+INSERT INTO members.state (us_state, state_name, state_abb) VALUES (true, 'Wyoming', 'WY');
+INSERT INTO members.state (us_state, state_name, state_abb) VALUES (false, 'Alberta', 'AB');
+INSERT INTO members.state (us_state, state_name, state_abb) VALUES (false, 'American Samoa', 'AS');
+INSERT INTO members.state (us_state, state_name, state_abb) VALUES (false, 'British Columbia', 'BC');
+INSERT INTO members.state (us_state, state_name, state_abb) VALUES (false, 'Federated States of Micronesia', 'FM');
+INSERT INTO members.state (us_state, state_name, state_abb) VALUES (false, 'Guam', 'GU');
+INSERT INTO members.state (us_state, state_name, state_abb) VALUES (false, 'Manitoba', 'MB');
+INSERT INTO members.state (us_state, state_name, state_abb) VALUES (false, 'Marshall Islands', 'MH');
+INSERT INTO members.state (us_state, state_name, state_abb) VALUES (false, 'New Brunswick', 'NB');
+INSERT INTO members.state (us_state, state_name, state_abb) VALUES (false, 'Newfoundland', 'NF');
+INSERT INTO members.state (us_state, state_name, state_abb) VALUES (false, 'Northern Mariana Islands', 'MP');
+INSERT INTO members.state (us_state, state_name, state_abb) VALUES (false, 'Northwest Territories', 'NT');
+INSERT INTO members.state (us_state, state_name, state_abb) VALUES (false, 'Nova Scotia', 'NS');
+INSERT INTO members.state (us_state, state_name, state_abb) VALUES (false, 'Ontario', 'ON');
+INSERT INTO members.state (us_state, state_name, state_abb) VALUES (false, 'Palau', 'PW');
+INSERT INTO members.state (us_state, state_name, state_abb) VALUES (false, 'Prince Edward Island', 'PE');
+INSERT INTO members.state (us_state, state_name, state_abb) VALUES (false, 'Puerto Rico', 'PR');
+INSERT INTO members.state (us_state, state_name, state_abb) VALUES (false, 'Quebec', 'QC');
+INSERT INTO members.state (us_state, state_name, state_abb) VALUES (false, 'Saskatchewan', 'SK');
+INSERT INTO members.state (us_state, state_name, state_abb) VALUES (false, 'Virgin Islands', 'VI');
+INSERT INTO members.state (us_state, state_name, state_abb) VALUES (false, 'Yukon', 'YT');
+
+GRANT ALL ON members.state_state_id_seq TO nobody;
+GRANT ALL ON members.state TO nobody;
diff --git a/Toolkit/Members/Database/tables/streamsend.sql b/Toolkit/Members/Database/tables/streamsend.sql
new file mode 100644 (file)
index 0000000..7065c61
--- /dev/null
@@ -0,0 +1,21 @@
+CREATE TABLE members.streamsend (
+ id SERIAL,
+ field_id INT NOT NULL,
+ field_name TEXT NOT NULL,
+ option_id INT NOT NULL,
+ option_name TEXT NOT NULL,
+ category_id INT NOT NULL
+    REFERENCES members.category(category_id)
+    ON DELETE CASCADE,
+ parent INT NOT NULL
+    REFERENCES members.category(category_id)
+    ON DELETE CASCADE,
+ PRIMARY KEY (id)
+);
+
+GRANT ALL ON members.streamsend TO nobody;
+GRANT ALL ON members.streamsend_id_seq TO nobody;
+
+CREATE UNIQUE INDEX streamsend_category_id_uniq_inx on members.streamsend (category_id);
+CREATE UNIQUE INDEX streamsend_option_name_uniq_inx on members.streamsend (option_name);
+CREATE UNIQUE INDEX streamsend_option_id_uniq_inx on members.streamsend (option_id);
\ No newline at end of file
diff --git a/Toolkit/Members/Database/utilities/unnest.sql b/Toolkit/Members/Database/utilities/unnest.sql
new file mode 100644 (file)
index 0000000..7859d56
--- /dev/null
@@ -0,0 +1,5 @@
+CREATE OR REPLACE FUNCTION members.unnest(anyarray) RETURNS SETOF anyelement
+LANGUAGE SQL AS $$
+       SELECT $1[i] FROM generate_series(array_lower($1, 1),
+                                                                         array_upper($1, 1)) AS i;
+$$;
diff --git a/Toolkit/Members/Database/utilities/update_exploded_members_name_table_POST_8.3.sql b/Toolkit/Members/Database/utilities/update_exploded_members_name_table_POST_8.3.sql
new file mode 100644 (file)
index 0000000..a1e4cb0
--- /dev/null
@@ -0,0 +1,17 @@
+--
+-- This utility requires postgresq vrs >= 8.3 so you can utilize the
+-- regexp_split_to_table function.  If you don't have access to this version
+-- you can look into the Members/Database/utilities/string_to_rows.sql function
+-- for splitting a string to a table by space.
+--
+
+DELETE FROM exploded_members_name;
+
+INSERT INTO exploded_members_name (part, mid)
+SELECT regexp_split_to_table(
+                  regexp_replace(
+                          regexp_replace(member_name, '[[:punct:]]', '', 'g'),
+                          '[^[:alnum:] ]', '', 'g'
+                  ), E'\\s+'
+       ), member_id
+  FROM member;
diff --git a/Toolkit/Members/Database/utilities/update_exploded_members_name_table_PRE_8.3.sql b/Toolkit/Members/Database/utilities/update_exploded_members_name_table_PRE_8.3.sql
new file mode 100644 (file)
index 0000000..52a725e
--- /dev/null
@@ -0,0 +1,22 @@
+--
+-- This utility requires postgresq vrs >= 8.3 so you can utilize the
+-- regexp_split_to_table function.  If you don't have access to this version
+-- you can look into the Members/Database/utilities/unnest.sql function
+-- for splitting a string to a table by space.
+--
+
+DELETE FROM members.exploded_members_name;
+
+INSERT INTO members.exploded_members_name (part, mid)
+SELECT unnest(
+                string_to_array(
+                        regexp_replace(
+                                regexp_replace(
+                                        regexp_replace(
+                                                member_name, '[[:punct:]]', '', 'g'
+                                        ), '[[:space:]]+', ' ', 'g'
+                                ), '[^[:alnum:] ]', '', 'g'
+                        ), ' '
+                )
+        ), member_id
+FROM members.member;
diff --git a/Toolkit/Members/Display.php b/Toolkit/Members/Display.php
new file mode 100644 (file)
index 0000000..8af6737
--- /dev/null
@@ -0,0 +1,214 @@
+<?php
+
+/**
+ * PHP version 5
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2008 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @version   CVS: $Id: Display.php,v 1.38 2010/05/25 14:01:16 jamie Exp $
+ * @link      http://demo.gaslightmedia.com
+ */
+
+/**
+ * Handles Displaying member lists and profile pages
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2008 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @link      http://demo.gaslightmedia.com
+ */
+class Toolkit_Members_Display
+{
+       //      {{{     properties
+
+       /**
+        * The Database Handler
+        *
+        * @var object
+        * @access protected
+        */
+       public $dbh;
+
+       /**
+        * Search radius distance
+        * @var integer
+        * @access protected
+        */
+       protected $radius = 35;
+
+       /**
+        * Page catid
+        *
+        * @var integer
+        * @access private
+        */
+       private $_catid;
+
+       /**
+        * Number of records to show per page
+        *
+        * Set to NULL to show all records on one page.
+        *
+        * @var integer
+        * @access protected
+        */
+       protected $limit = 10;
+
+       /**
+        * The list of members
+        *
+        * @var string
+        * @access protected
+        */
+        protected $membersList;
+
+       /**
+        * Whether to randomize the list of members or not
+        *
+        * @var bool
+        * @access protected
+        */
+       protected $randomize = false;
+
+       //      }}}
+       //      {{{     __construct()
+
+    /**
+     * Constructor
+     *
+     * @param PDO $pdo PHP Data Object to use for DB calls
+     *
+     * @return void
+     * @access public
+     */
+       public function __construct(PDO $pdo)
+       {
+               $this->dbh = $pdo;
+       }
+
+       //      }}}
+
+       //      {{{     hideUserSearchForm()
+
+       /**
+        * Get the status of displaying the search form for a page
+        *
+        * @return bool true if yes, false otherwise.
+        * @access public
+        */
+       public function hideUserSearchForm()
+       {
+               try {
+                       $sql = "
+                               SELECT CASE
+                                          WHEN no_search_form THEN 1 ELSE 0
+                                          END as no_search_form
+                                 FROM bus_category
+                                WHERE id = :catid";
+
+                       $stmt = $this->dbh->prepare($sql);
+                       $stmt->bindParam(':catid', $this->_catid, PDO::PARAM_INT);
+                       $stmt->execute();
+                       $row = $stmt->fetch();
+                       return $row['no_search_form'];
+               } catch (PDOException $e) {
+                       return Toolkit_Common::handleError($e);
+               }
+       }
+
+       //      }}}
+
+       //      {{{     includeMemberMap()
+
+       /**
+        * include a google map of the members on the page
+        *
+        * @return bool
+        * @access public
+        */
+       public function includeMemberMap()
+       {
+               try {
+                       $sql = "
+                               SELECT CASE
+                                          WHEN include_member_map THEN 1 ELSE 0
+                                          END as include_member_map
+                                 FROM bus_category
+                                WHERE id = :catid";
+
+                       $stmt = $this->dbh->prepare($sql);
+                       $stmt->bindParam(':catid', $this->_catid, PDO::PARAM_INT);
+                       $stmt->execute();
+                       $row = $stmt->fetch();
+                       return $row['include_member_map'];
+               } catch (PDOException $e) {
+                       return Toolkit_Common::handleError($e);
+               }
+       }
+
+       //      }}}
+
+    //  {{{ setCatId()
+
+    /**
+     * Set the catid for the page
+     *
+     * @param integer $catid page id from db
+     *
+     * @return void
+     * @access public
+     * @throws PEAR Error on invalid member id
+     */
+    public function setCatId($catid)
+    {
+               if (!ctype_digit((string)$catid)) {
+                       throw new InvalidArgumentException(
+                               '$catid must be an integer.'
+                       );
+               }
+
+               $this->_catid = $catid;
+    }
+
+    //  }}}
+       //      {{{     setMemberSections()
+
+    /**
+     * Sets an array with all the member category types assigned to a page
+     *
+        * Collects all member categories assigned to a page from the
+        * admin Toolbox.
+     *
+     * @return void
+     * @access public
+     */
+       public function setMemberSections()
+       {
+               try {
+                       $sql = "
+                               SELECT count(*) AS total, p.navigation_name
+                                 FROM member_categories2toolbox_pages mc2tp, pages p
+                                WHERE mc2tp.page = :catid
+                                  AND mc2tp.page = p.id
+                                GROUP BY p.navigation_name";
+
+                       $stmt = $this->dbh->prepare($sql);
+                       $stmt->bindParam(':catid', $this->_catid, PDO::PARAM_INT);
+                       $stmt->execute();
+                       $row = $stmt->fetch();
+                       if ($row['total']) {
+                               $this->memberSections[$this->_catid] = $row['navigation_name'];
+                       }
+               } catch (PDOException $e) {
+                       return Toolkit_Common::handleError($e);
+               }
+       }
+
+       //      }}}
+}
+?>
diff --git a/Toolkit/Members/EditContactForm.php b/Toolkit/Members/EditContactForm.php
new file mode 100644 (file)
index 0000000..41913c0
--- /dev/null
@@ -0,0 +1,276 @@
+<?php
+/**
+ * EditContactForm.php
+ *
+ * PHP Version 5
+ *
+ * @category Toolkit
+ * @package  Members
+ * @author   Jamie Kahgee <steve@gaslightmedia.com>
+ * @license  Gaslight Media
+ * @link     <>
+ */
+/**
+ * Toolkit_Members_EditContactForm
+ *
+ * Description for Toolkit_Members_EditContactForm
+ *
+ * @category Toolkit
+ * @package  Members
+ * @author   Jamie Kahgee <steve@gaslightmedia.com>
+ * @license  Gaslight Media
+ * @link     <>
+ */
+class Toolkit_Members_EditContactForm
+       extends Toolkit_FormBuilder
+{
+       //      {{{     properties
+
+       /**
+        * Some special forms dont utlize this stylesheet
+        * Allow classes to override this setting so it doesn't
+        * get included
+        *
+        * @var boolean
+        * @access protected
+        */
+       protected $includeContactStyleSheet = false;
+
+       //      }}}
+    
+    /**
+     * Description for addMembersContactsToStreamSend
+     * 
+     * @param PDO     $dbh Database handler
+     * @param unknown $mid Member ID
+     * 
+     * @return void
+     * @access public
+     */
+    public function addMemberContactsToStreamSend(PDO $dbh, $mid)
+    {
+        if (   defined('MEMBER_STREAMSEND_API')
+            && MEMBER_STREAMSEND_API
+        ) {
+            // send member to streamsend
+            $memberStreamSend = new Toolkit_Members_StreamSend($dbh);
+            $memberStreamSend->sendMemberContactsByMemberId($mid);
+        }
+    }
+    
+       //      {{{     configureDefaults()
+
+       /**
+        * Configure element default values
+        *
+        * Elements will only have a default value if we are editing
+        * an existing contact
+        *
+     * @param PDO $pdo Datbase handler
+     * 
+        * @return void
+        * @access public
+        */
+       public function configureDefaults(PDO $pdo)
+       {
+               $d = array();
+
+               if ($contactId = filter_input(INPUT_GET, 'cid', FILTER_VALIDATE_INT)) {
+                       $contact = Toolkit_Members_Contact::fetch($pdo, $contactId);
+                       $d['title'] = $contact->getTitle();
+                       $d['fname'] = $contact->getFname();
+                       $d['lname'] = $contact->getLname();
+                       $d['email'] = $contact->getEmail();
+                       $d['phone'] = $contact->getPhone();
+                       $d['send_mail'] = $contact->getCanReceiveMail();
+                       $d['id'] = $contactId;
+                       $d['submit'] = 'Update Contact';
+               } else {
+                       $d['submit'] = 'Create Contact';
+               }
+
+               $this->setupDefaults($d);
+       }
+
+       //      }}}
+       //      {{{     configureElements()
+
+       /**
+        * Configure the elements that will be used on the form
+        *
+        * @return void
+        * @access public
+        */
+       public function configureElements()
+       {
+               $e = array();
+
+               $e[] = array(
+            'type' => 'hidden',
+            'req' => false,
+            'name' => 'id'
+        );
+               $e[] = array(
+            'type' => 'text',
+            'req' => false,
+            'name' => 'title',
+            'display' => 'Title',
+            'opts' => array('class' => 'text')
+        );
+               $e[] = array(
+            'type' => 'text',
+            'req' => true,
+            'name' => 'fname',
+            'display' => 'First Name',
+            'opts' => array('class' => 'text')
+        );
+               $e[] = array(
+            'type' => 'text',
+            'req' => true,
+            'name' => 'lname',
+            'display' => 'Last Name',
+            'opts' => array('class' => 'text')
+        );
+               $e[] = array(
+            'type' => 'text',
+            'req' => false,
+            'name' => 'email',
+            'display' => 'Email',
+            'opts' => array('class' => 'text')
+        );
+               $e[] = array(
+            'type' => 'text',
+            'req' => false,
+            'name' => 'phone',
+            'display' => 'Phone',
+            'opts' => array('class' => 'text')
+        );
+               $e[] = array(
+            'type' => 'advcheckbox',
+            'req' => false,
+            'name' => 'send_mail',
+            'display' => 'Receive Mail',
+            'val' => array(0, 1)
+        );
+               $e[] = array(
+            'type' => 'submit',
+            'req' => false,
+            'name' => 'submit',
+            'display' => 'Submit',
+            'opts' => array('class' => 'submit')
+        );
+
+               $this->setupElements($e);
+       }
+
+       //      }}}
+       //      {{{     configureForm()
+
+       /**
+        * Wrapper function to handle quickly setting up the form
+     * 
+     * @param PDO $pdo Database handler
+        *
+        * @return void
+        * @access public
+        */
+       public function configureForm(PDO $pdo)
+       {
+               $this->configureElements();
+               $this->configureRules();
+               $this->configureDefaults($pdo);
+       }
+
+       //      }}}
+       //      {{{     configureRules()
+
+       /**
+        *      Configure element rules for form validation
+        *
+        * @return void
+        * @access public
+        */
+       public function configureRules()
+       {
+               $this->registeredRules = array('email');
+
+               $r = array();
+               $r[] = array(
+                       'element' => 'email',
+                       'message' => 'ERROR: Invalid Email Address!',
+                       'type' => 'email'
+               );
+
+               $this->setupRules($r);
+       }
+
+       //      }}}
+       //      {{{     createContact()
+
+    /**
+     * Description for createContact()
+     * 
+     * @param array $values Values array
+     * 
+     * @return \Toolkit_Members_Contact 
+     * @access public
+     */
+       protected function createContact(array $values)
+       {
+               return new Toolkit_Members_Contact(
+                       $values['fname'],
+                       $values['lname'],
+                       $values['title'],
+                       $values['email'],
+                       $values['phone'],
+                       $values['send_mail']
+               );
+       }
+
+       //      }}}
+       //      {{{     toHtml()
+
+    /**
+     * Description for toHtml()
+     * 
+     * @param PDO                            $pdo      database handler
+     * @param HTML_QuickForm_Renderer_Object $renderer Description of $renderer
+     * @param HTML_Template_Flexy            $tpl      Description of $tpl
+     * 
+     * @return string
+     * @access public
+     */
+       public function toHtml(
+               PDO $pdo,
+               HTML_QuickForm_Renderer_Object $renderer,
+               HTML_Template_Flexy $tpl
+       ) {
+               if ($this->validate()) {
+                       $contact = $this->process(array(&$this, 'createContact'));
+
+                       if (ctype_digit($this->getSubmitValue('id'))) {
+                               $contact->update($pdo, $this->getSubmitValue('id'));
+                       } else {
+                               $contact->save($pdo, $_GET['id']);
+                       }
+
+            $this->addMemberContactsToStreamSend($pdo, $_GET['id']);
+
+                       header('Location: ' . $this->getAttribute('action'));
+                       exit();
+               } elseif ($this->isSubmitted()) {
+                       $output = $this->errorMsg;
+               }
+
+               $this->accept($renderer);
+               $view = new stdClass;
+               $view->form = $renderer->toObject();
+               $tpl->compile('editContact.html');
+               $output .= $tpl->bufferedOutputObject($view);
+
+               return $output;
+       }
+
+       //      }}}
+}
+?>
diff --git a/Toolkit/Members/EditMemberAmenities.php b/Toolkit/Members/EditMemberAmenities.php
new file mode 100644 (file)
index 0000000..2063eb0
--- /dev/null
@@ -0,0 +1,304 @@
+<?php
+//     vim:set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker syntax=php:
+
+/**
+ * File Doc Comment
+ *
+ * PHP version 5
+ *
+ * @category MembersDB
+ * @package  Toolkit_Members
+ * @author   Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @license     http://www.gaslightmedia.com Gaslightmedia
+ * @version  CVS: $Id: EditMemberAmenities.php,v 1.11 2009/11/16 12:11:39 jamie Exp $
+ * @link        http://demo.gaslightmedia.com
+ */
+
+/**
+ * Short description for class
+ * 
+ * Long description (if any) ...
+ * 
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @link      http://demo.gaslightmedia.com
+ */
+class Toolkit_Members_EditMemberAmenities
+       extends Toolkit_Members_EditMemberInfo implements Toolkit_Form
+{
+       //      {{{ properties
+
+       /**
+        * The Table name used to store the data of the member record in the database.
+        *
+        * @var string
+        * @access public
+        */
+       public $tableName = 'member_amenity';
+
+       /**
+        * The name of the template used to render the business info form.
+        *
+        * @var string
+        * @access protected 
+        */
+       protected $formTemplate = 'editAmenities.tpl';
+
+       //      }}}
+       //      {{{ __construct()
+
+       /**
+        * Class constructor
+        *
+     * @param PDO    $pdo         PHP Data Object to use for DB calls
+     * @param string $formName    Form's name.
+     * @param string $method      (optional)Form's method defaults to 'POST'
+     * @param string $action      (optional)Form's action
+     * @param string $target      (optional)Form's target defaults to '_self'
+     * @param mixed  $attributes  (optional)Extra attributes for <form> tag
+     * @param bool   $trackSubmit (optional)Whether to track if the form was
+        *                                                        submitted by adding a special hidden field
+     *                            
+        * @access public
+        * @see    Toolkit_Members_EditMemberInfo
+        */
+       public function __construct(
+        PDO $pdo,
+        $formName,
+        $method = 'post',
+        $action = '',
+        $target = '',
+        $attributes = null,
+        $trackSubmit = false
+    ) {
+               parent::__construct(
+            $pdo,
+            $formName,
+            $method,
+            $action,
+            $target,
+            $attributes,
+            $trackSubmit
+        );
+       }
+
+       //      }}}
+
+       //      {{{ configureDefaults()
+
+       /**
+        * Sets the defaults for elements in the form.
+        *
+        * @return array form element default values
+        * @access public
+        */
+       public function configureDefaults()
+       {
+               try {
+            $sql = "
+              SELECT *
+                FROM {$this->tableName}
+                          WHERE member_id = :id ";
+                       $stmt = $this->dbh->prepare($sql);
+                       $stmt->bindParam(':id', $_GET['id'], PDO::PARAM_INT);
+                       $stmt->execute();
+
+                       while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                               $defaults[$row['amenity_id']] = true;
+                       }
+                       $this->setupDefaults($defaults);
+                       return $defaults;
+               } catch (PDOException $e) {
+                       return Toolkit_Common::handleError($e);
+               }
+       }
+
+       //      }}}
+       //      {{{ configureElements()
+
+       /**
+        * Setup the element definitions to use on the form.
+        *
+     * @return void
+        * @access public
+        */
+       public function configureElements()
+       {
+        //  Element definitions
+        $e = array();
+               $amenities = $this->getAmenities();
+               $half = floor(count($amenities) / 2);
+               $i = 0;
+               //      All Grouped Elements are created here.
+
+               //      All Elements are created here.  This includes group element definitions.
+               $e[] = array(
+            'type' => 'header',
+            'req' => false,
+            'name' => 'row1'
+        );
+               while ($i < $half) {
+                       $e[] = array(
+                'type' => 'checkbox',
+                'req' => false,
+                'name' => $amenities[$i]['amenity_id'],
+                'display' => $amenities[$i++]['amenity_name']
+            );
+               }
+               $e[] = array(
+            'type' => 'header',
+            'req' => false,
+            'name' => 'row2'
+        );
+               while ($i < count($amenities)) {
+                       $e[] = array(
+                'type' => 'checkbox',
+                'req' => false,
+                'name' => $amenities[$i]['amenity_id'],
+                'display' => $amenities[$i++]['amenity_name']
+            );
+               }
+
+               $this->setupElements($e);
+       }
+
+       //      }}}
+    //  {{{ configureForm()
+
+    /**
+     * Wrapper function to handle setting up the form
+     *
+     * @return void
+     * @access public
+     */
+    public function configureForm()
+    {
+        $this->configureElements();
+        $this->configureRules();
+        $this->configureDefaults();
+    }
+
+    //  }}}
+       //      {{{ configureRules()
+
+       /**
+        * Sets up all the rules to be used when the form is validated.
+        *
+     * @return void
+        * @access public
+        */
+       public function configureRules()
+       {
+               $this->setupRules();
+       }
+
+       //      }}}
+
+       //      {{{ getAmenities()
+
+       /**
+        * Gets all the amentities
+        *
+        * @return $a array The array of amenities from the DB.
+        * @access protected
+        */
+       protected function getAmenities()
+       {
+        $a = array();
+               try {
+                       $sql = "
+                               SELECT *
+                                 FROM amenity
+                                ORDER BY amenity_name";
+                       foreach ($this->dbh->query($sql) as $row) {
+                               $a[] = $row;
+                       }
+               } catch (PDOException $e) {
+                       $this->handleError($e);
+               }
+               return $a;
+       }
+
+       //      }}}
+
+       //      {{{ insertData()
+
+    /**
+        * Create a new record in the database from the data on the form
+     * 
+        * @param array $values Form values
+     *
+        * @return boolean False on error, True otherwise.
+     * @access public
+     */
+       public function insertData($values)
+       {
+               try {
+                       $sql = "
+                DELETE FROM {$this->tableName}
+                 WHERE member_id = :id";
+                       $stmt = $this->dbh->prepare($sql);
+                       $stmt->bindParam(':id', $_GET['id'], PDO::PARAM_INT);
+                       $stmt->execute();
+
+                       $params = implode(', ', array_keys($values));
+                       $bindParams = ':' . implode(', :', array_keys($values));
+                       $sql = "
+                               INSERT INTO {$this->tableName} (member_id, amenity_id)
+                               VALUES (:member_id, :aid)";
+                       $stmt = $this->dbh->prepare($sql);
+                       $stmt->bindParam(':member_id', $_GET['id'], PDO::PARAM_INT);
+                       foreach ($values as $k => $v) {
+                               $stmt->bindParam(':aid', $k, PDO::PARAM_INT);
+                               $stmt->execute();
+                       }
+
+            $cache = new Cache_Lite(Toolkit_Members::getCacheOptions());
+            $cache->remove("Member-{$_GET['id']}", 'Profile');
+
+                       return true;
+               } catch (PDOException $e) {
+                       return Toolkit_Common::handleError($e);
+               }
+       }
+
+       //      }}}
+
+       //      {{{ processData()
+
+    /**
+     * Short description for function
+     * 
+     * Long description (if any) ...
+     * 
+     * @param array $values Parameter description (if any) ...
+     *
+     * @return array     Return description (if any) ...
+     * @access public
+     */
+       public function processData($values)
+       {
+               $this->tableMetaData = Toolkit_Common::getTableMetaData(
+            $this->dbh,
+            $this->tableName
+        );
+
+               foreach ($values as $k => $v) {
+                       switch ($k) {
+                       default :
+                               if (preg_match('/^.+_rmv$/', $k)) {
+                                       unset($values[$k]);
+                               }
+                               break;
+                       }
+               }
+
+               return $this->insertData($values);
+       }
+
+       //      }}}
+}
+?>
diff --git a/Toolkit/Members/EditMemberContacts.php b/Toolkit/Members/EditMemberContacts.php
new file mode 100644 (file)
index 0000000..61b44a2
--- /dev/null
@@ -0,0 +1,719 @@
+<?php
+//     vim:set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker syntax=php:
+
+/**
+ * File Doc Comment
+ *
+ * PHP version 5
+ *
+ * @category MembersDB
+ * @package  Toolkit_Members
+ * @author   Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @license     http://www.gaslightmedia.com Gaslightmedia
+ * @version  CVS: $Id: EditMemberContacts.php,v 1.11 2010/01/20 19:40:16 jamie Exp $
+ * @link        http://demo.gaslightmedia.com
+ */
+
+/**
+ * Handle associating contacts along with member records
+ *
+ * Controls all aspects of creating and rendering the form used to manipulate
+ * the business contacts. Form is not rendered until the user is added into
+ * the Database.
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2008 Gaslight Media
+ * @license      http://www.gaslightmedia.com Gaslightmedia
+ * @link         http://demo.gaslightmedia.com
+ * @see       Toolkit_Members
+ */
+class Toolkit_Members_EditMemberContacts
+       extends Toolkit_Members_EditMemberInfo implements Toolkit_Form
+{
+       //      {{{ properties
+
+       /**
+        * The table name in the database used to store the data of the files
+        *
+        * @var string
+        * @access public
+        */
+       public $tableName = 'member_contacts';
+
+       /**
+        * The template used to render the form
+        *
+        * @var string
+        * @access protected
+        */
+       protected $formTemplate = 'editContacts.tpl';
+
+       /**
+        * Message to return if the form successfully submits
+        *
+        * @var string
+        * @access protected
+        */
+    protected $successMsg = '
+        <div id="form-success-top">
+            You successfully updated your contact.
+        </div>';
+
+       //      }}}
+       //      {{{ __construct()
+
+       /**
+        * Class constructor
+        *
+     * @param PDO    $pdo         PHP Data Object to use for DB calls
+     * @param string $formName    Form's name.
+     * @param string $method      (optional)Form's method defaults to 'POST'
+     * @param string $action      (optional)Form's action
+     * @param string $target      (optional)Form's target defaults to '_self'
+     * @param mixed  $attributes  (optional)Extra attributes for <form> tag
+     * @param bool   $trackSubmit (optional)Whether to track if the form was
+        *                                                        submitted by adding a special hidden field
+     *
+        * @access public
+        * @see    Toolkit_Members_EditMemberInfo
+        */
+       public function __construct(
+        PDO $pdo,
+        $formName,
+        $method = 'post',
+        $action = '',
+        $target = '',
+        $attributes = null,
+        $trackSubmit = false
+    ) {
+               parent::__construct(
+            $pdo,
+            $formName,
+            $method,
+            $action,
+            $target,
+            $attributes,
+            $trackSubmit
+        );
+       }
+
+       //      }}}
+
+       //      {{{ configureDefaults()
+
+       /**
+        * Sets the defaults for elements in the form.
+        *
+     * @return void
+        * @access public
+        */
+       public function configureDefaults()
+       {
+               try {
+                       if (isset($_GET['cid'])) {
+                               $sql = "
+                                       select * from {$this->tableName} where id = :cid";
+                               $stmt = $this->dbh->prepare($sql);
+                               $stmt->bindParam(':cid', $_GET['cid'], PDO::PARAM_INT);
+                               $stmt->execute();
+                               $row = $stmt->fetch(PDO::FETCH_ASSOC);
+                               $defaults['id']                 = $row['id'];
+                               $defaults['title']              = $row['title'];
+                               $defaults['fname']              = $row['fname'];
+                               $defaults['lname']              = $row['lname'];
+                               $defaults['email']              = $row['email'];
+                               $defaults['phone']              = $row['phone'];
+                               $defaults['send_mail']  = $row['send_mail'];
+                       }
+            $sql = "
+              SELECT *, fname || ' ' || lname AS name,
+                     CASE send_mail
+                     WHEN 'true' THEN 'Can Mail'
+                     ELSE 'No Mail'
+                     END AS send_mail
+                FROM {$this->tableName}
+               WHERE member_id = :id
+                          ORDER BY id";
+                       $stmt = $this->dbh->prepare($sql);
+                       $stmt->bindParam(':id', $_GET['id'], PDO::PARAM_INT);
+                       $stmt->execute();
+
+                       $i = 0;
+                       while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                               $defaults["cid$i"]                      = $row['id'];
+                               $defaults["title$i"]            = $row['title'];
+                               $defaults["name$i"]             = $row['name'];
+                               $defaults["email$i"]            = $row['email'];
+                               $defaults["phone$i"]            = $row['phone'];
+                               $defaults["send_mail$i"]        = $row['send_mail'];
+                               ++$i;
+                       }
+                       $this->setupDefaults($defaults);
+               } catch (PDOException $e) {
+                       return Toolkit_Common::handleError($e);
+               }
+       }
+
+       //      }}}
+       //      {{{ configureElements()
+
+       /**
+        * Setup the elements to use on the form.
+        *
+     * @return void
+        * @access public
+        */
+       public function configureElements()
+       {
+        $e = array();
+               $contacts = $this->getMemberContacts();
+               //      All Grouped Elements are created here.
+               $submitText = (isset($_GET['cid'])) ? 'Update' : 'Submit';
+
+               //      All Elements are created here.  This includes group element definitions.
+               $e[] = array(
+            'type' => 'header',
+            'req' => false,
+            'name' => 'fileHdr',
+            'display' => 'Contacts'
+        );
+               $e[] = array(
+            'type' => 'hidden',
+            'req' => false,
+            'name' => 'id'
+        );
+               $e[] = array(
+            'type' => 'text',
+            'req' => false,
+            'name' => 'title',
+            'display' => 'Title',
+            'opts' => array('class' => 'text')
+        );
+               $e[] = array(
+            'type' => 'text',
+            'req' => true,
+            'name' => 'fname',
+            'display' => 'First Name',
+            'opts' => array('class' => 'text')
+        );
+               $e[] = array(
+            'type' => 'text',
+            'req' => true,
+            'name' => 'lname',
+            'display' => 'Last Name',
+            'opts' => array('class' => 'text')
+        );
+               $e[] = array(
+            'type' => 'text',
+            'req' => false,
+            'name' => 'email',
+            'display' => 'Email',
+            'opts' => array('class' => 'text')
+        );
+               $e[] = array(
+            'type' => 'text',
+            'req' => false,
+            'name' => 'phone',
+            'display' => 'Phone',
+            'opts' => array('class' => 'text')
+        );
+               $e[] = array(
+            'type' => 'advcheckbox',
+            'req' => false,
+            'name' => 'send_mail',
+            'display' => 'Receive Mail',
+            'val' => array(0, 1)
+        );
+               $e[] = array(
+            'type' => 'submit',
+            'req' => false,
+            'name' => 'submit',
+            'display' => $submitText,
+            'opts' => array('class' => 'submit')
+        );
+               if (is_array($contacts)) {
+                       foreach ($contacts as $k => $v) {
+                               $e[] = array(
+                    'type' => 'header',
+                    'req' => false,
+                    'name' => "fileHdr$k"
+                );
+                               $e[] = array(
+                    'type' => 'static',
+                    'req' => false,
+                    'name' => "cid$k"
+                );
+                               $e[] = array(
+                    'type' => 'static',
+                    'req' => false,
+                    'name' => "name$k",
+                    'display' => 'Name'
+                );
+                               $e[] = array(
+                    'type' => 'static',
+                    'req' => false,
+                    'name' => "title$k"
+                );
+                               $e[] = array(
+                    'type' => 'static',
+                    'req' => false,
+                    'name' => "email$k",
+                    'display' => 'Email'
+                );
+                               $e[] = array(
+                    'type' => 'static',
+                    'req' => false,
+                    'name' => "phone$k",
+                    'display' => 'Phone'
+                );
+                               $e[] = array(
+                    'type' => 'static',
+                    'req' => false,
+                    'name' => "send_mail$k",
+                    'display' => 'Receive Mail'
+                );
+                       }
+               }
+
+               $this->setupElements($e);
+       }
+
+       //      }}}
+    //  {{{ configureForm()
+
+    /**
+     * Wrapper function to handle setting up the form
+     *
+     * @return void
+     * @access public
+     */
+    public function configureForm()
+    {
+        $this->configureElements();
+        $this->configureRules();
+        $this->configureDefaults();
+    }
+
+    //  }}}
+       //      {{{ configureRules()
+
+       /**
+        * Sets up all the rules to be used when the form is validated.
+        *
+     * @return void
+        * @access public
+        */
+       public function configureRules()
+       {
+        //  form rules
+        $r = array();
+               $this->registeredRules = array('phone', 'email');
+               $r[] = array(
+            'element' => 'email',
+            'message' => 'ERROR: Invalid Email Address!',
+            'type' => 'email',
+            'format' => null,
+            'validation' => $this->validationType,
+            'reset' => false,
+            'force' => false
+        );
+        /*
+               $r[] = array(
+            'element' => 'phone',
+            'message' => 'ERROR: Invalid Phone Number!',
+            'type' => 'phone',
+            'format' => null,
+            'validation' => $this->validationType,
+            'reset' => false,
+            'force' => false
+        );
+        */
+               $this->setupRules($r);
+       }
+
+       //      }}}
+
+       //      {{{     getDelUrl()
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @param object $sec Parameter description (if any) ...
+     *
+     * @return mixed  Return description (if any) ...
+     * @access public
+     */
+       public function getDelUrl($sec)
+       {
+               return $_SERVER['REQUEST_URI'] . "&d=t&cid={$sec->elements[0]->html}";
+       }
+
+       //      }}}
+       //      {{{     getEditUrl()
+
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @param object $sec Parameter description (if any) ...
+     *
+     * @return mixed  Return description (if any) ...
+     * @access public
+     */
+       public function getEditUrl($sec)
+       {
+               return $_SERVER['REQUEST_URI'] . "&cid={$sec->elements[0]->html}";
+       }
+
+       //      }}}
+       //      {{{     getMailIcon()
+
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @param object $sec Parameter description (if any) ...
+     *
+     * @return string Return description (if any) ...
+     * @access public
+     */
+       public function getMailIcon($sec)
+       {
+               if ($sec->elements[5]->html == 'No Mail') {
+                       $icon = 'delete';
+                       $title = 'Does not receive newsletters';
+               } else {
+                       $icon = 'add';
+                       $title = 'Receives newsletters';
+               }
+               return '<img width="16" height="16" title="'.$title.'"
+                          class="mail" alt="mail"
+                          src="'. MEDIA_APP_BASE_URL . "assets/icons/email_$icon.png" . '">';
+       }
+
+       //      }}}
+       //      {{{ getMemberContacts()
+
+       /**
+        * Get all the contacts data uploaded to a member into an array
+        *
+     * @return array member contacts
+        * @access protected
+        */
+       protected function getMemberContacts()
+       {
+        $c = array();
+               try {
+                       $sql = "
+                SELECT *
+                  FROM {$this->tableName}
+                 WHERE member_id = :id
+                                ORDER BY id";
+                       $stmt = $this->dbh->prepare($sql);
+                       $stmt->bindParam(':id', $_GET['id'], PDO::PARAM_INT);
+                       $stmt->execute();
+
+                       while ($row = $stmt->fetch()) {
+                               $c[] = $row;
+                       }
+                       return $c;
+               } catch (PDOException $e) {
+                       return Toolkit_Common::handleError($e);
+               }
+       }
+
+       //      }}}
+
+       //      {{{ insertData()
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @param array $values Parameter description (if any) ...
+     *
+     * @return object    Return description (if any) ...
+     * @access public
+     */
+       public function insertData($values)
+       {
+               unset(
+                       $values['id'],
+                       $values['target'],
+                       $values['submit']
+               );
+               $values['member_id'] = $_GET['id'];
+               try {
+                       $params = implode(', ', array_keys($values));
+                       $bindParams = ':' . implode(', :', array_keys($values));
+                       $sql = "
+                               INSERT INTO {$this->tableName} ($params)
+                               VALUES ($bindParams)";
+                       $stmt = $this->dbh->prepare($sql);
+                       foreach ($values as $k => $v) {
+                               $metaData = $this->tableMetaData[$k];
+                               if ($metaData == 'integer') {
+                                       $dataType = PDO::PARAM_INT;
+                               } else if ($metaData == 'boolean') {
+                                       $dataType = PDO::PARAM_BOOL;
+                               } elseif ($metaData == 'double precision') {
+                                       $dataType = null;
+                               } else {
+                                       $dataType = PDO::PARAM_STR;
+                               }
+                               $stmt->bindParam(":$k", $values[$k], $dataType);
+                       }
+                       return $stmt->execute();
+               } catch (PDOException $e) {
+                       return Toolkit_Common::handleError($e);
+               }
+       }
+
+       //      }}}
+       //      {{{ isForm()
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @param unknown $sec Parameter description (if any) ...
+     *
+     * @return unknown Return description (if any) ...
+     * @access public
+     */
+       public function isForm($sec)
+       {
+               return !$sec;
+       }
+
+       //      }}}
+       //      {{{     isName()
+
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @param unknown $name Parameter description (if any) ...
+     *
+     * @return mixed   Return description (if any) ...
+     * @access public
+     */
+       public function isName($name)
+       {
+               return (substr($name, 0, 4) == 'name');
+       }
+
+       //      }}}
+       //      {{{     isPhone()
+
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @param unknown $name Parameter description (if any) ...
+     *
+     * @return mixed   Return description (if any) ...
+     * @access public
+     */
+       public function isPhone($name)
+       {
+               return (substr($name, 0, 5) == 'phone');
+       }
+
+       //      }}}
+       //      {{{     isEmail()
+
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @param unknown $name Parameter description (if any) ...
+     *
+     * @return mixed   Return description (if any) ...
+     * @access public
+     */
+       public function isEmail($name)
+       {
+               return (substr($name, 0, 5) == 'email');
+       }
+
+       //      }}}
+       //      {{{     isTitle()
+
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @param unknown $name Parameter description (if any) ...
+     *
+     * @return mixed   Return description (if any) ...
+     * @access public
+     */
+       public function isTitle($name)
+       {
+               return (substr($name, 0, 5) == 'title');
+       }
+
+       //      }}}
+
+       //      {{{ processData()
+
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @param array $values Parameter description (if any) ...
+     *
+     * @return void
+     * @access public
+     */
+       public function processData($values)
+       {
+               $this->tableMetaData = Toolkit_Common::getTableMetaData(
+            $this->dbh,
+            $this->tableName
+        );
+               if (empty($values['id'])) {
+                       $this->insertData($values);
+               } else {
+                       $this->updateData($values);
+               }
+               $listPage = MEDIA_BASE_URL .
+                       "admin/members.php?page=editMember&module=addMember&tab=contacts&id={$_GET['id']}";
+               header("Location: $listPage");
+       }
+
+       //      }}}
+
+       //      {{{ removeContact()
+
+    /**
+     * Remove a contact from a member record
+     *
+     * @param integer $cid contact id
+     * @param integer $mid member id
+     *
+     * @return void
+     * @access public
+     */
+       public function removeContact($cid, $mid)
+       {
+               try {
+                       $sql = "
+                DELETE FROM {$this->tableName}
+                 WHERE id = :cid
+                   AND member_id = :mid";
+                       $stmt = $this->dbh->prepare($sql);
+                       $stmt->bindParam(':cid', $cid, PDO::PARAM_INT);
+                       $stmt->bindParam(':mid', $mid, PDO::PARAM_INT);
+                       $stmt->execute();
+               } catch (PDOException $e) {
+                       return Toolkit_Common::handleError($e);
+               }
+       }
+
+       //      }}}
+
+       //      {{{ show()
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @return void
+     * @access public
+     */
+       public function show()
+       {
+               if (isset($_GET['d'])) {
+                       $this->removeContact($_GET['cid']);
+                       $target = MEDIA_BASE_URL . 'admin/members.php?page=editMember&module=addMember&tab=contacts&id=';
+                       $target .= $_GET['id'];
+                       header("Location: $target");
+               }
+               Toolkit_Common::show();
+       }
+
+       //      }}}
+
+       //      {{{ updateData()
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @param array $values Parameter description (if any) ...
+     *
+     * @return boolean   Return description (if any) ...
+     * @access public
+     */
+       public function updateData($values)
+       {
+               if (array_key_exists('delete', $values)) {
+                       $this->deleteFile($values);
+               }
+
+               unset(
+                       $values['target'],
+                       $values['submit']
+               );
+
+               try {
+                       $params = array_keys($values);
+                       $length = count($params);
+                       for ($i = 0; $i < $length; ++$i) {
+                               $bindParams .= "{$params[$i]} = :{$params[$i]}";
+                if ($i < ($length - 1)) {
+                    $bindParams .= ', ';
+                }
+                       }
+                       $sql = "
+                               UPDATE {$this->tableName}
+                                  SET $bindParams
+                                WHERE id = :id";
+                       $stmt = $this->dbh->prepare($sql);
+                       $stmt->bindParam(':id', $pid, PDO::PARAM_INT);
+                       foreach ($values as $k => $v) {
+                               $metaData = $this->tableMetaData[$k];
+                               if ($metaData == 'integer') {
+                                       $dataType = PDO::PARAM_INT;
+                               } else if ($metaData == 'boolean') {
+                                       $dataType = PDO::PARAM_BOOL;
+                               } else {
+                                       $dataType = PDO::PARAM_STR;
+                               }
+                               $stmt->bindParam(":$k", $values[$k], $dataType);
+                       }
+                       $stmt->execute();
+               } catch (PDOException $e) {
+                       Toolkit_Common::handleError($e);
+                       return false;
+               }
+               return true;
+       }
+
+       //      }}}
+}
+?>
diff --git a/Toolkit/Members/EditMemberFile.php b/Toolkit/Members/EditMemberFile.php
new file mode 100644 (file)
index 0000000..5bb1f36
--- /dev/null
@@ -0,0 +1,558 @@
+<?php
+
+/**
+ * File Doc Comment
+ *
+ * PHP version 5
+ *
+ * @category MembersDB
+ * @package  Toolkit_Members
+ * @author   Steve Sutton <steve@gaslightmedia.com>
+ * @author   Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @license  http://www.gaslightmedia.com Gaslightmedia
+ * @version  CVS: $Id: EditMemberFile.php,v 1.16 2010/07/16 20:52:15 jamie Exp $
+ * @link     http://demo.gaslightmedia.com
+ */
+
+/**
+ * Controls for uploading / editing files for a business record
+ *
+ * Controls all aspects of creating and rendering the form used to manipulate
+ * the business files. Form is not rendered until the user is added into the Database.
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @author    Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2008 Gaslight Media
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @link      http://demo.gaslightmedia.com
+ */
+class Toolkit_Members_EditMemberFile
+    extends Toolkit_Members_EditMemberInfo implements Toolkit_Form
+{
+
+    /**
+     * The table name in the database used to store the data of the files
+     *
+     * @var string
+     * @access public
+     */
+    public $tableName = 'member_files';
+
+    /**
+     * The template used to render the form
+     *
+     * @var string
+     * @access protected
+     */
+    protected $formTemplate = 'editFile.tpl';
+
+    /**
+     * Message to return if the form successfully submits
+     *
+     * @var string
+     * @access protected
+     */
+    protected $successMsg = '
+        <div id="form-success-top">
+            You successfully updated your file.
+        </div>';
+
+    /**
+     * Class constructor
+     *
+     * @param PDO    $pdo         PHP Data Object to use for DB calls
+     * @param string $formName    Form's name.
+     * @param string $method      (optional)Form's method defaults to 'POST'
+     * @param string $action      (optional)Form's action
+     * @param string $target      (optional)Form's target defaults to '_self'
+     * @param mixed  $attributes  (optional)Extra attributes for <form> tag
+     * @param bool   $trackSubmit (optional)Whether to track if the form was
+     *                            submitted by adding a special hidden field
+     *
+     * @access public
+     * @see    Toolkit_Members_EditMemberInfo
+     */
+    public function __construct(
+        PDO $pdo,
+        $formName,
+        $method = 'post',
+        $action = '',
+        $target = '',
+        $attributes = null,
+        $trackSubmit = false
+    ) {
+        parent::__construct(
+            $pdo,
+            $formName,
+            $method,
+            $action,
+            $target,
+            $attributes,
+            $trackSubmit
+        );
+    }
+
+    /**
+     * Sets the defaults for elements in the form.
+     *
+     * @return void
+     * @access public
+     */
+    public function configureDefaults()
+    {
+        try {
+            $sql = "
+              SELECT *
+                FROM {$this->tableName}
+               WHERE member_id = :id
+               ORDER BY id";
+            $stmt = $this->dbh->prepare($sql);
+            $stmt->bindParam(':id', $_GET['id'], PDO::PARAM_INT);
+            $stmt->execute();
+
+            $i = 0;
+            $imgTag = '<img src="'.MEDIA_BASE_URL.'images/file-ext/pdf.png"
+                alt="pdf Image" style="display: inline;"> ';
+            while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                $defaults["file$i"] = $row['id'];
+                $defaults["file_name$i"] = (empty($row['file_name'])) ?
+                    $row['original_name'] : $row['file_name'];
+
+                $oname = (empty($row['file_name'])) ?
+                    $row['original_name'] : $row['file_name'];
+                $defaults["original_name$i"] = '<div class="thumb">' .
+                        $imgTag . $oname . '</div>';
+                ++$i;
+            }
+            $this->setupDefaults($defaults);
+        } catch (PDOException $e) {
+            return Toolkit_Common::handleError($e);
+        }
+    }
+
+    /**
+     * Setup the elements to use on the form.
+     *
+     * @return void
+     * @access public
+     */
+    public function configureElements()
+    {
+        $e = array();
+
+        $files = $this->getMemberFiles();
+        //  All Grouped Elements are created here.
+
+        //  All Elements are created here.  This includes group element definitions.
+        $e[] = array(
+            'type' => 'header',
+            'req' => false,
+            'name' => 'fileHdr',
+            'display' => 'Files'
+        );
+        if (isset($_POST['uploaded_file_rmv'])) {
+            $e[] = array(
+                'type' => 'hidden',
+                'req' => false,
+                'name' => 'uploaded_file_rmv'
+            );
+        }
+        $e[] = array(
+            'type' => 'text',
+            'req' => false,
+            'name' => 'file_name',
+            'display' => 'File Name'
+        );
+        $e[] = array(
+            'type' => 'file',
+            'req' => false,
+            'name' => 'file_rmv',
+            'display' => 'Upload a File',
+            'opts' => array('class' => 'submit')
+        );
+        $e[] = array(
+            'type' => 'submit',
+            'req' => false,
+            'name' => 'submitNewFile',
+            'display' => 'Upload new file',
+            'opts' => array('class' => 'submit')
+        );
+
+        if (is_array($files)) {
+            foreach ($files as $k => $v) {
+                $e[] = array(
+                    'type' => 'header',
+                    'req' => false,
+                    'name' => "fileHdr$k"
+                );
+                $e[] = array(
+                    'type' => 'hidden',
+                    'req' => false,
+                    'name' => "file$k"
+                );
+                $e[] = array(
+                    'type' => 'static',
+                    'req' => false,
+                    'name' => "original_name$k"
+                );
+                $e[] = array(
+                    'type' => 'text',
+                    'req' => false,
+                    'name' => "file_name$k",
+                    'display' => 'File Name',
+                    'opts' => array('class' => 'text')
+                );
+                $e[] = array(
+                    'type' => 'submit',
+                    'req' => false,
+                    'name' => 'update',
+                    'display' => 'Update File Name',
+                    'opts' => array('class' => 'submit')
+                );
+                $e[] = array(
+                    'type' => 'submit',
+                    'req' => false,
+                    'name' => 'delete',
+                    'display' => 'Delete File',
+                    'opts' => array('class' => 'photoDelete fileDelete')
+                );
+            }
+        }
+
+        $this->setupElements($e);
+    }
+
+    /**
+     * Wrapper function to handle setting up the form
+     *
+     * @return void
+     * @access public
+     */
+    public function configureForm()
+    {
+        $this->configureElements();
+        $this->configureRules();
+        $this->configureDefaults();
+    }
+
+    //  }}}
+    //  {{{ configureRules()
+
+    /**
+     * Sets up all the rules to be used when the form is validated.
+     *
+     * @return void
+     * @access public
+     */
+    public function configureRules()
+    {
+        $mimeTypes = array(
+            'application/pdf',
+            'application/msword',
+            'application/force-download'
+        );
+
+        $r = array();
+
+        if ($_FILES['file_rmv']['error'] == UPLOAD_ERR_OK) {
+            $r[] = array(
+                'element'    => 'file_rmv',
+                'message'    => 'ERROR: Incorrect File Type (.pdf, .doc) only!',
+                'type'       => 'mimetype',
+                'format'     => $mimeTypes,
+                'validation' => $this->validationType,
+                'reset'      => false,
+                'force'      => false
+            );
+        }
+        $r[] = array(
+            'element'    => 'file_rmv',
+            'message'    => 'ERROR: Invalid File!',
+            'type'       => 'MemberFile',
+            'format'     => array($this, false),
+            'validation' => $this->validationType,
+            'reset'      => false,
+            'force'      => false
+        );
+        $this->setupRules($r);
+    }
+
+    /**
+     * Remove the file from the disk and delete from the database
+     *
+     * @param string $values Form values
+     *
+     * @return boolean
+     * @access protected
+     */
+    protected function deleteFile($values)
+    {
+        try {
+            $fid = $values["file{$values['target']}"];
+            $this->dbh->beginTransaction();
+            //  Get the file name so we know what to look for when we try to
+            //  delete the file from the disk.
+            $sql = "
+                SELECT *
+                  FROM {$this->tableName}
+                 WHERE id      = :id";
+            $stmt = $this->dbh->prepare($sql);
+            $stmt->bindParam(':id', $fid, PDO::PARAM_INT);
+            $stmt->execute();
+            $file = $stmt->fetch(PDO::FETCH_ASSOC);
+            //  Remove the file from the IS0 server
+            $fs = new Toolkit_FileServer_FileAdapter();
+            $fs->delete($file['name_on_disk']);
+
+            //  Remove the File from the updates table if one is present.
+            $sql = "
+                DELETE FROM {$this->pendingTable}
+                 WHERE db_table     = '{$this->tableName}'
+                   AND member_id    = :member_id
+                   AND foreign_key  = :id";
+            $stmt = $this->dbh->prepare($sql);
+            $stmt->bindParam(':member_id', $_GET['id'], PDO::PARAM_INT);
+            $stmt->bindParam(':id', $fid, PDO::PARAM_INT);
+            $stmt->execute();
+
+            //  Remove the file from the member.
+            $sql = "
+                DELETE FROM {$this->tableName}
+                 WHERE id = :id";
+            $stmt = $this->dbh->prepare($sql);
+            $stmt->bindParam(':id', $fid, PDO::PARAM_INT);
+            $stmt->execute();
+            return $this->dbh->commit();
+        } catch (PDOException $e) {
+            $this->dbh->rollBack();
+            return Toolkit_Common::handleError($e);
+        }
+    }
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @param array $values Parameter description (if any) ...
+     *
+     * @return integer   Return description (if any) ...
+     * @access protected
+     */
+    protected function findTargetFile($values)
+    {
+        foreach ($values as $k => $v) {
+            if (preg_match('/^file_name.+$/', $k, $matches)) {
+                return substr($k, 9);
+            }
+        }
+    }
+
+    /**
+     * Get all the file data uploaded to a member into an array
+     *
+     * @return array member files
+     * @access protected
+     * @see    Toolkit_FormBuilder::setupDefaults()
+     */
+    protected function getMemberFiles()
+    {
+        try {
+            $sql = "
+                SELECT *
+                  FROM {$this->tableName}
+                 WHERE member_id = :id
+                 ORDER BY id";
+            $stmt = $this->dbh->prepare($sql);
+            $stmt->bindParam(':id', $_GET['id'], PDO::PARAM_INT);
+            $stmt->execute();
+
+            while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                $files[] = $row;
+            }
+            return $files;
+        } catch (PDOException $e) {
+            return Toolkit_Common::handleError($e);
+        }
+    }
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @param array   $values  Parameter description (if any) ...
+     * @param boolean $pending Parameter description (if any) ...
+     *
+     * @return object    Return description (if any) ...
+     * @access public
+     */
+    public function insertData($values, $pending = false)
+    {
+        $cache = new Cache_Lite(Toolkit_Members::getCacheOptions());
+        $cache->remove("Member-{$_GET['id']}", 'Profile');
+
+        $insertData['name_on_disk']  = $values['file'];
+        $insertData['member_id']     = $_GET['id'];
+        $insertData['size']          = $values['file_rmv']['size'];
+        $insertData['original_name'] = $values['file_rmv']['name'];
+        $insertData['file_name']     = $values['file_name'];
+        $insertData['pending']       = $pending;
+        try {
+            $params = implode(', ', array_keys($insertData));
+            $bindParams = ':' . implode(', :', array_keys($insertData));
+            $sql = "
+                INSERT INTO {$this->tableName} ($params)
+                VALUES ($bindParams)";
+            $stmt = $this->dbh->prepare($sql);
+            foreach ($insertData as $k => &$v) {
+                $metaData = $this->tableMetaData[$k];
+                if ($metaData == 'integer') {
+                    $dataType = PDO::PARAM_INT;
+                } else if ($metaData == 'boolean') {
+                    $dataType = PDO::PARAM_BOOL;
+                } else {
+                    $dataType = PDO::PARAM_STR;
+                }
+                $stmt->bindParam(":$k", $v, $dataType);
+            }
+
+            return $stmt->execute();
+        } catch (PDOException $e) {
+            return Toolkit_Common::handleError($e);
+        }
+    }
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @param array $values Parameter description (if any) ...
+     *
+     * @return void
+     * @access public
+     */
+    public function processData($values)
+    {
+        if ($values['file_rmv']['name']) {
+            $fs = new Toolkit_FileServer_FileAdapter();
+            try {
+                $res = $fs->upload('file_rmv');
+            } catch (Toolkit_FileServer_Exception $e) {
+                Toolkit_Logger::logException('File Server', $e);
+                echo -1;
+                return;
+            }
+            $values['file'] = $res['name'];
+        }
+        $this->tableMetaData = Toolkit_Common::getTableMetaData(
+            $this->dbh,
+            $this->tableName
+        );
+        $values['target'] = $this->findTargetFile($values);
+
+        if (array_key_exists('submitNewFile', $values)) {
+            $this->insertData($values);
+        } else {
+            $this->updateData($values);
+        }
+        $listPage = MEDIA_BASE_URL .
+            "admin/members.php?rt=Members&ac=editMember&tab=files&id={$_REQUEST['id']}";
+        header("Location: $listPage");
+    }
+
+    /**
+     * Used in the template to determine when to show the File
+     *
+     * Since the file tag falls outside of the normal flow of the section loop
+     * we need to determine early if we should display the file or not. Since
+     * the file is the only element that uses a "static" quickform element,
+     * check to see if the first element type in a section is a static element
+     * and if so output the element (file).
+     *
+     * @param array $data The array of element objec
+     *
+     * @return boolean Whether an element is of type static or not.
+     * @access public
+     */
+    public function showFile($data)
+    {
+        return ($data->type == 'static');
+    }
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @param array $values Parameter description (if any) ...
+     *
+     * @return mixed     Return description (if any) ...
+     * @access public
+     */
+    public function updateData($values)
+    {
+        $cache = new Cache_Lite(Toolkit_Members::getCacheOptions());
+        $cache->remove("Member-{$_GET['id']}", 'Profile');
+
+        if (array_key_exists('delete', $values)) {
+            return $this->deleteFile($values);
+        }
+        $target = $values['target'];
+        foreach ($values as $k => $v) {
+            switch ($k) {
+            case "file_name$target" :
+                $values['file_name'] = $v;
+                unset($values[$k]);
+                break;
+
+            case "file$target" :
+                $pid = $v;
+                unset($values[$k]);
+                break;
+
+            default :
+                unset($values[$k]);
+                break;
+            }
+        }
+
+        try {
+            $params = array_keys($values);
+            $length = count($params);
+            for ($i = 0; $i < $length; ++$i) {
+                $bindParams .= "{$params[$i]} = :{$params[$i]}";
+                if ($i < ($length - 1)) {
+                    $bindParams .= ', ';
+                }
+            }
+            $sql = "
+                UPDATE {$this->tableName}
+                   SET $bindParams
+                 WHERE id = :id";
+            $stmt = $this->dbh->prepare($sql);
+            $stmt->bindParam(':id', $pid, PDO::PARAM_INT);
+            foreach ($values as $k => $v) {
+                $metaData = $this->tableMetaData[$k];
+                if ($metaData == 'integer') {
+                    $dataType = PDO::PARAM_INT;
+                } else if ($metaData == 'boolean') {
+                    $dataType = PDO::PARAM_BOOL;
+                } else {
+                    $dataType = PDO::PARAM_STR;
+                }
+                $stmt->bindParam(":$k", $values[$k], $dataType);
+            }
+            $stmt->execute();
+
+        } catch (PDOException $e) {
+            Toolkit_Common::handleError($e);
+            return false;
+        }
+        return true;
+    }
+
+}
diff --git a/Toolkit/Members/EditMemberInfo.php b/Toolkit/Members/EditMemberInfo.php
new file mode 100644 (file)
index 0000000..83d9af3
--- /dev/null
@@ -0,0 +1,3314 @@
+<?php
+//    vim:set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker syntax=php:
+
+/**
+ * EditMemberInfo
+ *
+ * PHP version 5
+ *
+ * @category MembersDB
+ * @package  Toolkit_Members
+ * @author   Jamie Kahgee <steve@gaslightmedia.com.com>
+ * @license     http://www.gaslightmedia.com Gaslightmedia
+ * @version  CVS: $Id: EditMemberInfo.php,v 1.52 2010/08/09 17:58:03 jamie Exp $
+ * @link     http://demo.gaslightmedia.com
+ */
+
+require_once BASE . 'Toolkit/Image/Server.php';
+
+/**
+ * Edit member info stored in the database
+ *
+ * Controls all aspects of creating and rendering the form used to manipulate
+ * the member data.  Handles adding / editing module details, including:
+ * 1. Restaurants
+ * 2. Accommodations
+ * 3. Golfing
+ *
+ * Controls the addition of categories to a member record.  Categories have
+ * dynamic settings which allow users to dynamically set which categories are
+ * associated to which modules (golf, accommodations, restaurant, etc...).
+ * Depending on which category is selected and assigned to a member record
+ * controls which modules are displayed on the form.
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2008 Gaslight Media
+ * @license      http://www.gaslightmedia.com Gaslightmedia
+ * @link      http://demo.gaslightmedia.com/admin/members.php?rt=Members&ac=editMember&tab=info
+ * @see       Toolkit_Members, member_admin
+ */
+class Toolkit_Members_EditMemberInfo
+    extends Toolkit_FormBuilder implements Toolkit_Form
+{
+    //    {{{ properties
+
+    /**
+     * The Table name used to store the data of the member record in the database.
+     *
+     * @var string
+     * @access public
+     */
+    public $tableName = 'member';
+
+    /**
+     * Array of data that holds the meta data info on the table
+     *
+     * Contains information on the type of fields in the database.
+     * That way when we run our automated SQL queries with our PDO
+     * we can properly bind data to our SQL queries.  This will
+     * allow for one more layer of protection against any sql
+     * injection attempts.
+     *
+     * @var string
+     * @access public
+     * @see    Toolkit_Common::processData()
+     */
+    public $tableMetaData;
+
+    /**
+     * Determines if member updates have to be approved if changed
+     *
+     * If set to true, all module info must be approved by admin before it
+     * will be displayed on the front end to visitors. This also includes
+     * ccard information.
+     *
+     * Since all member forms will be spawned from this class, if we put
+     * this value here we can set once / change once and affect the entire
+     * app if we need to alter this.  We can also set this up in any child
+     * class and override on individual classes if need be.
+     *
+     * @var string
+     * @access protected
+     */
+    protected $strictPending = true;
+
+    /**
+     * Some special forms dont utlize this stylesheet
+     * Allow classes to override this setting so it doesn't
+     * get included
+     *
+     * @var boolean
+     * @access protected
+     */
+    protected $includeContactStyleSheet = false;
+
+    /**
+     * The table used to store the data of the member record in the database.
+     *
+     * @var string
+     * @access public
+     */
+    public $pendingTable = 'member_updates';
+
+    /**
+     * Determines if a member is in the pending state or not
+     *
+     * When the member has rows of data in the member_updates table in the DB
+     * they are considered "pending updates" until those changes are accepted
+     * or rejected by an admin user.
+     *
+     * @var string
+     * @access public
+     */
+    public $pending = false;
+
+    /**
+     * holds all the fields that are currently pending
+     *
+     * used in conjunction with the template, all fields that are in this
+     * list, will have a pending class for the <tr> row, so they can easily
+     * be identified.
+     *
+     * @var string
+     * @access public
+     */
+    public $pendingFields = array();
+
+    //    What are the modules associated with this form.
+    //    New members won't have access to these right away.
+    //    This boolean settings will be overridden inside the class.
+
+    /**
+     * The status of accommodation related categories associated w/ the member
+     *
+     * If the member has any categories that are assigned to the accommodations
+     * module, then this setting will become true and the form will show
+     * the accommodation module.
+     *
+     * @var bool
+     * @access protected
+     */
+    protected $accommodations = false;
+
+    /**
+     * The status of restaurant related categories associated w/ the member
+     *
+     * If the member has any categories that are assigned to the restaurant
+     * module, then this setting will become true and the form will show
+     * the restaurant module.
+     *
+     * @var bool
+     * @access protected
+     */
+    protected $restaurant = false;
+
+    /**
+     * The status of golf related categories associated w/ the member
+     *
+     * If the member has any categories that are assigned to the golf
+     * module, then this setting will become true and the form will show
+     * the golf module.
+     *
+     * @var bool
+     * @access protected
+     */
+    protected $golf = false;
+
+    /**
+     * Cities array for select list
+     *
+     * @var array
+     * @access protected
+     */
+    protected $cities;
+
+    /**
+     * States array for select list
+     *
+     * @var array
+     * @access protected
+     */
+    protected $states;
+
+    /**
+     * Category record objects
+     *
+     * Used when creating the category select list.
+     *
+     * @var array
+     * @access protected
+     */
+    protected $records = array();
+
+    /**
+     * The categories the member has associated with it
+     *
+     * @var array
+     * @access protected
+     */
+    protected $memberCategories = null;
+
+    /**
+     * Primary email address to send notification a record has been updated
+     *
+     * Set this value to false and it will turn off the email
+     * update notifications.
+     *
+     * @var string
+     * @access protected
+     */
+    protected $primaryAdvisee;
+
+    /**
+     * Email address of people that might also want to be advised of updates
+     *
+     * A list of all email address that might also want to be advised
+     * updates have been made on a business record. You can add as many
+     * as you would like and they will be CC in the email.
+     *
+     * N.B. You cannot make this array bigger than 5 people or you will need to
+     * rewrite the function to handle the server spam issue.
+     * @var array
+     * @access private
+     */
+     protected $secondaryAdvisees = array();
+
+    /**
+     * Flexy options used in the renderer
+     *
+     * @var array
+     * @access protected
+     */
+    protected $flexyOptions;
+
+    /**
+     * The name of the template used to render the business info form
+     *
+     * @var string
+     * @access protected
+     */
+    protected $formTemplate = 'editMember.tpl';
+
+    /**
+     * The name of the template used to email the owner for any updates
+     *
+     * When a member makes an update to thier record, this is the template
+     * we will use to send out the email notification to the site owner.
+     *
+     * @var string
+     * @access protected
+     */
+    protected $emailTemplate = 'emailOwner.tpl';
+
+    /**
+     * Message to return if the form successfully submits
+     *
+     * @var string
+     * @access protected
+     */
+    protected $successMsg = '
+        <div id="form-success-top">
+            The information below has been successfully submitted.
+        </div>';
+
+    /**
+     * The flexy template object which holds the rendered object
+     *
+     * @var object
+     * @access protected
+     */
+    protected $template;
+
+    /**
+     * Form access from inside the template
+     *
+     * @var object
+     * @access protected
+     */
+    protected $view;
+
+    /**
+     * Extra rules we need to register so we can fully validate form elements
+     *
+     * @var array
+     * @access protected
+     */
+    protected $registeredRules = array(
+        'zip',
+        'phone',
+        array(
+            'checkEmail',
+            'callback',
+            'email',
+            'Validate'
+        ),
+        array(
+            'checkURI',
+            'callback',
+            'uri',
+            'Validate'
+        )
+    );
+
+    //    }}}
+    //    {{{ __construct()
+
+    /**
+     * Class constructor
+     *
+     * @param PDO    $pdo         PHP Data Object to use for DB calls
+     * @param string $formName    Form's name.
+     * @param string $method      (optional)Form's method defaults to 'POST'
+     * @param string $action      (optional)Form's action
+     * @param string $target      (optional)Form's target defaults to '_self'
+     * @param mixed  $attributes  (optional)Extra attributes for <form> tag
+     * @param bool   $trackSubmit (optional)Whether to track if the form was
+     *                              submitted by adding a special hidden field
+     *
+     * @access public
+     * @see    Toolkit_FormBuilder, HTML_QuickForm
+     */
+    public function __construct(
+        PDO $pdo,
+        $formName,
+        $method = 'post',
+        $action = '',
+        $target = '',
+        $attributes = null,
+        $trackSubmit = false
+    ) {
+        parent::__construct(
+            $formName,
+            $method,
+            $action,
+            $target,
+            $attributes,
+            $trackSubmit
+        );
+        //    if this value is set to false, then no emails will be sent out
+        //    upon update of a record.
+        if ($this->primaryAdvisee !== false) {
+            if (!defined('MEMBER_RECORD_UPDATES_ADVISOR')) {
+                //    Please see property definition above for explanation.
+                echo 'in file: [' . __FILE__ . ' ] on line: [' . __LINE__ . ']<br>';
+                echo '
+                <p>
+                    Please define the
+                    <strong>MEMBER_RECORD_UPDATES_ADVISOR</strong> constant
+                    in the setup file first.<br><br> This Constant should hold
+                    the email address of the person you would like notified
+                    when a member submits an update.<br>Set to false if you do
+                    not want an email to be sent.
+                </p>';
+                die;
+            }
+            $this->primaryAdvisee = MEMBER_RECORD_UPDATES_ADVISOR;
+        }
+        //    If you want to add secondary advisors that will also receive
+        //    an email when a record is updated, update the property here.
+        //    If you use associative key values then they will be appended
+        //    to the email string. i.e.
+        //    Jamie Kahgee <jamie.kahgee+secondaryAdvisorTest1@gmail.com>
+        //    Other wise the address will just be added.
+        $this->secondaryAdvisees = array(
+            //'Jamie Kahgee' => 'jamie.kahgee+secondaryAdvisorTest1@gmail.com',
+            //'Jodie Bissonette' => 'jodie@gaslightmedia.com',
+        );
+
+        $this->dbh = $pdo;
+
+        $this->flexyOptions = Toolkit_Members::getFlexyOptions();
+    }
+
+    //    }}}
+
+    // {{{ addInfoElement()
+
+    /**
+     * Determines if the element is supposed to display additional info
+     *
+     * @param string $element name of element
+     *
+     * @return boolean True if supposed to display additional info.
+     *                   false otherwise
+     * @access public
+     */
+    function addInfoElement($element)
+    {
+        switch ($element) {
+        case 'street' :
+        case 'member_contact_email' :
+            return true;
+            break;
+
+        default :
+            return false;
+            break;
+        }
+    }
+
+    //    }}}
+
+    /**
+     * Description of addMemberToStreamSend()
+     *
+     * @param integer $mid Member ID
+     *
+     * @return void
+     * @access public
+     */
+    public function addMemberToStreamSend($mid)
+    {
+        if (   defined('MEMBER_STREAMSEND_API')
+            && MEMBER_STREAMSEND_API) {
+            // send member to streamsend
+            $memberStreamSend = new Toolkit_Members_StreamSend($this->dbh);
+            $memberStreamSend->sendMemberById($mid);
+        }
+    }
+
+    //    {{{ checkMemberName()
+
+    /**
+     * Checks if the member name already exists in the database
+     *
+     * @param array $data The name of the member to check for.
+     *
+     * @return bool False on SQL Query error, otherwise true.
+     * @access    protected
+     */
+    public function checkMemberName($data)
+    {
+        try {
+            //    If we're editing a member, they
+            //    can save that member as its
+            //    own name. so don't include that
+            //    member in the check.
+            if (is_numeric($_GET['id'])) {
+                $and = "AND member_id <> :id";
+            }
+            $sql = "
+                SELECT count(*) AS total
+                  FROM member
+                 WHERE member_name = :name
+                  $and";
+            $stmt = $this->dbh->prepare($sql);
+            $stmt->bindParam(':name', $data, PDO::PARAM_STR);
+            if (is_numeric($_GET['id'])) {
+                $stmt->bindParam(':id', $_GET['id'], PDO::PARAM_STR);
+            }
+            $stmt->execute();
+            $stmt->bindColumn('total', $valid);
+            $stmt->fetch();
+
+            return !(bool) $valid;
+        } catch (PDOException $e) {
+            return Toolkit_Common::handleError($e);
+        }
+    }
+
+    //    }}}
+    //    {{{ checkUName()
+
+    /**
+     * Checks if the login name already exists in the database
+     *
+     * @param array $data The name of the member to check for.
+     *
+     * @return boolean False on SQL Query error, otherwise true.
+     * @access    protected
+     */
+    public function checkUName($data)
+    {
+        try {
+            //    If we're editing a member, they
+            //    can save that member as its
+            //    own name. so don't include that
+            //    member in the check.
+            if (is_numeric($_GET['id'])) {
+                $and = "AND member_id <> :id";
+            }
+            $sql = "
+                SELECT count(*) AS total
+                  FROM {$this->tableName}
+                 WHERE member_login = :name
+                  $and";
+            $stmt = $this->dbh->prepare($sql);
+            $stmt->bindParam(':name', $data, PDO::PARAM_STR);
+            if (is_numeric($_GET['id'])) {
+                $stmt->bindParam(':id', $_GET['id'], PDO::PARAM_STR);
+            }
+            $stmt->execute();
+            $stmt->bindColumn('total', $valid);
+            $stmt->fetch();
+
+            return !(bool) $valid;
+        } catch (PDOException $e) {
+            return Toolkit_Common::handleError($e);
+        }
+    }
+
+    //    }}}
+    //    {{{    checkDate()
+
+    /**
+     * Validate date input
+     *
+     * allows for empty dates to be valid
+     *
+     * @param array $date date group from form
+     *
+     * @return boolean true if valid, false if not
+     * @access public
+     */
+    public function checkDate($date)
+    {
+        $validate = false;
+        foreach ($date as $i) {
+            if (is_numeric($i)) {
+                $validate = true;
+                break;
+            }
+        }
+
+        if ($validate) {
+            //    at least one date list had a number in it.
+            $d = implode('-', $date);
+            return Validate::date($d, array('format' => '%n-%j-%Y'));
+        } else {
+            //    no date numbers were selected
+            return true;
+        }
+    }
+
+    //    }}}
+    //    {{{    checkLatitude()
+
+    /**
+     * Validate latitude input
+     *
+     * @param float $lat Latitude from form
+     *
+     * @return boolean true if valid, false if not
+     * @access public
+     */
+    public function checkLatitude($lat)
+    {
+        $newLat = (float) $lat;
+        return ($newLat >= -90.0 && $newLat <= 90.0);
+    }
+
+    //    }}}
+    //    {{{    checkLongitude()
+
+    /**
+     * Validate longitude input
+     *
+     * @param float $lng Longitude from form
+     *
+     * @return boolean true if valid, false if not
+     * @access public
+     */
+    public function checkLongitude($lng)
+    {
+        $newLng = (float) $lng;
+        return ($newLng >= -180.0 && $newLng <= 180.0);
+    }
+
+    //    }}}
+    //    {{{ clearModule()
+
+    /**
+     * Removes a module from a member
+     *
+     * When updating a member this function is called for every module.
+     * Then if the member still has that module category assigned to them
+     * the data will be re-inserted back into the db.  Otherwise it will
+     * removed and not associated with that member anymore.
+     *
+     * @param string $tableName    The name of the table / module to clear
+     *
+     * @return Boolean False on SQL Query error, otherwise true.
+     * @access    protected
+     */
+    protected function clearModule($tableName)
+    {
+        try {
+            $sql = "
+                DELETE FROM $tableName
+                 WHERE member_id = :member_id";
+            $stmt = $this->dbh->prepare($sql);
+            $stmt->bindParam(':member_id', $_GET['id'], PDO::PARAM_INT);
+            return $stmt->execute();
+        } catch (PDOException $e) {
+            return Toolkit_Common::handleError($e);
+        }
+    }
+
+    //    }}}
+    //    {{{ createCategories()
+
+    /**
+     * Creates an array from the array of category tree objects
+     *
+     * @param array   $tree  linear tree array of member categories
+     * @param integer $depth what level we are on
+     *
+     * @return array The array list of categories that
+     *                 that can be loaded into a select element
+     * @access protected
+     */
+    protected function createCategories($tree, $depth = 0)
+    {
+        $this->records[] = $tree->category;
+        if ($depth == 0) {
+            $this->categories[$tree->catid] = "<span>{$tree->category}</span>";
+        } else {
+            $this->categories[$tree->catid] = $tree->category;
+        }
+        if (empty($tree->children)) {
+            return;
+        } else {
+            ++$depth;
+            foreach ($tree->children as $miniTrees) {
+                $this->createCategories($miniTrees, $depth);
+            }
+        }
+    }
+
+    //    }}}
+    //    {{{ configureConstants()
+
+    /**
+     * Sets the constants for the form
+     *
+     * The member category select list must always default
+     * to the -- Select Category -- option
+     *
+     * @return void
+     * @access public
+     */
+    public function configureConstants()
+    {
+        $c = array();
+
+        //    set the remove_logo_rmv element to 0 so
+        //    if a logo is uploaded and then the remove
+        //    checkbox is checked and the form is submitted
+        //    it won't retain its checked status.
+        $c = array(
+            'member_cats[]' => '',
+            'remove_logo_rmv' => 0,
+        );
+
+        $this->setupConstants($c);
+    }
+
+    //    }}}
+    //    {{{ configureDefaults()
+
+    /**
+     * Sets the defaults for the an existing member
+     *
+     * Populates data for the main member form.  Also grabs
+     * data to populate the modules on the form if needed.
+     *
+     * @return array $defaults Returns the array of defaults
+     *                           so children who call this function
+     *                           can obtain a copy of these values.
+     * @access public
+     */
+    public function configureDefaults()
+    {
+        if (ctype_digit($_GET['id'])) {
+            try {
+                $member_id = $_GET['id'];
+                //    Get the data for the main form items
+                $sql = "
+                    SELECT *
+                      FROM {$this->tableName}
+                     WHERE member_id = :member_id";
+                $stmt = $this->dbh->prepare($sql);
+                $stmt->bindParam(':member_id', $member_id, PDO::PARAM_INT);
+                $stmt->execute();
+                while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                    foreach ($row as $k => $v) {
+                        if ($k == 'logo') {
+                            $defaults['old_logo_rmv'] = $v;
+                            $k = 'image_rmv';
+                            $v = '<img src="'.MEMBER_PHOTOS.$v.'"
+                                        alt="'.$v.'">';
+                        }
+                        $defaults[$k] = $v;
+                    }
+                }
+
+                //    Set the defaults for the credit cards.
+                $sql = "
+                    SELECT cct.*, mcct.*
+                      FROM ccard_type cct, member_ccard_type mcct
+                     WHERE cct.ccard_type_id = mcct.ccard_type_id
+                       AND mcct.member_id    = :member_id";
+                $stmt = $this->dbh->prepare($sql);
+                $stmt->bindParam(':member_id', $member_id, PDO::PARAM_INT);
+                $stmt->execute();
+                while ($member = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                    $defaults["creditCards[{$member['ccard_type_name']}]"] = true;
+                }
+
+                $moduleTables = array(
+                    'member_accommodations',
+                    'member_golf',
+                    'member_restaurants',
+                );
+                foreach ($moduleTables as $table) {
+                    $sql = "
+                        SELECT *
+                          FROM $table
+                         WHERE member_id = :member_id";
+                    $stmt = $this->dbh->prepare($sql);
+                    $stmt->bindParam(':member_id', $member_id, PDO::PARAM_INT);
+                    $stmt->execute();
+                    while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                        foreach ($row as $k => $v) {
+                            $defaults[$k] = $v;
+                        }
+                    }
+                }
+            } catch (PDOException $e) {
+                Toolkit_Common::handleError($e);
+            }
+        } else {
+            //  get member type
+            $defaultState = $this->config
+                ->getItem('section', 'conf')
+                ->getItem('directive', 'defaultState')
+                ->getContent();
+            $defaults = array(
+                'member_cats[]'    => '',
+                'state_id'        => $defaultState,
+                'country'        => '',
+            );
+        }
+
+        $this->setupDefaults($defaults);
+        return $defaults;
+    }
+
+    //    }}}
+    //    {{{ configureElements()
+
+    /**
+     * Setup the elements to use on the form.
+     *
+     * Categories are populated into the protected class property $categories.
+     * These categories are used to populate the select list of categories.
+     * Modules are configured before any elements are setup, that way we will
+     * know if we need to include them in the rendering of the form.
+     *
+     * @return void
+     * @access public
+     */
+    public function configureElements()
+    {
+        $e = array();
+
+        //  get member type
+        $singularType = $this->config
+            ->getItem('section', 'listing type')
+            ->getItem('directive', 'singular')
+            ->getContent();
+        $pluralType = $this->config
+            ->getItem('section', 'listing type')
+            ->getItem('directive', 'plural')
+            ->getContent();
+        $useCtrlCities = $this->config
+            ->getItem('section', 'conf')
+            ->getItem('directive', 'controlledCities')
+            ->getContent();
+        $allowRegions = $this->config
+            ->getItem('section', 'conf')
+            ->getItem('directive', 'regions')
+            ->getContent();
+        $dateStartYear = $this->config
+            ->getItem('section', 'conf')
+            ->getItem('directive', 'dateStartYear')
+            ->getContent();
+        $usesGlmReservations = $this->config
+            ->getItem('section', 'conf')
+            ->getItem('directive', 'glmReservations')
+            ->getContent();
+        $nonMembers = $this->config
+            ->getItem('section', 'conf')
+            ->getItem('directive', 'nonMembers')
+            ->getContent();
+
+        $this->setupFormCategories();
+        $this->configureModules();
+
+        $this->getCategories();
+
+        //    All Grouped Elements are created here.
+        //    All Elements are created here.  This includes group element definitions.
+
+        //  {{{ Member Information
+
+        $e[] = array(
+            'type'    => 'header',
+            'req'     => false,
+            'name'    => 'memberInfoHdr',
+            'display' => "$singularType Information",
+            'col1'    => true
+        );
+        $e[] = array(
+            'type'    => 'advcheckbox',
+            'req'     => true,
+            'name'    => 'active',
+            'display' => 'Status',
+            'opts'    => 'Active',
+            'val'     => array(0, 1),
+        );
+        if ($nonMembers) {
+            $e[] = array(
+                'type'    => 'advcheckbox',
+                'req'     => true,
+                'name'    => 'non_member',
+                'display' => 'Member?',
+                'opts'    => 'Non Member',
+                'val'     => array(0, 1),
+            );
+        }
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => true,
+            'name'    => 'member_name',
+            'display' => "$singularType Name",
+            'opts'    => array('class' => 'text')
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => 'primary_contact_fname',
+            'display' => 'Primary Contact First Name',
+            'opts'    => array('class' => 'text')
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => 'primary_contact_lname',
+            'display' => 'Primary Contact Last Name',
+            'opts'    => array('class' => 'text')
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => 'process_email',
+            'display' => 'Primary Contact Email',
+            'opts'    => array('class' => 'text')
+        );
+        $e[] = array(
+            'type'    => 'date',
+            'req'     => false,
+            'name'    => 'join_date',
+            'display' => "$singularType Since",
+            'opts'    => array(
+                'format'           => 'm / d / Y',
+                'minYear'          => $dateStartYear,
+                'maxYear'          => date('Y'),
+                'addEmptyOption'   => true,
+                'emptyOptionValue' => '',
+                'emptyOptionText'  => array(
+                    'm' => 'mm',
+                    'd' => 'dd',
+                    'Y' => 'yyyy',
+                )
+            )
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => 'phone',
+            'display' => 'Primary Phone',
+            'opts'    => array('class' => 'text')
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => 'toll_free',
+            'display' => 'Phone 2',
+            'opts'    => array('class' => 'text')
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => 'fax',
+            'display' => 'Fax Number',
+            'opts'    => array('class' => 'text')
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => 'member_contact_email',
+            'display' => 'Email on Website',
+            'opts'    => array('class' => 'text')
+        );
+        /*
+        $e[] = array(
+            'type'    => 'select',
+            'req'     => false,
+            'name'    => 'country',
+            'display' => 'Country',
+            'opts'    => array(
+                ''       => '-- Select Country --',
+                'USA'    => 'USA',
+                'Canada' => 'Canada'
+            )
+        );
+        */
+        $e[] = array(
+            'type'        => 'text',
+            'req'         => false,
+            'name'        => 'url',
+            'display'     => 'Website Address',
+            'opts'        => array('class' => 'text'),
+            'noCharLimit' => true
+        );
+
+        //  }}}
+        //    {{{    Member Categories
+
+        $e[] = array(
+            'type'    => 'header',
+            'req'     => false,
+            'name'    => 'memberCategoriesHdr',
+            'display' => "$singularType Categories",
+            'col1'    => true
+        );
+        $e[] = array(
+            'type'    => 'select3',
+            'req'     => false,
+            'name'    => 'member_cats[]',
+            'opts'    => $this->categories,
+            'att'     => array('id' => 'categories')
+        );
+
+        //    }}}
+        //  {{{ Physical Address
+
+        $e[] = array(
+            'type'    => 'header',
+            'req'     => false,
+            'name'    => 'physicalAddressHdr',
+            'display' => 'Street Address',
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => 'street',
+            'display' => 'Street Address',
+            'opts'    => array('class' => 'text')
+        );
+        if ($useCtrlCities) {
+            $e[] = array(
+                'type'    => 'select',
+                'req'     => true,
+                'name'    => 'city_id',
+                'display' => 'City',
+                'opts'    => array('' => '-- Select --') + $this->cities
+            );
+        } else {
+            $e[] = array(
+                'type'    => 'text',
+                'req'     => true,
+                'name'    => 'city',
+                'display' => 'City',
+                'opts'    => array('class' => 'text')
+            );
+        }
+        $e[] = array(
+            'type'    => 'select',
+            'req'     => true,
+            'name'    => 'state_id',
+            'display' => 'State / Province',
+            'opts'    => array('' => '-- Select --') + $this->states
+        );
+        if ($allowRegions) {
+            $e[] = array(
+                'type'    => 'select',
+                'req'     => false,
+                'name'    => 'region',
+                'display' => 'Region',
+                'opts'    => $this->getRegions()
+            );
+        }
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => true,
+            'name'    => 'zip',
+            'display' => 'Zip / Postal Code',
+            'opts'    => array('class' => 'text')
+        );
+        $e[] = array(
+            'type' => 'text',
+            'req' => false,
+            'name' => 'lat',
+            'display' => 'Latitude',
+            'opts' => array('class' => 'text')
+        );
+        $e[] = array(
+            'type' => 'text',
+            'req' => false,
+            'name' => 'lon',
+            'display' => 'Longitude',
+            'opts' => array('class' => 'text')
+        );
+
+        //  }}}
+        //    {{{ Mailing Address
+
+        $e[] = array(
+            'type'    => 'header',
+            'req'     => false,
+            'name'    => 'MailingAddressHdrRmv',
+            'display' => 'Mailing Address (if different than physical address)',
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => 'mailing_address',
+            'display' => 'Mailing Address',
+            'opts'    => array('class' => 'text')
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => 'mailing_city',
+            'display' => 'City',
+            'opts'    => array('class' => 'text')
+        );
+        $e[] = array(
+            'type'    => 'select',
+            'req'     => false,
+            'name'    => 'mailing_state_id',
+            'display' => 'State / Province',
+            'opts'    => array('' => '-- Select --') + $this->states
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => 'mailing_zip',
+            'display' => 'Zip / Postal Code',
+            'opts'    => array('class' => 'text')
+        );
+
+        //  }}}
+        //    {{{ Member Description
+
+        $e[] = array(
+            'type'    => 'header',
+            'req'     => false,
+            'name'    => 'memberDescHdr',
+            'display' => "$singularType Description",
+            'col1'    => true
+        );
+        $e[] = array(
+            'type'    => 'textarea',
+            'req'     => false,
+            'name'    => 'description',
+            'display' => null,
+            'opts'    => array(
+                'id' => 'description',
+                'rows' => 8,
+                'cols' => 43
+            ),
+            'noCharLimit' => true
+        );
+
+        //  }}}
+        //    {{{ Account Info
+
+        $e[] = array(
+            'type'    => 'header',
+            'req'     => false,
+            'name'    => 'accountInfoHdr',
+            'display' => 'Account Info',
+            'col2'    => true
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => 'member_login',
+            'display' => 'Username',
+            'opts'    => array('class' => 'text')
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => 'member_passwd',
+            'display' => 'Password',
+            'opts'    => array('class' => 'text')
+        );
+
+        //  }}}
+        //    {{{ Main Image
+
+        $e[] = array(
+            'type'    => 'header',
+            'req'     => false,
+            'name'    => 'logoHdr',
+            'display' => 'Main Image',
+            'col2'    => true
+        );
+        if ($this->hasLogo()) {
+            $e[] = array(
+                'type'    => 'checkbox',
+                'req'     => false,
+                'name'    => 'remove_logo_rmv',
+                'display' => 'Remove Image'
+            );
+            $e[] = array(
+                'type'    => 'static',
+                'req'     => false,
+                'name'    => 'image_rmv',
+                'display' => 'Current Image'
+            );
+            $e[] = array(
+                'type' => 'hidden',
+                'req'  => false,
+                'name' => 'old_logo_rmv'
+            );
+        }
+
+        $e[] = array(
+            'type'    => 'file',
+            'req'     => false,
+            'name'    => 'new_logo_rmv',
+            'display' => 'New Image',
+            'opts'    => array('class' => 'file')
+        );
+
+        //  }}}
+        //    {{{ Payment Type Accepted
+
+        $e[] = array(
+            'type'    => 'header',
+            'req'     => false,
+            'name'    => 'CreditCardsHdr',
+            'display' => 'Payment Type Accepted',
+            'col2'    => true
+        );
+        $e[] = array(
+            'type'    => 'checkbox',
+            'req'     => false,
+            'name'    => 'creditCards[American Express]',
+            'display' => 'American Express'
+        );
+        $e[] = array(
+            'type'    => 'checkbox',
+            'req'     => false,
+            'name'    => 'creditCards[Discover]',
+            'display' => 'Discover'
+        );
+        $e[] = array(
+            'type'    => 'checkbox',
+            'req'     => false,
+            'name'    => 'creditCards[Master Card]',
+            'display' => 'Master Card'
+        );
+        $e[] = array(
+            'type'    => 'checkbox',
+            'req'     => false,
+            'name'    => 'creditCards[Visa]',
+            'display' => 'Visa'
+        );
+        $e[] = array(
+            'type'    => 'checkbox',
+            'req'     => false,
+            'name'    => 'creditCards[Diners]',
+            'display' => 'Diners'
+        );
+
+        //  }}}
+        //    {{{ Accommodations Information
+
+        if ($this->accommodations) {
+            $e[] = array(
+                'type'    => 'header',
+                'req'     => false,
+                'name'    => 'AccommodationsHdr',
+                'display' => 'Accommodations Information',
+                'col2'    => true
+            );
+            $e[] = array(
+                'type'    => 'text',
+                'req'     => false,
+                'name'    => 'reservation_url',
+                'display' => 'Reservation Url',
+                'opts'    => array('class' => 'text')
+            );
+            if ($usesGlmReservations) {
+                $e[] = array(
+                    'type'    => 'text',
+                    'req'     => false,
+                    'name'    => 'reservation_id',
+                    'display' => 'Reservation Id',
+                    'opts'    => array('class' => 'text')
+                );
+            }
+            $e[] = array(
+                'type'    => 'text',
+                'req'     => false,
+                'name'    => 'num_rooms',
+                'display' => 'Number of Rooms',
+                'opts'    => array('class' => 'text')
+            );
+            $e[] = array(
+                'type'    => 'advcheckbox',
+                'req'     => false,
+                'name'    => 'year_round',
+                'display' => 'Open all Year',
+                'val'     => array(0, 1)
+            );
+        }
+
+        //  }}}
+        //    {{{ Restaurant Information
+
+        if ($this->restaurant) {
+            $e[] = array(
+                'type'    => 'header',
+                'req'     => false,
+                'name'    => 'RestaurantHdr',
+                'display' => 'Restaurant Information',
+                'col2'    => true
+            );
+            $e[] = array(
+                'type'    => 'advcheckbox',
+                'req'     => false,
+                'name'    => 'breakfast',
+                'display' => 'Breakfast',
+                'att'     => array('id'  => 'breakfast'),
+                'val'     => array(0, 1)
+            );
+//            $e[] = array(
+//                'type'    => 'text',
+//                'req'     => false,
+//                'name'    => 'breakfast_from',
+//                'display' => 'From',
+//                'opts'    => array('class' => 'priceFrom')
+//            );
+//            $e[] = array(
+//                'type'    => 'text',
+//                'req'     => false,
+//                'name'    => 'breakfast_to',
+//                'display' => 'To',
+//                'opts'    => array('class' => 'priceTo')
+//            );
+            $e[] = array(
+                'type'    => 'advcheckbox',
+                'req'     => false,
+                'name'    => 'brunch',
+                'display' => 'Brunch',
+                'att'     => array('id' => 'brunch'),
+                'val'     => array(0, 1)
+            );
+//            $e[] = array(
+//                'type'    => 'text',
+//                'req'     => false,
+//                'name'    => 'brunch_from',
+//                'display' => 'From',
+//                'opts'    => array('class' => 'priceFrom')
+//            );
+//            $e[] = array(
+//                'type'    => 'text',
+//                'req'     => false,
+//                'name'    => 'brunch_to',
+//                'display' => 'To',
+//                'opts'    => array('class' => 'priceTo')
+//            );
+            $e[] = array(
+                'type'    => 'advcheckbox',
+                'req'     => false,
+                'name'    => 'lunch',
+                'display' => 'Lunch',
+                'att'     => array('id' => 'lunch'),
+                'val'     => array(0, 1)
+            );
+//            $e[] = array(
+//                'type'    => 'text',
+//                'req'     => false,
+//                'name'    => 'lunch_from',
+//                'display' => 'From',
+//                'opts'    => array('class' => 'priceFrom')
+//            );
+//            $e[] = array(
+//                'type'    => 'text',
+//                'req'     => false,
+//                'name'    => 'lunch_to',
+//                'display' => 'To',
+//                'opts'    => array('class' => 'priceTo')
+//            );
+            $e[] = array(
+                'type'    => 'advcheckbox',
+                'req'     => false,
+                'name'    => 'dinner',
+                'display' => 'Dinner',
+                'att'     => array('id' => 'dinner'),
+                'val'     => array(0, 1)
+            );
+//            $e[] = array(
+//                'type'    => 'text',
+//                'req'     => false,
+//                'name'    => 'dinner_from',
+//                'display' => 'From',
+//                'opts'    => array('class' => 'priceFrom')
+//            );
+//            $e[] = array(
+//                'type'    => 'text',
+//                'req'     => false,
+//                'name'    => 'dinner_to',
+//                'display' => 'To',
+//                'opts'    => array('class' => 'priceTo')
+//            );
+            $e[] = array(
+                'type'    => 'advcheckbox',
+                'req'     => false,
+                'name'    => 'alcohol',
+                'display' => 'Alcohol',
+                'val'     => array(0, 1)
+            );
+//            $e[] = array(
+//                'type'    => 'advcheckbox',
+//                'req'     => false,
+//                'name'    => 'non_smoking',
+//                'display' => 'Non-Smoking',
+//                'val'     => array(0, 1)
+//            );
+        }
+
+        //  }}}
+        //    {{{ Golf Information
+
+        if ($this->golf) {
+            $e[] = array(
+                'type' => 'header',
+                'req' => false,
+                'name' => 'GolfingHdr',
+                'display' => 'Golf Course Information',
+                'col2' => true
+            );
+            $e[] = array(
+                'type'    => 'text',
+                'req'     => false,
+                'name'    => 'res_url',
+                'display' => 'TeeTime URL',
+                'opts'    => array('class' => 'text'),
+                'noCharLimit' => true
+            );
+            $e[] = array(
+                'type'    => 'text',
+                'req'     => false,
+                'name'    => 'par',
+                'display' => 'Par',
+                'opts'    => array('class' => 'text')
+            );
+            $e[] = array(
+                'type'    => 'text',
+                'req'     => false,
+                'name'    => 'yardage',
+                'display' => 'Yardage',
+                'opts'    => array('class' => 'text')
+            );
+            $e[] = array(
+                'type'    => 'text',
+                'req'     => false,
+                'name'    => 'course_rating',
+                'display' => 'Course Rating',
+                'opts'    => array('class' => 'text')
+            );
+            $e[] = array(
+                'type'    => 'text',
+                'req'     => false,
+                'name'    => 'slope_rating',
+                'display' => 'Slope Rating',
+                'opts'    => array('class' => 'text')
+            );
+            $e[] = array(
+                'type'    => 'advcheckbox',
+                'req'     => false,
+                'name'    => 'walking_course',
+                'display' => 'Walking Course',
+                'val'     => array(0, 1)
+            );
+            $e[] = array(
+                'type'    => 'text',
+                'req'     => false,
+                'name'    => 'holes18',
+                'display' => '18 Holes',
+                'opts'    => array('class' => 'text')
+            );
+            $e[] = array(
+                'type'    => 'text',
+                'req'     => false,
+                'name'    => 'holes9',
+                'display' => '9 Holes',
+                'opts'    => array('class' => 'text')
+            );
+        }
+
+        //  }}}
+        //  {{{ Social Media Links
+
+        $e[] = array(
+            'type' => 'header',
+            'req' => false,
+            'name' => 'SocialMediaHdr',
+            'display' => 'Social Media Links',
+            'col2' => true,
+        );
+        $e[] = array(
+            'type' => 'text',
+            'req' => false,
+            'name' => 'facebook',
+            'display' => 'Facebook',
+            'col2' => true,
+            'noCharLimit' => true
+        );
+        $e[] = array(
+            'type' => 'text',
+            'req' => false,
+            'name' => 'twitter',
+            'display' => 'Twitter',
+            'col2' => true,
+            'noCharLimit' => true
+        );
+        $e[] = array(
+            'type' => 'text',
+            'req' => false,
+            'name' => 'pinterest',
+            'display' => 'Pinterest',
+            'col2' => true,
+            'noCharLimit' => true
+        );
+        $e[] = array(
+            'type' => 'text',
+            'req' => false,
+            'name' => 'instagram',
+            'display' => 'Instagram',
+            'col2' => true,
+            'noCharLimit' => true
+        );
+        $e[] = array(
+            'type' => 'text',
+            'req' => false,
+            'name' => 'google_plus',
+            'display' => 'Google+',
+            'col2' => true,
+            'noCharLimit' => true
+        );
+
+        $e[] = array(
+            'type' => 'text',
+            'req' => false,
+            'name' => 'linkedin',
+            'display' => 'LinkedIn',
+            'col2' => true,
+            'noCharLimit' => true
+        );
+        $e[] = array(
+            'type' => 'text',
+            'req' => false,
+            'name' => 'blog',
+            'display' => 'Blog',
+            'col2' => true,
+            'noCharLimit' => true
+        );
+        $e[] = array(
+            'type' => 'text',
+            'req' => false,
+            'name' => 'youtube',
+            'display' => 'YouTube',
+            'col2' => true,
+            'noCharLimit' => true
+        );
+        $e[] = array(
+            'type' => 'text',
+            'req' => false,
+            'name' => 'myspace',
+            'display' => 'MySpace',
+            'col2' => true,
+            'noCharLimit' => true
+        );
+
+        //  }}}
+
+        $this->setupElements($e);
+    }
+
+    //    }}}
+    //    {{{ configureFilters()
+
+    /**
+     * Setup the filters to apply to the elements before we are
+     * handed the values submitted
+     *
+     * @return void
+     * @access public
+     */
+    public function configureFilters()
+    {
+        $f = array();
+        $f[] = array(
+            'element' => '__ALL__',
+            'filter' => 'trim'
+        );
+        $f[] = array(
+            'element' => 'reservation_url',
+            'filter' => array('Toolkit_Common', 'filterURI')
+        );
+        $f[] = array(
+            'element' => 'facebook',
+            'filter' => array('Toolkit_Common', 'filterURI')
+        );
+        $f[] = array(
+            'element' => 'pinterest',
+            'filter' => array('Toolkit_Common', 'filterURI')
+        );
+        $f[] = array(
+            'element' => 'twitter',
+            'filter' => array('Toolkit_Common', 'filterURI')
+        );
+        $f[] = array(
+            'element' => 'instagram',
+            'filter' => array('Toolkit_Common', 'filterURI')
+        );
+        $f[] = array(
+            'element' => 'google_plus',
+            'filter' => array('Toolkit_Common', 'filterURI')
+        );
+        $f[] = array(
+            'element' => 'myspace',
+            'filter' => array('Toolkit_Common', 'filterURI')
+        );
+        $f[] = array(
+            'element' => 'linkedin',
+            'filter' => array('Toolkit_Common', 'filterURI')
+        );
+        $f[] = array(
+            'element' => 'blog',
+            'filter' => array('Toolkit_Common', 'filterURI')
+        );
+        $f[] = array(
+            'element' => 'youtube',
+            'filter' => array('Toolkit_Common', 'filterURI')
+        );
+        $f[] = array(
+            'element' => 'url',
+            'filter' => array('Toolkit_Common', 'filterURI')
+        );
+//        $f[] = array(
+//            'element' => 'phone',
+//            'filter' => array('Toolkit_Common', 'filterPhone')
+//        );
+//        $f[] = array(
+//            'element' => 'fax',
+//            'filter' => array('Toolkit_Common', 'filterPhone')
+//        );
+//        $f[] = array(
+//            'element' => 'toll_free',
+//            'filter' => array('Toolkit_Common', 'filterPhone')
+//        );
+
+        $this->setupFilters($f);
+    }
+
+    //    }}}
+    //  {{{ configureForm()
+
+    /**
+     * Wrapper function to handle setting up the form
+     *
+     * @return void
+     * @access public
+     */
+    public function configureForm()
+    {
+        $this->configureElements();
+        $this->configureFilters();
+        $this->configureRules();
+        $this->configureDefaults();
+        $this->configureConstants();
+    }
+
+    //  }}}
+    //    {{{ configureModules()
+
+    /**
+     * Turns on a modules if the member has the appropriate category
+     *
+     * @return void
+     * @access protected
+     */
+    protected function configureModules()
+    {
+        if (is_array($this->memberCategories)) {
+            try {
+                $sql = "
+                    SELECT *
+                      FROM category
+                     WHERE category_id = :cid";
+                $stmt = $this->dbh->prepare($sql);
+                foreach ($this->memberCategories as $cid => $v) {
+                    $stmt->bindParam(':cid', $cid, PDO::PARAM_INT);
+                    $stmt->execute();
+                    $row = $stmt->fetch(PDO::FETCH_ASSOC);
+                    if ($row['accommodations'] == 't') {
+                        $this->accommodations = true;
+                    }
+                    if ($row['restaurant'] == 't') {
+                        $this->restaurant = true;
+                    }
+                    if ($row['golf'] == 't') {
+                        $this->golf = true;
+                    }
+                }
+            } catch (PDOException $e) {
+                return Toolkit_Common::handleError($e);
+            }
+        }
+    }
+
+    //    }}}
+    //    {{{ configureRules()
+
+    /**
+     * Sets up all the rules to be used when the form is validated.
+     *
+     * @return void
+     * @access public
+     */
+    public function configureRules()
+    {
+        $mimeTypes = array(
+            'image/jpe',
+            'image/jpeg',
+            'image/jpg',
+            'image/jfif',
+            'image/pjpeg',
+            'image/pjp',
+            'image/gif',
+            'image/png',
+        );
+
+        $r = array();
+
+        $r[] = array(
+            'element'    => 'reservation_id',
+            'message'    => 'ERROR: Must be an integer!',
+            'type'       => 'numeric',
+            'format'     => null,
+            'validation' => $this->validationType,
+            'reset'      => false,
+            'force'      => false
+        );
+        $r[] = array(
+            'element'    => 'process_email',
+            'message'    => 'ERROR: Invalid email format!',
+            'type'       => 'checkEmail',
+            'format'     => array('use_rfc822' => true),
+            'validation' => $this->validationType,
+            'reset'      => false,
+            'force'      => false
+        );
+        $r[] = array(
+            'element'    => 'member_contact_email',
+            'message'    => 'ERROR: Invalid email format!',
+            'type'       => 'checkEmail',
+            'format'     => array('use_rfc822' => true),
+            'validation' => $this->validationType,
+            'reset'      => false,
+            'force'      => false
+        );
+        $r[] = array(
+            'element'    => 'join_date',
+            'message'    => 'ERROR: Invalid date!',
+            'type'       => 'callback',
+            'format'     => array(&$this, 'checkDate'),
+            'validation' => $this->validationType,
+            'reset'      => false,
+            'force'      => false
+        );
+        $r[] = array(
+            'element'    => 'url',
+            'message'    => 'ERROR: Invalid URL format',
+            'type'       => 'checkURI',
+            'format'     => array(
+                'allowed_schemes' => array('http', 'https'),
+                'strict' => false
+            ),
+            'validation' => $this->validationType,
+            'reset'      => false,
+            'force'      => false
+        );
+        $r[] = array(
+            'element'    => 'reservation_url',
+            'message'    => 'ERROR: Invalid URL format',
+            'type'       => 'checkURI',
+            'format'     => array(
+                'allowed_schemes' => array('http', 'https'),
+                'strict' => false
+            ),
+            'validation' => $this->validationType,
+            'reset'      => false,
+            'force'      => false
+        );
+        $r[] = array(
+            'element'    => 'num_rooms',
+            'message'    => 'ERROR: Must be an integer!',
+            'type'       => 'numeric',
+            'format'     => null,
+            'validation' => $this->validationType,
+            'reset'      => false,
+            'force'      => false
+        );
+        $r[] = array(
+            'element'    => 'lat',
+            'message'    => 'ERROR: Must be a numeric!',
+            'type'       => 'numeric',
+            'format'     => null,
+            'validation' => $this->validationType,
+            'reset'      => false,
+            'force'      => false
+        );
+        $r[] = array(
+            'element'    => 'lat',
+            'message'    => 'ERROR: Latitude out of range!',
+            'type'       => 'callback',
+            'format'     => array(&$this, 'checkLatitude'),
+            'validation' => $this->validationType,
+            'reset'      => false,
+            'force'      => false
+        );
+        $r[] = array(
+            'element'    => 'lon',
+            'message'    => 'ERROR: Must be a numeric!',
+            'type'       => 'numeric',
+            'format'     => null,
+            'validation' => $this->validationType,
+            'reset'      => false,
+            'force'      => false
+        );
+        $r[] = array(
+            'element'    => 'lon',
+            'message'    => 'ERROR: Longitude out of range!',
+            'type'       => 'callback',
+            'format'     => array(&$this, 'checkLongitude'),
+            'validation' => $this->validationType,
+            'reset'      => false,
+            'force'      => false
+        );
+        $allowDuplicateMemberNames
+            = $this->config
+                   ->getItem('section', 'conf')
+                   ->getItem('directive', 'duplicateMembers')
+                   ->getContent();
+        if (!$allowDuplicateMemberNames) {
+            $r[] = array(
+                'element'    => 'member_name',
+                'message'    => 'ERROR: There is an existing account with this name!',
+                'type'       => 'callback',
+                'format'     => array($this, 'checkMemberName'),
+                'validation' => $this->validationType,
+                'reset'      => false,
+                'force'      => false
+            );
+        }
+        $r[] = array(
+            'element'    => 'member_login',
+            'message'    => 'ERROR: Sorry, but this username has already been taken!',
+            'type'       => 'callback',
+            'format'     => array($this, 'checkUName'),
+            'validation' => $this->validationType,
+            'reset'      => false,
+            'force'      => false
+        );
+        $r[] = array(
+            'element'    => 'state_id',
+            'message'    => 'ERROR: Invalid State!',
+            'type'       => 'numeric',
+            'format'     => null,
+            'validation' => $this->validationType,
+            'reset'      => false,
+            'force'      => false
+        );
+        $r[] = array(
+            'element'    => 'mailing_state_id',
+            'message'    => 'ERROR: Invalid State!',
+            'type'       => 'numeric',
+            'format'     => null,
+            'validation' => $this->validationType,
+            'reset'      => false,
+            'force'      => false
+        );
+        $r[] = array(
+            'element'    => 'region',
+            'message'    => 'ERROR: Invalid Region!',
+            'type'       => 'numeric',
+            'format'     => null,
+            'validation' => $this->validationType,
+            'reset'      => false,
+            'force'      => false
+        );
+        $r[] = array(
+            'element'    => 'zip',
+            'message'    => 'ERROR: Invalid Zip Code!',
+            'type'       => 'zip',
+            'format'     => array('requireDBCheck' => false),
+            'validation' => $this->validationType,
+            'reset'      => false,
+            'force'      => false
+        );
+        $r[] = array(
+            'element'    => 'mailing_zip',
+            'message'    => 'ERROR: Invalid Zip Code!',
+            'type'       => 'zip',
+            'format'     => array('requireDBCheck' => false),
+            'validation' => $this->validationType,
+            'reset'      => false,
+            'force'      => false
+        );
+        $r[] = array(
+            'element'    => 'new_logo_rmv',
+            'message'    => 'ERROR: Incorrect File Type (.gif, .png, .jpg) only!',
+            'type'       => 'mimetype',
+            'format'     => $mimeTypes,
+            'validation' => $this->validationType,
+            'reset'      => false,
+            'force'      => false
+        );
+        $r[] = array(
+            'element'    => 'facebook',
+            'message'    => 'ERROR: 200 characters max!',
+            'type'       => 'maxlength',
+            'format'     => 200,
+            'validatios' => $this->validationType,
+            'reset'      => false,
+            'force'      => false
+        );
+        $r[] = array(
+            'element'    => 'twitter',
+            'message'    => 'ERROR: 200 characters max!',
+            'type'       => 'maxlength',
+            'format'     => 200,
+            'validatios' => $this->validationType,
+            'reset'      => false,
+            'force'      => false
+        );
+        $r[] = array(
+            'element'    => 'pinterest',
+            'message'    => 'ERROR: 200 characters max!',
+            'type'       => 'maxlength',
+            'format'     => 200,
+            'validatios' => $this->validationType,
+            'reset'      => false,
+            'force'      => false
+        );
+        $r[] = array(
+            'element'    => 'instagram',
+            'message'    => 'ERROR: 200 characters max!',
+            'type'       => 'maxlength',
+            'format'     => 200,
+            'validatios' => $this->validationType,
+            'reset'      => false,
+            'force'      => false
+        );
+        $r[] = array(
+            'element'    => 'google_plus',
+            'message'    => 'ERROR: 200 characters max!',
+            'type'       => 'maxlength',
+            'format'     => 200,
+            'validatios' => $this->validationType,
+            'reset'      => false,
+            'force'      => false
+        );
+        $r[] = array(
+            'element'    => 'myspace',
+            'message'    => 'ERROR: 200 characters max!',
+            'type'       => 'maxlength',
+            'format'     => 200,
+            'validatios' => $this->validationType,
+            'reset'      => false,
+            'force'      => false
+        );
+        $r[] = array(
+            'element'    => 'linkedin',
+            'message'    => 'ERROR: 200 characters max!',
+            'type'       => 'maxlength',
+            'format'     => 200,
+            'validatios' => $this->validationType,
+            'reset'      => false,
+            'force'      => false
+        );
+        $r[] = array(
+            'element'    => 'blog',
+            'message'    => 'ERROR: 200 characters max!',
+            'type'       => 'maxlength',
+            'format'     => 200,
+            'validatios' => $this->validationType,
+            'reset'      => false,
+            'force'      => false
+        );
+        $r[] = array(
+            'element'    => 'youtube',
+            'message'    => 'ERROR: 200 characters max!',
+            'type'       => 'maxlength',
+            'format'     => 200,
+            'validatios' => $this->validationType,
+            'reset'      => false,
+            'force'      => false
+        );
+
+        $this->setupRules($r);
+    }
+
+    //    }}}
+
+    //    {{{    emailOwner()
+
+    /**
+     * Emails the owner and anyone else who wants to be advised of updates
+     *
+     * A false value in the primaryAdvisee will cause no email to be sent.
+     * all secondary advisees listed in the constructor are carbon copied
+     * in the email.
+     *
+     * Emails are sent out in both HTML and TXT forms.
+     *
+     * @return boolean result of email
+     * @access protected
+     */
+    protected function emailOwner()
+    {
+        if ($this->primaryAdvisee === false) {
+            return;
+        } else {
+            try {
+                $sql = "
+                    SELECT member_name
+                      FROM member
+                     WHERE member_id = :member_id";
+                $stmt = $this->dbh->prepare($sql);
+                $stmt->bindParam(':member_id', $_GET['id'], PDO::PARAM_INT);
+                $stmt->execute();
+                $row = $stmt->fetch(PDO::FETCH_ASSOC);
+                $memberName = $row['member_name'];
+            } catch (PDOException $e) {
+                return Toolkit_Common::handleError($e);
+            }
+            $template = new HTML_Template_Flexy($this->flexyOptions);
+            $page = new stdClass();
+            $page->member = $memberName;
+            $page->url = ($_SERVER['HTTPS'] == 'on') ? BASE_SECURE_URL : MEDIA_BASE_URL;
+            $page->email = DO_NOT_REPLY_EMAIL;
+            $page->siteName = SITENAME;
+            $page->link = '<a target="_blank"  href="'.MEDIA_BASE_URL.'pending-member/'.$_GET['id'].'/">link</a>';
+
+            $template->compile($this->emailTemplate);
+            //    Merge the compiled template with the $page object.
+            $htmlMsg = $template->bufferedOutputObject($page);
+
+            $msg = "
+                <h3>$memberName</h3>
+                <p>
+                    Has updated thier business record and is now in a pending
+                    state. To approve / reject thier changes you can either log
+                    into your {$page->siteName} admin area or follow this
+                    {$page->link}
+                </p>";
+            $crlf = "\n";
+            $mimeMail = new Mail_mime($crlf);
+            $from = preg_replace("/[^A-Za-z ]/", "", SITENAME) . ' <' . DO_NOT_REPLY_EMAIL . '>';
+            $mimeMail->setFrom($from);
+            $mimeMail->setSubject('Member Record Update');
+            if (!empty($this->secondaryAdvisees)) {
+                foreach ($this->secondaryAdvisees as $k => $v) {
+                    if (is_string($k) && !is_numeric($k)) {
+                        $email = "$k <$v>";
+                    } else {
+                        $email = $v;
+                    }
+                    $mimeMail->addCC($email);
+                }
+            }
+            $mimeMail->setHTMLBody($htmlMsg);
+            $mimeMail->setTXTBody($msg);
+
+            $mail =& Mail::factory('mail');
+            $body = $mimeMail->get();
+            $headers = $mimeMail->headers($hdrs);
+
+            $res = $mail->send($this->primaryAdvisee, $headers, $body);
+            if (PEAR::isError($res)) {
+                return Toolkit_Common::handleError($res);
+            } else {
+                return $res;
+            }
+        }
+    }
+
+    //    }}}
+
+    //    {{{ fieldPending()
+
+    /**
+     * Determines if template fields are pending or not
+     *
+     * Each field in the template calls this function to determine if it
+     * is in a pending state or not.  Fields that return true get a
+     * different colored background and border so they are easily recognizable.
+     *
+     * fields are considered pending if they are in the pendingFields array.
+     *
+     * @param string $elem element to check for pending
+     *
+     * @return boolean If the field is pending
+     * @access protected
+     */
+    public function fieldPending($elem)
+    {
+        if (!is_array($this->pendingFields)) {
+            return false;
+        } else {
+            return in_array($elem->name, $this->pendingFields);
+        }
+    }
+
+    // }}}
+
+    //    {{{ getCategories()
+
+    /**
+     * Creates array structure of the category tree for use in a select element.
+     *
+     * This method will also instantiate a class property called tree for your
+     * class. This object will hold the tree structure of the categories list
+     * from the Database.
+     *
+     * @return    array        The array list of categories that can be loaded
+     *                        into a select element
+     *
+     * @access    protected
+     * @see        Toolkit_Members_CategoryTree
+     */
+    protected function getCategories()
+    {
+        try {
+            $sql = "
+                SELECT *
+                  FROM category
+                 WHERE parent_id = 0
+                 ORDER BY name";
+            foreach ($this->dbh->query($sql, PDO::FETCH_ASSOC) as $row) {
+                $this->tree[] = new Toolkit_Members_CategoryTree(
+                    $row['category_id'],
+                    $row['name'],
+                    $this->dbh
+                );
+            }
+            if (!empty($this->tree)) {
+                foreach ($this->tree as $t) {
+                    $this->createCategories($t);
+                }
+            }
+
+            if (empty($this->categories)) {
+                $this->categories
+                    = array('' => '-- No Categories Created Yet --');
+            } else {
+                $this->categories
+                    = array('' => '-- Select Category --') + $this->categories;
+            }
+        } catch (PDOException $e) {
+            return Toolkit_Common::handleError($e);
+        }
+    }
+
+    //    }}}
+    /**
+     * Description of getCurrentAddress()
+     *
+     * @return array
+     * @access protected
+     */
+    protected function getCurrentAddress()
+    {
+        try {
+            $sql = "
+            SELECT street,city_id,state_id,zip
+              FROM member
+             WHERE member_id = :member_id";
+            $stmt = $this->dbh->prepare($sql);
+            $stmt->bindParam(':member_id', $_GET['id']);
+            $stmt->execute();
+            return $stmt->fetch(PDO::FETCH_ASSOC);
+        } catch(PDOException $e) {
+            var_dump($e);
+            exit;
+        }
+    }
+    //    {{{ getRegions()
+
+    /**
+     * Configure the regions for member stored in the database into an array
+     *
+     * @return array the regions
+     * @access protected
+     */
+    protected function getRegions()
+    {
+        try {
+            $sql = "
+                SELECT *
+                  FROM region
+                 ORDER BY region_name";
+            foreach ($this->dbh->query($sql) as $row) {
+                $regions[$row['region_id']] = $row['region_name'];
+            }
+            if (!empty($regions)) {
+                $regions = array('' => '-- Select Region --') + $regions;
+            } else {
+                $regions = array('' => '-- No Regions Created Yet -- ');
+            }
+            return $regions;
+        } catch (PDOException $e) {
+            return Toolkit_Common::handleError($e);
+        }
+    }
+
+    //    }}}
+
+    //    {{{ hasLogo()
+
+    /**
+     * Determine if the member record has a logo
+     *
+     * @return boolean if the record has a logo or not
+     * @access protected
+     */
+    protected function hasLogo()
+    {
+        try {
+            $member_id = $_GET['id'];
+
+            $sql = "
+                SELECT logo
+                  FROM {$this->tableName}
+                 WHERE member_id = :member_id";
+
+            $stmt = $this->dbh->prepare($sql);
+            $stmt->bindParam(':member_id', $member_id, PDO::PARAM_INT);
+            $stmt->execute();
+            $row = $stmt->fetch(PDO::FETCH_ASSOC);
+
+            if (get_class($this) === 'Toolkit_Members_EditMemberOnlyInfo') {
+                $sql = "
+                    SELECT count(*) AS total
+                      FROM {$this->pendingTable}
+                     WHERE member_id = :member_id
+                       AND field     = 'logo'";
+
+                $stmt = $this->dbh->prepare($sql);
+                $stmt->bindParam(':member_id', $member_id, PDO::PARAM_INT);
+                $stmt->execute();
+                $pendingLogos = $stmt->fetch(PDO::FETCH_ASSOC);
+            }
+
+            $hasPending = !empty($pendingLogos['total']);
+            $hasLogo    = !is_null($row['logo']);
+
+            return ($hasLogo || $hasPending);
+        } catch (PDOException $e) {
+            return Toolkit_Common::handleError($e);
+        }
+    }
+
+    //    }}}
+
+    //    {{{ inColumn2()
+
+    /**
+     * Lets the form know when to add a new column
+     *
+     * When rendering the form, we need to know what element
+     * to start rendering the new columns on, this function
+     * checks to see if we should start on the current
+     * element or not.
+     *
+     * This function is called from within the template.
+     *
+     * @param string $section element to check
+     *
+     * @return boolean if the section is in the 2nd column or not
+     * @access protected
+     */
+    public function inColumn2($section)
+    {
+        return ($this->formHeaders[$section]['col2']);
+    }
+
+    //    }}}
+    //    {{{ insertData()
+
+    /**
+     * Create a new record in the database from the data on the form
+     *
+     * When a member is first created, we don't have to worry about
+     * and module data ie.(golf, restaurant, accommodations), since
+     * they initially don't have any categories associated with them.
+     *
+     * Lat / Lon coordinates are not generated here, but they are
+     * injected into the form values if they are not filled in. This
+     * enables us to use google driving directions on all our members.
+     *
+     * Two groups of data are pulled from the values array of data.
+     * (categories & credit cards), these values are then unset in
+     * the values array, b/c we don't insert this data into the primary
+     * table.  They each have their own table they get inserted into and
+     * are then associated to the newly inserted member.
+     *
+     * Empty field values are inserted as NULLS vs empty strings to allow
+     * for unique member_login names.
+     *
+     * A transaction is used to avoid orphaned data if any of the sql queries
+     * fail to properly insert the data.
+     *
+     * @param array $values Form values
+     *
+     * @return boolean False on error, True otherwise.
+     * @access public
+     */
+    public function insertData($values)
+    {
+        //    {{{ Golfing
+
+        //    Remove Golf Fields for Special Insert.
+        $fields = array(
+            'res_url',
+            'par',
+            'yardage',
+            'course_rating',
+            'slope_rating',
+            'walking_course',
+            'holes18',
+            'holes9',
+        );
+
+        foreach ($fields as $v) {
+            $golfFields[$v] = $values[$v];
+            unset($values[$v]);
+        }
+
+        //    }}}
+        //    {{{ Accommodations
+
+        //    Remove Accommodation Fields for Special Insert.
+        $fields = array(
+            'reservation_url',
+            'reservation_id',
+            'num_rooms',
+            'year_round',
+        );
+        foreach ($fields as $v) {
+            $accommodationFields[$v] = $values[$v];
+            unset($values[$v]);
+        }
+
+        //    }}}
+        //    {{{ Restaurant
+
+        //    Remove Restaurant Fields for Special Insert.
+        $fields = array(
+            'breakfast',
+            'breakfast_from',
+            'breakfast_to',
+            'brunch',
+            'brunch_from',
+            'brunch_to',
+            'lunch',
+            'lunch_from',
+            'lunch_to',
+            'dinner',
+            'dinner_from',
+            'dinner_to',
+            'alcohol',
+            'non_smoking',
+        );
+        foreach ($fields as $v) {
+            $restaurantFields[$v] = $values[$v];
+            unset($values[$v]);
+        }
+
+        //    }}}
+
+        if (empty($values['lat']) || empty($values['lon'])) {
+            try {
+                $this->setLatLngCoords($values);
+            } catch (Exception $e) {
+                return Toolkit_Common::handleError($e);
+            }
+        }
+
+        $ccards = $values['creditCards'];
+        //    If they dynamically add any categories, but then realize they
+        //    want to remove them, they will show up in this array.
+        if (isset($values['removeCat'])) {
+            //    So remove them before we get the categories to insert for the member.
+            $this->removeCategories($values['removeCat'], $values['member_cats']);
+        }
+        //    Now get the cats so we can insert them into a seperate table later.
+        $categories = $values['member_cats'];
+
+        //    Unneeded data that will not be stored in the member table
+        unset($values['member_cats'],
+              $values['removeCat'],
+              $values['creditCards']
+        );
+
+        try {
+            $params = implode(', ', array_keys($values));
+            $bindParams = ':' . implode(', :', array_keys($values));
+            $sql = "
+                INSERT INTO {$this->tableName} ($params)
+                VALUES ($bindParams)
+             RETURNING member_id";
+            $this->dbh->beginTransaction();
+            $stmt = $this->dbh->prepare($sql);
+            foreach ($values as $k => &$v) {
+                $metaData = $this->tableMetaData[$k];
+                if ($metaData == 'integer') {
+                    $dataType = PDO::PARAM_INT;
+                } elseif ($metaData == 'boolean') {
+                    $dataType = PDO::PARAM_BOOL;
+                } else {
+                    $dataType = PDO::PARAM_STR;
+                }
+                //    for empty values that are not actually a zero (0), we
+                //    want to insert null's.
+                //    This will help hold the unique values for member_logins,
+                //    as empty values '', are not considered unique
+                if (empty($v) && $v !== 0) {
+                    $v = null;
+                    $dataType = PDO::PARAM_NULL;
+                }
+                $stmt->bindParam(":$k", $v, $dataType);
+            }
+            $stmt->execute();
+            $stmt->bindColumn('member_id', $mid);
+            $stmt->fetch();
+
+            //    Insert the categories into the DB and associate
+            //    them w/ the newly inserted member.
+            if (!empty($categories)) {
+                $sql = "
+                    INSERT INTO member_category(member_id, category_id)
+                    VALUES(:member_id, :cid)";
+                $stmt = $this->dbh->prepare($sql);
+                $stmt->bindParam(':member_id', $mid, PDO::PARAM_INT);
+                foreach ($categories as $cid) {
+                    //    If the user didn't select a category,
+                    //    Then don't run an insert query.
+                    if (!empty($cid)) {
+                        $stmt->bindParam(':cid', $cid, PDO::PARAM_INT);
+                        $stmt->execute();
+                    }
+                }
+            }
+
+            //    Insert the members credit cards into the DB and associate
+            //    the w/ our new member. We get the ccard ID # by querying for
+            //    it in the ccard_type table by the ccard name.
+            if (!empty($ccards)) {
+                $sql = "
+                    INSERT INTO member_ccard_type(member_id, ccard_type_id)
+                        SELECT :member_id, ccard_type_id
+                          FROM ccard_type
+                         WHERE ccard_type_name = :cctn";
+
+                $stmt = $this->dbh->prepare($sql);
+                $stmt->bindParam(':member_id', $mid, PDO::PARAM_INT);
+                foreach ($ccards as $k => $v) {
+                    $stmt->bindParam(':cctn', $k, PDO::PARAM_STR);
+                    $stmt->execute();
+                }
+            }
+
+            $golf = $restaurant = $accommodations = false;
+            if (!empty($categories)) {
+                $sql = "
+                    SELECT *
+                      FROM category
+                     WHERE category_id = :cid";
+                $stmt = $this->dbh->prepare($sql);
+                foreach ($categories as &$cid) {
+                    if (!empty($cid)) {
+                        $stmt->bindParam(':cid', $cid, PDO::PARAM_INT);
+                        $stmt->execute();
+                        $row = $stmt->fetch(PDO::FETCH_ASSOC);
+                        //    If any of the module statuses are true, set them.
+                        //    otherwise leave them as they were, (we don't want
+                        //    to set a true back to a false).
+                        $golf = ($row['golf'] == 't') ? true : $golf;
+                        $accommodations = ($row['accommodations'] == 't') ?
+                            true : $accommodations;
+                        $restaurant = ($row['restaurant'] == 't') ?
+                            true : $restaurant;
+                    }
+                }
+            }
+            $this->clearModule('member_golf');
+            if ($golf) {
+                $this->updateModuleFields($golfFields, 'member_golf', $mid);
+            }
+            $this->clearModule('member_accommodations');
+            if ($accommodations) {
+                $this->updateModuleFields($accommodationFields, 'member_accommodations', $mid);
+            }
+            $this->clearModule('member_restaurants');
+            if ($restaurant) {
+                $this->updateModuleFields($restaurantFields, 'member_restaurants', $mid);
+            }
+            $this->dbh->commit();
+        } catch (PDOException $e) {
+            return Toolkit_Common::handleError($e);
+        }
+        $this->addMemberToStreamSend($mid);
+
+        header('Location: ' . MEDIA_BASE_URL . "admin/members.php?rt=Members&ac=editMember&tab=info&id=$mid");
+        return true;
+    }
+
+    //    }}}
+
+    //    {{{ newColumn()
+
+    /**
+     * Determines if we've reached the field that should start a new column
+     *
+     * This is used from the Flexy template.  Each field name is passed into
+     * this function, when we reach the appropriate field, we will know that
+     * we need to start a new column.
+     *
+     * @param string $string The name of the field we're checking.
+     *
+     * @return boolean True on the right field, otherwise false.
+     * @access protected
+     */
+    public function newColumn($string)
+    {
+        return ($string == 'Account Info');
+    }
+
+    //    }}}
+
+    //    {{{ processData()
+
+    /**
+     * Handles processing the submitted forms data.
+     *
+     * Sets up the table meta data so we can insert into the member table.
+     * removes any unwanted fields from the values array.  these fields
+     * will be denoted by the '_rmv' at the end of thier name in the
+     * configureElements function
+     *
+     * <example>
+     * ... 'name' => 'remove_rmv', ...
+     * </example>
+     *
+     * This function also verifies that the lat/lon values were properly filled
+     * out, if they were left blank then the values will either be looked up
+     * via google maps or through our own zip DB. These values will then be
+     * inserted into the values array AND also injected into the form field
+     * values so they will display when the form comes back around.
+     *
+     * The form then decides if it is handeling a new member or not and calls
+     * the appropriate function to insert or update data.
+     *
+     * @param array $values The array of all submitted form values.
+     *
+     * @return boolean Whether the insert/update succeeded.
+     * @access public
+     */
+    public function processData($values)
+    {
+        //    Clean up some form elements before we try any processing
+        if (is_array($values['join_date'])) {
+            if (is_numeric($values['join_date']['m'])) {
+                $values['join_date'] = implode('-', $values['join_date']);
+            } else {
+                $values['join_date'] = null;
+            }
+        }
+        $this->processLogo($values);
+        $this->tableMetaData = Toolkit_Common::getTableMetaData(
+            $this->dbh,
+            $this->tableName
+        );
+
+        foreach ($values as $k => $v) {
+            switch ($k) {
+            case 'MAX_FILE_SIZE' :
+                unset($values[$k]);
+                break;
+
+            default :
+                if (preg_match('/^.+_rmv$/', $k)) {
+                    unset($values[$k]);
+                }
+                break;
+            }
+        }
+
+        if (ctype_digit($_GET['id'])) {
+            return $this->updateData($values);
+        } else {
+            return $this->insertData($values);
+        }
+    }
+
+    //    }}}
+
+    //    {{{    processLogo()
+
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @param array $values Parameter description (if any) ...
+     *
+     * @return object    Return description (if any) ...
+     * @access protected
+     */
+    protected function processLogo(&$values)
+    {
+        //    Keep a tidy house.
+        //    There are 2 scenarios to deal w/ logos:
+        //    1.  Removing logo:
+        //        Delete the image from the image server
+        //        and set the values['logo'] to null.
+        //    2.  New logo:
+        //        Check to see if old_logo_rmv holds a value,
+        //        if it does then remove that logo
+        //        Upload the image and put new filename
+        //        into values['logo'] variable.
+        $imgServer  = new Toolkit_Image_Server();
+
+        if ($values['remove_logo_rmv'] == 1) {
+            $imgServer->imageDelete($values['old_logo_rmv']);
+            $values['logo'] = null;
+
+            if (get_class($this) == 'Toolkit_Members_EditMemberOnlyInfo') {
+                try {
+                    //    Make sure any logos that were pending are removed as well.
+                    $sql = "
+                        DELETE FROM {$this->pendingTable}
+                         WHERE field        = 'logo'
+                           AND member_id    = :member_id";
+                    $stmt = $this->dbh->prepare($sql);
+                    $stmt->bindParam(':member_id', $_GET['id'], PDO::PARAM_INT);
+                    $stmt->execute();
+
+                    $sql = "
+                        UPDATE {$this->tableName}
+                           SET logo = :logo
+                         WHERE member_id = :member_id";
+                    $stmt = $this->dbh->prepare($sql);
+                    $stmt->bindParam(':logo', $values['logo'], PDO::PARAM_NULL);
+                    $stmt->bindParam(':member_id', $_GET['id'], PDO::PARAM_INT);
+                    $stmt->execute();
+                } catch (PDOException $e) {
+                    return Toolkit_Common::handleError($e);
+                }
+            }
+        }
+
+        //    If a new logo is uploaded, have the image server
+        //    process that logo and give us back the file name on
+        //    the server.
+        if ($values['new_logo_rmv']['size'] > 0) {
+            $imgName = $imgServer->imageUpload('new_logo_rmv');
+            $values['logo'] = $imgName;
+            $img = '<img alt="'. $imgName . '" src="'.MEMBER_PHOTOS . $imgName.'">';
+
+            //    If the old_logo_rmv key is set and not empty
+            //    then we are replacing an existing logo and did not
+            //    check the remove logo checkbox.
+            //    remove this old logo just for good house keeping.
+            if (isset($values['old_logo_rmv']) && !empty($values['old_logo_rmv'])) {
+                $imgServer->imageDelete($values['old_logo_rmv']);
+                //    update the elements on the form if we are replacing an existing logo.
+                if ($this->elementExists('old_logo_rmv')) {
+                    $e =& $this->getElement('old_logo_rmv');
+                    $e->setValue($imgName);
+                }
+                if ($this->elementExists('image_rmv')) {
+                    $e =& $this->getElement('image_rmv');
+                    $e->setValue($img);
+                }
+            } else {
+                $source =& $this->createElement(
+                    'checkbox',
+                    'remove_logo_rmv',
+                    'Remove Logo'
+                );
+                $this->insertElementBefore($source, 'new_logo_rmv');
+
+                $source =& $this->addElement('hidden', 'old_logo_rmv');
+                $source->setValue($imgName);
+
+                $source =& $this->createElement(
+                    'static',
+                    'image_rmv',
+                    'Current Image'
+                );
+                $source->setValue($img);
+                $element = $this->insertElementBefore($source, 'new_logo_rmv');
+
+                if (PEAR::isError($element)) {
+                    die ('there was an error uploading your file!');
+                } else {
+                    //  I don't know why, but the insertElementBefore
+                    //  function was erasing the value we set earlier.
+                    //  so just reset it to make double sure its there.
+                    $element->setValue($img);
+                }
+            }
+        }
+
+        //    We clicked to remove the logo and did not upload a new one.
+        if ($values['remove_logo_rmv'] == 1 && $values['new_logo_rmv']['size'] == 0) {
+            if ($this->elementExists('remove_logo_rmv')) {
+                $this->removeElement('remove_logo_rmv', false);
+            }
+            if ($this->elementExists('image_rmv')) {
+                $this->removeElement('image_rmv', false);
+            }
+            if ($this->elementExists('old_logo_rmv')) {
+                $this->removeElement('old_logo_rmv', false);
+            }
+        }
+    }
+
+    //    }}}
+
+    //    {{{ removeCategories()
+
+    /**
+     * Remove categories from the values['member_cats'] array
+     *
+     * Any values in the removeCats array are removed from the
+     * values['member_cats'] array which is passed in by reference
+     * so we don't have to return any values.
+     *
+     * @param array $targets All the category id's we want to remove
+     * @param array &$values All the categories submitte.
+     *
+     * @return void
+     * @access    protected
+     */
+    protected function removeCategories($targets, &$values)
+    {
+        foreach ($targets as $cid) {
+            $key = array_search($cid, $values);
+            unset($values[$key]);
+        }
+    }
+
+    //    }}}
+
+    //  {{{ setConfig()
+
+    /**
+     * Sets the query to use to fetch the datagrid results
+     *
+     * @param Config_Container $c Configuration object
+     *
+     * @return void
+     * @access public
+     */
+    public function setConfig(Config_Container $c)
+    {
+        $this->config = $c;
+    }
+
+    //  }}}
+    //  {{{ setStates()
+
+    /**
+     * Set the states available for the form
+     *
+     * @param array $states Array of states available for the form
+     *
+     * @return void
+     * @access public
+     */
+    public function setStates(array $states)
+    {
+        $this->states = $states;
+    }
+
+    //  }}}
+    //  {{{ setCities()
+
+    /**
+     * Set the cities available for the form
+     *
+     * @param array $cities Array of cities available for the form
+     *
+     * @return void
+     * @access public
+     */
+    public function setCities(array $cities)
+    {
+        $this->cities = $cities;
+    }
+
+    //  }}}
+    //    {{{    setLatLngCoords()
+
+    /**
+     * Description for setLatLngCoords()
+     *
+     * @param array &$values Values array
+     *
+     * @return void|mixed Only on pdo error
+     * @access protected
+     */
+    protected function setLatLngCoords(&$values)
+    {
+        $geocoder = new GeocodeYahoo();
+
+        $sql = "
+            SELECT c.city_name, s.state_abb
+              FROM city c, state s
+             WHERE c.city_id  = :cityId
+               AND s.state_id = :stateId";
+
+        try {
+            $stmt = $this->dbh->prepare($sql);
+            $stmt->bindParam(':cityId', $values['city_id'], PDO::PARAM_INT);
+            $stmt->bindParam(':stateId', $values['state_id'], PDO::PARAM_INT);
+            $stmt->execute();
+            $stmt->bindColumn('city_name', $cityName);
+            $stmt->bindColumn('state_abb', $stateName);
+            $stmt->fetch(PDO::FETCH_ASSOC);
+        } catch (PDOException $e) {
+            return Toolkit_Common::handleError($e);
+        }
+
+        $address = array(
+            'city' => $cityName,
+            'state' => $stateName,
+            'zip' => $values['zip'],
+        );
+        if (!empty($values['street'])) {
+            $address['street'] = $values['street'];
+        }
+
+        if (   $this->elementExists('lat')
+            && $this->elementExists('lon')
+        ) {
+            try {
+                $response = $geocoder->geocodeAddress($address);
+                $responseArray = unserialize($response);
+                if ($responseArray['ResultSet']['Result'][0]['Latitude']) {
+                    $values['lat'] = $responseArray['ResultSet']['Result'][0]['Latitude'];
+                    $values['lon'] = $responseArray['ResultSet']['Result'][0]['Longitude'];
+                } else {
+                    $values['lat'] = $responseArray['ResultSet']['Result']['Latitude'];
+                    $values['lon'] = $responseArray['ResultSet']['Result']['Longitude'];
+                }
+
+                $lat =& $this->getElement('lat');
+                $lat->setValue($values['lat']);
+                $lng =& $this->getElement('lon');
+                $lng->setValue($values['lon']);
+            } catch (BadMethodCallException $e) {
+                Toolkit_Logger::logException('Invalid Arg', $e);
+            } catch (Exception $e) {
+                Toolkit_Logger::logException('Yahoo GeoCode', $e);
+            }
+        }
+    }
+
+    //    }}}
+    //    {{{ setupFormCategories()
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @return void
+     * @access protected
+     */
+    protected function setupFormCategories()
+    {
+        try {
+            //    Determine if this is a new member record or we're editing
+            //    an existing member record.
+            $member_id = $_GET['id'];
+            //    Get all the categories already associated w/ this member.
+            //    This part is primarily for editing existing members.
+            //    That way they categories initially show up.
+            //    They are ordered by the mem_cat_id which should keep
+            //    them in the same order as they were added.
+            $sql = "
+                SELECT *
+                  FROM member_category
+                 WHERE member_id = :id
+                 ORDER BY member_category_id";
+            $stmt = $this->dbh->prepare($sql);
+            $stmt->bindParam(':id', $member_id, PDO::PARAM_INT);
+            $stmt->execute();
+
+            while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                $memCats[$row['category_id']] = true;
+            }
+
+            //    If we submitted the form:
+            //    1. Get all the member_cat values on the form that were submitted.
+            //        If we flip the key/value pairs when we add the submitted values
+            //        we won't have to worry about checking for duplicate cats submitted.
+            //    2. If we clicked on a remove button determine the x/y coords
+            //        (hint: the coords will be empty if we didn't click on one).
+            //        if the coords are not empty, then remove the category we clicked
+            //        on, it will be the removeCat form element.
+            if ($this->isSubmitted()) {
+                if (!is_array($memCats)) {
+                    $memCats = array();
+                }
+                $submittedMemberCats = $this->getSubmitValue('member_cats');
+                if (is_array($submittedMemberCats) && !empty($submittedMemberCats)) {
+                    $memCats += array_flip($this->getSubmitValue('member_cats'));
+                }
+                //    Holds a possible array of category id's we want to remove.
+                $removeTargets = $this->getSubmitValue('removeCat');
+                //    If the array is not empty, then scoot through it and remove
+                //    any of the categories form the memCats array so they won't
+                //    show up again.
+                if (!empty($removeTargets)) {
+                    foreach ($removeTargets as $k => $v) {
+                        unset($memCats[$v]);
+                    }
+                }
+            }
+
+            //    Unset the blank key so the sql query doesn't puke when trying to
+            //    add it into the DB.  The blank key will occur if no cat is selected.
+            //    its the default option for the select list.
+            unset($memCats['']);
+            $this->memberCategories = $memCats;
+        } catch (PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+    }
+
+    //    }}}
+    //    {{{ setupRenderers()
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @return void
+     * @access protected
+     */
+    protected function setupRenderers()
+    {
+        $renderer = new HTML_QuickForm_Renderer_Object(true);
+
+        $this->accept($renderer);
+
+        $this->template = new HTML_Template_Flexy($this->flexyOptions);
+
+        $m = new Toolkit_Members_RecordNavigation($this->config);
+        $m->setupAdminNavStructure();
+        //    Make the view a copy of the $this object
+        //    That way we have access to call functions in
+        //    this class from within the template.
+        $this->view = $this;
+        $this->view->baseUrl = MEDIA_BASE_URL;
+        $this->view->form = $renderer->toObject();
+        $this->view->nav = $m->getPageNav();
+        $this->template->compile($this->formTemplate);
+    }
+
+    //    }}}
+    //    {{{ showCategories()
+
+    /**
+     * Creates the <i> tags for the member categories
+     *
+     * @return string set of <i> tags for categories assigned to a member
+     * @access public
+     */
+    public function showCategories()
+    {
+        if (!empty($this->memberCategories)) {
+            try {
+                $sql = "
+                    SELECT *
+                      FROM category
+                     WHERE category_id = :cid";
+                $stmt = $this->dbh->prepare($sql);
+                $out = '';
+                foreach ($this->memberCategories as $cid => $v) {
+                    $stmt->bindParam(':cid', $cid, PDO::PARAM_INT);
+                    $stmt->execute();
+                    $category = $stmt->fetch(PDO::FETCH_ASSOC);
+
+                    $out .= '<i>';
+                    $out .=        '<input id="catid'.$category['category_id'].'" ';
+                    $out .=            'type="hidden" name="member_cats[]" ';
+                    $out .=            'value="'.$category['category_id'].'">';
+                    $out .=     '<label class="remove" for="'.$category['category_id'].'">';
+                    $out .=            '<input type="checkbox" name="removeCat[]" ';
+                    $out .=                'id="'.$category['category_id'].'" ';
+                    $out .=                'value="'.$category['category_id'].'">';
+                    $out .=            'Remove';
+                    $out .=     '</label>';
+                    $out .=     $category['name'];
+                    $out .= '</i>';
+                }
+            } catch (PDOException $e) {
+                return Toolkit_Common::handleError($e);
+            }
+        }
+
+        return $out;
+    }
+
+    //    }}}
+
+    //    {{{ toHTML()
+
+    /**
+     * Renders the form
+     *
+     * sets the page the form should be redirected to instead of coming back
+     * around to itself.
+     *
+     * @return    string    The rendered form
+     * @access    public
+     */
+    public function toHTML()
+    {
+        //    We need to validate (and freeze if needed)
+        //    before we render the form. That way the
+        //    template knows about any errors on the form.
+        $this->validated = $this->validate();
+        if ($this->validated) {
+            $processed = $this->process(array(&$this, 'processData'), $this->mergeFiles);
+        }
+
+        //    ProcessData handles settingup the lat/lon coordinates if they were not entered
+        //    into the form.  these values ar calculated and then inserted into the forms
+        //    element values.  So we need to process the data first and then render the form.
+        $this->setupRenderers();
+
+        return $this->template->bufferedOutputObject($this->view);
+    }
+
+    //    }}}
+
+    //    {{{ updateData()
+
+    /**
+     * Update the record data
+     *
+     * @param array $values scrubbed values submitted from the form
+     *
+     * @return object True on success
+     * @access public
+     */
+    public function updateData($values)
+    {
+        $addressUpdated = false;
+
+        $currentAddress = $this->getCurrentAddress();
+        // check current address with new one to see ifthere's a change
+        $newAddress = array(
+            'street'   => $values['street'],
+            'city_id'  => (int)$values['city_id'],
+            'state_id' => (int)$values['state_id'],
+            'zip'      => $values['zip']
+        );
+        if ($newAddress != $currentAddress) {
+            $values['lat'] = '';
+            $values['lon'] = '';
+        }
+
+        $member_id = $_GET['id'];
+        //    {{{ Golfing
+
+        //    Remove Golf Fields for Special Insert.
+        $fields = array(
+            'res_url',
+            'par',
+            'yardage',
+            'course_rating',
+            'slope_rating',
+            'walking_course',
+            'holes18',
+            'holes9',
+        );
+
+        foreach ($fields as $v) {
+            $golfFields[$v] = $values[$v];
+            unset($values[$v]);
+        }
+
+        //    }}}
+        //    {{{ Accommodations
+
+        //    Remove Accommodation Fields for Special Insert.
+        $fields = array(
+            'reservation_url',
+            'reservation_id',
+            'num_rooms',
+            'year_round',
+        );
+        foreach ($fields as $v) {
+            $accommodationFields[$v] = $values[$v];
+            unset($values[$v]);
+        }
+
+        //    }}}
+        //    {{{ Restaurant
+
+        //    Remove Restaurant Fields for Special Insert.
+        $fields = array(
+            'breakfast',
+            'breakfast_from',
+            'breakfast_to',
+            'brunch',
+            'brunch_from',
+            'brunch_to',
+            'lunch',
+            'lunch_from',
+            'lunch_to',
+            'dinner',
+            'dinner_from',
+            'dinner_to',
+            'alcohol',
+            'non_smoking',
+        );
+        foreach ($fields as $v) {
+            $restaurantFields[$v] = $values[$v];
+            unset($values[$v]);
+        }
+
+        //    }}}
+
+        if (empty($values['lat']) || empty($values['lon'])) {
+            try {
+                $this->setLatLngCoords($values);
+            } catch (Exception $e) {
+                return Toolkit_Common::handleError($e);
+            }
+        }
+
+        //    Remove values not wanted before we get all the member categories.
+        if (isset($values['removeCat'])) {
+            //    So remove them before we get the categories to insert for the member.
+            $this->removeCategories($values['removeCat'], $values['member_cats']);
+        }
+        //    Now get the cats so we can insert them into a seperate table later.
+        $categories = $values['member_cats'];
+
+        $ccards = $values['creditCards'];
+        unset($values['member_cats'],
+              $values['removeCat'],
+              $values['creditCards']
+        );
+
+        try {
+            $params = array_keys($values);
+            $length = count($params);
+            for ($i = 0; $i < $length; ++$i) {
+                $bindParams .= "{$params[$i]} = :{$params[$i]}";
+                if ($i < ($length - 1)) {
+                    $bindParams .= ', ';
+                }
+            }
+            $this->dbh->beginTransaction();
+            $sql = "
+                UPDATE {$this->tableName}
+                   SET $bindParams
+                 WHERE member_id = :member_id";
+            $stmt = $this->dbh->prepare($sql);
+            $stmt->bindParam(':member_id', $member_id, PDO::PARAM_INT);
+            foreach ($values as $k => &$v) {
+                $metaData = $this->tableMetaData[$k];
+                if ($metaData == 'integer') {
+                    $dataType = PDO::PARAM_INT;
+                } else if ($metaData == 'boolean') {
+                    $dataType = PDO::PARAM_BOOL;
+                } else {
+                    $dataType = PDO::PARAM_STR;
+                }
+                //    for empty values that are not actually a zero (0), we
+                //    want to insert null's.
+                //    This will help hold the unique values for member_logins,
+                //    as empty values '', are not considered unique
+                if (empty($v) && $v !== 0) {
+                    $v = null;
+                    $dataType = PDO::PARAM_NULL;
+                }
+                $stmt->bindParam(":$k", $v, $dataType);
+            }
+            $stmt->execute();
+
+            //    Handle updating the credit cards
+            $sql = "
+                DELETE FROM member_ccard_type
+                 WHERE member_id = :id";
+            $stmt = $this->dbh->prepare($sql);
+            $stmt->bindParam(':id', $member_id, PDO::PARAM_INT);
+            $stmt->execute();
+
+            if (is_array($ccards)) {
+                $sql = "
+                    INSERT INTO member_ccard_type (member_id, ccard_type_id)
+                    SELECT :id, ccard_type_id
+                      FROM ccard_type
+                     WHERE ccard_type_name = :cctn";
+                $stmt = $this->dbh->prepare($sql);
+                $stmt->bindParam(':id', $member_id, PDO::PARAM_INT);
+                foreach ($ccards as $k => &$v) {
+                    if ($v) {
+                        $stmt->bindParam(':cctn', $k, PDO::PARAM_STR);
+                        $stmt->execute();
+                    }
+                }
+            }
+
+            //    Handle updating the categories
+            $sql = "
+                DELETE FROM member_category
+                 WHERE member_id = :id";
+            $stmt = $this->dbh->prepare($sql);
+            $stmt->bindParam(':id', $member_id, PDO::PARAM_INT);
+            $stmt->execute();
+
+            if (is_array($categories)) {
+                $sql = "
+                    INSERT INTO member_category (member_id, category_id)
+                    VALUES (:member_id, :cid)";
+                $stmt = $this->dbh->prepare($sql);
+                $stmt->bindParam(':member_id', $member_id, PDO::PARAM_INT);
+                foreach ($categories as &$cid) {
+                    if (!empty($cid)) {
+                        $stmt->bindParam(':cid', $cid, PDO::PARAM_INT);
+                        $stmt->execute();
+                    }
+                }
+            }
+
+            $golf = $restaurant = $accommodations = false;
+            if (!empty($categories)) {
+                $sql = "
+                    SELECT *
+                      FROM category
+                     WHERE category_id = :cid";
+                $stmt = $this->dbh->prepare($sql);
+                foreach ($categories as &$cid) {
+                    if (!empty($cid)) {
+                        $stmt->bindParam(':cid', $cid, PDO::PARAM_INT);
+                        $stmt->execute();
+                        $row = $stmt->fetch(PDO::FETCH_ASSOC);
+                        //    If any of the module statuses are true, set them.
+                        //    otherwise leave them as they were, (we don't want
+                        //    to set a true back to a false).
+                        $golf = ($row['golf'] == 't') ? true : $golf;
+                        $accommodations = ($row['accommodations'] == 't') ?
+                            true : $accommodations;
+                        $restaurant = ($row['restaurant'] == 't') ?
+                            true : $restaurant;
+                    }
+                }
+            }
+            $this->clearModule('member_golf');
+            if ($golf) {
+                $this->updateModuleFields($golfFields, 'member_golf');
+            }
+            $this->clearModule('member_accommodations');
+            if ($accommodations) {
+                $this->updateModuleFields($accommodationFields, 'member_accommodations');
+            }
+            $this->clearModule('member_restaurants');
+            if ($restaurant) {
+                $this->updateModuleFields($restaurantFields, 'member_restaurants');
+            }
+
+            $cache = new Cache_Lite(Toolkit_Members::getCacheOptions());
+            $cache->remove("Member-{$_GET['id']}", 'Profile');
+
+            $ret = $this->dbh->commit();
+
+            $this->addMemberToStreamSend($_GET['id']);
+
+            return $ret;
+        } catch (PDOException $e) {
+            $this->dbh->rollBack();
+            return Toolkit_Common::handleError($e);
+        }
+    }
+
+    //    }}}
+    //    {{{ updateModuleFields()
+
+    //    member_id in the params is for inserting the data if the form fails validation
+    //    first and comes back around and module categories will be included on the form
+    //    and you need to be able to insert the data and associate to the member.
+
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @param array   $values    Parameter description (if any) ...
+     * @param unknown $tableName Parameter description (if any) ...
+     * @param unknown $member_id Parameter description (if any) ...
+     *
+     * @return object    Return description (if any) ...
+     * @access protected
+     */
+    protected function updateModuleFields(
+        $values,
+        $tableName,
+        $member_id = null
+    ) {
+        $this->tableMetaData = Toolkit_Common::getTableMetaData(
+            $this->dbh,
+            $tableName
+        );
+        $values['member_id'] = !is_null($member_id)
+                              ? $member_id
+                              : $_GET['id'];
+        try {
+            $params = implode(', ', array_keys($values));
+            $bindParams = ':' . implode(', :', array_keys($values));
+            $sql = "
+                INSERT INTO $tableName ($params)
+                VALUES ($bindParams)";
+            $stmt = $this->dbh->prepare($sql);
+            foreach ($values as $k => &$v) {
+                $metaData = $this->tableMetaData[$k];
+                if ($metaData == 'integer') {
+                    $dataType = PDO::PARAM_INT;
+                } else if ($metaData == 'boolean') {
+                    $dataType = PDO::PARAM_BOOL;
+                } else {
+                    $dataType = PDO::PARAM_STR;
+                }
+                //    for empty values that are not actually a zero (0), we
+                //    want to insert null's.
+                //    This will help hold the unique values for member_logins,
+                //    as empty values '', are not considered unique
+                if (empty($v) && $v !== 0) {
+                    $v = null;
+                    $dataType = PDO::PARAM_NULL;
+                }
+                $stmt->bindParam(":$k", $v, $dataType);
+            }
+            return $stmt->execute();
+        } catch (PDOException $e) {
+            return Toolkit_Common::handleError($e);
+        }
+    }
+
+    //    }}}
+
+    //    {{{ validated()
+
+    /**
+     * Checks if the form is validated
+     *
+     * If the form is validated display the success msg.
+     * If the form is submitted but not validated, display
+     * the error msg.  Other wise display nothing.
+     *
+     * This function is called from within the template.
+     *
+     * @return    string        The success or error msg for the user.
+     * @access    protected
+     */
+    public function validated()
+    {
+        if ($this->validated) {
+            return $this->successMsg;
+        } elseif ($this->isSubmitted()) {
+            return $this->errorMsg;
+        }
+    }
+
+    //    }}}
+}
+?>
diff --git a/Toolkit/Members/EditMemberOnlyAmenities.php b/Toolkit/Members/EditMemberOnlyAmenities.php
new file mode 100644 (file)
index 0000000..1ced800
--- /dev/null
@@ -0,0 +1,454 @@
+<?php
+//     vim:set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker syntax=php:
+
+/**
+ * File Doc Comment
+ *
+ * PHP version 5
+ *
+ * @category MembersDB
+ * @package  Toolkit_Members
+ * @author   Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @license     http://www.gaslightmedia.com Gaslightmedia
+ * @version  CVS: $Id: EditMemberOnlyAmenities.php,v 1.14 2010/07/18 16:44:51 jamie Exp $
+ * @link        http://demo.gaslightmedia.com
+ */
+
+/**
+ * Short description for class
+ *
+ * Long description (if any) ...
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @link      http://demo.gaslightmedia.com
+ */
+class Toolkit_Members_EditMemberOnlyAmenities
+       extends Toolkit_Members_EditMemberAmenities implements Toolkit_Form
+{
+       //      {{{ properties
+
+       /**
+        * All the default data
+        *
+        * When we load the default data (member record from the database), we will
+        * store all that data in this variable. That way when we process the form
+        * we will have all the information to compare against, so we can find the
+        * updates.
+        *
+        * @var string
+        * @access protected
+        */
+       protected $oldData;
+
+       //      }}}
+       //      {{{ __construct()
+
+       /**
+        * Class constructor
+        *
+     * @param PDO    $pdo         PHP Data Object to use for DB calls
+     * @param string $formName    Form's name.
+     * @param string $method      (optional)Form's method defaults to 'POST'
+     * @param string $action      (optional)Form's action
+     * @param string $target      (optional)Form's target defaults to '_self'
+     * @param mixed  $attributes  (optional)Extra attributes for <form> tag
+     * @param bool   $trackSubmit (optional)Whether to track if the form was
+        *                                                        submitted by adding a special hidden field
+     *
+        * @access public
+        * @see    Toolkit_Members_EditMemberInfo
+        */
+       public function __construct(
+        PDO $pdo,
+        $formName,
+        $method = 'post',
+        $action = '',
+        $target = '',
+        $attributes = null,
+        $trackSubmit = false
+    ) {
+               parent::__construct(
+            $pdo,
+            $formName,
+            $method,
+            $action,
+            $target,
+            $attributes,
+            $trackSubmit
+        );
+               $this->setPending();
+       }
+
+       //      }}}
+
+       //      {{{ createPendingData()
+
+       /**
+        * Make any changes into a pending state
+        *
+        * If strict pending is turned on, any changes that were made will
+        * have to fall into a pending state.  to do that we need to drop
+        * any update into the updates table.
+        *
+     * @param array $values submitted form values
+     *
+     * @return boolean
+        * @access Protected
+        */
+       protected function createPendingData($values)
+       {
+               try {
+                       $this->handleReconsiderations($values);
+                       //      Find out all the amenities the member currently has.
+                       $sql = "
+                SELECT *
+                  FROM {$this->tableName}
+                 WHERE member_id = :member_id";
+                       $stmt = $this->dbh->prepare($sql);
+                       $stmt->bindParam(':member_id', $_GET['id'], PDO::PARAM_INT);
+                       $stmt->execute();
+                       while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                               $currentAmenities[] = $row['amenity_id'];
+                       }
+
+                       //      If they have no amenities already, we need to make sure
+                       //      this variable is at least defined as an array so
+                       //      we can get the array_diff.
+                       if (empty($currentAmenities)) {
+                               $currentAmenities = array();
+                       }
+                       //      Find out all the new amenities that where added.
+                       $alteredAmenities
+                = array_diff(array_keys($values), $currentAmenities);
+
+                       $sql = "
+                SELECT *
+                  FROM amenity
+                 WHERE amenity_id = :aid";
+                       $fetchStmt = $this->dbh->prepare($sql);
+                       $sql = "
+                               INSERT INTO member_updates (update, data_type, member_id, field,
+                               db_table, label)
+                               VALUES (:update, 'string', :member_id, :field, :table, :label)";
+                       $stmt = $this->dbh->prepare($sql);
+                       $stmt->bindParam(':member_id', $_GET['id'], PDO::PARAM_INT);
+                       $stmt->bindParam(':table', $this->tableName, PDO::PARAM_STR);
+                       //      If any amenities where added
+                       //      insert them into the updates table to be in a pending state.
+                       if (!empty($alteredAmenities)) {
+                               $update = 1;
+                               $stmt->bindParam(':update', $update, PDO::PARAM_STR);
+                               foreach ($alteredAmenities as $k => &$v) {
+                                       $fetchStmt->bindParam(':aid', $v, PDO::PARAM_INT);
+                                       $fetchStmt->execute();
+                                       $row = $fetchStmt->fetch(PDO::FETCH_ASSOC);
+
+                                       $stmt->bindParam(':label', $row['amenity_name'], PDO::PARAM_STR);
+                                       $stmt->bindParam(':field', $v, PDO::PARAM_STR);
+                                       $stmt->execute();
+                               }
+                       }
+
+                       $update = 0;
+                       //      If any amenities are removed
+                       //      insert the removal request into the updates table
+                       //      so it can be in a pending state.
+                       foreach ($currentAmenities as $k => &$v) {
+                               if (!array_key_exists($v, $values)) {
+                                       $fetchStmt->bindParam(':aid', $v, PDO::PARAM_INT);
+                                       $fetchStmt->execute();
+                                       $row = $fetchStmt->fetch(PDO::FETCH_ASSOC);
+
+                                       $stmt->bindParam(':update', $update, PDO::PARAM_STR);
+                                       $stmt->bindParam(':label', $row['amenity_name'], PDO::PARAM_STR);
+                                       $stmt->bindParam(':field', $v, PDO::PARAM_STR);
+                                       $stmt->execute();
+                               }
+                       }
+
+                       return true;
+               } catch (PDOException $e) {
+                       return Toolkit_Common::handleError($e);
+               }
+       }
+
+       //      }}}
+       //      {{{ configureDefaults()
+
+       /**
+        * Loads default values for the form
+        *
+        * Setup the members default values to load into the form.
+        * Copy all these values, which is the old data we will use to compare
+        * against when the form is submitted to determine the updates, into
+        * the oldData variable.
+        *
+        * @return void
+        * @access protected
+        */
+       public function configureDefaults()
+       {
+               $this->oldData = parent::configureDefaults();
+
+               try {
+                       $sql = "
+                SELECT field, \"update\"
+                  FROM member_updates
+                 WHERE id in (
+                                               SELECT max(id)
+                                                 FROM member_updates
+                 GROUP BY field)
+                   AND member_id = :member_id
+                                  AND db_table = '{$this->tableName}'";
+                       $stmt = $this->dbh->prepare($sql);
+                       $stmt->bindParam(':member_id', $_GET['id'], PDO::PARAM_INT);
+                       $stmt->execute();
+                       while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                               $this->oldData[$row['field']] = (bool)$row['update'];
+                       }
+                       $this->setupDefaults($this->oldData);
+               } catch (PDOException $e) {
+                       Toolkit_Common::handleError($e);
+               }
+       }
+
+       //      }}}
+
+       //      {{{ fieldPending()
+
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @param object $elem Parameter description (if any) ...
+     *
+     * @return mixed  Return description (if any) ...
+     * @access public
+     */
+       public function fieldPending($elem)
+       {
+               if (!is_array($this->pendingFields)) {
+                       return false;
+               } else {
+                       return in_array($elem->label, $this->pendingFields);
+               }
+       }
+
+       // }}}
+
+       //      {{{ handleReconsiderations()
+
+       /**
+        * Makes amenities not pending anymore if they get re-added or re-removed
+        *
+        * These two cases both reset the field to its previous state before
+        * the original request was made.  So it bypasses the need of an admin
+        * to approve or reject the request, removes the fields from the updates
+        * table, and resets the field to its state before the request was made.
+        *
+        * If an amenity was added and removed before the admin has the ability
+        * to approve the request, we remove the field from the updates table
+        * and reset the pending field.  Same happens if an amenity is removed
+        * and added back in before the admin approves the removal request.
+        *
+        * @param mixed $values array of values submitted from the form.
+     *
+     * @return void
+        * @access public
+        */
+       protected function handleReconsiderations($values)
+       {
+               //      Get all the latest updates for this member's amenities.
+               $sql = "
+            SELECT *
+              FROM member_updates
+             WHERE id IN (
+                                       SELECT max(id)
+                                         FROM member_updates
+                                        WHERE member_id      = :member_id
+             GROUP BY field)
+               AND db_table = '{$this->tableName}'";
+               $stmt = $this->dbh->prepare($sql);
+               $stmt->bindParam(':member_id', $_GET['id'], PDO::PARAM_INT);
+               $stmt->execute();
+               while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                       $updates[$row['field']] = $row;
+               }
+
+               //      Set up the query to remove an amenity.
+               $sql = "
+                       DELETE FROM member_updates
+                        WHERE field    = :field
+                          AND db_table = '{$this->tableName}'
+                          AND member_id = :member_id";
+               $delStmt = $this->dbh->prepare($sql);
+               $delStmt->bindParam(':member_id', $_GET['id'], PDO::PARAM_INT);
+               //      This case can only happen if there are updates pending.
+               //      If any amenities were re-added or re-removed, then remove
+               //      them from the updates table cause they went back to their
+               //      original state.
+               if (!empty($updates)) {
+                       $updateableAmenities = array_keys($updates);
+                       foreach ($updateableAmenities as $k => &$v) {
+                               $keyExists = array_key_exists($v, $values);
+                               $adding = (bool) $updates[$v]['update'];
+                               //      Removed then added back in.
+                               $reAdded = (!$keyExists && $adding) ? true : false;
+                               //      Added then removed again.
+                               $reRemoved = ($keyExists && !$adding) ? true : false;
+
+                               if ($reAdded || $reRemoved) {
+                                       $delStmt->bindParam(':field', $v, PDO::PARAM_INT);
+                                       $delStmt->execute();
+                               }
+                       }
+               }
+       }
+
+       //      }}}
+
+       //      {{{ processData()
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @param array $values Parameter description (if any) ...
+     *
+     * @return void
+     * @access public
+     */
+       public function processData($values)
+       {
+               $this->tableMetaData = Toolkit_Common::getTableMetaData(
+            $this->dbh,
+            $this->tableName
+        );
+
+               foreach ($values as $k => $v) {
+                       switch ($k) {
+                       default :
+                               if (preg_match('/^.+_rmv$/', $k)) {
+                                       unset($values[$k]);
+                               }
+                               break;
+                       }
+               }
+
+               $this->dbh->beginTransaction();
+               if ($this->updateData($values)) {
+                       // insert into pending table
+                       $this->dbh->commit();
+               } else {
+                       $this->dbh->rollBack();
+               }
+
+               $listPage = MEDIA_BASE_URL .
+            "members-only-area/?rt=EditProfile&tab=amenities";
+               header("Location: $listPage");
+       }
+
+       //      }}}
+
+       //      {{{ setPending()
+
+       /**
+        * Determines if the member has made any update requests
+        *
+        * Any update requests that are in the member_updates table
+        * will let us know if this member is pending or not.
+        *
+     * @return void
+        * @access protected
+        * @see    Toolkit_FormBuilder::setupElements()
+        */
+       public function setPending()
+       {
+               unset ($this->pendingFields);
+               try {
+                       $sql = "
+                SELECT DISTINCT label
+                  FROM {$this->pendingTable}
+                 WHERE member_id      = :member_id
+                   AND db_table = '{$this->tableName}'";
+                       $stmt = $this->dbh->prepare($sql);
+                       $stmt->bindParam(':member_id', $_GET['id'], PDO::PARAM_INT);
+                       $stmt->execute();
+                       while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                               $this->pendingFields[] = $row['label'];
+                       }
+                       $this->pending = (count($this->pendingFields)) ? true : false;
+               } catch (PDOException $e) {
+                       Toolkit_Common::handleError($e);
+               }
+       }
+
+       //      }}}
+       //      {{{ setupRenderers()
+
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @return void
+     * @access protected
+     */
+       protected function setupRenderers()
+       {
+               $renderer = new HTML_QuickForm_Renderer_Object(true);
+
+               $this->accept($renderer);
+
+               $this->template = new HTML_Template_Flexy($this->flexyOptions);
+
+        $m = new Toolkit_Members_RecordNavigation($this->config);
+        $m->setupUserNavStructure();
+               //      Make the view a copy of the $this object
+               //      That way we have access to call functions in
+               //      this class from within the template.
+               $this->view = $this;
+               $this->view->form = $renderer->toObject();
+        $this->view->nav = $m->getPageNav();
+               $this->template->compile($this->formTemplate);
+       }
+
+       //      }}}
+
+       //      {{{ updateData()
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @param unknown $values Parameter description (if any) ...
+     *
+     * @return unknown   Return description (if any) ...
+     * @access public
+     */
+       public function updateData($values)
+       {
+               $id = $GLOBALS['memberAuth']->getAuthData('member_id');
+        $cache = new Cache_Lite(Toolkit_Members::getCacheOptions());
+        $cache->remove("Member-$id", 'Profile');
+
+               if ($this->strictPending) {
+                       $this->emailOwner();
+                       return $this->createPendingData($values);
+               } else {
+                       return parent::insertData($values);
+               }
+       }
+
+       //      }}}
+}
+?>
diff --git a/Toolkit/Members/EditMemberOnlyContacts.php b/Toolkit/Members/EditMemberOnlyContacts.php
new file mode 100644 (file)
index 0000000..2796fd4
--- /dev/null
@@ -0,0 +1,722 @@
+<?php
+//     vim:set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker syntax=php:
+
+/**
+ * File Doc Comment
+ *
+ * PHP version 5
+ *
+ * @category MembersDB
+ * @package  Toolkit_Members
+ * @author   Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @license     http://www.gaslightmedia.com Gaslightmedia
+ * @version  CVS: $Id: EditMemberOnlyContacts.php,v 1.15 2010/07/18 16:45:11 jamie Exp $
+ * @link        http://demo.gaslightmedia.com
+ */
+
+/**
+ * Handle associating contacts along with member records
+ *
+ * Controls all aspects of creating and rendering the form used to manipulate
+ * the business contacts. Form is not rendered until the user is added into the Database.
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2008 Gaslight Media
+ * @license      http://www.gaslightmedia.com Gaslightmedia
+ * @link         http://demo.gaslightmedia.com
+ */
+class Toolkit_Members_EditMemberOnlyContacts
+       extends Toolkit_Members_EditMemberOnlyInfo implements Toolkit_Form
+{
+       //      {{{ properties
+
+       /**
+        * The table name in the database used to store the data of the files
+        *
+        * @var string
+        * @access public
+        */
+       public $tableName = 'member_contacts';
+
+       /**
+        * The template used to render the form
+        *
+        * @var string
+        * @access protected
+        */
+       protected $formTemplate = 'editContacts.tpl';
+
+       //      }}}
+       //      {{{ __construct()
+
+       /**
+        * Class constructor
+        *
+     * @param PDO    $pdo         PHP Data Object to use for DB calls
+     * @param string $formName    Form's name.
+     * @param string $method      (optional)Form's method defaults to 'POST'
+     * @param string $action      (optional)Form's action
+     * @param string $target      (optional)Form's target defaults to '_self'
+     * @param mixed  $attributes  (optional)Extra attributes for <form> tag
+     * @param bool   $trackSubmit (optional)Whether to track if the form was
+        *                                                        submitted by adding a special hidden field
+     *
+        * @access public
+        */
+       public function __construct(
+        PDO $pdo,
+        $formName,
+        $method = 'post',
+        $action = '',
+        $target = '',
+        $attributes = null,
+        $trackSubmit = false
+    ) {
+               parent::__construct(
+            $pdo,
+            $formName,
+            $method,
+            $action,
+            $target,
+            $attributes,
+            $trackSubmit
+        );
+       }
+
+       //      }}}
+
+       //      {{{ configureDefaults()
+
+       /**
+        * Sets the defaults for elements in the form.
+        *
+     * @return void
+        * @access public
+        */
+       public function configureDefaults()
+       {
+               try {
+                       if (isset($_GET['cid'])) {
+                               $sql = "
+                                       select * from {$this->tableName} where id = :cid";
+                               $stmt = $this->dbh->prepare($sql);
+                               $stmt->bindParam(':cid', $_GET['cid'], PDO::PARAM_INT);
+                               $stmt->execute();
+                               $row = $stmt->fetch(PDO::FETCH_ASSOC);
+                               $defaults['id']                 = $row['id'];
+                               $defaults['title']              = $row['title'];
+                               $defaults['fname']              = $row['fname'];
+                               $defaults['lname']              = $row['lname'];
+                               $defaults['email']              = $row['email'];
+                               $defaults['phone']              = $row['phone'];
+                               $defaults['send_mail']  = $row['send_mail'];
+                       }
+            $sql = "
+              SELECT *, fname || ' ' || lname AS name,
+                     CASE send_mail
+                     WHEN 'true' THEN 'Can Mail'
+                     ELSE 'No Mail'
+                     END AS send_mail
+                FROM {$this->tableName}
+               WHERE member_id = :id
+                          ORDER BY id";
+                       $stmt = $this->dbh->prepare($sql);
+                       $stmt->bindParam(':id', $_GET['id'], PDO::PARAM_INT);
+                       $stmt->execute();
+
+                       $i = 0;
+                       while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                               $defaults["cid$i"]                      = $row['id'];
+                               $defaults["title$i"]            = $row['title'];
+                               $defaults["name$i"]             = $row['name'];
+                               $defaults["email$i"]            = $row['email'];
+                               $defaults["phone$i"]            = $row['phone'];
+                               $defaults["send_mail$i"]        = $row['send_mail'];
+                               ++$i;
+                       }
+                       $this->setupDefaults($defaults);
+               } catch (PDOException $e) {
+                       return Toolkit_Common::handleError($e);
+               }
+       }
+
+       //      }}}
+       //      {{{ configureElements()
+
+       /**
+        * Setup the elements to use on the form.
+        *
+        * @return void
+        * @access public
+        */
+       public function configureElements()
+       {
+        $e = array();
+               $contacts = $this->getMemberContacts();
+               //      All Grouped Elements are created here.
+               $submitText = (is_numeric($_GET['cid'])) ? 'Update' : 'Submit';
+
+               //      All Elements are created here.  This includes group element definitions.
+               $e[] = array(
+            'type' => 'header',
+            'req' => false,
+            'name' => 'fileHdr',
+            'display' => 'Contacts'
+        );
+               $e[] = array(
+            'type' => 'hidden',
+            'req' => false,
+            'name' => 'id'
+        );
+               $e[] = array(
+            'type' => 'text',
+            'req' => false,
+            'name' => 'title',
+            'display' => 'Title',
+            'opts' => array('class' => 'text')
+        );
+               $e[] = array(
+            'type' => 'text',
+            'req' => true,
+            'name' => 'fname',
+            'display' => 'First Name',
+            'opts' => array('class' => 'text')
+        );
+               $e[] = array(
+            'type' => 'text',
+            'req' => true,
+            'name' => 'lname',
+            'display' => 'Last Name',
+            'opts' => array('class' => 'text')
+        );
+               $e[] = array(
+            'type' => 'text',
+            'req' => false,
+            'name' => 'email',
+            'display' => 'Email',
+            'opts' => array('class' => 'text')
+        );
+               $e[] = array(
+            'type' => 'text',
+            'req' => false,
+            'name' => 'phone',
+            'display' => 'Phone',
+            'opts' => array('class' => 'text')
+        );
+               $e[] = array(
+            'type' => 'advcheckbox',
+            'req' => false,
+            'name' => 'send_mail',
+            'display' => 'Receive Mail',
+            'val' => array(0,
+            1)
+        );
+               $e[] = array(
+            'type' => 'submit',
+            'req' => false,
+            'name' => 'submit',
+            'display' => $submitText,
+            'opts' => array('class' => 'submit')
+        );
+               if (is_array($contacts)) {
+                       foreach ($contacts as $k => $v) {
+                               $e[] = array(
+                    'type' => 'header',
+                    'req' => false,
+                    'name' => "fileHdr$k"
+                );
+                               $e[] = array(
+                    'type' => 'static',
+                    'req' => false,
+                    'name' => "cid$k"
+                );
+                               $e[] = array(
+                    'type' => 'static',
+                    'req' => false,
+                    'name' => "name$k",
+                    'display' => 'Name'
+                );
+                               $e[] = array(
+                    'type' => 'static',
+                    'req' => false,
+                    'name' => "title$k"
+                );
+                               $e[] = array(
+                    'type' => 'static',
+                    'req' => false,
+                    'name' => "email$k",
+                    'display' => 'Email'
+                );
+                               $e[] = array(
+                    'type' => 'static',
+                    'req' => false,
+                    'name' => "phone$k",
+                    'display' => 'Phone'
+                );
+                               $e[] = array(
+                    'type' => 'static',
+                    'req' => false,
+                    'name' => "send_mail$k",
+                    'display' => 'Receive Mail'
+                );
+                       }
+               }
+
+               $this->setupElements($e);
+       }
+
+       //      }}}
+       //      {{{ configureRules()
+
+       /**
+        * Sets up all the rules to be used when the form is validated.
+        *
+     * @return void
+        * @access public
+        */
+       public function configureRules()
+       {
+        //  form rules
+        $r = array();
+               $this->registeredRules = array('phone', 'email');
+               $r[] = array(
+            'element' => 'email',
+            'message' => 'ERROR: Invalid Email Address!',
+            'type' => 'email',
+            'format' => null,
+            'validation' => $this->validationType,
+            'reset' => false,
+            'force' => false
+        );
+        /*
+               $r[] = array(
+            'element' => 'phone',
+            'message' => 'ERROR: Invalid Phone Number!',
+            'type' => 'phone',
+            'format' => null,
+            'validation' => $this->validationType,
+            'reset' => false,
+            'force' => false
+        );
+        */
+               $this->setupRules($r);
+       }
+
+       //      }}}
+
+       //      {{{     getDelUrl()
+
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @param object $sec Parameter description (if any) ...
+     *
+     * @return mixed  Return description (if any) ...
+     * @access public
+     */
+       public function getDelUrl($sec)
+       {
+               return $_SERVER['REQUEST_URI'] . "&d=t&cid={$sec->elements[0]->html}";
+       }
+
+       //      }}}
+       //      {{{     getEditUrl()
+
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @param object $sec Parameter description (if any) ...
+     *
+     * @return mixed  Return description (if any) ...
+     * @access public
+     */
+       public function getEditUrl($sec)
+       {
+               return $_SERVER['REQUEST_URI'] . "&cid={$sec->elements[0]->html}";
+       }
+
+       //      }}}
+       //      {{{     getMailIcon()
+
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @param object $sec Parameter description (if any) ...
+     *
+     * @return string Return description (if any) ...
+     * @access public
+     */
+       public function getMailIcon($sec)
+       {
+        if ($sec->html == 'No Mail') {
+            $icon = 'delete';
+            $title = 'Does not receive newsletters';
+        } else {
+            $icon = 'add';
+            $title = 'Receives newsletters';
+        }
+        return '<img width="16" height="16" title="'.$title.'"
+                   class="mail" alt="mail"
+                   src="'. MEDIA_APP_BASE_URL . "assets/icons/email_$icon.png" . '">';
+       }
+
+       //      }}}
+       //      {{{ getMemberContacts()
+
+       /**
+        * Get all the contacts data uploaded to a member into an array
+        *
+     * @return array member contacts
+        * @access protected
+        */
+       protected function getMemberContacts()
+       {
+               try {
+                       $sql = "
+                SELECT *
+                  FROM {$this->tableName}
+                 WHERE member_id = :id
+                                ORDER BY id";
+                       $stmt = $this->dbh->prepare($sql);
+                       $stmt->bindParam(':id', $_GET['id'], PDO::PARAM_INT);
+                       $stmt->execute();
+
+                       while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                               $contacts[] = $row;
+                       }
+                       return $contacts;
+               } catch (PDOException $e) {
+                       return Toolkit_Common::handleError($e);
+               }
+       }
+
+       //      }}}
+
+       //      {{{ insertData()
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @param array $values Parameter description (if any) ...
+     *
+     * @return object    Return description (if any) ...
+     * @access public
+     */
+       public function insertData($values)
+       {
+               unset(
+                       $values['id'],
+                       $values['target'],
+                       $values['submit']
+               );
+               $values['member_id'] = $_GET['id'];
+               try {
+                       $params = implode(', ', array_keys($values));
+                       $bindParams = ':' . implode(', :', array_keys($values));
+                       $sql = "
+                               INSERT INTO {$this->tableName} ($params)
+                               VALUES ($bindParams)";
+                       $stmt = $this->dbh->prepare($sql);
+                       foreach ($values as $k => $v) {
+                               $metaData = $this->tableMetaData[$k];
+                               if ($metaData == 'integer') {
+                                       $dataType = PDO::PARAM_INT;
+                               } else if ($metaData == 'boolean') {
+                                       $dataType = PDO::PARAM_BOOL;
+                               } elseif ($metaData == 'double precision') {
+                                       $dataType = null;
+                               } else {
+                                       $dataType = PDO::PARAM_STR;
+                               }
+                               $stmt->bindParam(":$k", $values[$k], $dataType);
+                       }
+                       return $stmt->execute();
+               } catch (PDOException $e) {
+                       return Toolkit_Common::handleError($e);
+               }
+    }
+
+        //     }}}
+       //      {{{     isEmail()
+
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @param unknown $name Parameter description (if any) ...
+     *
+     * @return mixed   Return description (if any) ...
+     * @access public
+     */
+       public function isEmail($name)
+       {
+               return (substr($name, 0, 5) == 'email');
+       }
+
+       //      }}}
+       //      {{{ isForm()
+
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @param unknown $sec Parameter description (if any) ...
+     *
+     * @return unknown Return description (if any) ...
+     * @access public
+     */
+       public function isForm($sec)
+       {
+               return !$sec;
+       }
+
+       //      }}}
+       //      {{{     isName()
+
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @param unknown $name Parameter description (if any) ...
+     *
+     * @return mixed   Return description (if any) ...
+     * @access public
+     */
+       public function isName($name)
+       {
+               return (substr($name, 0, 4) == 'name');
+       }
+
+       //      }}}
+       //      {{{     isPhone()
+
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @param unknown $name Parameter description (if any) ...
+     *
+     * @return mixed   Return description (if any) ...
+     * @access public
+     */
+       public function isPhone($name)
+       {
+               return (substr($name, 0, 5) == 'phone');
+       }
+
+       //      }}}
+       //      {{{     isTitle()
+
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @param unknown $name Parameter description (if any) ...
+     *
+     * @return mixed   Return description (if any) ...
+     * @access public
+     */
+       public function isTitle($name)
+       {
+               return (substr($name, 0, 5) == 'title');
+       }
+
+       //      }}}
+
+       //      {{{ processData()
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @param array $values Parameter description (if any) ...
+     *
+     * @return void
+     * @access public
+     */
+       public function processData($values)
+       {
+               $this->tableMetaData = Toolkit_Common::getTableMetaData(
+            $this->dbh,
+            $this->tableName
+        );
+               if (empty($values['id'])) {
+                       $this->insertData($values);
+               } else {
+                       $this->updateData($values);
+               }
+               $listPage = MEDIA_BASE_URL .
+            "members-only-area/?rt=EditProfile&tab=contacts";
+               header("Location: $listPage");
+       }
+
+       //      }}}
+
+       //      {{{ removeContact()
+
+    /**
+     * Remove a contact from a member record
+     *
+     * @param integer $cid contact id
+     * @param integer $mid member id
+     *
+     * @return void
+     * @access public
+     */
+       public function removeContact($cid, $mid)
+       {
+               try {
+                       $sql = "
+                DELETE FROM {$this->tableName}
+                 WHERE id = :cid
+                   AND member_id = :mid";
+                       $stmt = $this->dbh->prepare($sql);
+                       $stmt->bindParam(':cid', $cid, PDO::PARAM_INT);
+                       $stmt->bindParam(':mid', $mid, PDO::PARAM_INT);
+                       $stmt->execute();
+                       $target = MEDIA_BASE_URL .
+                               "members-only-area/?rt=EditProfile&tab=contacts";
+                       header("Location: $target");
+               } catch (PDOException $e) {
+                       return Toolkit_Common::handleError($e);
+               }
+       }
+
+       //      }}}
+
+       //      {{{ setupRenderers()
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @return void
+     * @access protected
+     */
+       protected function setupRenderers()
+       {
+               $renderer = new HTML_QuickForm_Renderer_Object(true);
+
+               $this->accept($renderer);
+
+               $this->template = new HTML_Template_Flexy($this->flexyOptions);
+
+        $m = new Toolkit_Members_RecordNavigation($this->config);
+        $m->setupUserNavStructure();
+               //      Make the view a copy of the $this object
+               //      That way we have access to call functions in
+               //      this class from within the template.
+               $this->view = $this;
+               $this->view->form = $renderer->toObject();
+        $this->view->nav = $m->getPageNav();
+               $this->template->compile($this->formTemplate);
+       }
+
+       //      }}}
+       //      {{{ show()
+
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @return void
+     * @access public
+     */
+       public function show()
+       {
+               if (isset($_GET['d'])) {
+                       $this->removeContact($_GET['cid']);
+                       $target = MEDIA_BASE_URL .
+                               "members-only-area/?rt=EditProfile&tab=contacts";
+                       header("Location: $target");
+               }
+               Toolkit_Common::show();
+       }
+
+       //      }}}
+
+       //      {{{ updateData()
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @param array $values Parameter description (if any) ...
+     *
+     * @return object    Return description (if any) ...
+     * @access public
+     */
+       public function updateData($values)
+       {
+               if (array_key_exists('delete', $values)) {
+                       $this->deleteFile($values);
+               }
+
+               unset(
+                       $values['target'],
+                       $values['submit']
+               );
+
+               try {
+                       $params = array_keys($values);
+                       $length = count($params);
+                       for ($i = 0; $i < $length; ++$i) {
+                               $bindParams .= "{$params[$i]} = :{$params[$i]}";
+                if ($i < ($length - 1)) {
+                    $bindParams .= ', ';
+                }
+                       }
+                       $sql = "
+                               UPDATE {$this->tableName}
+                                  SET $bindParams
+                                WHERE id = :id";
+                       $stmt = $this->dbh->prepare($sql);
+                       $stmt->bindParam(':id', $pid, PDO::PARAM_INT);
+                       foreach ($values as $k => $v) {
+                               $metaData = $this->tableMetaData[$k];
+                               if ($metaData == 'integer') {
+                                       $dataType = PDO::PARAM_INT;
+                               } else if ($metaData == 'boolean') {
+                                       $dataType = PDO::PARAM_BOOL;
+                               } else {
+                                       $dataType = PDO::PARAM_STR;
+                               }
+                               $stmt->bindParam(":$k", $values[$k], $dataType);
+                       }
+                       return $stmt->execute();
+               } catch (PDOException $e) {
+                       return Toolkit_Common::handleError($e);
+               }
+       }
+
+       //      }}}
+}
+?>
diff --git a/Toolkit/Members/EditMemberOnlyFile.php b/Toolkit/Members/EditMemberOnlyFile.php
new file mode 100644 (file)
index 0000000..67628a4
--- /dev/null
@@ -0,0 +1,428 @@
+<?php
+//     vim:set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker syntax=php:
+
+/**
+ * File Doc Comment
+ *
+ * PHP version 5
+ *
+ * @category MembersDB
+ * @package  Toolkit_Members
+ * @author   Steve Sutton <steve@gaslightmedia.com>
+ * @author   Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @license     http://www.gaslightmedia.com Gaslightmedia
+ * @version  CVS: $Id: EditMemberOnlyFile.php,v 1.14 2010/07/18 16:45:23 jamie Exp $
+ * @link        http://demo.gaslightmedia.com
+ */
+
+/**
+ * Controls for uploading / editing files for a business record
+ *
+ * Controls all aspects of creating and rendering the form used to manipulate
+ * the business files. Form is not rendered until the user is added into the
+ * Database.
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @author    Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2008 Gaslight Media
+ * @license      http://www.gaslightmedia.com Gaslightmedia
+ * @link         http://demo.gaslightmedia.com
+ * @see       Toolkit_Members_EditMemberInfo, Toolkit_Members_EditMemberFile
+ */
+class Toolkit_Members_EditMemberOnlyFile
+       extends Toolkit_Members_EditMemberFile implements Toolkit_Form
+{
+       //      {{{ __construct()
+
+       /**
+        * Class constructor
+        *
+     * @param PDO    $pdo         PHP Data Object to use for DB calls
+     * @param string $formName    Form's name.
+     * @param string $method      (optional)Form's method defaults to 'POST'
+     * @param string $action      (optional)Form's action
+     * @param string $target      (optional)Form's target defaults to '_self'
+     * @param mixed  $attributes  (optional)Extra attributes for <form> tag
+     * @param bool   $trackSubmit (optional)Whether to track if the form was
+        *                                                        submitted by adding a special hidden field
+     *
+        * @access public
+        */
+       public function __construct(
+        PDO $pdo,
+        $formName,
+        $method = 'post',
+        $action = '',
+        $target = '',
+        $attributes = null,
+        $trackSubmit = false
+    ) {
+               parent::__construct(
+            $pdo,
+            $formName,
+            $method,
+            $action,
+            $target,
+            $attributes,
+            $trackSubmit
+        );
+               $this->setPending();
+       }
+
+       //      }}}
+
+       //      {{{ configureDefaults()
+
+       /**
+        * Sets the defaults for elements in the form
+        *
+        * Handles setting the normal defaults by way of the parents function.
+        * Then overrides any caption updates by grabbing the latest update
+        * submitted for any caption in the updates table and sets those as
+        * the defaults instead.
+        *
+        * @return void
+        * @access public
+        */
+       public function configureDefaults()
+       {
+               parent::configureDefaults();
+               try {
+                       //      Get all the latest file name updates for the files
+                       //      EXCEPT any file that has an update of [0].
+                       //      zero denotes a newly uploaded file and doesn't yet have
+                       //      a caption.
+                       $sql = "
+                SELECT 'mu.update', mf.id
+                  FROM {$this->pendingTable} mu, {$this->tableName} mf
+                 WHERE mu.id in (
+                                               SELECT max(id)
+                                                 FROM {$this->pendingTable}
+                                                WHERE db_table  =  '{$this->tableName}'
+                                                  AND member_id       =  :member_id
+                                                GROUP BY foreign_key)
+                   AND mu.field = 'file_name'
+                   AND mu.foreign_key = mf.id";
+                       $stmt = $this->dbh->prepare($sql);
+                       $stmt->bindParam(':member_id', $_GET['id'], PDO::PARAM_INT);
+                       $stmt->execute();
+
+                       while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                               $fields[] = $row;
+                       }
+
+                       if (!empty($fields)) {
+                               $sql = "
+                                       SELECT count(*)
+                                         FROM {$this->tableName}
+                                        WHERE id      < :id
+                                          AND member_id     = :member_id";
+                               $stmt = $this->dbh->prepare($sql);
+                               $stmt->bindParam(':member_id', $_GET['id'], PDO::PARAM_INT);
+                               foreach ($fields as &$f) {
+                                       $stmt->bindParam(':id', $f['id'], PDO::PARAM_INT);
+                                       $stmt->execute();
+                                       $row = $stmt->fetch(PDO::FETCH_ASSOC);
+                                       $defaults["file_name{$row['count']}"] = $f['update'];
+                               }
+                               $this->setupDefaults($defaults);
+                       }
+               } catch (PDOException $e) {
+                       return Toolkit_Common::handleError($e);
+               }
+       }
+
+       //      }}}
+
+       //      {{{ insertData()
+
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @param array $values Parameter description (if any) ...
+     *
+     * @return mixed     Return description (if any) ...
+     * @access public
+     */
+       public function insertData($values)
+       {
+               if ($this->strictPending) {
+                       try {
+                               $this->dbh->beginTransaction();
+                               //      Insert the file the normal way, w/ pending being true.
+                               parent::insertData($values, $pending = true);
+
+                               //      Get the last file inserted.
+                               $sql = "
+                                       SELECT *
+                                         FROM {$this->tableName}
+                                        WHERE pending = true
+                                          AND member_id     = :member_id
+                                        ORDER BY id DESC";
+                               $stmt = $this->dbh->prepare($sql);
+                               $stmt->bindParam(':member_id', $_GET['id'], PDO::PARAM_INT);
+                               $stmt->execute();
+                               $file = $stmt->fetch(PDO::FETCH_ASSOC);
+
+                               //      Insert the file into the updates table for approval.
+                               $fname = empty($values['file_name']) ?
+                                                       $values['file_rmv']['name'] :
+                                                       $values['file_name'];
+                               $imgTag = '<img src="'.MEDIA_BASE_URL.'images/file-ext/pdf.png"
+                                       alt="pdf Image" style="display: inline;">';
+                               $label = '<div class="pendingFileName">
+                                                       <a href="' . MEDIA_BASE_URL . $this->uploadDir .
+                                               $values['uploaded_file_rmv'].'" target="_blank">' .
+                                               $imgTag . $fname . '</a></div>';
+                               $update = false;
+                               $dataType = 'boolean';
+                               $field = 'pending';
+                               $sql =  "
+                                       INSERT INTO {$this->pendingTable}
+                                               (member_id, field, update, db_table, data_type, label, foreign_key)
+                                       VALUES
+                                               (:member_id, :field, :update, :db_table, :data_type, :label, :foreign_key)";
+
+                               $stmt = $this->dbh->prepare($sql);
+                               $stmt->bindParam(':member_id', $_GET['id'], PDO::PARAM_INT);
+                               $stmt->bindParam(':field', $field, PDO::PARAM_STR);
+                               $stmt->bindParam(':update', $update, PDO::PARAM_BOOL);
+                               $stmt->bindParam(':db_table', $this->tableName, PDO::PARAM_STR);
+                               $stmt->bindParam(':data_type', $dataType, PDO::PARAM_STR);
+                               $stmt->bindParam(':label', $label, PDO::PARAM_STR);
+                               $stmt->bindParam(':foreign_key', $file['id'], PDO::PARAM_STR);
+                               $stmt->execute();
+                               $this->emailOwner();
+                               return $this->dbh->commit();
+                       } catch (PDOException $e) {
+                               $this->dbh->rollBack();
+                               return Toolkit_Common::handleError($e);
+                       }
+               } else {
+                       return parent::insertData($values);
+           }
+    }
+
+    // }}}
+
+    // {{{     pendingClass()
+
+       /**
+        * Returns a pending class for the file if it is in a pending state
+        *
+        * The offset passed in is controlled from the loop in the template
+        * that creates each file form.
+     *
+     * @param integer $offset which file we're dealing with
+        *
+     * @return mixed
+        * @access Protected
+        * @see    Toolkit_Members_EditMemberFile::getMemberFiles()
+        */
+    public function pendingClass($offset)
+    {
+        $files = $this->getMemberFiles();
+        //     If the offset is empty (0) then we know we're dealing
+        //     with the upload box.
+        if (empty($offset)) {
+            return;
+        } else {
+            // Otherwise we need to see if this file is pending or not.
+            try {
+                //     Get all the files that are pending in the updates table.
+                $sql = "
+                    SELECT DISTINCT foreign_key AS fid
+                      FROM {$this->pendingTable}
+                     WHERE db_table  = '{$this->tableName}'
+                       AND member_id = :member_id";
+                $stmt = $this->dbh->prepare($sql);
+                $stmt->bindParam(':member_id', $_GET['id'], PDO::PARAM_INT);
+                $stmt->execute();
+                while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                    $pendingFiles[] = $row['fid'];
+                }
+            } catch (PDOException $e) {
+                return Toolkit_Common::handleError($e);
+            }
+
+            --$offset;
+
+            // If our current file is in the pending table
+            // return the pending class.
+            if (   is_array($files)
+                               && is_array($pendingFiles)
+                               && in_array($files[$offset]['id'], $pendingFiles)
+                       ) {
+                return 'pending';
+            } else {
+                               return;
+                       }
+        }
+    }
+
+    // }}}
+       //      {{{ processData()
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @param unknown $values Parameter description (if any) ...
+     *
+     * @return void
+     * @access public
+     */
+       public function processData($values)
+       {
+               $id = $GLOBALS['memberAuth']->getAuthData('member_id');
+        $cache = new Cache_Lite(Toolkit_Members::getCacheOptions());
+        $cache->remove("Member-$id", 'Profile');
+
+               parent::processData($values);
+               $listPage = MEDIA_BASE_URL .
+            "members-only-area/?rt=EditProfile&tab=files";
+               header("Location: $listPage");
+       }
+
+       //      }}}
+
+       //      {{{ setPending()
+
+       /**
+        * Determines if the member has made any update requests
+        *
+        * Any update requests that are in the member_updates table
+        * will let us know if this member is pending or not.
+        *
+     * @return boolean
+        * @access protected
+        */
+       public function setPending()
+       {
+               unset ($this->pendingFields);
+               try {
+                       $sql = "
+                SELECT DISTINCT foreign_key AS fid
+                  FROM {$this->pendingTable}
+                 WHERE member_id      = :member_id
+                   AND db_table = '{$this->tableName}'";
+                       $stmt = $this->dbh->prepare($sql);
+                       $stmt->bindParam(':member_id', $_GET['id'], PDO::PARAM_INT);
+                       $stmt->execute();
+                       while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                               $this->pendingFields[] = $row['fid'];
+                       }
+                       $this->pending = (count($this->pendingFields)) ? true : false;
+                       return true;
+               } catch (PDOException $e) {
+                       return Toolkit_Common::handleError($e);
+               }
+       }
+
+       //      }}}
+       //      {{{ setupRenderers()
+
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @return void
+     * @access protected
+     */
+       protected function setupRenderers()
+       {
+               $renderer = new HTML_QuickForm_Renderer_Object(true);
+
+               $this->accept($renderer);
+
+               $this->template = new HTML_Template_Flexy($this->flexyOptions);
+
+        $m = new Toolkit_Members_RecordNavigation($this->config);
+        $m->setupUserNavStructure();
+               //      Make the view a copy of the $this object
+               //      That way we have access to call functions in
+               //      this class from within the template.
+               $this->view = $this;
+               $this->view->form = $renderer->toObject();
+        $this->view->nav = $m->getPageNav();
+               $this->template->compile($this->formTemplate);
+       }
+
+       //      }}}
+
+       //      {{{ updateData()
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @param array $values Parameter description (if any) ...
+     *
+     * @return mixed     Return description (if any) ...
+     * @access public
+     */
+       public function updateData($values)
+       {
+               if ($this->strictPending) {
+                       if (array_key_exists('delete', $values)) {
+                               return $this->deleteFile($values);
+                       }
+                       try {
+                               $fid = $values["file{$values['target']}"];
+                               $sql = "
+                                       SELECT *
+                                         FROM {$this->tableName}
+                                        WHERE id      = :id";
+                               $stmt = $this->dbh->prepare($sql);
+                               $stmt->bindParam(':id', $fid, PDO::PARAM_INT);
+                               $stmt->execute();
+                               $file = $stmt->fetch(PDO::FETCH_ASSOC);
+
+                               $fname = empty($file['file_name']) ?
+                                                       $file['original_name'] :
+                                                       $file['file_name'];
+                               $imgTag = '<img src="'.MEDIA_BASE_URL.'images/file-ext/pdf.png"
+                                       alt="pdf Image" style="display: inline;">';
+                               $label = '<a href="' . MEDIA_BASE_URL . $this->uploadDir .
+                                               $file['name_on_disk'].'" target="_blank">' .
+                                               $imgTag . $fname . '</a>';
+
+                               $dataType = 'text';
+                               $label = '<div class="pendingFileName">' .
+                                                       $label . 'File Name</div>';
+                               $field = 'file_name';
+                               $sql = "
+                    INSERT INTO {$this->pendingTable}
+                                               (member_id, field, update, db_table, data_type, label, foreign_key)
+                    VALUES
+                                               (:member_id, :field, :update, :db_table, :data_type, :label, :foreign_key)";
+                               $stmt = $this->dbh->prepare($sql);
+                               $stmt->bindParam(':member_id', $_GET['id'], PDO::PARAM_INT);
+                               $stmt->bindParam(':field', $field, PDO::PARAM_INT);
+                               $stmt->bindParam(':update', $values["file_name{$values['target']}"], PDO::PARAM_STR);
+                               $stmt->bindParam(':db_table', $this->tableName, PDO::PARAM_INT);
+                               $stmt->bindParam(':data_type', $dataType, PDO::PARAM_INT);
+                               $stmt->bindParam(':label', $label, PDO::PARAM_INT);
+                               $stmt->bindParam(':foreign_key', $fid, PDO::PARAM_INT);
+                               $this->emailOwner();
+                               return $stmt->execute();
+                       } catch (PDOException $e) {
+                               return Toolkit_Common::handleError($e);
+                       }
+               } else {
+                       return parent::updateData($values);
+               }
+       }
+
+       //      }}}
+}
+?>
diff --git a/Toolkit/Members/EditMemberOnlyInfo.php b/Toolkit/Members/EditMemberOnlyInfo.php
new file mode 100644 (file)
index 0000000..eca64a9
--- /dev/null
@@ -0,0 +1,1272 @@
+<?php
+//     vim:set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker syntax=php:
+
+/**
+ * File Doc Comment
+ *
+ * PHP version 5
+ *
+ * @category MembersDB
+ * @package  Toolkit_Members
+ * @author   Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @license     http://www.gaslightmedia.com Gaslightmedia
+ * @version  CVS: $Id: EditMemberOnlyInfo.php,v 1.27 2010/03/12 18:22:53 jamie Exp $
+ * @link        http://demo.gaslightmedia.com
+ */
+
+/**
+ * Edit member info stored in the database
+ *
+ * Controls all aspects of creating and rendering the form used to manipulate
+ * the member data.  Handles adding / editing module details, including:
+ * 1. Restaurants
+ * 2. Accommodations
+ * 3. Golfing
+ *
+ * Controls the addition of categories to a member record.  Categories have
+ * dynamic settings which allow users to dynamically set which categories are
+ * associated to which modules (golf, accommodations, restaurant, etc...).
+ * Depending on which category is selected and assigned to a member record
+ * controls which modules are displayed on the form.
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2008 Gaslight Media
+ * @license      http://www.gaslightmedia.com Gaslightmedia
+ * @link      http://demo.gaslightmedia.com
+ * @see       Toolkit_Members, member_admin
+ */
+class Toolkit_Members_EditMemberOnlyInfo
+    extends Toolkit_Members_EditMemberInfo implements Toolkit_Form
+{
+       //      {{{ properties
+
+       /**
+        * Set all the tables in the DB that hold the module info
+        *
+        * @var string
+        * @access protected
+        */
+       protected $moduleTables = array(
+               'member_golf' => array(),
+               'member_restaurants' => array(),
+               'member_accommodations' => array(),
+       );
+
+       /**
+        * All the default data
+        *
+        * When we load the default data (member record from the database), we will
+        * store all that data in this variable. That way when we process the form
+        * we will have all the information to compare against, so we can find the
+        * updates.
+        *
+        * @var string
+        * @access protected
+        */
+       protected $oldData;
+
+       //      }}}
+
+       //      {{{ __construct()
+
+       /**
+        * Class constructor
+        *
+     * @param PDO    $pdo         PHP Data Object to use for DB calls
+     * @param string $formName    Form's name.
+     * @param string $method      (optional)Form's method defaults to 'POST'
+     * @param string $action      (optional)Form's action
+     * @param string $target      (optional)Form's target defaults to '_self'
+     * @param mixed  $attributes  (optional)Extra attributes for <form> tag
+     * @param bool   $trackSubmit (optional)Whether to track if the form was
+        *                                                        submitted by adding a special hidden field
+     *
+        * @access public
+        * @see    Toolkit_Members_EditMemberInfo
+        */
+       public function __construct(
+        PDO $pdo,
+        $formName,
+        $method = 'post',
+        $action = '',
+        $target = '',
+        $attributes = null,
+        $trackSubmit = false
+    ) {
+               parent::__construct(
+            $pdo,
+            $formName,
+            $method,
+            $action,
+            $target,
+            $attributes,
+            $trackSubmit
+        );
+               $this->setPending();
+               //      Unset this so it won't get displayed.
+               $this->successMsg = null;
+       }
+
+       //      }}}
+
+       //      {{{ configureDefaults()
+
+       /**
+        * Loads default values for the form
+        *
+        * Setup the members default values to load into the form.
+        * Copy all these values, which is the old data we will use to compare
+        * against when the form is submitted to determine the updates, into
+        * the oldData variable.
+        *
+        * @return array default values
+        * @access protected
+        */
+       public function configureDefaults()
+       {
+               $this->oldData = parent::configureDefaults();
+
+               try {
+                       $sql = "
+                SELECT field, 'update'
+                  FROM {$this->pendingTable}
+                 WHERE id in (
+                                               SELECT max(id)
+                                                 FROM {$this->pendingTable}
+                                                WHERE member_id = :member_id
+                                                  AND (db_table = 'member' OR
+                                                               db_table = 'member_ccard_type' OR
+                                                               db_table = 'member_accommodations' OR
+                                                               db_table = 'member_restaurants' OR
+                                                               db_table = 'member_golf')
+                                                GROUP BY field)";
+                       $stmt = $this->dbh->prepare($sql);
+                       $stmt->bindParam(':member_id', $_GET['id'], PDO::PARAM_INT);
+                       $stmt->execute();
+                       while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                               if ($row['field'] == 'logo') {
+                                       $this->oldData['old_logo_rmv'] = $row['update'];
+                                       $row['update'] = '<img alt="'.$row['update'].'"
+                                               src="'.MEMBER_PHOTOS.$row['update'].'">';
+                                       $row['field'] = 'image_rmv';
+                               }
+                               $this->oldData[$row['field']] = $row['update'];
+                       }
+                       return $this->setupDefaults($this->oldData);
+               } catch (PDOException $e) {
+                       return Toolkit_Common::handleError($e);
+               }
+       }
+
+       //      }}}
+       //      {{{ configureElements()
+
+       /**
+        * Setup the elements to use on the form.
+        *
+        * Categories are populated into the protected class property $categories.
+        * These categories are used to populate the select list of categories.
+        * Modules are configured before any elements are setup, that way we will
+        * know if we need to include them in the rendering of the form.
+        *
+        * @return void
+        * @access public
+        */
+       public function configureElements()
+       {
+               $e = array();
+
+               $singularType = $this->config
+                       ->getItem('section', 'listing type')
+                       ->getItem('directive', 'singular')
+                       ->getContent();
+               $pluralType = $this->config
+                       ->getItem('section', 'listing type')
+                       ->getItem('directive', 'plural')
+                       ->getContent();
+               $useCtrlCities = $this->config
+                       ->getItem('section', 'conf')
+                       ->getItem('directive', 'controlledCities')
+                       ->getContent();
+               $allowRegions = $this->config
+                       ->getItem('section', 'conf')
+                       ->getItem('directive', 'regions')
+                       ->getContent();
+               $usesGlmReservations= $this->config
+                       ->getItem('section', 'conf')
+                       ->getItem('directive', 'glmReservations')
+                       ->getContent();
+
+               $this->setupFormCategories();
+               $this->configureModules();
+
+               $this->getCategories();
+
+               //      All Grouped Elements are created here.
+               //      All Elements are created here.  This includes group element definitions.
+
+               //      {{{ Member Information
+
+               $e[] = array(
+                       'type'    => 'header',
+                       'req'     => false,
+                       'name'    => 'memberInfoHdr',
+                       'display' => "$singularType Information",
+                       'col1'    => true
+               );
+               $e[] = array(
+                       'type'    => 'text',
+                       'req'     => false,
+                       'name'    => 'primary_contact_fname',
+                       'display' => 'Primary Contact First Name',
+                       'opts'    => array('class' => 'text')
+               );
+               $e[] = array(
+                       'type'    => 'text',
+                       'req'     => false,
+                       'name'    => 'primary_contact_lname',
+                       'display' => 'Primary Contact Last Name',
+                       'opts'    => array('class' => 'text')
+               );
+               $e[] = array(
+                       'type'    => 'text',
+                       'req'     => false,
+                       'name'    => 'process_email',
+                       'display' => 'Primary Contact Email',
+                       'opts'    => array('class' => 'text')
+               );
+               $e[] = array(
+                       'type'    => 'text',
+                       'req'     => false,
+                       'name'    => 'phone',
+                       'display' => 'Primary Phone',
+                       'opts'    => array('class' => 'text')
+               );
+               $e[] = array(
+                       'type'    => 'text',
+                       'req'     => false,
+                       'name'    => 'toll_free',
+                       'display' => 'Phone 2',
+                       'opts'    => array('class' => 'text')
+               );
+               $e[] = array(
+                       'type'    => 'text',
+                       'req'     => false,
+                       'name'    => 'fax',
+                       'display' => 'Fax Number',
+                       'opts'    => array('class' => 'text')
+               );
+               $e[] = array(
+                       'type'    => 'text',
+                       'req'     => false,
+                       'name'    => 'member_contact_email',
+                       'display' => 'Email on Website',
+                       'opts'    => array('class' => 'text')
+               );
+               /*
+               $e[] = array(
+                       'type'    => 'select',
+                       'req'     => false,
+                       'name'    => 'country',
+                       'display' => 'Country',
+                       'opts'    => array(
+                               ''       => '-- Select Country --',
+                               'USA'    => 'USA',
+                               'Canada' => 'Canada'
+                       )
+               );
+               */
+               $e[] = array(
+                       'type'    => 'text',
+                       'req'     => false,
+                       'name'    => 'url',
+                       'display' => 'Website Address',
+                       'opts'    => array('class' => 'text'),
+            'noCharLimit' => true
+               );
+
+        //  }}}
+               //      {{{     Member Categories
+
+               $e[] = array(
+                       'type'    => 'header',
+                       'req'     => false,
+                       'name'    => 'memberCategoryHdr',
+                       'display' => "$singularType Categories",
+                       'col1'    => true
+               );
+               $e[] = array(
+                       'type'    => 'static',
+                       'req'     => false,
+                       'name'    => 'member_cats[]',
+                       'opts'    => $this->categories,
+                       'att'     => array('id' => 'categories')
+               );
+
+               //      }}}
+               //      {{{ Physical Address
+
+               $e[] = array(
+                       'type'    => 'header',
+                       'req'     => false,
+                       'name'    => 'physicalAddressHdr',
+                       'display' => 'Street Address',
+               );
+               $e[] = array(
+                       'type'    => 'text',
+                       'req'     => false,
+                       'name'    => 'street',
+                       'display' => 'Street Address',
+                       'opts'    => array('class' => 'text')
+               );
+        if ($useCtrlCities) {
+            $e[] = array(
+                'type'    => 'select',
+                'req'     => true,
+                'name'    => 'city_id',
+                'display' => 'City',
+                'opts'    => array('' => '-- Select --') + $this->cities
+            );
+        } else {
+            $e[] = array(
+                'type'    => 'text',
+                'req'     => true,
+                'name'    => 'city',
+                'display' => 'City',
+                'opts'    => array('class' => 'text')
+            );
+        }
+               $e[] = array(
+                       'type'    => 'select',
+                       'req'     => true,
+                       'name'    => 'state_id',
+                       'display' => 'State / Province',
+                       'opts'    => array('' => '-- Select --') + $this->states
+               );
+               $e[] = array(
+                       'type'    => 'text',
+                       'req'     => true,
+                       'name'    => 'zip',
+                       'display' => 'Zip / Postal Code',
+                       'opts'    => array('class' => 'text')
+               );
+
+        //  }}}
+               //      {{{ Mailing Address
+
+               $e[] = array(
+                       'type'    => 'header',
+                       'req'     => false,
+                       'name'    => 'MailingAddressHdrRmv',
+                       'display' => 'Mailing Address (if different than physical address)',
+               );
+               $e[] = array(
+                       'type'    => 'text',
+                       'req'     => false,
+                       'name'    => 'mailing_address',
+                       'display' => 'Mailing Address',
+                       'opts'    => array('class' => 'text')
+               );
+               $e[] = array(
+                       'type'    => 'text',
+                       'req'     => false,
+                       'name'    => 'mailing_city',
+                       'display' => 'City',
+                       'opts'    => array('class' => 'text')
+               );
+               $e[] = array(
+                       'type'    => 'select',
+                       'req'     => false,
+                       'name'    => 'mailing_state_id',
+                       'display' => 'State / Province',
+                       'opts'    => array('' => '-- Select --') + $this->states
+               );
+               $e[] = array(
+                       'type'    => 'text',
+                       'req'     => false,
+                       'name'    => 'mailing_zip',
+                       'display' => 'Zip / Postal Code',
+                       'opts'    => array('class' => 'text')
+               );
+
+        //  }}}
+               //      {{{ Member Description
+
+               $e[] = array(
+                       'type'    => 'header',
+                       'req'     => false,
+                       'name'    => 'memberDescHdr',
+                       'display' => "$singularType Description",
+                       'col1'    => true
+               );
+               $e[] = array(
+                       'type'    => 'textarea',
+                       'req'     => false,
+                       'name'    => 'description',
+                       'display' => 'Description',
+                       'opts'    => array(
+                               'id'   => 'description',
+                               'rows' => 8,
+                               'cols' => 43
+                       ),
+            'noCharLimit' => true
+               );
+
+        //  }}}
+               //      {{{ Account Info
+
+               $e[] = array(
+                       'type'    => 'header',
+                       'req'     => false,
+                       'name'    => 'accountInfoHdr',
+                       'display' => 'Account Info',
+                       'col2'    => true
+               );
+               $e[] = array(
+                       'type'    => 'text',
+                       'req'     => false,
+                       'name'    => 'member_login',
+                       'display' => 'Username',
+                       'opts'    => array('class' => 'text')
+               );
+               $e[] = array(
+                       'type'    => 'text',
+                       'req'     => false,
+                       'name'    => 'member_passwd',
+                       'display' => 'Password',
+                       'opts'    => array('class' => 'text')
+               );
+
+        //  }}}
+               //      {{{ Main Image
+
+               $e[] = array(
+                       'type'    => 'header',
+                       'req'     => false,
+                       'name'    => 'logoHdr',
+                       'display' => 'Main Image',
+                       'col2'    => true
+               );
+               if ($this->hasLogo()) {
+                       $e[] = array(
+                               'type'    => 'checkbox',
+                               'req'     => false,
+                               'name'    => 'remove_logo_rmv',
+                               'display' => 'Remove Image'
+                       );
+                       $e[] = array(
+                               'type'    => 'static',
+                               'req'     => false,
+                               'name'    => 'image_rmv',
+                               'display' => 'Current Image'
+                       );
+            $e[] = array(
+                               'type' => 'hidden',
+                               'req'  => false,
+                               'name' => 'old_logo_rmv'
+                       );
+               }
+
+               $e[] = array(
+                       'type'    => 'file',
+                       'req'     => false,
+                       'name'    => 'new_logo_rmv',
+                       'display' => 'New Image',
+                       'opts'    => array('class' => 'file')
+               );
+
+        //  }}}
+               //      {{{ Payment Type Accepted
+
+               $e[] = array(
+                       'type'    => 'header',
+                       'req'     => false,
+                       'name'    => 'CreditCardsHdr',
+                       'display' => 'Payment Type Accepted',
+                       'col2'    => true
+               );
+               $e[] = array(
+                       'type'    => 'advcheckbox',
+                       'req'     => false,
+                       'name'    => 'creditCards[American Express]',
+                       'display' => 'American Express',
+                       'val'     => array(0, 1)
+               );
+               $e[] = array(
+                       'type'    => 'advcheckbox',
+                       'req'     => false,
+                       'name'    => 'creditCards[Discover]',
+                       'display' => 'Discover',
+                       'val'     => array(0, 1)
+               );
+               $e[] = array(
+                       'type'    => 'advcheckbox',
+                       'req'     => false,
+                       'name'    => 'creditCards[Master Card]',
+                       'display' => 'Master Card',
+                       'val'     => array(0, 1)
+               );
+               $e[] = array(
+                       'type'    => 'advcheckbox',
+                       'req'     => false,
+                       'name'    => 'creditCards[Visa]',
+                       'display' => 'Visa',
+                       'val'     => array(0, 1)
+               );
+        $e[] = array(
+                       'type'    => 'advcheckbox',
+                       'req'     => false,
+                       'name'    => 'creditCards[Diners]',
+                       'display' => 'Diners'
+               );
+
+        //  }}}
+               //      {{{ Accommodations Information
+
+               if ($this->accommodations) {
+                       $e[] = array(
+                               'type'    => 'header',
+                               'req'     => false,
+                               'name'    => 'AccommodationsHdr',
+                               'display' => 'Accommodations Information',
+                               'col2'    => true
+                       );
+                       $e[] = array(
+                               'type'    => 'text',
+                               'req'     => false,
+                               'name'    => 'reservation_url',
+                               'display' => 'Reservation Url',
+                               'opts'    => array('class' => 'text')
+                       );
+                       if ($usesGlmReservations) {
+                               $e[] = array(
+                                       'type'    => 'text',
+                                       'req'     => false,
+                                       'name'    => 'reservation_id',
+                                       'display' => 'Reservation Id',
+                                       'opts'    => array('class' => 'text')
+                               );
+                       }
+                       $e[] = array(
+                               'type'    => 'text',
+                               'req'     => false,
+                               'name'    => 'num_rooms',
+                               'display' => 'Number of Rooms',
+                               'opts'    => array('class' => 'text')
+                       );
+                       $e[] = array(
+                               'type'    => 'advcheckbox',
+                               'req'     => false,
+                               'name'    => 'year_round',
+                               'display' => 'Open all Year',
+                               'val'     => array(0, 1)
+                       );
+               }
+
+        //  }}}
+               //      {{{ Restaurant Information
+
+               if ($this->restaurant) {
+                       $e[] = array(
+                               'type'    => 'header',
+                               'req'     => false,
+                               'name'    => 'RestaurantHdr',
+                               'display' => 'Restaurant Information',
+                               'col2'    => true
+                       );
+                       $e[] = array(
+                               'type'    => 'advcheckbox',
+                               'req'     => false,
+                               'name'    => 'breakfast',
+                               'display' => 'Breakfast',
+                               'att'     => array('id' => 'breakfast'),
+                               'val'     => array(0, 1)
+                       );
+//                     $e[] = array(
+//                             'type'    => 'text',
+//                             'req'     => false,
+//                             'name'    => 'breakfast_from',
+//                             'display' => 'From',
+//                             'opts'    => array('class' => 'priceFrom')
+//                     );
+//                     $e[] = array(
+//                             'type'    => 'text',
+//                             'req'     => false,
+//                             'name'    => 'breakfast_to',
+//                             'display' => 'To',
+//                             'opts'    => array('class' => 'priceTo')
+//                     );
+                       $e[] = array(
+                               'type'    => 'advcheckbox',
+                               'req'     => false,
+                               'name'    => 'brunch',
+                               'display' => 'Brunch',
+                               'att'     => array('id' => 'brunch'),
+                               'val'     => array(0, 1)
+                       );
+//                     $e[] = array(
+//                             'type'    => 'text',
+//                             'req'     => false,
+//                             'name'    => 'brunch_from',
+//                             'display' => 'From',
+//                             'opts'    => array('class' => 'priceFrom')
+//                     );
+//                     $e[] = array(
+//                             'type'    => 'text',
+//                             'req'     => false,
+//                             'name'    => 'brunch_to',
+//                             'display' => 'To',
+//                             'opts'    => array('class' => 'priceTo')
+//                     );
+                       $e[] = array(
+                               'type'    => 'advcheckbox',
+                               'req'     => false,
+                               'name'    => 'lunch',
+                               'display' => 'Lunch',
+                               'att'     => array('id' => 'lunch'),
+                               'val'     => array(0, 1)
+                       );
+//                     $e[] = array(
+//                             'type'    => 'text',
+//                             'req'     => false,
+//                             'name'    => 'lunch_from',
+//                             'display' => 'From',
+//                             'opts'    => array('class' => 'priceFrom')
+//                     );
+//                     $e[] = array(
+//                             'type'    => 'text',
+//                             'req'     => false,
+//                             'name'    => 'lunch_to',
+//                             'display' => 'To',
+//                             'opts'    => array('class' => 'priceTo')
+//                     );
+                       $e[] = array(
+                               'type'    => 'advcheckbox',
+                               'req'     => false,
+                               'name'    => 'dinner',
+                               'display' => 'Dinner',
+                               'att'     => array('id' => 'dinner'),
+                               'val'     => array(0, 1)
+                       );
+//                     $e[] = array(
+//                             'type'    => 'text',
+//                             'req'     => false,
+//                             'name'    => 'dinner_from',
+//                             'display' => 'From',
+//                             'opts'    => array('class' => 'priceFrom')
+//                     );
+//                     $e[] = array(
+//                             'type'    => 'text',
+//                             'req'     => false,
+//                             'name'    => 'dinner_to',
+//                             'display' => 'To',
+//                             'opts'    => array('class' => 'priceTo')
+//                     );
+                       $e[] = array(
+                               'type'    => 'advcheckbox',
+                               'req'     => false,
+                               'name'    => 'alcohol',
+                               'display' => 'Alcohol',
+                               'val'     => array(0, 1)
+                       );
+//                     $e[] = array(
+//                             'type'    => 'advcheckbox',
+//                             'req'     => false,
+//                             'name'    => 'non_smoking',
+//                             'display' => 'Non-Smoking',
+//                             'val'     => array(0, 1)
+//                     );
+               }
+
+        //  }}}
+               //      {{{ Golf Information
+
+               if ($this->golf) {
+                       $e[] = array(
+                               'type'    => 'header',
+                               'req'     => false,
+                               'name'    => 'GolfingHdr',
+                               'display' => 'Golf Course Information',
+                               'col2'    => true
+                       );
+                       $e[] = array(
+                               'type'    => 'text',
+                               'req'     => false,
+                'name'    => 'res_url',
+                'display' => 'TeeTime URL',
+                'opts'    => array('class' => 'text'),
+               'noCharLimit' => true
+            );
+            $e[] = array(
+                'type'    => 'text',
+                'req'     => false,
+                               'name'    => 'par',
+                               'display' => 'Par',
+                               'opts'    => array('class' => 'text')
+                       );
+                       $e[] = array(
+                               'type'    => 'text',
+                               'req'     => false,
+                               'name'    => 'yardage',
+                               'display' => 'Yardage',
+                               'opts'    => array('class' => 'text')
+                       );
+                       $e[] = array(
+                               'type'    => 'text',
+                               'req'     => false,
+                               'name'    => 'course_rating',
+                               'display' => 'Course Rating',
+                               'opts'    => array('class' => 'text')
+                       );
+                       $e[] = array(
+                               'type'    => 'text',
+                               'req'     => false,
+                               'name'    => 'slope_rating',
+                               'display' => 'Slope Rating',
+                               'opts'    => array('class' => 'text')
+                       );
+                       $e[] = array(
+                               'type'    => 'advcheckbox',
+                               'req'     => false,
+                               'name'    => 'walking_course',
+                               'display' => 'Walking Course',
+                               'val'     => array(0, 1)
+                       );
+                       $e[] = array(
+                               'type'    => 'text',
+                               'req'     => false,
+                               'name'    => 'holes18',
+                               'display' => '18 Holes',
+                               'opts'    => array('class' => 'text')
+                       );
+                       $e[] = array(
+                               'type'    => 'text',
+                               'req'     => false,
+                               'name'    => 'holes9',
+                               'display' => '9 Holes',
+                               'opts'    => array('class' => 'text')
+                       );
+               }
+
+        //  }}}
+        //  {{{ Social Media Links
+
+        $e[] = array(
+            'type' => 'header',
+            'req' => false,
+            'name' => 'SocialMediaHdr',
+            'display' => 'Social Media Links',
+            'col2' => true,
+        );
+        $e[] = array(
+            'type' => 'text',
+            'req' => false,
+            'name' => 'facebook',
+            'display' => 'Facebook',
+            'col2' => true,
+            'noCharLimit' => true
+        );
+        $e[] = array(
+            'type' => 'text',
+            'req' => false,
+            'name' => 'twitter',
+            'display' => 'Twitter',
+            'col2' => true,
+            'noCharLimit' => true
+        );
+        $e[] = array(
+            'type' => 'text',
+            'req' => false,
+            'name' => 'pinterest',
+            'display' => 'Pinterest',
+            'col2' => true,
+            'noCharLimit' => true
+        );
+        $e[] = array(
+            'type' => 'text',
+            'req' => false,
+            'name' => 'instagram',
+            'display' => 'Instagram',
+            'col2' => true,
+            'noCharLimit' => true
+        );
+        $e[] = array(
+            'type' => 'text',
+            'req' => false,
+            'name' => 'google_plus',
+            'display' => 'Google+',
+            'col2' => true,
+            'noCharLimit' => true
+        );
+        $e[] = array(
+            'type' => 'text',
+            'req' => false,
+            'name' => 'linkedin',
+            'display' => 'LinkedIn',
+            'col2' => true,
+            'noCharLimit' => true
+        );
+        $e[] = array(
+            'type' => 'text',
+            'req' => false,
+            'name' => 'blog',
+            'display' => 'Blog',
+            'col2' => true,
+            'noCharLimit' => true
+        );
+        $e[] = array(
+            'type' => 'text',
+            'req' => false,
+            'name' => 'youtube',
+            'display' => 'YouTube',
+            'col2' => true,
+            'noCharLimit' => true
+        );
+        $e[] = array(
+            'type' => 'text',
+            'req' => false,
+            'name' => 'myspace',
+            'display' => 'MySpace',
+            'col2' => true,
+            'noCharLimit' => true
+        );
+
+        //  }}}
+
+               $this->setupElements($e);
+       }
+
+       //      }}}
+
+       //      {{{     processData()
+
+       /**
+        * Handles processing the submitted forms data
+        *
+        * See parent function for better description.
+        *
+        * @param array $values The array of all submitted form values.
+        *
+        * @return boolean Whether the insert/update succeeded.
+        * @access public
+        * @see    Toolkit_Members_EditMemberInfo::processData()
+        */
+       public function processData($values)
+       {
+               $id = $GLOBALS['memberAuth']->getAuthData('member_id');
+        $cache = new Cache_Lite(Toolkit_Members::getCacheOptions());
+        $cache->remove("Member-$id", 'Profile');
+
+               $return = parent::processData($values);
+               $this->setPending();
+               return $return;
+       }
+
+       //      }}}
+
+       //      {{{ setPending()
+
+       /**
+        * Determines if the member has made any update requests
+        *
+        * Any update requests that are in the member_updates table
+        * will let us know if this member is pending or not.
+        *
+     * @return void
+        * @access public
+        */
+       public function setPending()
+       {
+               unset ($this->pendingFields);
+               try {
+                       $sql = "
+                SELECT DISTINCT
+                       CASE field
+                       WHEN 'lat' THEN 'latitude'
+                       WHEN 'lon' THEN 'longitude'
+                       WHEN 'logo' THEN 'image_rmv'
+                       ELSE field
+                       END AS field
+                  FROM {$this->pendingTable}
+                 WHERE member_id        = :member_id
+                                  AND (db_table = '{$this->tableName}'
+                                  OR db_table = 'member_ccard_type'
+                                  OR db_table = 'member_restaurants'
+                                  OR db_table = 'member_accommodations'
+                                  OR db_table = 'member_golf')";
+                       $stmt = $this->dbh->prepare($sql);
+                       $stmt->bindParam(':member_id', $_GET['id'], PDO::PARAM_INT);
+                       $stmt->execute();
+                       while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                               $this->pendingFields[] = $row['field'];
+                       }
+                       $this->pending = (count($this->pendingFields));
+               } catch (PDOException $e) {
+                       Toolkit_Common::handleError($e);
+               }
+       }
+
+       //      }}}
+       //      {{{ setupRenderers()
+
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @return void
+     * @access protected
+     */
+       protected function setupRenderers()
+       {
+               $renderer = new HTML_QuickForm_Renderer_Object(true);
+
+               $this->accept($renderer);
+
+               $this->template = new HTML_Template_Flexy($this->flexyOptions);
+
+        $m = new Toolkit_Members_RecordNavigation($this->config);
+        $m->setupUserNavStructure();
+               //      Make the view a copy of the $this object
+               //      That way we have access to call functions in
+               //      this class from within the template.
+               $this->view = $this;
+               $this->view->form = $renderer->toObject();
+        $this->view->nav = $m->getPageNav();
+               $this->template->compile($this->formTemplate);
+       }
+
+       //      }}}
+       //      {{{ showCategories()
+
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @return string Return description (if any) ...
+     * @access public
+     */
+       public function showCategories()
+       {
+               if (!empty($this->memberCategories)) {
+                       try {
+                               $sql = "
+                                       SELECT *
+                                         FROM category
+                                        WHERE category_id = :cid";
+                               $stmt = $this->dbh->prepare($sql);
+                               foreach ($this->memberCategories as $cid => $v) {
+                                       $stmt->bindParam(':cid', $cid, PDO::PARAM_INT);
+                                       $stmt->execute();
+                                       $category = $stmt->fetch(PDO::FETCH_ASSOC);
+
+                                       $out .= '
+                                               <i>
+                                                       <input id="catid'.$category['category_id'].'"
+                                                               type="hidden" name="member_cats[]"
+                                                               value="'.$category['category_id'].'" >';
+                                       if (   defined('MEMBERS_ONLY_AREA_ON')
+                                               && MEMBERS_ONLY_AREA_ON == true
+                                       ) {
+                                               //      do nothing
+                                       } else {
+                                               $out .= '
+                                                       <input type="image" name="removeCat" height="16" width="16"
+                                                               class="remove" src="'.MEDIA_BASE_URL.'assets/cancel.png"
+                                                               value="'.$category['category_id'].'" >';
+                                       }
+                                       $out .= "{$category['name']}</i>";
+                               }
+                       } catch (PDOException $e) {
+                               return Toolkit_Common::handleError($e);
+                       }
+               }
+
+               return $out;
+       }
+
+       //      }}}
+
+       //      {{{ updateData()
+
+       /**
+        * Insert the values that were updated from the member only form
+        *
+        * 1. Get all the field types in the DB for the module tables
+        * 2. Check the module fields against the old Data (the original values
+        *        we obtained when we got the defaults for the form).  If the
+        *        value is not the same, then add the value to the column field of
+        *        the table in the moduleUpdates array.
+        * 3. Get the updates for the member record.
+        * 4. Insert all the updates into the member_updates table.
+     *
+     * @param array $values submitted form values
+        *
+     * @return mixed
+        * @access public
+        */
+       public function updateData($values)
+       {
+               if ($this->strictPending) {
+                       foreach ($this->moduleTables as $tname => &$arr) {
+                               $this->tableMetaData = Toolkit_Common::getTableMetaData(
+                    $this->dbh,
+                    $tname
+                );
+                               if (is_array($this->tableMetaData)) {
+                                       foreach ($this->tableMetaData as $k => $v) {
+                                               switch ($k) {
+                                               case 'id' :
+                                               case 'member_id' :
+                                                       break;
+
+                                               default :
+                                                       $arr[$k]['data_type'] = $v;
+                                                       $arr[$k]['value'] = $values[$k];
+                                                       unset($values[$k]);
+                                                       break;
+                                               }
+                                       }
+                               }
+                       }
+                       //      Check for updates in the module tables.
+                       foreach ($this->moduleTables as $tname => &$arr) {
+                               foreach ($arr as $k => $v) {
+                                       if (array_key_exists($k, $this->oldData)) {
+                                               if ($v['data_type'] != 'boolean') {
+                                                       if ($v['value'] != $this->oldData[$k]) {
+                                                               $moduleUpdates[$tname][$k] = $v;
+                                                       }
+                                               } else {
+                                                       //      Due to how PDO's cast a boolean value False
+                                                       //      to '' when you query the DB.
+                                                       //      we need to adjust any empty fields
+                                                       //      that are of data_type boolean to a 0, True
+                                                       //      values are queried as 1.
+                                                       //      See:  http://bugs.php.net/bug.php?id=33876
+                                                       //      for more info.
+                                                       $oldData = (empty($this->oldData[$k])) ? 0 : 1;
+                                                       if ($v['value'] != $oldData) {
+                                                               $moduleUpdates[$tname][$k] = $v;
+                                                       }
+                                               }
+                                               //      If the field was set in the module Updates array
+                                               //      then also try and set what type of field we are
+                                               //      dealing w/ on the form.
+                                               if (isset($moduleUpdates[$tname][$k])) {
+                                                       if ($this->elementExists($k)) {
+                                                               $e =& $this->getElement($k);
+                                                               if (PEAR::isError($e)) {
+                                                                       return Toolkit_Common::handleError($e);
+                                                               }
+                                                               $moduleUpdates[$tname][$k]['field_type'] = $e->getType();
+                                                       }
+                                               }
+                                       }
+                               }
+                       }
+
+                       //      Need a special case for the credit cards.
+            if (is_array($values['creditCards'])) {
+                foreach ($values['creditCards'] as $k => $v) {
+                    if (array_key_exists("creditCards[$k]", $this->oldData)) {
+                        if ($v != $this->oldData["creditCards[$k]"]) {
+                            $ccUpdates["creditCards[$k]"]['data_type'] = 'integer';
+                            $ccUpdates["creditCards[$k]"]['value'] = $v;
+                        }
+                    } elseif (!empty($v)) {
+                        $ccUpdates["creditCards[$k]"]['data_type'] = 'integer';
+                        $ccUpdates["creditCards[$k]"]['value'] = $v;
+                    }
+                    // If the credit card was set in the credit card updates array
+                    // then also try and set what type of field we are dealing w/
+                    // on the form.
+                    if (isset($ccUpdates["creditCards[$k]"])) {
+                        if ($this->elementExists("creditCards[$k]")) {
+                            $e =& $this->getElement("creditCards[$k]");
+                            if (PEAR::isError($e)) {
+                                return Toolkit_Common::handleError($e);
+                            }
+                            $ccUpdates["creditCards[$k]"]['field_type'] = $e->getType();
+                        }
+                    }
+                }
+            }
+               } else {
+                       return parent::updateData($values);
+               }
+
+               $ccards = $values['creditCards'];
+               $pattern = '/^-?(.+)$/i';
+               $replacement = '$1';
+               //      Adjust the lat/lon coordinates to negative #'s if needed
+               $coords = array('lat' => 'South', 'lon' => 'East');
+               foreach ($coords as $k => $v) {
+                       if ($values["{$k}Direction"] == $v && !empty($values[$k])) {
+                               $string = $values[$k];
+                               $values[$k] = preg_replace($pattern, $replacement, $string);
+                               $values[$k] = "-{$values[$k]}";
+                       }
+               }
+               unset($values['lonDirection'],
+                       $values['latDirection'],
+                       $values['member_cats'],
+                       $values['creditCards']
+               );
+
+        $this->tableMetaData = Toolkit_Common::getTableMetaData(
+            $this->dbh,
+            $this->tableName
+        );
+               //      Check for updates in the member record.
+               //      If we find any, add that field to the list of record updates
+               //      along with what type of field it is in the member table.
+               //      We will use the data_type field when the admin is dealing
+               //      w/ the updates to determine values on boolean records and empty
+               //      text field records.
+               foreach ($values as $k => $v) {
+                       if (array_key_exists($k, $this->oldData)) {
+                               if ($v != $this->oldData[$k]) {
+                                       if ($this->elementExists($k)) {
+                                               $e =& $this->getElement($k);
+                                               if (PEAR::isError($e)) {
+                                                       return Toolkit_Common::handleError($e);
+                                               }
+                                               $recordUpdates[$k]['field_type'] = $e->getType();
+                                       }
+                                       $recordUpdates[$k]['data_type'] = $this->tableMetaData[$k];
+                                       $recordUpdates[$k]['value'] = $v;
+                               }
+                       }
+                       //      We need a special case for the member image. cause Its a pain
+                       //      in the ass.
+                       if ($k == 'logo') {
+                               if (!empty($v)) {
+                                       if (array_key_exists('old_logo_rmv', $this->oldData)) {
+                                               if ($v != $this->oldData['old_logo_rmv']) {
+                                                       $recordUpdates[$k]['data_type'] = 'boolean';
+                                                       $recordUpdates[$k]['field_type'] = 'file';
+                                                       $recordUpdates[$k]['value'] = $v;
+                                               }
+                                       }
+                               }
+                       }
+               }
+
+               //      Combine the update arrays for the modules and the member record
+               //      into one single array. The array will hold this pattern.
+               //      An array of tables that holds an array of fields that were updated
+               //      [DB Table] => array
+               //              (
+               //                      [Column Name] => array
+               //                              (
+               //                                      [data_type] => boolean / text / integer
+               //                                      [field_type] => text / select / checkbox
+               //                                      [value] => updated value from the form.
+               //                              )
+               //              )
+               //      Make sure to only add these array's if there is update information
+               //      in them.
+               $updates = array();
+               if (is_array($moduleUpdates)) {
+                       $updates += $moduleUpdates;
+               }
+               if (is_array($recordUpdates)) {
+                       $updates += array('member' => $recordUpdates);
+               }
+               if (is_array($ccUpdates)) {
+                       $updates += array('member_ccard_type' => $ccUpdates);
+               }
+
+               try {
+                       //      If we have an array of updates, we need to insert all the
+                       //      updates into the member_updates table.  This will signal that
+                       //      the member has pending updates that the admin needs to
+                       //      authorize.
+                       if (!empty($updates)) {
+                               //      Loop through all the elements that were updated and get
+                               //      the label we use on the form so when the admin is looking
+                               //      at the updates they will know what field its for.
+                               //      We can be sure we are getting the right label because
+                               //      there can only be unique element names in the form.
+                               foreach ($updates as &$sec) {
+                                       foreach ($sec as $field => $v) {
+                                               switch ($field) {
+                        case 'lat' :
+                            $g =& $this->getElement('latitude');
+                            $e =& $g->getElements();
+                            $sec[$field]['label'] = $e[1]->getLabel();
+                            break;
+
+                        case 'lon' :
+                            $g =& $this->getElement('longitude');
+                            $e =& $g->getElements();
+                            $sec[$field]['label'] = $e[1]->getLabel();
+                            break;
+
+                        case '' :
+                        case null :
+                            break;
+
+                        default :
+                            $e =& $this->getElement($field);
+                            // Check to make sure an actual element was
+                            // returned.  If a "module" is unassigned from a
+                            // category, those elements will not be created.
+                            // so we won't be able to get the labels for those
+                            // elements.  In this case, just use the field
+                            // name.
+                            if (PEAR::isError($e)) {
+                                $sec [$field]['label'] = $field;
+                            } else {
+                                $sec[$field]['label'] = $e->getLabel();
+                            }
+                            break;
+                                               }
+                                       }
+                               }
+                               $this->dbh->beginTransaction();
+
+                               $sql = "
+                                       INSERT INTO {$this->pendingTable} (member_id, field, update, db_table, data_type, field_type, label)
+                                        VALUES (:member_id, :field, :update, :table, :data_type, :field_type, :label)";
+
+                               $stmt = $this->dbh->prepare($sql);
+                               $stmt->bindParam(':member_id', $GLOBALS['memberAuth']->getAuthData('member_id'), PDO::PARAM_INT);
+                               foreach ($updates as $table => &$sec) {
+                                       $stmt->bindParam(':table', $table, PDO::PARAM_STR);
+                                       foreach ($sec as $k => &$v) {
+                                               $dataType = PDO::PARAM_STR;
+                                               //      Fields that are empty which have a data_type of text
+                                               //      can be inserted as NULL values.
+                                               if (empty($v['value']) && $v['data_type'] == 'text') {
+                                                       $v['value'] = null;
+                                                       $dataType = PDO::PARAM_NULL;
+                                               }
+                                               $stmt->bindParam(":field", $k, PDO::PARAM_STR);
+                                               $stmt->bindParam(":update", $v['value'], $dataType);
+                                               $stmt->bindParam(":data_type", $v['data_type'], PDO::PARAM_STR);
+                                               $stmt->bindParam(":field_type", $v['field_type'], PDO::PARAM_STR);
+                                               $stmt->bindParam(":label", $v['label'], PDO::PARAM_STR);
+                                               $stmt->execute();
+                                       }
+                               }
+                               $this->setPending();
+                               $this->emailOwner();
+                               return $this->dbh->commit();
+                       }
+               } catch (PDOException $e) {
+                       return Toolkit_Common::handleError($e);
+               }
+
+               return;
+       }
+
+       //      }}}
+}
+?>
diff --git a/Toolkit/Members/EditPackages.php b/Toolkit/Members/EditPackages.php
new file mode 100644 (file)
index 0000000..9f1cb4c
--- /dev/null
@@ -0,0 +1,1935 @@
+<?php
+//    vim:set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker syntax=php:
+
+/**
+ * Handles the packages tab in the member record
+ *
+ * Controls setting up the add package form if applicable, and rendering
+ * each uploaded package edit form to edit/delete the package.
+ *
+ * PHP version 5
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @version   CVS: $Id: EditPackages.php,v 1.16 2010/07/18 16:45:33 jamie Exp $
+ * @link      http://demo.gaslightmedia.com
+ * @see       Toolkit/Image/Server.php
+ */
+
+
+/**
+ * The image server processing class
+ */
+require_once BASE . 'Toolkit/Image/Server.php';
+
+/**
+ * Constructor class to setup the page layout
+ *
+ * this class determines if the user can upload any more packages to their
+ * account and if so renders the add package form.  It also controls
+ * rending the individual forms for each previously uploaded package.
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @link      http://demo.gaslightmedia.com
+ */
+class Toolkit_Members_EditPackages
+    extends Toolkit_Members_Admin_EditPackages
+{
+    //    {{{ properties
+
+    /**
+     * The table name in the database used to store the data
+     * @var string
+     * @access public
+     */
+    public $tableName = 'member_packages';
+
+    /**
+     * Template used to layout form when editing a package
+     * @var    string
+     * @access protected
+     */
+    protected $pageTemplate = 'editPackages.tpl';
+
+    /**
+     * What is the maximum caption length for packages
+     *
+     * @var    array
+     * @access public
+     * @static
+     */
+    static public $maxTitleLength = 60;
+
+    /**
+     * Objects that will go into the page (add form, edit package forms)
+     * @var    object
+     * @access protected
+     */
+    protected $page;
+
+    /**
+     * Description for protected
+     * @var    PDO
+     * @access protected
+     */
+    protected $dbh;
+
+    //    }}}
+    //    {{{    canAddPackages()
+
+    /**
+     * Determine if this member can have more packages added to their profile
+     *
+     * Load the entire package gallery into member via a linked list.
+     * Then return if the # of linked list nodes is smaller than
+     * the maximum limit of packages.
+     *
+     * @access protected
+     * @return boolean If the linked list is smaller than max packages allowed
+     */
+    protected function canAddPackages()
+    {
+        $membersConf = new Config;
+        $membersRoot =& $membersConf->parseConfig(
+            BASE . 'Toolkit/Members/config.ini',
+            'IniFile'
+        );
+        $memberPackageLimit = $membersRoot->getItem('section', 'conf')
+            ->getItem('directive', 'memberPackageLimit')
+            ->getContent();
+        if (   !$memberPackageLimit
+            || !filter_var($memberPackageLimit, FILTER_VALIDATE_INT)
+        ) {
+            return true;
+        }
+        $id = $GLOBALS['memberAuth']->getAuthData('member_id');
+        $ll = new Toolkit_Members_Packages(null, $id);
+        $ll->setDbh($this->dbh);
+        $ll->createMemberList();
+        return ($ll->getListSize() < $memberPackageLimit);
+    }
+
+    //    }}}
+
+    //    {{{    displayPage()
+
+    /**
+     * Displays the page to the screen
+     *
+     * @return void
+     * @access public
+     */
+    public function displayPage()
+    {
+        echo $this->getPage();
+    }
+
+    //    }}}
+
+    //    {{{    getUploadedPackages()
+
+    /**
+     * Get an array of package ids from the DB that have been uploaded for this member
+     *
+     * - Create a linked list of all the members packages
+     * - Walk through the linked list extracting the id from each node into an array
+     *
+     * @access protected
+     * @return array Ids of all uploaded packages for this member
+     */
+    protected function getUploadedPackages()
+    {
+        $id = $GLOBALS['memberAuth']->getAuthData('member_id');
+        $packages = new Toolkit_Members_Packages(null, $id);
+        $packages->setDbh($this->dbh);
+        $packages->createMemberList();
+        $packages->rewind();
+
+        $ids = array();
+        foreach ($packages as $i) {
+            $ids[] = $i->getId();
+        }
+
+        return $ids;
+    }
+
+    //    }}}
+
+    //    {{{    setUpPage()
+
+    /**
+     * Sets up the page to manipulate packages for a member
+     *
+     * Checks if all the packages uploaded for a member (pending & non-pending)
+     * exceed or match the maximum # of packages allowed for each member to
+     * upload to their account.
+     *
+     * For every package that is already uploaded, create an edit-package form that
+     * will allow the user to update the caption or delete the package.
+     *
+     * @param Config_Container $c Application configuration
+     *
+     * @return void
+     * @access public
+     */
+    public function setUpPage(Config_Container $c)
+    {
+        $this->page = new StdClass;
+
+        //    Find out if we can still add packages to the record.
+        //    If we can, then add the upload form to the page for the member to see.
+        if ($this->canAddPackages()) {
+            $addForm = new AddPackage(
+                $this->dbh,
+                'new_member_package',
+                'post',
+                '',
+                '',
+                null,
+                true
+            );
+
+            $addForm->configureForm($c);
+            $this->page->uploadForm = $addForm->toHtml($this->tEngine);
+        }
+
+        //    Find out if we have any packages already uploaded.
+        //    If we do, then add the edit package form to the page for each package
+        //    so the member can edit/delete their packages.
+        if ($packages = $this->getUploadedPackages()) {
+            $this->page->editForm = array();
+            while (list($i, $j) = each($packages)) {
+                $editForm = new EditPackage(
+                    $this->dbh,
+                    "edit_member_package_$j",
+                    'post',
+                    '',
+                    '',
+                    array('id' => $j),
+                    true
+                );
+
+                $editForm->configureForm();
+                $this->page->editForm[] = $editForm->toHtml($this->tEngine);
+            }
+        }
+    }
+
+    //    }}}
+}
+
+/**
+ * Form to handle creating a new package in the members only area
+ *
+ * Handles inserting new package into db as a pending package and creating a
+ * tuple in the member_updates table which will allow the admin to
+ * approve/deny the new package request.
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @link      <>
+ */
+class AddPackage extends Toolkit_FormBuilder
+{
+    //    {{{ properties
+
+    /**
+     * The table name in the database used to store the data
+     *
+     * @var string
+     * @access public
+     */
+    public $tableName = 'member_packages';
+
+    /**
+     * The template used to render the form
+     *
+     * @var string
+     * @access protected
+     */
+    protected $formTemplate = 'addPackage.tpl';
+
+    /**
+     * The name of the template used to email the owner for any updates
+     *
+     * When a member makes an update to thier record, this is the template
+     * we will use to send out the email notification to the site owner.
+     *
+     * @var string
+     * @access protected
+     */
+    protected $emailTemplate = 'emailOwner.tpl';
+
+    /**
+     * Description for protected
+     * @var    array
+     * @access protected
+     */
+    protected $registeredRules = array();
+
+    /**
+     * Description for protected
+     * @var    string
+     * @access protected
+     */
+    protected $successMsg
+        = '<div id="form-success-top">
+            You successfully uploaded your package.
+           </div>';
+
+    /**
+     * Description for protected
+     * @var    array
+     * @access protected
+     */
+    protected $mimeTypes = array(
+        'image/jpe',
+        'image/jpeg',
+        'image/jpg',
+        'image/jfif',
+        'image/pjpeg',
+        'image/pjp',
+        'image/gif',
+        'image/png',
+    );
+
+    //    }}}
+    //    {{{ __construct()
+
+    /**
+     * Class constructor
+     *
+     * @param PDO    $pdo         PHP Data Object to use for DB calls
+     * @param string $formName    Form's name.
+     * @param string $method      (optional) Form's method defaults to 'POST'
+     * @param string $action      (optional) Form's action.
+     * @param string $target      (optional) Form's target defaults to '_self'
+     * @param mixed  $attributes  (optional) Extra attributes for <form> tag.
+     * @param bool   $trackSubmit (optional) Whether to track if the form was
+     *                                         submitted by adding a special hidden
+     *                                         field.
+     *
+     * @access public
+     * @see    Toolkit_Members_Admin_EditPackages
+     */
+    public function __construct(
+        PDO $pdo,
+        $formName,
+        $method = 'post',
+        $action = '',
+        $target = '',
+        $attributes = null,
+        $trackSubmit = false
+    ) {
+        parent::__construct(
+            $formName,
+            $method,
+            $action,
+            $target,
+            $attributes,
+            $trackSubmit
+        );
+        $this->dbh = $pdo;
+    }
+
+    //    }}}
+
+    //  {{{ configureForm()
+
+    /**
+     * Quick form configuration
+     *
+     * @param Config_Container $c Config container
+     *
+     * @return void
+     * @access public
+     */
+    public function configureForm(Config_Container $c)
+    {
+        $this->configureElements();
+        $this->configureFilters();
+        $this->configureRules();
+        $this->configureConstants($c);
+    }
+
+    //  }}}
+    //    {{{ configureElements()
+
+    /**
+     * Setup the elements to use on the form.
+     *
+     * @access public
+     * @see    Toolkit_FormBuilder::setupElements()
+     * @return void
+     */
+    public function configureElements()
+    {
+        $e = array();
+        //    All Grouped Elements are created here.
+
+        //    All Elements are created here.  This includes group element definitions.
+        $e[] = array(
+            'type'    => 'hidden',
+            'req'     => false,
+            'name'    => 'pending',
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => 'title',
+            'display' => 'Package Title',
+        );
+        $e[] = array(
+            'type'        => 'textarea',
+            'req'         => false,
+            'name'        => 'description',
+            'display'     => 'Package Description',
+            'opts'        => array('id' => 'descrAdd', 'class' => 'ckeditor'),
+            'noCharLimit' => true
+        );
+        $e[] = array(
+            'type'    => 'date',
+            'req'     => true,
+            'name'    => 'sdate',
+            'display' => 'Start Date',
+            'opts'    => array(
+                'format'           => 'm / d / Y',
+                'minYear'          => date('Y'),
+                'maxYear'          => date('Y') + 10,
+                'addEmptyOption'   => true,
+                'emptyOptionValue' => '',
+                'emptyOptionText'  => array(
+                    'm' => 'mm',
+                    'd' => 'dd',
+                    'Y' => 'yyyy',
+                ),
+            )
+        );
+        $e[] = array(
+            'type'    => 'date',
+            'req'     => true,
+            'name'    => 'edate',
+            'display' => 'End Date',
+            'opts'    => array(
+                'format'           => 'm / d / Y',
+                'minYear'          => date('Y'),
+                'maxYear'          => date('Y') + 10,
+                'addEmptyOption'   => true,
+                'emptyOptionValue' => '',
+                'emptyOptionText'  => array(
+                    'm' => 'mm',
+                    'd' => 'dd',
+                    'Y' => 'yyyy',
+                ),
+            )
+        );
+        $e[] = array(
+            'type'    => 'checkbox',
+            'req'     => false,
+            'name'    => 'remove_img_rmv',
+            'display' => 'Remove Image',
+        );
+        $e[] = array(
+            'type'    => 'static',
+            'req'     => false,
+            'name'    => 'curr_image',
+            'display' => 'Current Image',
+        );
+        $e[] = array(
+            'type'    => 'hidden',
+            'req'     => false,
+            'name'    => 'curr_image_rmv',
+        );
+        $e[] = array(
+            'type'    => 'file',
+            'req'     => false,
+            'name'    => 'image',
+            'display' => 'Upload a Package Photo / Image',
+        );
+        $e[] = array(
+            'type'    => 'submit',
+            'req'     => false,
+            'name'    => 'add_rmv',
+            'display' => 'Upload new package',
+            'opts'    => array('class' => 'submit')
+        );
+
+        $this->setupElements($e);
+    }
+
+    //    }}}
+    //    {{{ configureRules()
+
+    /**
+     * Configure rules for form
+     *
+     * @return void
+     * @access public
+     */
+    public function configureRules()
+    {
+        $r = array();
+
+        $checkDate = create_function('$d', '$d = implode("-", $d); return Validate::date($d, array("format" => "%n-%j-%Y"));');
+        $r[] = array(
+            'element'    => 'sdate',
+            'message'    => 'ERROR: Invalid Date!',
+            'type'       => 'callback',
+            'format'     => $checkDate,
+            'validation' => $this->validationType,
+            'reset'      => false,
+            'force'      => false
+        );
+        $r[] = array(
+            'element'    => 'edate',
+            'message'    => 'ERROR: Invalid Date!',
+            'type'       => 'callback',
+            'format'     => $checkDate,
+            'validation' => $this->validationType,
+            'reset'      => false,
+            'force'      => false
+        );
+        $r[] = array(
+            'element'    => 'image',
+            'message'    => 'ERROR: Incorrect File Type (.gif, .png, .jpg) only!',
+            'type'       => 'mimetype',
+            'format'     => $this->mimeTypes,
+            'validation' => $this->validationType,
+            'reset'      => false,
+            'force'      => false
+        );
+
+        $this->setupRules($r);
+    }
+
+    //    }}}
+    //    {{{ configureConstants()
+
+    /**
+     * Configure constants for form
+     *
+     * @param Config_Container $conf Application configuration
+     *
+     * @return void
+     * @access public
+     */
+    public function configureConstants(Config_Container $conf)
+    {
+        $pending = $conf->getItem('section', 'conf')
+            ->getItem('directive', 'strictPending')
+            ->getContent();
+
+        $c = array(
+            'pending' => $pending
+        );
+
+        $this->setupConstants($c);
+    }
+
+    //    }}}
+    //    {{{ configureFilters()
+
+    /**
+     * Configure filters for form
+     *
+     * @return void
+     * @access public
+     */
+    public function configureFilters()
+    {
+        $f = array();
+
+        $f[] = array(
+            'element' => '__ALL__',
+            'filter' => 'trim'
+        );
+
+        $this->setupFilters($f);
+    }
+
+    //    }}}
+
+    //  {{{ deleteImage()
+
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @param Toolkit_Image_Server  $is  Parameter description (if any) ...
+     * @param string                $img Parameter description (if any) ...
+     *
+     * @return object    Return description (if any) ...
+     * @access protected
+     */
+    protected function deleteImage(Toolkit_Image_Server $is, $img)
+    {
+        return $is->imageDelete($img);
+    }
+
+    //  }}}
+
+    //  {{{ emailOwner()
+
+    /**
+     * Emails the owner and anyone else who wants to be advised of updates
+     *
+     * A false value in the primaryAdvisee will cause no email to be sent.
+     * all secondary advisees listed in the constructor are carbon copied
+     * in the email.
+     *
+     * Emails are sent out in both HTML and TXT forms.
+     *
+     * @return boolean result of email
+     * @access protected
+     */
+    protected function emailOwner()
+    {
+        if (MEMBER_RECORD_UPDATES_ADVISOR === false) {
+            return;
+        } else {
+            $this->flexyOptions = Toolkit_Members::getFlexyOptions();
+            $id = $GLOBALS['memberAuth']->getAuthData('member_id');
+            try {
+                $sql = "
+                    SELECT member_name
+                      FROM member
+                     WHERE member_id = :member_id";
+                $stmt = $this->dbh->prepare($sql);
+                $stmt->bindParam(':member_id', $id, PDO::PARAM_INT);
+                $stmt->execute();
+                $row = $stmt->fetch(PDO::FETCH_ASSOC);
+                $memberName = $row['member_name'];
+            } catch (PDOException $e) {
+                return Toolkit_Common::handleError($e);
+            }
+            $template = new HTML_Template_Flexy($this->flexyOptions);
+            $page = new stdClass();
+            $page->member = $memberName;
+            $page->url = ($_SERVER['HTTPS'] == 'on') ? BASE_SECURE_URL : MEDIA_BASE_URL;
+            $page->email = OWNER_EMAIL;
+            $page->siteName = SITENAME;
+            $page->link = '<a target="_blank"  href="'.MEDIA_BASE_URL.'pending-member/'.$id.'/">link</a>';
+
+            $template->compile($this->emailTemplate);
+            //  Merge the compiled template with the $page object.
+            $htmlMsg = $template->bufferedOutputObject($page);
+
+            $msg = "
+                <h3>$memberName</h3>
+                <p>
+                    Has updated thier business record and is now in a pending
+                    state. To approve / reject thier changes you can either log
+                    into your {$page->siteName} admin area or follow this
+                    {$page->link}
+                </p>";
+            $crlf = "\n";
+            $mimeMail = new Mail_mime($crlf);
+            $from = preg_replace("/[^A-Za-z ]/", "", SITENAME) . ' <' . OWNER_EMAIL . '>';
+            $mimeMail->setFrom($from);
+            $mimeMail->setSubject('Member Record Update');
+            $mimeMail->setHTMLBody($htmlMsg);
+            $mimeMail->setTXTBody($msg);
+
+            $mail =& Mail::factory('mail');
+            $body = $mimeMail->get();
+            $headers = $mimeMail->headers($hdrs);
+
+            $res = $mail->send(MEMBER_RECORD_UPDATES_ADVISOR, $headers, $body);
+            if (PEAR::isError($res)) {
+                return Toolkit_Common::handleError($res);
+            } else {
+                return $res;
+            }
+        }
+    }
+
+    //  }}}
+    //    {{{ insertData()
+
+    /**
+     * Create a new package in the db
+     *
+     * @param array &$values Form submitted values
+     *
+     * @return object    db result of adding package
+     * @access protected
+     */
+    protected function insertData(&$values)
+    {
+        try {
+            $this->dbh->beginTransaction();
+            $sql = Toolkit_Common::createSQLInsert(
+                $this->tableName,
+                array_keys($values)
+            );
+
+            $res = Toolkit_Common::processQuery(
+                $this->dbh,
+                $this->tableName,
+                $sql,
+                $values
+            );
+
+            if ($values['pending']) {
+                $this->_createPendingData($values);
+            }
+
+            $this->dbh->commit();
+            return $res;
+        } catch (PDOException $e) {
+            $this->dbh->rollback();
+            return Toolkit_Common::handleError($e);
+        }
+    }
+
+    //    }}}
+
+    //    {{{    _createPendingData()
+
+    /**
+     * Description of _createPendingData()
+     *
+     * @param array $values Value array
+     *
+     * @return void|mixed
+     * @access private
+     */
+    private function _createPendingData($values)
+    {
+        try {
+            $sql = "
+                SELECT *
+                  FROM member_packages
+                 WHERE member_id = :member_id
+                 ORDER BY id DESC LIMIT 1";
+
+            $stmt = $this->dbh->prepare($sql);
+            $stmt->bindParam(':member_id', $values['member_id'], PDO::PARAM_INT);
+            $stmt->execute();
+            $row = $stmt->fetch();
+
+            $sql = "
+                INSERT INTO member_updates
+                    (field, update, db_table, data_type, label,
+                     foreign_key, member_id, field_type)
+                VALUES (:field, :update, :db_table, :data_type, :label,
+                        :foreign_key, :member_id, :field_type)";
+
+            $dataToInsert = array();
+            if (!empty($values['title'])) {
+                $dataToInsert[] = array(
+                     'field' => 'title',
+                     'update' => $values['title'],
+                     'label' => 'Title'
+                );
+            }
+
+            if (!empty($values['description'])) {
+                $dataToInsert[] = array(
+                        'field' => 'description',
+                        'update' => $values['description'],
+                        'label' => 'Description'
+                );
+            }
+            /*if (!empty($values['image'])) {
+                $dataToInsert[] = array(
+                        'field' => 'image',
+                        'update' => $values['image'],
+                        'label' => 'Image'
+                );
+            }*/
+            $stmt = $this->dbh->prepare($sql);
+            $stmt->bindValue(':db_table', 'member_packages', PDO::PARAM_STR);
+            $stmt->bindValue(':data_type', 'text', PDO::PARAM_STR);
+            $stmt->bindValue(':field_type', 'text', PDO::PARAM_STR);
+            $stmt->bindParam(':foreign_key', $row['id'], PDO::PARAM_INT);
+            $stmt->bindParam(':member_id', $values['member_id'], PDO::PARAM_INT);
+
+            foreach ($dataToInsert as $i) {
+                $stmt->bindParam(':field', $i['field'], PDO::PARAM_STR);
+                $stmt->bindParam(':update', $i['update'], PDO::PARAM_STR);
+                $stmt->bindParam(':label', $i['label'], PDO::PARAM_STR);
+                $stmt->execute();
+            }
+        } catch (PDOException $e) {
+            return Toolkit_Common::handleError($e);
+        }
+    }
+
+    //    }}}
+
+    //    {{{ processData()
+
+    /**
+     * Handles setting up the from processing and which function to get it done
+     *
+     * @param array $values Submitted values from the form.
+     *
+     * @return void
+     * @access protected
+     */
+    protected function processData($values)
+    {
+        $id = $GLOBALS['memberAuth']->getAuthData('member_id');
+        $cache = new Cache_Lite(Toolkit_Members::getCacheOptions());
+        $cache->remove("Member-$id", 'Profile');
+
+        $e =& $this->getElement('curr_image_rmv');
+
+        $packages = new Toolkit_Members_Packages(null, $id);
+        $packages->setDbh($this->dbh);
+        $packages->createMemberList();
+        $values['pos']       = $packages->getListSize() + 1;
+        $values['image'] = $e->getValue('curr_image_rmv');
+        $values['member_id'] = $GLOBALS['memberAuth']->getAuthData('member_id');
+        $values['sdate'] = implode('-', $values['sdate']);
+        $values['edate'] = implode('-', $values['edate']);
+        unset($values['MAX_FILE_SIZE'],
+              $values['curr_image_rmv'],
+              $values['remove_img_rmv'],
+              $values['add_rmv']);
+
+        $this->tableMetaData = Toolkit_Common::getTableMetaData(
+            $this->dbh,
+            $this->tableName
+        );
+        $this->insertData($values);
+        $this->emailOwner();
+
+        $listPage = MEDIA_BASE_URL .
+            "members-only-area/?rt=EditProfile&tab=packages";
+        header("Location: $listPage");
+    }
+
+    //    }}}
+
+    //    {{{ setupRenderers()
+
+    /**
+     * Set up the rendering engine we are going to use to display this form
+     *
+     * @return void
+     * @access protected
+     */
+    protected function setupRenderers()
+    {
+        $fo = Toolkit_Members::getFlexyOptions();
+
+        $this->template = new HTML_Template_Flexy($fo);
+
+        $renderer = new HTML_QuickForm_Renderer_ObjectFlexy($this->template);
+
+        $this->accept($renderer);
+        $this->view              = new StdClass;
+        $this->view->showCurrImg = $this->showCurrImg;
+        $this->view->form        = $renderer->toObject();
+        $this->template->compile($this->formTemplate);
+    }
+
+    //    }}}
+
+    //  {{{ validNewImg()
+
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @param array $newImg Parameter description (if any) ...
+     *
+     * @return mixed  Return description (if any) ...
+     * @access public
+     */
+    function validNewImg(array $newImg)
+    {
+        return (is_numeric($newImg['size']) &&
+                $newImg['size'] > 0 &&
+                in_array($newImg['type'], $this->mimeTypes));
+    }
+
+    //  }}}
+    //  {{{ removeOldImage()
+
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @param Toolkit_Image_Server $is     Parameter description (if any) ...
+     * @param string               $oldImg Parameter description (if any) ...
+     *
+     * @return void
+     * @access public
+     */
+    function removeOldImage(Toolkit_Image_Server $is, $oldImg)
+    {
+        $this->deleteImage($is, $oldImg);
+        if ($this->elementExists('curr_image_rmv')) {
+            $e =& $this->getElement('curr_image_rmv');
+            $e->setValue(null);
+            $this->_submitValues['curr_image_rmv'] = null;
+        }
+    }
+
+    //  }}}
+    //  {{{ syncCurrImage()
+
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @return void
+     * @access protected
+     */
+    protected function syncCurrImage()
+    {
+        $is = new Toolkit_Image_Server();
+
+        $delImg = $this->getSubmitValue('remove_img_rmv');
+        $oldImg = $this->getSubmitValue('curr_image_rmv');
+        $newImg = $this->getSubmitValue('image');
+
+        if ($delImg && $oldImg) {
+            $this->removeOldImage($is, $oldImg);
+            unset($oldImg);
+        } elseif ($oldImg && $this->validNewImg($newImg)) {
+            $this->removeOldImage($is, $oldImg);
+            unset($oldImg);
+        }
+
+        if ($this->validNewImg($newImg)) {
+            $image = $this->uploadImage($is, 'image');
+        } else {
+            $image = $oldImg;
+        }
+
+        if ($image) {
+            $this->updatePhotoElements($is, $image);
+            $this->showCurrImg = true;
+        }
+    }
+
+    //  }}}
+    //    {{{ toHtml()
+
+    /**
+     * Renders the form
+     *
+     * sets the page the form should be redirected to instead of coming back
+     * around to itself.
+     *
+     * @return string The rendered form
+     * @access public
+     */
+    public function toHtml()
+    {
+        //    We need to validate (and freeze if needed)
+        //    before we render the form. That way the
+        //    template knows about any errors on the form.
+        $this->validated = $this->validate();
+
+        //  If they have submitted the form and uploaded a proper image
+        //  but some other element had an error, then we need to show
+        //  their uploaded image in the form
+        if ($this->isSubmitted()) {
+            $this->syncCurrImage();
+        }
+
+        $this->setupRenderers();
+
+        if ($this->validated) {
+            $processed = $this->process(
+                array(&$this, 'processData'),
+                $this->mergeFiles
+            );
+        }
+
+        return $this->template->bufferedOutputObject($this->view);
+    }
+
+    //    }}}
+
+    //  {{{ updatePhotoElements()
+
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @param Toolkit_Image_Server $is    Parameter description (if any) ...
+     * @param string               $image Parameter description (if any) ...
+     *
+     * @return void
+     * @access public
+     */
+    public function updatePhotoElements(Toolkit_Image_Server $is, $image)
+    {
+        //  Get the dimensions of the image
+        $dimensions = $is->getImageSize(MEMBER_PHOTOS . $image);
+        if (PEAR::isError($dimensions)) {
+            Toolkit_Common::handleError($dimensions);
+        }
+        list($w, $h) = $dimensions;
+        $s = MEMBER_PHOTOS . $image;
+
+        //  Set the image to show in the element
+        $e =& $this->getElement('curr_image');
+        $e->setText('<img width="'.$w.'" height="'.$h.'" src="'.$s.'">');
+
+        //  updated the hidden elements value to make sure it
+        //  holds the most up-to-date image name
+        $e =& $this->getElement('curr_image_rmv');
+        $e->setValue($image);
+    }
+
+    //  }}}
+    //  {{{ uploadImage()
+
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @param Toolkit_Image_Server  $is    Parameter description (if any) ...
+     * @param string                $field Form field name of image
+     *
+     * @return string image name
+     * @access protected
+     */
+    protected function uploadImage(Toolkit_Image_Server $is, $field)
+    {
+        return $is->imageUpload($field);
+    }
+
+    //  }}}
+}
+
+/**
+ * Form to handle editing/deleting existing packages in members only area
+ *
+ * Handles updating caption requests for a member or to remove a
+ * package from thier profile
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @link      <>
+ * @see       Toolkit_FormBuilder
+ */
+class EditPackage extends Toolkit_FormBuilder
+{
+    //    {{{ properties
+
+    /**
+     * The table name in the database used to store the data
+     *
+     * @var string
+     * @access public
+     */
+    public $tableName = 'member_packages';
+
+    /**
+     * The template used to render the form
+     *
+     * @var string
+     * @access protected
+     */
+    protected $formTemplate = 'editPackage.tpl';
+
+    /**
+     * The name of the template used to email the owner for any updates
+     *
+     * When a member makes an update to thier record, this is the template
+     * we will use to send out the email notification to the site owner.
+     *
+     * @var string
+     * @access protected
+     */
+    protected $emailTemplate = 'emailOwner.tpl';
+
+    /**
+     * Id of package in db
+     * @var    integer
+     * @access protected
+     */
+    protected $packageId;
+
+    /**
+     * Description for protected
+     * @var    string
+     * @access protected
+     */
+    protected $successMsg
+        = '<div id="form-success-top">
+            You successfully updated your package.
+          </div>';
+
+    /**
+     * Any rules we want to register for this form
+     * @var    array
+     * @access protected
+     */
+    protected $registeredRules = array();
+
+    /**
+     * Description for protected
+     * @var    array
+     * @access protected
+     */
+    protected $mimeTypes = array(
+        'image/jpe',
+        'image/jpeg',
+        'image/jpg',
+        'image/jfif',
+        'image/pjpeg',
+        'image/pjp',
+        'image/gif',
+        'image/png',
+    );
+
+    //    }}}
+    //    {{{ __construct()
+
+    /**
+     * Class constructor
+     *
+     * @param PDO    $pdo         PHP Data Object to use for DB calls
+     * @param string $formName    Form's name.
+     * @param string $method      (optional) Form's method defaults to 'POST'
+     * @param string $action      (optional) Form's action.
+     * @param string $target      (optional) Form's target defaults to '_self'
+     * @param mixed  $attributes  (optional) Extra attributes for <form> tag.
+     * @param bool   $trackSubmit (optional) Whether to track if the form was
+     *                                         submitted by adding a special hidden
+     *                                         field.
+     *
+     * @access public
+     * @see    Toolkit_Members_Admin_EditPackages
+     */
+    public function __construct(
+        PDO $pdo,
+        $formName,
+        $method = 'post',
+        $action = '',
+        $target = '',
+        $attributes = null,
+        $trackSubmit = false
+    ) {
+        parent::__construct(
+            $formName,
+            $method,
+            $action,
+            $target,
+            $attributes,
+            $trackSubmit
+        );
+
+        $this->packageId = $attributes['id'];
+        $id = $GLOBALS['memberAuth']->getAuthData('member_id');
+        $this->packages  = new Toolkit_Members_Packages(null, $id);
+        $this->packages->setDbh($pdo);
+        $this->packages->createMemberList();
+        $this->dbh = $pdo;
+    }
+
+    //    }}}
+
+    //  {{{ configureForm()
+
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @return void
+     * @access public
+     */
+    public function configureForm()
+    {
+        $this->configureElements();
+        $this->configureDefaults();
+        $this->configureFilters();
+        $this->configureRules();
+    }
+
+    //  }}}
+    //    {{{ configureDefaults()
+
+    /**
+     * Setup the element default values for form
+     *
+     * @access public
+     * @see    Toolkit_FormBuilder::setupDefaults()
+     * @return void
+     */
+    public function configureDefaults()
+    {
+        $sql = "
+            SELECT *
+              FROM {$this->tableName}
+             WHERE id = {$this->packageId}";
+
+        $defaults = $this->dbh->query($sql)->fetch(PDO::FETCH_ASSOC);
+        $defaults['curr_image_rmv'] = $defaults['image'];
+        $img = '<img src="%s">';
+        $defaults['curr_image'] = sprintf($img, MEMBER_PHOTOS . $defaults['image']);
+
+            //    Get any updates for that photo that are still in
+            //    a pending status.
+            $sql = "
+                SELECT *
+                  FROM member_updates
+                 WHERE id in (
+                    SELECT max(id)
+                      FROM member_updates
+                     WHERE foreign_key = :foreign_key
+                       AND db_table = 'member_packages'
+                     GROUP BY field)";
+
+            $stmt = $this->dbh->prepare($sql);
+            $stmt->execute(array($this->packageId));
+        while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
+            if ($row['field'] == 'title') {
+                $defaults['title'] = $row['update'];
+            } elseif ($row['field'] == 'description') {
+                $defaults['description'] = $row['update'];
+            }
+        }
+
+        $this->showCurrImg = $defaults['image'];
+        $this->setupDefaults($defaults);
+    }
+
+    //    }}}
+    //    {{{ configureElements()
+
+    /**
+     * Setup the elements to use on the form.
+     *
+     * @access public
+     * @see    Toolkit_FormBuilder::setupElements()
+     * @return void
+     */
+    public function configureElements()
+    {
+        $e = array();
+        //    All Grouped Elements are created here.
+
+        //    All Elements are created here.  This includes group element definitions.
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => 'title',
+            'display' => 'Package Title',
+        );
+        $e[] = array(
+            'type'        => 'textarea',
+            'req'         => false,
+            'name'        => 'description',
+            'display'     => 'Package Description',
+            'opts'        => array('id' => 'descr' . $this->packageId, 'class' => 'ckeditor'),
+            'noCharLimit' => true
+        );
+        $e[] = array(
+            'type'    => 'date',
+            'req'     => true,
+            'name'    => 'sdate',
+            'display' => 'Start Date',
+            'opts'    => array(
+                'format'           => 'm / d / Y',
+                'minYear'          => date('Y'),
+                'maxYear'          => date('Y') + 10,
+                'addEmptyOption'   => true,
+                'emptyOptionValue' => '',
+                'emptyOptionText'  => array(
+                    'm' => 'mm',
+                    'd' => 'dd',
+                    'Y' => 'yyyy',
+                ),
+            )
+        );
+        $e[] = array(
+            'type'    => 'date',
+            'req'     => true,
+            'name'    => 'edate',
+            'display' => 'End Date',
+            'opts'    => array(
+                'format'           => 'm / d / Y',
+                'minYear'          => date('Y'),
+                'maxYear'          => date('Y') + 10,
+                'addEmptyOption'   => true,
+                'emptyOptionValue' => '',
+                'emptyOptionText'  => array(
+                    'm' => 'mm',
+                    'd' => 'dd',
+                    'Y' => 'yyyy',
+                ),
+            )
+        );
+        $e[] = array(
+            'type'    => 'checkbox',
+            'req'     => false,
+            'name'    => 'remove_img_rmv',
+            'display' => 'Remove Image',
+        );
+        $e[] = array(
+            'type'    => 'static',
+            'req'     => false,
+            'name'    => 'curr_image',
+            'display' => 'Current Image',
+        );
+        $e[] = array(
+            'type'    => 'hidden',
+            'req'     => false,
+            'name'    => 'curr_image_rmv',
+        );
+        $e[] = array(
+            'type'    => 'file',
+            'req'     => false,
+            'name'    => 'image',
+            'display' => 'Upload a Package Photo / Image',
+        );
+        $e[] = array(
+            'type'    => 'submit',
+            'req'     => false,
+            'name'    => 'add_rmv',
+            'display' => 'Update Package',
+            'opts'    => array('class' => 'submit')
+        );
+        $e[] = array(
+            'type'    => 'submit',
+            'req'     => false,
+            'name'    => 'remove_rmv',
+            'display' => 'Remove Package',
+            'opts'    => array('class' => 'submit')
+        );
+
+        $this->setupElements($e);
+    }
+
+    //    }}}
+    //    {{{ configureRules()
+
+    /**
+     * Configure rules for form
+     *
+     * @return void
+     * @access public
+     */
+    public function configureRules()
+    {
+        $r = array();
+
+        $checkDate = create_function('$d', '$d = implode("-", $d); return Validate::date($d, array("format" => "%n-%j-%Y"));');
+        $r[] = array(
+            'element'    => 'sdate',
+            'message'    => 'ERROR: Invalid Date!',
+            'type'       => 'callback',
+            'format'     => $checkDate,
+            'validation' => $this->validationType,
+            'reset'      => false,
+            'force'      => false
+        );
+        $r[] = array(
+            'element'    => 'edate',
+            'message'    => 'ERROR: Invalid Date!',
+            'type'       => 'callback',
+            'format'     => $checkDate,
+            'validation' => $this->validationType,
+            'reset'      => false,
+            'force'      => false
+        );
+        $r[] = array(
+            'element'    => 'image',
+            'message'    => 'ERROR: Incorrect File Type (.gif, .png, .jpg) only!',
+            'type'       => 'mimetype',
+            'format'     => $this->mimeTypes,
+            'validation' => $this->validationType,
+            'reset'      => false,
+            'force'      => false
+        );
+
+        $this->setupRules($r);
+    }
+
+    //    }}}
+    //    {{{ configureConstants()
+
+    /**
+     * Configure constants for form
+     *
+     * @return void
+     * @access public
+     */
+    public function configureConstants()
+    {
+        $c = array(
+            'remove_img_rmv' => false
+        );
+
+        $this->setupConstants($c);
+    }
+
+    //    }}}
+    //    {{{ configureFilters()
+
+    /**
+     * Configure filters for form
+     *
+     * @return void
+     * @access public
+     */
+    public function configureFilters()
+    {
+        $f = array();
+
+        $f[] = array(
+            'element' => '__ALL__',
+            'filter' => 'trim'
+        );
+
+        $this->setupFilters($f);
+    }
+
+    //    }}}
+
+    //  {{{ deleteImage()
+
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @param Toolkit_Image_Server $is  Parameter description (if any) ...
+     * @param string               $img Parameter description (if any) ...
+     *
+     * @return unknown Return description (if any) ...
+     * @access protected
+     */
+    protected function deleteImage(Toolkit_Image_Server $is, $img)
+    {
+        return $is->imageDelete($img);
+    }
+
+    //  }}}
+
+    //    {{{    _createPendingData()
+
+    /**
+     * Description for _createPendingData()
+     *
+     * @param array $values Value array
+     *
+     * @return void|mixed only on error
+     * @access private
+     */
+    private function _createPendingData($values)
+    {
+        try {
+            $sql = "
+                INSERT INTO member_updates
+                    (field, update, db_table, data_type, label,
+                     foreign_key, member_id, field_type)
+                VALUES (:field, :update, :db_table, :data_type, :label,
+                        :foreign_key, :member_id, :field_type)";
+
+            $dataToInsert = array();
+            if ($values['title'] != $this->_defaultValues['title']) {
+                $dataToInsert[] = array(
+                     'field' => 'title',
+                     'update' => $values['title'],
+                     'label' => 'Title'
+                );
+            }
+
+            if ($values['description'] != $this->_defaultValues['description']) {
+                $dataToInsert[] = array(
+                        'field' => 'description',
+                        'update' => $values['description'],
+                        'label' => 'Description'
+                );
+            }
+            /*if ($values['image'] != $this->_defaultValues['image']) {
+                $dataToInsert[] = array(
+                        'field' => 'image',
+                        'update' => $values['image'],
+                        'label' => 'Image'
+                );
+            }*/
+            $stmt = $this->dbh->prepare($sql);
+            $stmt->bindValue(':db_table', 'member_packages', PDO::PARAM_STR);
+            $stmt->bindValue(':data_type', 'text', PDO::PARAM_STR);
+            $stmt->bindValue(':field_type', 'text', PDO::PARAM_STR);
+            $stmt->bindParam(':foreign_key', $values['id'], PDO::PARAM_INT);
+            $stmt->bindParam(':member_id', $values['member_id'], PDO::PARAM_INT);
+
+            foreach ($dataToInsert as $i) {
+                if (!empty($i['update'])) {
+                    $stmt->bindParam(':field', $i['field'], PDO::PARAM_STR);
+                    $stmt->bindParam(':update', $i['update'], PDO::PARAM_STR);
+                    $stmt->bindParam(':label', $i['label'], PDO::PARAM_STR);
+                    $stmt->execute();
+                }
+            }
+        } catch (PDOException $e) {
+            return Toolkit_Common::handleError($e);
+        }
+    }
+
+    //    }}}
+
+
+    //  {{{ emailOwner()
+
+    /**
+     * Emails the owner and anyone else who wants to be advised of updates
+     *
+     * A false value in the primaryAdvisee will cause no email to be sent.
+     * all secondary advisees listed in the constructor are carbon copied
+     * in the email.
+     *
+     * Emails are sent out in both HTML and TXT forms.
+     *
+     * @return boolean result of email
+     * @access protected
+     */
+    protected function emailOwner()
+    {
+        if (MEMBER_RECORD_UPDATES_ADVISOR === false) {
+            return;
+        } else {
+            $this->flexyOptions = Toolkit_Members::getFlexyOptions();
+            $id = $GLOBALS['memberAuth']->getAuthData('member_id');
+            try {
+                $sql = "
+                    SELECT member_name
+                      FROM member
+                     WHERE member_id = :member_id";
+                $stmt = $this->dbh->prepare($sql);
+                $stmt->bindParam(':member_id', $id, PDO::PARAM_INT);
+                $stmt->execute();
+                $row = $stmt->fetch(PDO::FETCH_ASSOC);
+                $memberName = $row['member_name'];
+            } catch (PDOException $e) {
+                return Toolkit_Common::handleError($e);
+            }
+            $template = new HTML_Template_Flexy($this->flexyOptions);
+            $page = new stdClass();
+            $page->member = $memberName;
+            $page->url = ($_SERVER['HTTPS'] == 'on') ? BASE_SECURE_URL : MEDIA_BASE_URL;
+            $page->email = OWNER_EMAIL;
+            $page->siteName = SITENAME;
+            $page->link = '<a target="_blank"  href="'.MEDIA_BASE_URL.'pending-member/'.$id.'/">link</a>';
+
+            $template->compile($this->emailTemplate);
+            //  Merge the compiled template with the $page object.
+            $htmlMsg = $template->bufferedOutputObject($page);
+
+            $msg = "
+                <h3>$memberName</h3>
+                <p>
+                    Has updated thier business record and is now in a pending
+                    state. To approve / reject thier changes you can either log
+                    into your {$page->siteName} admin area or follow this
+                    {$page->link}
+                </p>";
+            $crlf = "\n";
+            $mimeMail = new Mail_mime($crlf);
+            $from = preg_replace("/[^A-Za-z ]/", "", SITENAME) . ' <' . OWNER_EMAIL . '>';
+            $mimeMail->setFrom($from);
+            $mimeMail->setSubject('Member Record Update');
+            $mimeMail->setHTMLBody($htmlMsg);
+            $mimeMail->setTXTBody($msg);
+
+            $mail =& Mail::factory('mail');
+            $body = $mimeMail->get();
+            $headers = $mimeMail->headers($hdrs);
+
+            $res = $mail->send(MEMBER_RECORD_UPDATES_ADVISOR, $headers, $body);
+            if (PEAR::isError($res)) {
+                return Toolkit_Common::handleError($res);
+            } else {
+                return $res;
+            }
+        }
+    }
+
+    //  }}}
+    //    {{{ processData()
+
+    /**
+     * Handles setting up the from processing and which function to get it done
+     *
+     * @param array $values Submitted values from the form.
+     *
+     * @return void
+     * @access protected
+     */
+    protected function processData($values)
+    {
+        $id = $GLOBALS['memberAuth']->getAuthData('member_id');
+        $cache = new Cache_Lite(Toolkit_Members::getCacheOptions());
+        $cache->remove("Member-$id", 'Profile');
+
+        $e =& $this->getElement('curr_image_rmv');
+
+        $values['image'] = $e->getValue('curr_image_rmv');
+        $values['sdate'] = implode('-', $values['sdate']);
+        $values['edate'] = implode('-', $values['edate']);
+        unset($values['MAX_FILE_SIZE'],
+              $values['curr_image_rmv'],
+              $values['remove_img_rmv'],
+              $values['add_rmv']);
+
+        $this->tableMetaData = Toolkit_Common::getTableMetaData(
+            $this->dbh,
+            $this->tableName
+        );
+        $this->updateData($values);
+        if (   $values['title'] != $this->_defaultValues['title']
+            || $values['description'] != $this->_defaultValues['description']
+        ) {
+            $this->emailOwner();
+        }
+
+        $listPage = MEDIA_BASE_URL .
+            "members-only-area/?rt=EditProfile&tab=packages";
+        header("Location: $listPage");
+    }
+
+    //    }}}
+
+    //    {{{ setupRenderers()
+
+    /**
+     * Set up the rendering engine we are going to use to display this form
+     *
+     * @return void
+     * @access protected
+     */
+    protected function setupRenderers()
+    {
+        $fo = Toolkit_Members::getFlexyOptions();
+
+        $this->template = new HTML_Template_Flexy($fo);
+
+        $renderer = new HTML_QuickForm_Renderer_ObjectFlexy($this->template);
+
+        $this->accept($renderer);
+        $this->view              = new StdClass;
+        $this->view->showCurrImg = $this->showCurrImg;
+        $this->view->form        = $renderer->toObject();
+        $this->template->compile($this->formTemplate);
+    }
+
+    //    }}}
+
+    //  {{{ validNewImg()
+
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @param array $newImg Parameter description (if any) ...
+     *
+     * @return mixed  Return description (if any) ...
+     * @access public
+     */
+    function validNewImg(array $newImg)
+    {
+        return (is_numeric($newImg['size']) &&
+                $newImg['size'] > 0 &&
+                in_array($newImg['type'], $this->mimeTypes));
+    }
+
+    //  }}}
+    //  {{{ removeOldImage()
+
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @param Toolkit_Image_Server $is     Parameter description (if any) ...
+     * @param string               $oldImg Parameter description (if any) ...
+     *
+     * @return void
+     * @access public
+     */
+    function removeOldImage(Toolkit_Image_Server $is, $oldImg)
+    {
+        $this->deleteImage($is, $oldImg);
+        if ($this->elementExists('curr_image_rmv')) {
+            $e =& $this->getElement('curr_image_rmv');
+            $e->setValue(null);
+            $this->_submitValues['curr_image_rmv'] = null;
+        }
+    }
+
+    //  }}}
+    //  {{{ syncCurrImage()
+
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @return void
+     * @access protected
+     */
+    protected function syncCurrImage()
+    {
+        $is = new Toolkit_Image_Server();
+
+        $delImg = $this->getSubmitValue('remove_img_rmv');
+        $oldImg = $this->getSubmitValue('curr_image_rmv');
+        $newImg = $this->getSubmitValue('image');
+
+        if ($delImg && $oldImg) {
+            $this->removeOldImage($is, $oldImg);
+            unset($oldImg);
+        } elseif ($oldImg && $this->validNewImg($newImg)) {
+            $this->removeOldImage($is, $oldImg);
+            unset($oldImg);
+        }
+
+        if ($this->validNewImg($newImg)) {
+            $image = $this->uploadImage($is, 'image');
+        } else {
+            $image = $oldImg;
+        }
+
+        if ($image) {
+            $this->updatePhotoElements($is, $image);
+            $this->showCurrImg = true;
+        }
+    }
+
+    //  }}}
+
+    //    {{{ toHtml()
+
+    /**
+     * Renders the form
+     *
+     * sets the page the form should be redirected to instead of coming back
+     * around to itself.
+     *
+     * @return string The rendered form
+     * @access public
+     */
+    public function toHtml()
+    {
+        //    We need to validate (and freeze if needed)
+        //    before we render the form. That way the
+        //    template knows about any errors on the form.
+        $this->validated = $this->validate();
+
+        //  If they have submitted the form and uploaded a proper image
+        //  but some other element had an error, then we need to show
+        //  their uploaded image in the form
+        if ($this->isSubmitted()) {
+            $this->syncCurrImage();
+        }
+
+        $this->setupRenderers();
+
+        if ($this->validated) {
+            $processed = $this->process(
+                array(&$this, 'processData'),
+                $this->mergeFiles
+            );
+        }
+
+        return $this->template->bufferedOutputObject($this->view);
+    }
+
+    //    }}}
+
+    //    {{{    removePackage()
+
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @param integer $id Member ID
+     *
+     * @return object    Return description (if any) ...
+     * @access protected
+     */
+    protected function removePackage($id)
+    {
+        try {
+            $this->dbh->beginTransaction();
+            //  need to delete the image associated w/ this package here.
+            $sql = "
+                DELETE FROM {$this->tableName}
+                 WHERE id           = :id
+                   AND member_id    = :mid";
+
+            $stmt = $this->dbh->prepare($sql);
+            $stmt->bindParam(':id', $id, PDO::PARAM_INT);
+            $stmt->bindparam(
+                ':mid',
+                $GLOBALS['memberAuth']->getAuthData('member_id'),
+                PDO::PARAM_INT
+            );
+
+            $stmt->execute();
+
+            $sql = "
+                DELETE FROM member_updates
+                 WHERE db_table = '{$this->tableName}'
+                   AND member_id = :mid
+                   AND foreign_key = :id";
+
+            $stmt = $this->dbh->prepare($sql);
+            $stmt->bindParam(':id', $id, PDO::PARAM_INT);
+            $stmt->bindparam(
+                ':mid',
+                $GLOBALS['memberAuth']->getAuthData('member_id'),
+                PDO::PARAM_INT
+            );
+            $stmt->execute();
+            return $this->dbh->commit();
+        } catch (PDOException $e) {
+            $this->dbh->rollback();
+            return Toolkit_Common::handleError($e);
+        }
+    }
+
+    //    }}}
+
+    //    {{{ updateData()
+
+    /**
+     * Update the package caption
+     *
+     * @param array $values Submitted form values
+     *
+     * @return boolean Result of updating the caption in the db
+     * @access public
+     */
+    protected function updateData($values)
+    {
+        try {
+            if (array_key_exists('remove_rmv', $values)) {
+                return $this->removePackage($this->packageId);
+            }
+            $this->dbh->beginTransaction();
+
+            $pending = $values;
+            $pending['member_id'] = $GLOBALS['memberAuth']->getAuthData('member_id');
+            unset($pending['sdate'], $pending['edate']);
+            unset($values['title'], $values['description']);
+            $sql = Toolkit_Common::createSQLUpdate(
+                $this->tableName,
+                array_keys($values),
+                array('id = :id')
+            );
+
+            //$values['id'] = $this->packageId;
+            $pending['id'] = $values['id'] = $this->packageId;
+            $res = Toolkit_Common::processQuery(
+                $this->dbh,
+                $this->tableName,
+                $sql,
+                $values
+            );
+
+            $this->_createPendingData($pending);
+
+            $this->dbh->commit();
+            return $res;
+        } catch (PDOException $e) {
+            $this->dbh->rollback();
+            return Toolkit_Common::handleError($e);
+        }
+    }
+
+    //    }}}
+    //  {{{ updatePhotoElements()
+
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @param Toolkit_Image_Server $is    Parameter description (if any) ...
+     * @param string               $image Parameter description (if any) ...
+     *
+     * @return void
+     * @access public
+     */
+    public function updatePhotoElements(Toolkit_Image_Server $is, $image)
+    {
+        //  Get the dimensions of the image
+        $dimensions = $is->getImageSize(MEMBER_PHOTOS . $image);
+        if (PEAR::isError($dimensions)) {
+            Toolkit_Common::handleError($dimensions);
+        }
+        list($w, $h) = $dimensions;
+        $s = MEMBER_PHOTOS . $image;
+
+        //  Set the image to show in the element
+        $e =& $this->getElement('curr_image');
+        $e->setText('<img width="'.$w.'" height="'.$h.'" src="'.$s.'">');
+
+        //  updated the hidden elements value to make sure it
+        //  holds the most up-to-date image name
+        $e =& $this->getElement('curr_image_rmv');
+        $e->setValue($image);
+    }
+
+    //  }}}
+    //  {{{ uploadImage()
+
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @param Toolkit_Image_Server  $is    Parameter description (if any) ...
+     * @param string                $field Parameter description (if any) ...
+     *
+     * @return string    Return description (if any) ...
+     * @access protected
+     */
+    protected function uploadImage(Toolkit_Image_Server $is, $field)
+    {
+        return $is->imageUpload($field);
+    }
+
+    //  }}}
+}
+?>
diff --git a/Toolkit/Members/EditPhoto.php b/Toolkit/Members/EditPhoto.php
new file mode 100644 (file)
index 0000000..779b353
--- /dev/null
@@ -0,0 +1,235 @@
+<?php
+//     vim:set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker syntax=php:
+
+/**
+ * Handles the photos tab in the member record
+ *
+ * Controls setting up the add photo form if applicable, and rendering
+ * each uploaded photo edit form to edit/delete the photo.
+ *
+ * PHP version 5
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @version   CVS: $Id: EditPhoto.php,v 1.5 2010/07/16 20:52:51 jamie Exp $
+ * @link      http://demo.gaslightmedia.com
+ * @see       Toolkit/Image/Server.php
+ */
+
+
+/**
+ * The image server processing class
+ */
+require_once BASE . 'Toolkit/Image/Server.php';
+
+/**
+ * Form to handle editing/deleting existing photos in members only area
+ *
+ * Handles updating caption requests for a member or to remove a
+ * photo from thier profile
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @link      http://demo.gaslightmedia.com
+ */
+class Toolkit_Members_EditPhoto extends Toolkit_Members_Admin_EditPhoto
+{
+       //      {{{ configureDefaults()
+
+       /**
+        * Sets the defaults for elements in the form.
+        *
+        * @return void
+        * @access public
+        */
+       public function configureDefaults()
+       {
+               $photo    = $this->photos->findNode($this->photoId);
+               $defaults = array(
+                       'caption' => $photo->getCaption(),
+                       'pos' => $photo->getPosition(),
+                       'pid' => $photo->getId(),
+               );
+
+               //      overwrite the photo caption w/ the pending
+               //      caption, ONLY if the pending caption exists.
+               $pc = $photo->getPendingCaption();
+               if (!empty($pc)) {
+                       $defaults['caption'] = $pc;
+                       $photo->setFauxPending(true);
+               }
+               $this->setupDefaults($defaults);
+       }
+
+       //      }}}
+       //      {{{ configureElements()
+
+       /**
+        * Setup the elements to use on the form.
+        *
+     * @param Config_Container $c application configuration
+        *
+        * @return void
+        * @access public
+        */
+       public function configureElements(Config_Container $c)
+       {
+        $e = array();
+
+               $positions = range(1, $this->photos->getListSize(true));
+               $options   = array_combine($positions, $positions);
+
+        $config =& $c->getItem('section', 'photos');
+        $maxLength =& $config->getItem('directive', 'maxCaptionLength');
+               //      All Grouped Elements are created here.
+
+               //      All Elements are created here.  This includes group element definitions.
+               $cur = $this->photos->findNode($this->photoId);
+
+               $e[] = array(
+                       'type' => 'hidden',
+                       'req'  => false,
+                       'name' => 'pid'
+               );
+               $e[] = array(
+                       'type' => 'text',
+                       'req'  => false,
+                       'name' => 'caption',
+            'display' => 'Image Caption',
+                       'opts' => array(
+                               'class'     => 'text',
+                               'maxlength' => $maxLength->getContent(),
+                       ),
+            'noCharLimit' => true
+               );
+               $e[] = array(
+                       'type'    => 'select',
+                       'req'     => false,
+                       'name'    => 'pos',
+                       'display' => 'Position',
+                       'opts'    => $options,
+                       'att'     => array('id' => "pos{$cur->getPosition()}")
+               );
+               $e[] = array(
+                       'type'    => 'submit',
+                       'req'     => false,
+                       'name'    => 'update',
+                       'display' => 'Update Photo',
+                       'opts'    => array('class' => 'submit')
+               );
+               $e[] = array(
+                       'type'    => 'submit',
+                       'req'     => false,
+                       'name'    => 'delete',
+                       'display' => 'Delete Photo',
+                       'opts'    => array('class' => 'photoDelete')
+               );
+
+               $this->setupElements($e);
+       }
+
+       //      }}}
+       //      {{{ createPendingCaption()
+
+       /**
+        * Updates the caption in the member_updates table for the photo
+        *
+        * @param array            &$values submitted values from the form.
+     * @param Config_Container $c       Member Configuration
+        *
+        * @return sql insert result
+        * @access protected
+        */
+       protected function createPendingCaption(&$values, Config_Container $c)
+       {
+        $config = $c->getItem('section', 'tables');
+        $pendingTable = $config->getItem('directive', 'pendingTable');
+
+               try {
+                       //      Insert the picture into the updates table for approval.
+                       $sql =  "
+                INSERT INTO {$pendingTable->getContent()}
+                                       (member_id, field, update, db_table,
+                                       data_type, label, foreign_key)
+                VALUES
+                                       (:member_id, :field, :update, :db_table,
+                                       :data_type, :label, :foreign_key)";
+
+                       $stmt = $this->dbh->prepare($sql);
+                       $stmt->bindParam(
+                ':member_id',
+                $GLOBALS['memberAuth']->getAuthData('member_id'),
+                PDO::PARAM_INT
+            );
+                       $stmt->bindValue(':field', 'caption', PDO::PARAM_STR);
+                       $stmt->bindParam(':update', $values['caption'], PDO::PARAM_BOOL);
+                       $stmt->bindValue(':db_table', 'member_photos', PDO::PARAM_STR);
+                       $stmt->bindValue(':data_type', 'text', PDO::PARAM_STR);
+                       $stmt->bindValue(':label', 'Caption', PDO::PARAM_STR);
+                       $stmt->bindParam(':foreign_key', $values['pid'], PDO::PARAM_STR);
+                       return $stmt->execute();
+               } catch (PDOException $e) {
+                       return Toolkit_Common::handleError($e);
+               }
+       }
+
+        //     }}}
+
+       //      {{{ toHtml()
+
+       /**
+        * Renders the form
+        *
+        * sets the page the form should be redirected to instead of coming back
+        * around to itself.
+        *
+     * @param HTML_Template_Flexy  $tEngine Templating Engine
+     * @param Cache_Lite           $cache   Caching Engine
+     * @param Toolkit_Image_Server $is      Image Server
+     * @param Config_Container     $c       Member Configuration
+     *
+        * @return string The rendered form
+        * @access public
+        */
+       public function toHtml(
+        HTML_Template_Flexy $tEngine,
+        Cache_Lite $cache,
+        Toolkit_Image_Server $is,
+        Config_Container $c
+    ) {
+               if ($this->validate()) {
+            $config = $c->getItem('section', 'conf');
+            $strictPending = $config->getItem('directive', 'strictPending');
+
+            $id = $GLOBALS['memberAuth']->getAuthData('member_id');
+            $res = $cache->remove("Member-$id", 'Profile');
+
+            if ($this->getSubmitValue('delete')) {
+                $this->photos->removeNode($is, $c, $this->getSubmitValue('pid'));
+            }
+
+            if ($strictPending->getContent() && !$this->getSubmitValue('delete')) {
+                $this->createPendingCaption($this->getSubmitValues(), $c);
+                header('Location:' . $this->getAttribute('action').'&notify=1');
+            } else {
+                $this->process(
+                    array(&$this, 'processData'),
+                    $this->mergeFiles
+                );
+            }
+               }
+
+               $this->setupRenderers($tEngine);
+
+               return $tEngine->bufferedOutputObject($this->view);
+       }
+
+       //      }}}
+}
+?>
diff --git a/Toolkit/Members/Events/CommonEditEvent.php b/Toolkit/Members/Events/CommonEditEvent.php
new file mode 100644 (file)
index 0000000..5bc0a42
--- /dev/null
@@ -0,0 +1,1730 @@
+<?php
+/**
+ * New Event Form
+ *
+ * PHP version 5
+ *
+ * @category Toolkit
+ * @package  Members_Events
+ * @author   Steve Sutton <steve@gaslightmedia.com>
+ * @license  http://www.gaslightmedia.com Gaslightmedia
+ * @version  CVS: $Id: AddEventForm.php,v 1.20 2010/07/04 23:58:22 jamie Exp $
+ * @link     http://demo.gaslightmedia.com
+ */
+if (!defined('COMMON_APP_BASE')) {
+    define('COMMON_APP_BASE', '/var/www/server/CommonApps/');
+}
+require_once COMMON_APP_BASE . 'EventCalendar/V1/models/EventMapper.php';
+require_once BASE . 'Toolkit/Forms/Rules/Image.php';
+
+/**
+ * Toolkit_Members_Events_EditEvent
+ *
+ * @category  Toolkit
+ * @package   Members_Events
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @link      http://demo.gaslightmedia.com
+ */
+class Toolkit_Members_Events_CommonEditEvent
+    extends Toolkit_FormBuilder
+    implements Toolkit_Form
+{
+    //  {{{ properties
+
+    /**
+     * Table in Database which holds the contact data
+     *
+     * @var    string
+     * @access public
+     */
+    public $tableName = 'events.events';
+
+    /**
+     * Table meta data
+     *
+     * This is used when inserting/updating data for the records
+     * so the PDO's can use explicit data types for the parameters.
+     *
+     * @var    array
+     * @access public
+     */
+    public $tableMetaData;
+
+    /**
+     * Who to send the email to when the contact form is submitted
+     *
+     * If you leave this blank, its value will get set to the OWNER_EMAIL
+     * in the constructor.
+     *
+     * If you ***DO NOT*** want any emails to go out when the form is submitted
+     * then set the value to false. Do not set it to 0 for false, because the
+     * check uses a strict type check to determine if the value is actually
+     * false. This is what allows for the empty value as an option, which sets
+     * the value to OWNER_EMAIL and won't override the $email property if
+     * this class gets subclassed and the value for this property gets set in
+     * the properties of the subclass and not in the constructor after this
+     * constructor function is called.
+     *
+     * tongue twister...I know.
+     * <code>
+     * protected $email = false;
+     * </code>
+     *
+     * @var    unknown
+     * @access protected
+     */
+    protected $email;
+
+    /**
+     * From header in the owner email
+     *
+     * This just sets the From header in the owner email
+     * SITENAME <from@email.com>
+     *
+     * It gets set to the constant SITENAME in the constructor if you leave
+     * empty here, but you can set it to something different here to override
+     * that if you desire.
+     *
+     * @var    unknown
+     * @access protected
+     */
+    protected $siteName;
+
+    /**
+     * Email subject and <h1> header in email
+     *
+     * It gets set in the constructor if you leave empty here, but you
+     * can set it to something different here to override that if you desire.
+     *
+     * @var    string
+     * @access protected
+     */
+    protected $subject = 'New Event Submission';
+
+    /**
+     * Message to display if the form is successfully submitted
+     *
+     * @var    string
+     * @access protected
+     */
+    protected $successMsg = '
+    <style type="text/css">
+        #category {display:none};
+        .listings {display:none};
+    </style>
+        <div id="form-sucess-top">
+            Your event has been successfully added to the events calendar,
+            however will not be visible until it has been approved by
+            the Web site administrator. Thank You.
+        </div>';
+
+    /**
+     * Extra rules for processesing
+     *
+     * This registers the Zip validation rules (and any others listed) for
+     * QuickForm.
+     *
+     * Zip validation checks both US and Canadian Zip codes
+     *
+     * @var    array
+     * @access protected
+     */
+    protected $registeredRules = array(
+        'phone',
+        array(
+            'checkEmail',
+            'callback',
+            'email',
+            'Validate'
+        ),
+        array(
+            'checkURI',
+            'callback',
+            'uri',
+            'Validate'
+        )
+    );
+
+    /**
+     * Options for flexy templating engine
+     *
+     * Pulls the preset options from the setup.phtml file
+     * overwrites the templateDir and compileDir to match this classes needs
+     *
+     * @var    array
+     * @access protected
+     */
+    protected $flexyOptions;
+
+    protected $eventMapper;
+
+    //  }}}
+    //  {{{ __construct()
+
+    /**
+     * Class constructor
+     *
+     * @param PDO    $pdo         PHP Data Object
+     * @param string $formName    Form's name.
+     * @param string $method      (optional)Form's method defaults to 'POST'
+     * @param string $action      (optional)Form's action
+     * @param string $target      (optional)Form's target defaults to '_self'
+     * @param mixed  $attributes  (optional)Extra attributes for <form> tag
+     * @param bool   $trackSubmit (optional)Whether to track if the form was
+     *                            submitted by adding a special hidden field
+     *
+     * @access public
+     */
+    public function __construct(
+        PDO $pdo,
+        $formName,
+        $method = 'post',
+        $action = '',
+        $target = '',
+        $attributes = null,
+        $trackSubmit = false,
+        EventMapper $eventMapper
+    ) {
+        parent::__construct(
+            $formName,
+            $method,
+            $action,
+            $target,
+            $attributes,
+            $trackSubmit
+        );
+
+        $this->dbh = $pdo;
+        $this->eventMapper = $eventMapper;
+
+        if ($this->email !== false && empty($this->email)) {
+            //  Set to false to turn off email function.
+            $this->email
+                = (MEMBERS_EVENTS_NOTIFICATION_EMAIL)
+                ? MEMBERS_EVENTS_NOTIFICATION_EMAIL
+                : MEMBER_RECORD_UPDATES_ADVISOR;
+        }
+        if (empty($this->siteName)) {
+            $this->siteName = SITENAME;
+        }
+        if (empty($this->subject)) {
+            $this->subject = 'Contact Request from website ' . SITENAME;
+        }
+
+        $this->flexyOptions                = $GLOBALS['flexyOptions'];
+        $this->flexyOptions['templateDir'] = dirname(__FILE__) . "/templates/";
+        $this->flexyOptions['compileDir']
+            = dirname(__FILE__) . "/templates/compiled/";
+
+        $var = basename(__FILE__, '.php');
+
+        $callbackUrl = ($_SERVER['HTTPS'] == 'on') ?
+                              BASE_SECURE_URL : MEDIA_BASE_URL;
+
+        $this->captchaOptions = array(
+            'width' => 100,
+            'height' => 50,
+            'callback' => "{$callbackUrl}Toolkit/qfcaptcha.php?var=$var",
+            'sessionVar' => $var,
+            'imageOptions' => array(
+                'font_size' => 16,
+                'font_path' => GLM_APP_BASE . 'glmPEAR/Image/Canvas/Fonts/',
+                'font_file' => 'times.ttf',
+                'background_color' => '#cccccc',
+                'obfuscation' => false,
+                'angle' => true,
+            ),
+        );
+    }
+
+    //  }}}
+
+    //  {{{ checkDate()
+
+    /**
+     * Validate date input
+     *
+     * allows for empty dates to be valid
+     *
+     * @param array $date date group from form
+     *
+     * @return boolean true if valid, false if not
+     * @access public
+     */
+    public function checkDate($date)
+    {
+        if (!$date) {
+            return true;
+        } else {
+            return Validate::date($date, array('format' => '%m/%d/%Y'));
+        }
+    }
+
+    //  }}}
+    //  {{{ checkDateRange()
+
+    /**
+     * Validate date input
+     *
+     * allows for empty end date to be valid
+     *
+     * @param array $d date group from form
+     *
+     * @return boolean true if valid, false if not
+     * @access public
+     */
+    public function checkDateRange(array $d)
+    {
+        if (!$this->hasEndDate($d[1])) {
+            //  no end date is a valid date range
+            return true;
+        }
+
+        $pattern = '/([0-9]{2})\/([0-9]{2})\/([0-9]{4})/';
+        if (preg_match($pattern, $d[0], $m)) {
+            $t1 = mktime(0, 0, 0, (int) $m[1], (int) $m[2], (int) $m[3]);
+            $bdate = new Date($t1);
+        }
+        if (preg_match($pattern, $d[1], $m)) {
+            $t2    = mktime(0, 0, 0, (int) $m[1], (int) $m[2], (int) $m[3]);
+            $edate = new Date($t2);
+        }
+        if ($bdate && $edate) {
+            //  0 if the dates are equal - valid
+            // -1 if $bdate is before $edate - valid
+            //  1 if $bdate is after $edate - invalid
+            $res = Date::compare($bdate, $edate);
+            return ($res !== 1);
+        }
+        return true;
+    }
+
+    //  }}}
+    //  {{{ configureElements()
+
+    /**
+     * Form element definitions
+     *
+     * @return void
+     * @access public
+     */
+    public function configureElements()
+    {
+        $e = array();
+
+        //  All Elements are created here.  This includes group element definitions.
+        $e[] = array(
+            'type'    => 'header',
+            'name'    => 'eventInfoHeader_rmv',
+            'display' => 'Event Information'
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => true,
+            'name'    => 'header',
+            'display' => 'Event Name'
+        );
+        $e[] = array(
+            'type' => 'hidden',
+            'req'  => false,
+            'name' => 'id'
+        );
+        $e[] = array(
+            'type' => 'hidden',
+            'req'  => false,
+            'name' => 'member',
+        );
+        $e[] = array(
+            'type' => 'hidden',
+            'req'  => false,
+            'name' => 'visable',
+            'val'  => 0
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => true,
+            'name'    => 'starting',
+            'display' => 'Start Date',
+            'opts'    => array('id' => 'sdate')
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => true,
+            'name'    => 'ending',
+            'display' => 'End Date',
+            'opts'    => array('id' => 'edate')
+        );
+        $e[] = array(
+            'type'    => 'advcheckbox',
+            'req'     => false,
+            'name'    => 'allday',
+            'display' => 'All Day Event?',
+            'opts'    => 'Yes',
+            'val'     => array(0, 1)
+        );
+        $e[] = array(
+            'type'    => 'date',
+            'req'     => false,
+            'name'    => 'btime',
+            'display' => 'Start Time',
+            'opts'    => array(
+                'format'           => 'h : i A',
+                'addEmptyOption'   => true,
+                'emptyOptionValue' => '',
+                'emptyOptionText'  => array(
+                    'h' => 'hh',
+                    'i' => 'mm',
+                    'A' => 'am/pm'
+                ),
+                'optionIncrement' => array(
+                    'i' => 15,
+                ),
+            ),
+            'error' => 'ERROR: You must select a start time!',
+        );
+        $e[] = array(
+            'type'    => 'date',
+            'req'     => false,
+            'name'    => 'etime',
+            'display' => 'End Time',
+            'opts'    => array(
+                'format'           => 'h : i A',
+                'addEmptyOption'   => true,
+                'emptyOptionValue' => '',
+                'emptyOptionText'  => array(
+                    'h' => 'hh',
+                    'i' => 'mm',
+                    'A' => 'am/pm'
+                ),
+                'optionIncrement' => array(
+                    'i' => 15,
+                ),
+            ),
+        );
+        $e[] = array(
+            'type'    => 'advcheckbox',
+            'req'     => false,
+            'name'    => 'recurr',
+            'display' => 'Recurring Event',
+            'opts'    => 'Is this a recurring event?',
+            'val'     => array(0, 1)
+        );
+        $daysOm = array(''=>'');
+        for ($i = 1; $i <= 31; ++$i) {
+            $daysOm[$i] = $i;
+        }
+        $e[] = array(
+            'type'    => 'select',
+            'name'    => 'dayom',
+            'display' => 'Every Month on',
+            'opts'    => $daysOm
+        );
+        $e[] = array(
+            'type'    => 'select',
+            'req'     => false,
+            'name'    => 'weekom',
+            'display' => 'Recurs',
+            'opts'    => array(
+                ''  => 'Every Week of Month',
+                '9' => 'Every Other Week',
+                '1' => 'Every First Week of Month',
+                '2' => 'Every Second Week of Month',
+                '3' => 'Every Third Week of Month',
+                '4' => 'Every Fourth Week of Month',
+            ),
+        );
+        $weekdays = array(
+            1 => 'Sunday',
+            'Monday',
+            'Tuesday',
+            'Wednesday',
+            'Thursday',
+            'Friday',
+            'Saturday'
+        );
+        $ri = 1;
+        for ($i = 1; $i <= 7; ++$i) {
+            $daysOw[] = array(
+                'type' => 'advcheckbox',
+                'req'  => false,
+                'name' => $i,
+                'opts' => $weekdays[$i],
+                'val'  => array('', $ri)
+            );
+            $ri = $ri << 1;
+        }
+        //echo '<pre>'.print_r($daysOw, true).'</pre>';
+        $e[] = array(
+            'type'       => 'group',
+            'req'        => false,
+            'name'       => 'daysow',
+            'group'      => $daysOw,
+            'label'      => 'Days Of Week',
+            'seperator'  => ' ',
+            'appendName' => true
+        );
+        $e[] = array(
+            'type'    => 'select',
+            'req'     => true,
+            'name'    => 'category',
+            'display' => 'Category',
+            'opts'    => $this->getTopicFields(),
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => 'website',
+            'display' => 'Website'
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => 'facebook',
+            'display' => 'Facebook',
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => 'twitter',
+            'display' => 'Twitter',
+        );
+
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => 'cost',
+            'display' => 'Cost',
+        );
+        $e[] = array(
+            'type'    => 'textarea',
+            'req'     => false,
+            'name'    => 'intro',
+            'display' => 'Intro <span id="charleft">350</span> characters left',
+            'opts'    => array('id' => 'intro')
+        );
+        $e[] = array(
+            'type'    => 'textarea',
+            'req'     => false,
+            'name'    => 'description',
+            'display' => 'Description',
+            'opts'    => array('id' => 'descr')
+        );
+        $e[] = array(
+            'type'    => 'static',
+            'req'     => false,
+            'name'    => 'current_img_rmv',
+            'display' => 'Current Image'
+        );
+        $e[] = array(
+            'type' => 'hidden',
+            'req'  => false,
+            'name' => 'img'
+        );
+        $e[] = array(
+            'type'    => 'file',
+            'req'     => false,
+            'name'    => 'img_file_rmv',
+            'display' => 'Event Image'
+        );
+        $e[] = array(
+            'type' => 'static',
+            'req'  => false,
+            'name' => 'img_instructions_rmv',
+            'opts' => '.jpg or .gif images only'
+        );
+        $e[] = array(
+            'type'    => 'header',
+            'name'    => 'eventLocationInfoHeader_rmv',
+            'display' => 'Event Location Information
+                <div id="map-dialog">
+                    <div id="map_canvas" style="width:500px; height:400px"></div>
+                </div>
+                <a id="map-it" href="#">Map It</a>'
+        );
+        $e[] = array(
+            'type' => 'hidden',
+            'req'  => false,
+            'name' => 'lat',
+            'opts' => array('id' => 'lat')
+        );
+        $e[] = array(
+            'type' => 'hidden',
+            'req'  => false,
+            'name' => 'lon',
+            'opts' => array('id' => 'lon')
+        );
+        $e[] = array(
+            'type'    => 'advcheckbox',
+            'req'     => false,
+            'name'    => 'use_memberloc',
+            'display' => 'User Member Location?',
+            'opts'    => 'Yes',
+            'val'     => array(0, 1)
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => 'place',
+            'display' => 'Place'
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => 'address',
+            'display' => 'Address',
+            'opts'    => array('id' => 'address')
+        );
+        $e[]         = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => 'city',
+            'display' => 'City',
+            'opts'    => array('id' => 'city')
+        );
+        $e[]         = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => 'state',
+            'display' => 'State',
+            'opts'    => array('id' => 'state')
+        );
+        $e[]         = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => 'zip',
+            'display' => 'ZIP',
+            'opts'    => array('id' => 'zip')
+        );
+        $e[] = array(
+            'type'    => 'header',
+            'name'    => 'eventContactHeader_rmv',
+            'display' => 'Event Contact Information'
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => 'contact_name',
+            'display' => 'Contact Person'
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => 'contact_email',
+            'display' => 'Contact Email'
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => 'contact_phone',
+            'display' => 'Contact Phone'
+        );
+        $e[] = array(
+            'type'    => 'static',
+            'req'     => false,
+            'name'    => 'current_file_rmv',
+            'display' => 'Current File'
+        );
+        $e[] = array(
+            'type' => 'hidden',
+            'req'  => false,
+            'name' => 'file'
+        );
+        $e[] = array(
+            'type'    => 'file',
+            'req'     => false,
+            'name'    => 'file_rmv',
+            'display' => 'Event File'
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => 'filename',
+            'display' => 'File Name'
+        );
+        $e[] = array(
+            'type'    => 'header',
+            'name'    => 'eventInfoHeader_rmv',
+            'display' => 'Event Admin Information'
+        );
+        $e[]      = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => 'admin_contact',
+            'display' => 'Contact Name Submitting Event'
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => 'admin_org',
+            'display' => 'Organization Name Submitting Event'
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => 'admin_phone',
+            'display' => 'Phone'
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => 'admin_email',
+            'display' => 'Email Address'
+        );
+        $e[] = array(
+            'type'    => 'submit',
+            'req'     => false,
+            'name'    => 'submit_rmv',
+            'display' => 'Submit'
+        );
+
+        $this->setupElements($e);
+    }
+
+    //  }}}
+    //  {{{ configureFilters()
+
+    /**
+     * Form filter definitions
+     *
+     * Applies a data filter for the given fields when the form is submitted
+     *
+     * @return void
+     * @access public
+     */
+    public function configureFilters()
+    {
+        $f = array();
+
+        $f[] = array(
+            'element' => '__ALL__',
+            'filter' => 'trim'
+        );
+        $f[] = array(
+            'element' => 'website',
+            'filter' => array('Toolkit_Common', 'filterURI')
+        );
+
+        $this->setupFilters($f);
+    }
+
+    //  }}}
+    //  {{{ configureForm()
+
+    /**
+     * Helper function to handle setting up the form
+     *
+     * @return void
+     * @access public
+     */
+    public function configureForm()
+    {
+        $this->configureElements();
+        $this->configureDefaults();
+        $this->configureFilters();
+        $this->configureRules();
+    }
+
+    //  }}}
+    //  {{{ configureRules()
+
+    /**
+     * Form rule definitions
+     *
+     * Adds validation rules for the given fields
+     *
+     * @return void
+     * @access public
+     */
+    public function configureRules()
+    {
+        //  Form Rules
+        $r = array();
+
+        $mimeTypes = array(
+            'image/jpe',
+            'image/jpeg',
+            'image/jpg',
+            'image/jfif',
+            'image/pjpeg',
+            'image/pjp',
+            'image/gif',
+            'image/png',
+        );
+
+        $r[] = array(
+            'element' => 'topicid',
+            'message' => 'ERROR: Invalid Topic!',
+            'type' => 'numeric',
+            'format' => null,
+            'validation' => $this->validationType,
+            'reset' => true,
+            'force' => false
+        );
+        $r[] = array(
+            'element' => 'email',
+            'message' => 'ERROR: Invalid Email Format!',
+            'type' => 'checkEmail',
+            'format' => array('use_rfc822' => true),
+            'validation' => $this->validationType,
+            'reset' => true,
+            'force' => false
+        );
+        $r[] = array(
+            'element' => array('bdate', 'edate'),
+            'message' => 'ERROR: Starting Date must be before Ending Date',
+            'type' => 'callback',
+            'format' => array(&$this, 'checkDateRange'),
+            'validation' => $this->validationType,
+            'reset' => false,
+            'force' => false
+        );
+        $r[] = array(
+            'element' => 'bdate',
+            'message' => 'ERROR: Invalid date!',
+            'type' => 'callback',
+            'format' => array(&$this, 'checkDate'),
+            'validation' => $this->validationType,
+            'reset' => false,
+            'force' => false
+        );
+        $r[] = array(
+            'element' => 'edate',
+            'message' => 'ERROR: Invalid date!',
+            'type' => 'callback',
+            'format' => array(&$this, 'checkDate'),
+            'validation' => $this->validationType,
+            'reset' => false,
+            'force' => false
+        );
+        $r[] = array(
+            'element' => 'url',
+            'message' => 'ERROR: Invalid URL format',
+            'type' => 'checkURI',
+            'format' => array(
+                'allowed_schemes' => array('http', 'https'),
+                'strict' => true
+            ),
+            'validation' => $this->validationType,
+            'reset' => false,
+            'force' => false
+        );
+        $r[] = array(
+            'element' => 'phone',
+            'message' => 'ERROR: Invalid Phone Format (xxx) xxx - xxxx!',
+            'type' => 'phone',
+            'format' => null,
+            'validation' => $this->validationType,
+            'reset' => true,
+            'force' => false
+        );
+        if ($this->useCaptcha) {
+            $r[] = array(
+                'element' => 'captcha_rmv',
+                'message' => 'ERROR: What you entered didn\'t match!',
+                'type' => 'CAPTCHA',
+                'format' => $this->captchaQuestion,
+                'validation' => $this->validationType,
+                'reset' => true,
+                'force' => false
+            );
+        }
+        if (is_uploaded_file($_FILES['img_file_rmv']['tmp_name'])) {
+            $r[] = array(
+                'element' => 'img_file_rmv',
+                'message' => 'ERROR: Incorrect File Type (.gif, .png, .jpg) only!',
+                'type' => 'mimetype',
+                'format' => $mimeTypes,
+                'validation' => $this->validationType,
+                'reset' => false,
+                'force' => false
+            );
+        }
+        $r[] = array(
+            'element' => 'img_file_rmv',
+            'message' => 'ERROR: Error uploading image!',
+            'type' => 'Image',
+            'format' => array(
+                'form' => $this,
+                'fieldName' => 'img_file_rmv',
+                'imageField' => 'img',
+                'is' => new Toolkit_FileServer_ImageAdapter(),
+                'deleteExistingImage' => false,
+                'injectImage' => array('tgtElement' => 'current_img_rmv')
+            ),
+            'validation' => 'server',
+            'reset' => false,
+            'force' => false
+        );
+        /*
+        $r[] = array(
+            'element' => 'file_rmv',
+            'message' => 'ERROR: Error uploading file!',
+            'type' => 'Image',
+            'format' => array(
+                'form' => $this,
+                'fieldName' => 'file_rmv',
+                'imageField' => 'file',
+                'is' => new Toolkit_FileServer_FileAdapter(),
+                'deleteExistingImage' => false,
+                'injectImage' => array('tgtElement' => 'current_file_rmv')
+            ),
+            'validation' => 'server',
+            'reset' => false,
+            'force' => false
+        );
+         */
+
+        $this->setupRules($r);
+    }
+
+    //  }}}
+    //  {{{ configureDefaults()
+
+    /**
+     * Form defaults
+     *
+     * @return void
+     * @access public
+     */
+    public function configureDefaults()
+    {
+        $eventId = filter_var($_REQUEST['id'], FILTER_VALIDATE_INT);
+        if ($eventId) {
+            $event = $this->eventMapper->fetchEvent($eventId);
+            $eventFile = $event->getFile();
+            if ($eventFile) {
+                $file     = $eventFile->getFilename();
+                $filename = $eventFile->getUrltext();
+            } else {
+                $file = $filename = '';
+            }
+            $d = array(
+                'id'          => $event->getId(),
+                'starting'    => $event->getStarting(),
+                'ending'      => $event->getEnding(),
+                'description' => $event->getDescription(),
+                'category'    => $event->getCategory()->getId(),
+                'btime'       => $event->getStarthour(),
+                'etime'       => $event->getEndhour(),
+                'header'      => $event->getHeader(),
+                'website'     => $event->getWebsite(),
+                'facebook'    => $event->getFacebook(),
+                'twitter'     => $event->getTwitter(),
+                'allday'      => $event->getAllday(),
+                'hide_address' => $event->getHideAddress(),
+                'recurr'       => $event->getRecurr(),
+                'daysow'       => $event->getDaysow(),
+                'dayom'        => $event->getDayom(),
+                'weekom'       => $event->getWeekom(),
+                'place'        => $event->getPlace(),
+                'address'      => $event->getAddress(),
+                'city'         => $event->getCity(),
+                'state'        => $event->getState(),
+                'zip'          => $event->getZip(),
+                'lat'          => $event->getLat(),
+                'lon'          => $event->getLon(),
+                'cost'         => $event->getCost(),
+                'contact_name' => $event->getContactName(),
+                'contact_email' => $event->getContactEmail(),
+                'contact_phone' => $event->getContactPhone(),
+                'intro'         => $event->getIntro(),
+                'use_memberloc' => $event->getUseMemberLocation(),
+                'img'           => $event->getImage(),
+                'admin_contact' => $event->getAdminContact(),
+                'admin_email'   => $event->getAdminEmail(),
+                'admin_phone'   => $event->getAdminPhone(),
+                'admin_org'     => $event->getAdminOrg(),
+                'file'          => $file,
+                'filename'      => $filename
+            );
+            $d['active'] = 0;
+            $d['current_file_rmv']
+                = ($d['file'])
+                ? '<a href="' . UPLOADED_FILES . 'original/' . $d['file'] . '">
+                '.$d['file'].'</a>
+                <input type="hidden" name="del_file_rmv" value="0">
+                <input type="checkbox" name="del_file_rmv" value="1">Delete File?'
+                : 'File not yet uploaded';
+            $d['current_img_rmv']
+                = ($d['img'])
+                ? '<img src="' . THUMB . $d['img'] . '">
+                <input type="hidden" name="del_img_rmv" value="0">
+                <input type="checkbox" name="del_img_rmv" value="1">Delete Image?'
+                : 'Image not yet uploaded';
+            $ri = 1;
+            for ($r = 1; $r <= 7; ++$r) {
+                if ($d['daysow']&$ri) {
+                    $daysow[$r] = $ri;
+                }
+                $ri = $ri << 1;
+            }
+            $d['daysow'] = $daysow;
+        } else {
+            $d['current_img_rmv'] = 'Image not yet uploaded';
+            $d['current_file_rmv'] = 'File not yet uploaded';
+        }
+        $d['member'] = $GLOBALS['memberAuth']->getAuthData('member_id');
+        $this->setupDefaults($d);
+    }
+
+    //  }}}
+
+    //  {{{ emailOwner()
+
+    /**
+     * Emails the owner the submitted data from the submitted form
+     *
+     * Uses a flexy template to render a nice looking html email.
+     * Fills in the supplied data from the form and doesn't add the
+     * empty fields the user didn't fill in.
+     *
+     * @param string $mailFactory What type of mail factory should we use
+     *
+     * @return boolean result of the mailing
+     * @access protected
+     */
+    protected function emailOwner($mailFactory = 'mail')
+    {
+        if (!$this->email) {
+            return;
+        }
+
+        $topics = $this->getTopicFields();
+        $template = new HTML_Template_Flexy($this->flexyOptions);
+        $page     = new stdClass();
+
+        $page->email_from  = OWNER_EMAIL;
+        $page->subject     = $this->subject;
+        $page->client_info = $this->clientInfo;
+        $page->fname       = $this->_getMemberName();
+        $page->formData    = $this->formData;
+        // setup a link to admin section
+        $page->lname
+            = '<a href="'.MEDIA_BASE_URL.'admin/Events/list_events.phtml?pending=t">Events Admin</a>';
+        unset(
+            $page->formData['img'],
+            $page->formData['member_id'],
+            $page->formData['MAX_FILE_SIZE'],
+            $page->formData['comments'],
+            $page->formData['visable'],
+            $page->formData['id']
+        );
+        if ($page->formData['btime']['element']) {
+            $btime = explode(" / ", $page->formData['btime']['element']);
+            $page->formData['btime']['element']
+                = $btime[0] . ':' . (($btime[1] == '0')?'00':$btime[1]) . ' ' . $btime[2];
+        }
+        if ($page->formData['etime']['element']) {
+            $etime = explode(" / ", $page->formData['etime']['element']);
+            $page->formData['etime']['element']
+                = $etime[0] . ':' . (($etime[1] == '0')?'00':$etime[1]) . ' ' . $etime[2];
+        }
+        if ($page->formData['topicid']['element']) {
+            //  Clean up the mail_ok flag so its human readable
+            $page->formData['topicid']['element']
+                = $topics[$page->formData['topicid']['element']];
+        }
+
+        $template->compile('emailOwner.tpl');
+        $htmlMsg = $template->bufferedOutputObject($page);
+
+        $msg  = "{$page->subject}\n\n";
+        $msg .= "From {$page->fname} {$page->lname}\n\n";
+        $msg .= "Information\n\n";
+        foreach ($page->formData as $i) {
+            $msg .= "{$i['label']}: {$i['element']}\n";
+        }
+
+        $mimeMail = new Mail_mime("\n");
+        $mimeMail->setFrom("Online Form <{$page->email_from}>");
+        $mimeMail->setSubject($this->subject);
+        $mimeMail->setHTMLBody($htmlMsg);
+        $mimeMail->setTXTBody($msg);
+
+        $mail =& Mail::factory($mailFactory);
+        $body = $mimeMail->get();
+
+        $setHeader['Reply-To'] = "{$this->getSubmitValue('fname')} {$this->getSubmitValue('lname')} <{$this->getSubmitValue('email')}>";
+
+        $headers = $mimeMail->headers($setHeader);
+
+        $res = $mail->send($this->email, $headers, $body);
+        if (PEAR::isError($res)) {
+            return Toolkit_Common::handleError($res);
+        } else {
+            return $res;
+        }
+    }
+
+    //  }}}
+
+    // {{{ _getMemberName()
+    /**
+     * return the member name from PDO call
+     *
+     * @access protected
+     * @return string
+     */
+    function _getMemberName()
+    {
+        try {
+            $sql  = "
+            SELECT member_name
+              FROM member
+             WHERE member_id = :mid";
+            $stmt = $this->dbh->prepare($sql);
+            $stmt->bindParam(
+                ":mid",
+                $GLOBALS['memberAuth']->getAuthData('member_id'),
+                PDO::PARAM_INT
+            );
+            $stmt->execute();
+            return $stmt->fetchColumn();
+        } catch(PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+    }
+    // }}}
+    //  {{{ getTopicFields()
+
+    /**
+     * get event topics
+     *
+     * @return array topics
+     * @access protected
+     */
+    protected function getTopicFields()
+    {
+        try {
+            $sql = "
+                SELECT *
+                  FROM categories
+                 ORDER BY name";
+
+            $topics = array('' => '-- Select --');
+            foreach ($this->dbh->query($sql) as $row) {
+                $topics[$row['id']] = $row['name'];
+            }
+
+            return $topics;
+        } catch (PDOException $e) {
+            return Toolkit_Common::handleError($e);
+        }
+    }
+
+    //  }}}
+
+    //  {{{ hasEndDate()
+
+    /**
+     * verifies if we have a valid end date to work with
+     *
+     * @param string $d end date
+     *
+     * @return boolean if the end date is
+     */
+    protected function hasEndDate($d)
+    {
+        $pattern = '/([0-9]{2})\/([0-9]{2})\/([0-9]{4})/';
+        if (preg_match($pattern, $d, $m)) {
+            return checkdate((int) $m[1], (int) $m[2], (int) $m[3]);
+        } else {
+            return false;
+        }
+    }
+
+    //  }}}
+
+    //  {{{ formatValue()
+
+    /**
+     * Format an array into an acceptable string
+     *
+     * @param mixed  &$i     array values to format or null value for
+     *                       element that was not filled in
+     * @param string $format string to format values into
+     *
+     * @return string formatted string
+     * @access public
+     */
+    public function formatValue(&$i, $format)
+    {
+        //  Allow for 0 to be not empty.  This allows for minutes in the
+        //  time arrays to be valid if they are on the hour ie. (1:00 pm)
+        $notEmpty = create_function('$v', 'return strlen($v) > 0;');
+        if (is_array($i) && count(array_filter($i, $notEmpty)) == 3) {
+            list($x, $y, $z) = array_values($i);
+            eval("\$i = sprintf('$format', $x, $y, $z);");
+        } else {
+            $i = null;
+        }
+    }
+
+    //  }}}
+
+    //  {{{ insertData()
+
+    /**
+     * Inserts contact data into the contact db
+     *
+     * @param array $values submitted values
+     *
+     * @return object result of db insert query
+     * @access protected
+     */
+    protected function insertData($values)
+    {
+        $values = $this->_geocode($values);
+
+        try {
+            // need to set the dates up first
+            unset($values['id']);
+            $values['visable'] = 0;
+            $sql = Toolkit_Common::createSQLInsert(
+                $this->tableName,
+                array_keys($values)
+            );
+            $sql .= " RETURNING id";
+            $stmt = Toolkit_Common::prepareQuery(
+                $this->dbh,
+                $this->tableName,
+                $sql,
+                $values
+            );
+            $stmt->execute();
+            return $stmt->fetchColumn();
+        } catch (PDOException $e) {
+            return Toolkit_Common::handleError($e);
+        }
+    }
+
+    //  }}}
+
+    private function _geocode(array $values)
+    {
+        $geocoder = new GeocodeYahoo();
+        if (!$values['address'] && !$values['city'] && !$values['state']) {
+            return $values;
+        }
+        $address = array(
+            'city'  => $values['city'],
+            'state' => $values['state'],
+            'zip'   => $values['zip'],
+        );
+        if (!empty($values['address'])) {
+            $address['street'] = $values['address'];
+        }
+        try {
+            $response = $geocoder->geocodeAddress($address);
+            $responseArray = unserialize($response);
+            if ($responseArray['ResultSet']['Result'][0]['Latitude']) {
+                $values['lat'] = $responseArray['ResultSet']['Result'][0]['Latitude'];
+                $values['lon'] = $responseArray['ResultSet']['Result'][0]['Longitude'];
+            } else {
+                $values['lat'] = $responseArray['ResultSet']['Result']['Latitude'];
+                $values['lon'] = $responseArray['ResultSet']['Result']['Longitude'];
+            }
+        } catch (BadMethodCallException $e) {
+            Toolkit_Logger::logException('Invalid Arg', $e);
+        } catch (Exception $e) {
+            Toolkit_Logger::logException('Yahoo GeoCode', $e);
+        }
+        return $values;
+    }
+
+    //  {{{ processData()
+
+    /**
+     * Handles how to process the form when submitted
+     *
+     * @param array $values Form submitted values
+     *
+     * @return array Result of Insert / Update function
+     * @access protected
+     */
+    public function processData($values)
+    {
+        //  Form data used for the insert/update sql queries and
+        //  the form email.
+        $e = array();
+        $this->setFormData($e);
+
+        if (  (!$this->_didLatLonChange($values)
+            && $this->_didAddressChange($values))
+            || (!$values['lat'] && !$values['lon'])
+        ) {
+            $values = $this->_geocode($values);
+        }
+        unset($values['MAX_FILE_SIZE']);
+        if ($values['del_img_rmv'] && $values['img']) {
+            $is = new Toolkit_Image_Server();
+            $is->imageDelete($values['img']);
+            $values['image'] = '';
+        }
+        if ($values['img']) {
+            $values['image'] = $values['img'];
+        }
+        if ($values['del_file_rmv'] && $values['file']) {
+            $is = new Toolkit_Image_Server();
+            $is->imageDelete($values['file']);
+            $values['file'] = '';
+        }
+        if ($values['file_rmv']['name']) {
+            $fs = new Toolkit_FileServer_FileAdapter();
+            try {
+                $res = $fs->upload('file_rmv');
+            } catch (Toolkit_FileServer_Exception $e) {
+                Toolkit_Logger::logException('File Server', $e);
+                echo -1;
+                return;
+            }
+            $values['file'] = $res['name'];
+        }
+        if ($values['file']) {
+            $newFile = EventFile::createByValues(
+                array(
+                    'filename' => $values['file'],
+                    'urltext'  => $values['filename']
+                )
+            );
+            $values['files'] = array($newFile);
+        } else {
+            $values['files'] = array();
+        }
+        unset($values['file']);
+        unset($values['filename']);
+        //  Get rid of any defined un-needed elements.
+        //  un-needed elements after the form is submitted are defined
+        //  by the ending _rmv name.
+        foreach ($values as $k => &$v) {
+            if (!is_array($v)) {
+                $values[$k] = preg_replace("/\r/", "\n", $v);
+            }
+            if (preg_match('/^.+_rmv$/', $k)) {
+                unset($values[$k]);
+            }
+        }
+
+        if ($values['recurr']) {
+            if ($values['dayom']) {
+                $values['weekom'] = null;
+                $values['daysow'] = null;
+            } else {
+                $values['dayom'] = null;
+                $values['daysow'] = array_sum($values['daysow']);
+            }
+
+        } else {
+            $values['dayom']  = null;
+            $values['weekom'] = null;
+            $values['daysow'] = null;
+        }
+        $bdate = $values['bdate'];
+        $edate = $values['edate'];
+        $this->formatValue($values['btime'], '%d:%02d %s');
+        $this->formatValue($values['etime'], '%d:%02d %s');
+        if ($values['reacur']) {
+            $recur['dow']        = $values['daysow'];
+            $recur['dom']        = $values['dayom'];
+            if ($recur['dow']) {
+                $recur['recur_week'] = ($values['weekom']) ? $values['weekom'] :null;
+            } else {
+                $recur['recur_week'] = null;
+            }
+        }
+        $eventId = filter_var($_REQUEST['id'], FILTER_VALIDATE_INT);
+        if ($values['btime']) {
+            $values['starthour'] = $values['btime'];
+        } else {
+            $values['starthour'] = null;
+        }
+        unset($values['btime']);
+        if ($values['etime']) {
+            $values['endhour'] = $values['etime'];
+        } else {
+            $values['endhour'] = null;
+        }
+        unset($values['etime']);
+        if ($values['category']) {
+            $values['category'] = $this->eventMapper->fetchCategory(
+                $values['category']
+            );
+        }
+        $event = MemberEvent::createByValues($values);
+//        var_dump($event);
+//        var_dump($values);
+//        exit;
+        return $this->eventMapper->saveEvent($event);
+
+//      return true;
+    }
+
+    //  }}}
+
+    //  {{{ setupRenderers()
+
+    /**
+     * Custom rendering templates for special fields on the form
+     *
+     * @return void
+     * @access protected
+     */
+    protected function setupRenderers()
+    {
+        parent::setupRenderers();
+        $renderer =& $this->defaultRenderer();
+        $required = '<!-- BEGIN required --><span class="req">*</span>
+        <!-- END required -->';
+        $error    = '<!-- BEGIN error --><div class="req">{error}</div>
+        <!-- END error -->';
+        $recurTmpl = '<tr class="recur-event">
+        <td class="labelcell">'.$required.'<label>{label}</label></td>
+        <td class="fieldcell">'.$error.'{element}</td></tr>';
+        $renderer->setElementTemplate($recurTmpl, 'dayom');
+        $renderer->setElementTemplate($recurTmpl, 'weekom');
+        $renderer->setElementTemplate($recurTmpl, 'daysow');
+        $renderer->setElementTemplate(
+            '<tr><td colspan="2">'.$required.'{label}'
+            .$error.'{element}</td></tr>',
+            'description'
+        );
+        $renderer->setElementTemplate(
+            '<tr align="center"><td colspan="2">'
+            .$required.'{label}'.$error.'{element}</td></tr>', 'submit_rmv'
+        );
+    }
+
+    //  }}}
+
+    //  {{{ toHtml()
+
+    /**
+     * Handles how to display the current step the user is at in the form
+     *
+     * destroying and resetting the captcha value dis-allows someone from
+     * re-sending a form on a previous captcha.
+     *
+     * @return string form HTML state
+     * @access public
+     */
+    public function toHtml()
+    {
+        $GLOBALS['topScripts'][] = JQUERY_CDN_JS;
+        $GLOBALS['bottomScripts'][] = MEDIA_APP_BASE_URL . 'libjs/textlimit.js';
+        $baseSecureUrl
+            = ($_SERVER['HTTPS'] == 'on')
+            ? BASE_SECURE_URL
+            : MEDIA_BASE_URL;
+        $appBaseSecureUrl
+            = ($_SERVER['HTTPS'] == 'on')
+            ? GLM_APP_BASE_SECURE_URL
+            : MEDIA_APP_BASE_URL;
+        $GLOBALS['topScripts'][]
+            = $appBaseSecureUrl
+            . 'libjs/jqueryui/1.8.13/js/jquery-ui-1.8.13.custom.min.js';
+        $GLOBALS['topScripts'][] = CKEDITOR_JS.'';
+
+        $this->setupRenderers();
+        if ($this->validate()) {
+            $this->cleanForm();
+
+            if ($this->process(array(&$this, 'processData'), $this->mergeFiles)) {
+                $this->freeze();
+                $this->emailOwner();
+                $output = $this->successMsg;
+                header(
+                    'Location: ' . MEDIA_BASE_URL . "members-only-area/?rt=CommonEvents&page_id="
+                    . $_REQUEST['page_id']
+                );
+                exit;
+            }
+            $this->sent = true;
+        } elseif ($this->isSubmitted()) {
+            $output  = $this->errorMsg;
+            $GLOBALS['topScripts'][]
+                = 'http://maps.googleapis.com/maps/api/js?sensor=true';
+            $GLOBALS['bottomScripts'][]
+                = MEDIA_BASE_URL . 'Toolkit/Maps/geoCoder.js';
+            $GLOBALS['bottomScripts'][]
+                = MEDIA_BASE_URL . 'Toolkit/Events/libjs/edit-event.js';
+            $output .= parent::toHtml();
+        } else {
+            $GLOBALS['topScripts'][]
+                = 'http://maps.googleapis.com/maps/api/js?sensor=true';
+            $GLOBALS['bottomScripts'][]
+                = MEDIA_BASE_URL . 'Toolkit/Maps/geoCoder.js';
+            $GLOBALS['bottomScripts'][]
+                = MEDIA_BASE_URL . 'Toolkit/Events/libjs/edit-event.js';
+            $output = parent::toHtml();
+        }
+        return $output;
+    }
+
+    //  }}}
+    /**
+     * check to see if the address (old on) is different than the one submitted
+     * if the event is not found then return false
+     *
+     * @param array $values The submitted values for the edit event form
+     *
+     * @return boolean
+     */
+    private function _didAddressChange($values)
+    {
+        $didAddressChange = false;
+        if (!$values['id'] && !ctype_digit($values['id'])) {
+            return $didAddressChange;
+        }
+        try {
+            $sql = "
+            SELECT address,city,state,zip
+              FROM events.events
+             WHERE id = :id";
+            $stmt = $this->dbh->prepare($sql);
+            $stmt->bindParam(':id', $values['id'], PDO::PARAM_INT);
+            $stmt->execute();
+            $address = $stmt->fetch(PDO::FETCH_ASSOC);
+            if (!$address) {
+                return $didAddressChange;
+            } else {
+                if ($address['address'] != $values['address']) {
+                    $didAddressChange = true;
+                }
+                if ($address['city'] != $values['city']) {
+                    $didAddressChange = true;
+                }
+                if ($address['state'] != $values['state']) {
+                    $didAddressChange = true;
+                }
+                if ($address['zip'] != $values['zip']) {
+                    $didAddressChange = true;
+                }
+            }
+        } catch(PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+        return $didAddressChange;
+    }
+    private function _didLatLonChange($values)
+    {
+        $didLatLonChange = false;
+        if (!$values['id'] && !ctype_digit($values['id'])) {
+            return $didLatLonChange;
+        }
+        try {
+            $sql = "
+            SELECT lat,lon
+              FROM events.events
+             WHERE id = :id";
+            $stmt = $this->dbh->prepare($sql);
+            $stmt->bindParam(':id', $values['id'], PDO::PARAM_INT);
+            $stmt->execute();
+            $location = $stmt->fetch(PDO::FETCH_ASSOC);
+            if (!$location) {
+                return $didLatLonChange;
+            } else {
+                if ($location['lat'] != $values['lat']) {
+                    $didLatLonChange = true;
+                }
+                if ($location['lon'] != $values['lon']) {
+                    $didLatLonChange = true;
+                }
+            }
+        } catch(PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+        return $didLatLonChange;
+    }
+    //  {{{ updateData()
+
+    /**
+     * Inserts contact data into the contact db
+     *
+     * @param array $values submitted values
+     *
+     * @return object result of db insert query
+     * @access protected
+     */
+    protected function updateData($values)
+    {
+        try {
+            // if the address changes then get the lat lon
+            if (  (!$this->_didLatLonChange($values)
+                && $this->_didAddressChange($values))
+                || (!$values['lat'] && !$values['lon'])
+            ) {
+                $values = $this->_geocode($values);
+            }
+
+            $sql = Toolkit_Common::createSQLUpdate(
+                $this->tableName,
+                array_keys($values),
+                array('id = :id')
+            );
+
+            return Toolkit_Common::processQuery(
+                $this->dbh,
+                $this->tableName,
+                $sql,
+                $values
+            );
+        } catch (PDOException $e) {
+            return Toolkit_Common::handleError($e);
+        }
+    }
+
+    //  }}}
+
+    // {{{ getTimeStamp()
+    /**
+    * getTimeStamp
+    *
+    * @param mixed $MySqlDate Description of $MySqlDate
+    *
+    * @access public
+    * @return void
+    */
+    function getTimeStamp($MySqlDate)
+    {
+        $date_array    = explode("-", $MySqlDate); // split the array
+        $var_year      = $date_array[0];
+        $var_month     = $date_array[1];
+        $var_day       = $date_array[2];
+        $var_timestamp = mktime(0, 0, 0, $var_month, $var_day, $var_year);
+        return $var_timestamp; // return it to the user
+    }// }}}
+    // {{{ ordinalDay()
+    /**
+    * ordinalDay
+    *
+    * @param mixed $ord   Ord
+    * @param mixed $day   Day
+    * @param mixed $month Month
+    * @param mixed $year  Year
+    *
+    * @access public
+    * @return void
+    */
+    function ordinalDay($ord, $day, $month, $year)
+    {
+        $firstOfMonth = mktime(0, 0, 30, $month, 1, $year);
+        $lastOfMonth  = $firstOfMonth + date("t", $firstOfMonth) * 86400;
+        $dayOccurs = 0;
+
+        for ($i = $firstOfMonth; $i < $lastOfMonth ; $i += 86400) {
+            if (date("w", $i) == $day) {
+                $dayOccurs++;
+                if ($dayOccurs == $ord) {
+                    $ordDay = $i;
+                }
+            }
+        }
+        return $ordDay;
+    }// }}}
+    // {{{ getEventDates()
+    /**
+    * getEventDates
+    *
+    * @param mixed  $starttime Start time
+    * @param mixed  $endtime   End time
+    * @param mixed  $recur     Recur
+    * @param string $format    Format
+    *
+    * @access public
+    * @return void
+    */
+    function getEventDates($starttime, $endtime, $recur, $format = 'm/d/Y')
+    {
+        if (!is_array($recur)) {
+            return;
+        }
+        if ($starttime == $endtime) {
+            return;
+        }
+        if (is_array($recur['dow'])) {
+            $daysow = array_sum($recur['dow']);
+        }
+        if ($recur['recur_week'] == 9) {
+            $fWeekNum = date("W", $starttime);
+            if (date('w', $starttime) == 0) {
+                $fWeekNum++;
+            }
+            $lWeekNum = date("W", $endtime);
+            if (date('w', $endtime) == 0) {
+                $lWeekNum++;
+            }
+            for ($fi = $fWeekNum; $fi <= $lWeekNum; $fi = $fi + 2) {
+                $eWeeks[] = $fi;
+            }
+        }
+        for ($i = $starttime; $i <= $endtime; $i += 86400) {
+            if ($recur['recur_week'] != '') {
+                if ($daysow) {
+                    $ri = 1;
+                    for ($r=0;$r<7;$r++) {
+                        if ($daysow & $ri) {
+                            $ord = $this->ordinalDay($recur['recur_week'], $r, date('n', $i), date('Y', $i));
+                        }
+                        $ri = $ri << 1;
+                    }
+                } else {
+                    $ord = null;
+                }
+            } else {
+                $ord = null;
+            }
+            if ($recur['dom']) {
+                if (date("j", $i) == $recur['dom']) {
+                    $events[] = date($format, $i);
+                }
+            } elseif (is_array($recur['dow'])) {
+                $cur_dow = date("w", $i);
+                switch ($cur_dow) {
+                case 0:
+                    $cur_dow = 1;
+                    break;
+                case 1:
+                    $cur_dow = 2;
+                    break;
+                case 2:
+                    $cur_dow = 4;
+                    break;
+                case 3:
+                    $cur_dow = 8;
+                    break;
+                case 4:
+                    $cur_dow = 16;
+                    break;
+                case 5:
+                    $cur_dow = 32;
+                    break;
+                case 6:
+                    $cur_dow = 64;
+                    break;
+                }
+                if ((int)$cur_dow & $daysow) {
+                    $cDateWeek = date("W", $i);
+                    if (date('w', $time) == 0) {
+                        $cDateWeek++;
+                    }
+                    if ($recur['recur_week'] == 9) {
+                        if (in_array($cDateWeek, $eWeeks)) {
+                            $events[] = date($format, $i);
+                        }
+                    } elseif ($recur['recur_week'] != '') {
+                        if ($recur['recur_week'] && $i && $ord) {
+                            if ($i == $ord) {
+                                $events[] = date($format, $i);
+                            }
+                        }
+                    } else {
+                        $events[] = date($format, $i);
+                    }
+                }
+            }
+        }
+        return $events;
+    }// }}}
+}
diff --git a/Toolkit/Members/Events/EditEvent.php b/Toolkit/Members/Events/EditEvent.php
new file mode 100755 (executable)
index 0000000..e4c2127
--- /dev/null
@@ -0,0 +1,1664 @@
+<?php
+/**
+ * New Event Form
+ *
+ * PHP version 5
+ *
+ * @category Toolkit
+ * @package  Members_Events
+ * @author   Steve Sutton <steve@gaslightmedia.com>
+ * @license  http://www.gaslightmedia.com Gaslightmedia
+ * @version  CVS: $Id: AddEventForm.php,v 1.20 2010/07/04 23:58:22 jamie Exp $
+ * @link     http://demo.gaslightmedia.com
+ */
+
+require_once BASE . 'Toolkit/Forms/Rules/Image.php';
+
+/**
+ * Toolkit_Members_Events_EditEvent
+ *
+ * @category  Toolkit
+ * @package   Members_Events
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @link      http://demo.gaslightmedia.com
+ */
+class Toolkit_Members_Events_EditEvent
+    extends Toolkit_FormBuilder
+    implements Toolkit_Form
+{
+    //  {{{ properties
+
+    /**
+     * Table in Database which holds the contact data
+     *
+     * @var    string
+     * @access public
+     */
+    public $tableName = 'event';
+
+    /**
+     * Table meta data
+     *
+     * This is used when inserting/updating data for the records
+     * so the PDO's can use explicit data types for the parameters.
+     *
+     * @var    array
+     * @access public
+     */
+    public $tableMetaData;
+
+    /**
+     * Who to send the email to when the contact form is submitted
+     *
+     * If you leave this blank, its value will get set to the OWNER_EMAIL
+     * in the constructor.
+     *
+     * If you ***DO NOT*** want any emails to go out when the form is submitted
+     * then set the value to false. Do not set it to 0 for false, because the
+     * check uses a strict type check to determine if the value is actually
+     * false. This is what allows for the empty value as an option, which sets
+     * the value to OWNER_EMAIL and won't override the $email property if
+     * this class gets subclassed and the value for this property gets set in
+     * the properties of the subclass and not in the constructor after this
+     * constructor function is called.
+     *
+     * tongue twister...I know.
+     * <code>
+     * protected $email = false;
+     * </code>
+     *
+     * @var    unknown
+     * @access protected
+     */
+    protected $email;
+
+    /**
+     * From header in the owner email
+     *
+     * This just sets the From header in the owner email
+     * SITENAME <from@email.com>
+     *
+     * It gets set to the constant SITENAME in the constructor if you leave
+     * empty here, but you can set it to something different here to override
+     * that if you desire.
+     *
+     * @var    unknown
+     * @access protected
+     */
+    protected $siteName;
+
+    /**
+     * Email subject and <h1> header in email
+     *
+     * It gets set in the constructor if you leave empty here, but you
+     * can set it to something different here to override that if you desire.
+     *
+     * @var    string
+     * @access protected
+     */
+    protected $subject = 'New Event Submission';
+
+    /**
+     * Message to display if the form is successfully submitted
+     *
+     * @var    string
+     * @access protected
+     */
+    protected $successMsg = '
+    <style type="text/css">
+        #category {display:none};
+        .listings {display:none};
+    </style>
+        <div id="form-sucess-top">
+            Your event has been successfully added to the events calendar,
+            however will not be visible until it has been approved by
+            the Web site administrator. Thank You.
+        </div>';
+
+    /**
+     * Extra rules for processesing
+     *
+     * This registers the Zip validation rules (and any others listed) for
+     * QuickForm.
+     *
+     * Zip validation checks both US and Canadian Zip codes
+     *
+     * @var    array
+     * @access protected
+     */
+    protected $registeredRules = array(
+        'phone',
+        array(
+            'checkEmail',
+            'callback',
+            'email',
+            'Validate'
+        ),
+        array(
+            'checkURI',
+            'callback',
+            'uri',
+            'Validate'
+        )
+    );
+
+    /**
+     * Options for flexy templating engine
+     *
+     * Pulls the preset options from the setup.phtml file
+     * overwrites the templateDir and compileDir to match this classes needs
+     *
+     * @var    array
+     * @access protected
+     */
+    protected $flexyOptions;
+
+    //  }}}
+    //  {{{ __construct()
+
+    /**
+     * Class constructor
+     *
+     * @param PDO    $pdo         PHP Data Object
+     * @param string $formName    Form's name.
+     * @param string $method      (optional)Form's method defaults to 'POST'
+     * @param string $action      (optional)Form's action
+     * @param string $target      (optional)Form's target defaults to '_self'
+     * @param mixed  $attributes  (optional)Extra attributes for <form> tag
+     * @param bool   $trackSubmit (optional)Whether to track if the form was
+     *                            submitted by adding a special hidden field
+     *
+     * @access public
+     */
+    public function __construct(
+        PDO $pdo,
+        $formName,
+        $method = 'post',
+        $action = '',
+        $target = '',
+        $attributes = null,
+        $trackSubmit = false
+    ) {
+        parent::__construct(
+            $formName,
+            $method,
+            $action,
+            $target,
+            $attributes,
+            $trackSubmit
+        );
+
+        $this->dbh = $pdo;
+
+        if ($this->email !== false && empty($this->email)) {
+            //  Set to false to turn off email function.
+            $this->email
+                = (MEMBERS_EVENTS_NOTIFICATION_EMAIL)
+                ? MEMBERS_EVENTS_NOTIFICATION_EMAIL
+                : MEMBER_RECORD_UPDATES_ADVISOR;
+        }
+        if (empty($this->siteName)) {
+            $this->siteName = SITENAME;
+        }
+        if (empty($this->subject)) {
+            $this->subject = 'Contact Request from website ' . SITENAME;
+        }
+
+        $this->flexyOptions                = $GLOBALS['flexyOptions'];
+        $this->flexyOptions['templateDir'] = dirname(__FILE__) . "/templates/";
+        $this->flexyOptions['compileDir']
+            = dirname(__FILE__) . "/templates/compiled/";
+
+        $var = basename(__FILE__, '.php');
+
+        $callbackUrl = MEDIA_BASE_URL;
+
+        $this->captchaOptions = array(
+            'width' => 100,
+            'height' => 50,
+            'callback' => "{$callbackUrl}Toolkit/qfcaptcha.php?var=$var",
+            'sessionVar' => $var,
+            'imageOptions' => array(
+                'font_size' => 16,
+                'font_path' => GLM_APP_BASE . 'glmPEAR/Image/Canvas/Fonts/',
+                'font_file' => 'times.ttf',
+                'background_color' => '#cccccc',
+                'obfuscation' => false,
+                'angle' => true,
+            ),
+        );
+    }
+
+    //  }}}
+
+    //  {{{ checkDate()
+
+    /**
+     * Validate date input
+     *
+     * allows for empty dates to be valid
+     *
+     * @param array $date date group from form
+     *
+     * @return boolean true if valid, false if not
+     * @access public
+     */
+    public function checkDate($date)
+    {
+        if (!$date) {
+            return true;
+        } else {
+            return Validate::date($date, array('format' => '%m/%d/%Y'));
+        }
+    }
+
+    //  }}}
+    //  {{{ checkDateRange()
+
+    /**
+     * Validate date input
+     *
+     * allows for empty end date to be valid
+     *
+     * @param array $d date group from form
+     *
+     * @return boolean true if valid, false if not
+     * @access public
+     */
+    public function checkDateRange(array $d)
+    {
+        if (!$this->hasEndDate($d[1])) {
+            //  no end date is a valid date range
+            return true;
+        }
+
+        $pattern = '/([0-9]{2})\/([0-9]{2})\/([0-9]{4})/';
+        if (preg_match($pattern, $d[0], $m)) {
+            $t1 = mktime(0, 0, 0, (int) $m[1], (int) $m[2], (int) $m[3]);
+            $bdate = new Date($t1);
+        }
+        if (preg_match($pattern, $d[1], $m)) {
+            $t2    = mktime(0, 0, 0, (int) $m[1], (int) $m[2], (int) $m[3]);
+            $edate = new Date($t2);
+        }
+        if ($bdate && $edate) {
+            //  0 if the dates are equal - valid
+            // -1 if $bdate is before $edate - valid
+            //  1 if $bdate is after $edate - invalid
+            $res = Date::compare($bdate, $edate);
+            return ($res !== 1);
+        }
+        return true;
+    }
+
+    //  }}}
+    //  {{{ configureElements()
+
+    /**
+     * Form element definitions
+     *
+     * @return void
+     * @access public
+     */
+    public function configureElements()
+    {
+        $e = array();
+
+        //  All Elements are created here.  This includes group element definitions.
+        $e[] = array(
+            'type'    => 'header',
+            'name'    => 'eventInfoHeader_rmv',
+            'display' => 'Event Information'
+        );
+        $e[] = array(
+            'type' => 'text',
+            'req' => true,
+            'name' => 'header',
+            'display' => 'Event Name'
+        );
+        $e[] = array(
+            'type' => 'hidden',
+            'req'  => false,
+            'name' => 'id'
+        );
+        $e[] = array(
+            'type' => 'hidden',
+            'req' => false,
+            'name' => 'member_id',
+        );
+        $e[] = array(
+            'type' => 'hidden',
+            'req'  => false,
+            'name' => 'visable',
+            'val'  => 0
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => true,
+            'name'    => 'bdate',
+            'display' => 'Start Date',
+            'opts'     => array('id' => 'sdate')
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => true,
+            'name'    => 'edate',
+            'display' => 'End Date',
+            'opts'     => array('id' => 'edate')
+        );
+        $e[] = array(
+            'type'    => 'advcheckbox',
+            'req'     => false,
+            'name'    => 'all_day',
+            'display' => 'All Day Event?',
+            'opts'    => 'Yes',
+            'val'     => array(0, 1)
+        );
+        $e[] = array(
+            'type' => 'date',
+            'req' => false,
+            'name' => 'btime',
+            'display' => 'Start Time',
+            'opts' => array(
+                'format'   => 'h : i A',
+                'addEmptyOption' => true,
+                'emptyOptionValue' => '',
+                'emptyOptionText' => array(
+                    'h' => 'hh',
+                    'i' => 'mm',
+                    'A' => 'am/pm'
+                ),
+                'optionIncrement' => array(
+                    'i' => 15,
+                ),
+            ),
+            'error' => 'ERROR: You must select a start time!',
+        );
+        $e[] = array(
+            'type' => 'date',
+            'req' => false,
+            'name' => 'etime',
+            'display' => 'End Time',
+            'opts' => array(
+                'format'   => 'h : i A',
+                'addEmptyOption' => true,
+                'emptyOptionValue' => '',
+                'emptyOptionText' => array(
+                    'h' => 'hh',
+                    'i' => 'mm',
+                    'A' => 'am/pm'
+                ),
+                'optionIncrement' => array(
+                    'i' => 15,
+                ),
+            ),
+        );
+        $e[] = array(
+            'type'    => 'advcheckbox',
+            'req'     => false,
+            'name'    => 'reacur',
+            'display' => 'Recurring Event',
+            'opts'    => 'Is this a recurring event?',
+            'val'     => array(0, 1)
+        );
+        $daysOm = array(''=>'');
+        for ($i = 1; $i <= 31; ++$i) {
+            $daysOm[$i] = $i;
+        }
+        $e[] = array(
+            'type' => 'select',
+            'name' => 'dayom',
+            'display' => 'Every Month on',
+            'opts' => $daysOm
+        );
+        $e[] = array(
+            'type' => 'select',
+            'req' => false,
+            'name' => 'weekom',
+            'display' => 'Recurs',
+            'opts' => array(
+                '' => 'Every Week of Month',
+                '9' => 'Every Other Week',
+                '1' => 'Every First Week of Month',
+                '2' => 'Every Second Week of Month',
+                '3' => 'Every Third Week of Month',
+                '4' => 'Every Fourth Week of Month',
+            ),
+        );
+        $weekdays = array(
+            1 => 'Sunday',
+            'Monday',
+            'Tuesday',
+            'Wednesday',
+            'Thursday',
+            'Friday',
+            'Saturday'
+        );
+        $ri = 1;
+        for ($i = 1; $i <= 7; ++$i) {
+            $daysOw[] = array(
+                'type' => 'advcheckbox',
+                'req'  => false,
+                'name' => $i,//$weekdays[$i],
+                'opts' => $weekdays[$i],
+                'val' => array('', $ri)
+            );
+            $ri = $ri << 1;
+        }
+        //echo '<pre>'.print_r($daysOw, true).'</pre>';
+        $e[] = array(
+            'type'       => 'group',
+            'req'        => false,
+            'name'       => 'daysow',
+            'group'      => $daysOw,
+            'label'      => 'Days Of Week',
+            'seperator'  => ' ',
+            'appendName' => true
+        );
+        $e[] = array(
+            'type' => 'select',
+            'req' => true,
+            'name' => 'topicid',
+            'display' => 'Category',
+            'opts' => $this->getTopicFields(),
+        );
+        $e[] = array(
+            'type' => 'text',
+            'req' => false,
+            'name' => 'url',
+            'display' => 'Website'
+        );
+        $e[] = array(
+            'type' => 'textarea',
+            'req' => false,
+            'name' => 'descr',
+            'display' => 'Description',
+            'opts' => array('id' => 'descr')
+        );
+        $e[] = array(
+            'type' => 'static',
+            'req' => false,
+            'name' => 'current_img_rmv',
+            'display' => 'Current Image'
+        );
+        $e[] = array(
+            'type' => 'hidden',
+            'req' => false,
+            'name' => 'img'
+        );
+        $e[] = array(
+            'type' => 'file',
+            'req' => false,
+            'name' => 'img_file_rmv',
+            'display' => 'Event Image'
+        );
+        $e[] = array(
+            'type' => 'static',
+            'req' => false,
+            'name' => 'img_instructions_rmv',
+            'opts' => '.jpg or .gif images only'
+        );
+        $e[] = array(
+            'type'    => 'header',
+            'name'    => 'eventLocationInfoHeader_rmv',
+            'display' => 'Event Location Information
+                <div id="map-dialog">
+                    <div id="map_canvas" style="width:500px; height:400px"></div>
+                </div>
+                <a id="map-it" href="#">Map It</a>'
+        );
+        $e[] = array(
+            'type' => 'hidden',
+            'req'  => false,
+            'name' => 'lat',
+            'opts' => array('id' => 'lat')
+        );
+        $e[] = array(
+            'type' => 'hidden',
+            'req'  => false,
+            'name' => 'lon',
+            'opts' => array('id' => 'lon')
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => 'loc',
+            'display' => 'Place'
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => 'address',
+            'display' => 'Address',
+            'opts'     => array('id' => 'address')
+        );
+        $e[]         = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => 'city',
+            'display' => 'City',
+            'opts'     => array('id' => 'city')
+        );
+        $e[]         = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => 'state',
+            'display' => 'State',
+            'opts'     => array('id' => 'state')
+        );
+        $e[]         = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => 'zip',
+            'display' => 'ZIP',
+            'opts'     => array('id' => 'zip')
+        );
+        $e[] = array(
+            'type'    => 'header',
+            'name'    => 'eventContactHeader_rmv',
+            'display' => 'Event Contact Information'
+        );
+        $e[] = array(
+            'type' => 'text',
+            'req' => false,
+            'name' => 'contact',
+            'display' => 'Contact Person'
+        );
+        $e[] = array(
+            'type' => 'text',
+            'req' => false,
+            'name' => 'email',
+            'display' => 'Contact Email'
+        );
+        $e[] = array(
+            'type' => 'text',
+            'req' => false,
+            'name' => 'phone',
+            'display' => 'Contact Phone'
+        );
+        $e[] = array(
+            'type' => 'static',
+            'req' => false,
+            'name' => 'current_file_rmv',
+            'display' => 'Current File'
+        );
+        $e[] = array(
+            'type' => 'hidden',
+            'req' => false,
+            'name' => 'file'
+        );
+        $e[] = array(
+            'type' => 'file',
+            'req' => false,
+            'name' => 'file_rmv',
+            'display' => 'Event File'
+        );
+        $e[] = array(
+            'type' => 'text',
+            'req' => false,
+            'name' => 'filename',
+            'display' => 'File Name'
+        );
+        $e[] = array(
+            'type'    => 'header',
+            'name'    => 'eventInfoHeader_rmv',
+            'display' => 'Event Admin Information'
+        );
+        $e[]      = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => 'admin_contact_name',
+            'display' => 'Contact Name Submitting Event'
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => 'admin_org_name',
+            'display' => 'Organization Name Submitting Event'
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => 'admin_phone',
+            'display' => 'Phone'
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => 'admin_email',
+            'display' => 'Email Address'
+        );
+        $e[] = array(
+            'type' => 'submit',
+            'req' => false,
+            'name' => 'submit_rmv',
+            'display' => 'Submit'
+        );
+
+        $this->setupElements($e);
+    }
+
+    //  }}}
+    //  {{{ configureFilters()
+
+    /**
+     * Form filter definitions
+     *
+     * Applies a data filter for the given fields when the form is submitted
+     *
+     * @return void
+     * @access public
+     */
+    public function configureFilters()
+    {
+        $f = array();
+
+        $f[] = array(
+            'element' => '__ALL__',
+            'filter' => 'trim'
+        );
+        $f[] = array(
+            'element' => 'url',
+            'filter' => array('Toolkit_Common', 'filterURI')
+        );
+
+        $this->setupFilters($f);
+    }
+
+    //  }}}
+    //  {{{ configureForm()
+
+    /**
+     * Helper function to handle setting up the form
+     *
+     * @return void
+     * @access public
+     */
+    public function configureForm()
+    {
+        $this->configureElements();
+        $this->configureDefaults();
+        $this->configureFilters();
+        $this->configureRules();
+    }
+
+    //  }}}
+    //  {{{ configureRules()
+
+    /**
+     * Form rule definitions
+     *
+     * Adds validation rules for the given fields
+     *
+     * @return void
+     * @access public
+     */
+    public function configureRules()
+    {
+        //  Form Rules
+        $r = array();
+
+        $mimeTypes = array(
+            'image/jpe',
+            'image/jpeg',
+            'image/jpg',
+            'image/jfif',
+            'image/pjpeg',
+            'image/pjp',
+            'image/gif',
+            'image/png',
+        );
+
+        $r[] = array(
+            'element' => 'topicid',
+            'message' => 'ERROR: Invalid Topic!',
+            'type' => 'numeric',
+            'format' => null,
+            'validation' => $this->validationType,
+            'reset' => true,
+            'force' => false
+        );
+        $r[] = array(
+            'element' => 'email',
+            'message' => 'ERROR: Invalid Email Format!',
+            'type' => 'checkEmail',
+            'format' => array('use_rfc822' => true),
+            'validation' => $this->validationType,
+            'reset' => true,
+            'force' => false
+        );
+        $r[] = array(
+            'element' => array('bdate', 'edate'),
+            'message' => 'ERROR: Starting Date must be before Ending Date',
+            'type' => 'callback',
+            'format' => array(&$this, 'checkDateRange'),
+            'validation' => $this->validationType,
+            'reset' => false,
+            'force' => false
+        );
+        $r[] = array(
+            'element' => 'bdate',
+            'message' => 'ERROR: Invalid date!',
+            'type' => 'callback',
+            'format' => array(&$this, 'checkDate'),
+            'validation' => $this->validationType,
+            'reset' => false,
+            'force' => false
+        );
+        $r[] = array(
+            'element' => 'edate',
+            'message' => 'ERROR: Invalid date!',
+            'type' => 'callback',
+            'format' => array(&$this, 'checkDate'),
+            'validation' => $this->validationType,
+            'reset' => false,
+            'force' => false
+        );
+        $r[] = array(
+            'element' => 'url',
+            'message' => 'ERROR: Invalid URL format',
+            'type' => 'checkURI',
+            'format' => array(
+                'allowed_schemes' => array('http', 'https'),
+                'strict' => true
+            ),
+            'validation' => $this->validationType,
+            'reset' => false,
+            'force' => false
+        );
+        $r[] = array(
+            'element' => 'phone',
+            'message' => 'ERROR: Invalid Phone Format (xxx) xxx - xxxx!',
+            'type' => 'phone',
+            'format' => null,
+            'validation' => $this->validationType,
+            'reset' => true,
+            'force' => false
+        );
+        if ($this->useCaptcha) {
+            $r[] = array(
+                'element' => 'captcha_rmv',
+                'message' => 'ERROR: What you entered didn\'t match!',
+                'type' => 'CAPTCHA',
+                'format' => $this->captchaQuestion,
+                'validation' => $this->validationType,
+                'reset' => true,
+                'force' => false
+            );
+        }
+        if (is_uploaded_file($_FILES['img_file_rmv']['tmp_name'])) {
+            $r[] = array(
+                'element' => 'img_file_rmv',
+                'message' => 'ERROR: Incorrect File Type (.gif, .png, .jpg) only!',
+                'type' => 'mimetype',
+                'format' => $mimeTypes,
+                'validation' => $this->validationType,
+                'reset' => false,
+                'force' => false
+            );
+        }
+        $r[] = array(
+            'element' => 'img_file_rmv',
+            'message' => 'ERROR: Error uploading image!',
+            'type' => 'Image',
+            'format' => array(
+                'form' => $this,
+                'fieldName' => 'img_file_rmv',
+                'imageField' => 'img',
+                'is' => new Toolkit_FileServer_ImageAdapter(),
+                'deleteExistingImage' => false,
+                'injectImage' => array('tgtElement' => 'current_img_rmv')
+            ),
+            'validation' => 'server',
+            'reset' => false,
+            'force' => false
+        );
+        /*
+        $r[] = array(
+            'element' => 'file_rmv',
+            'message' => 'ERROR: Error uploading file!',
+            'type' => 'Image',
+            'format' => array(
+                'form' => $this,
+                'fieldName' => 'file_rmv',
+                'imageField' => 'file',
+                'is' => new Toolkit_FileServer_FileAdapter(),
+                'deleteExistingImage' => false,
+                'injectImage' => array('tgtElement' => 'current_file_rmv')
+            ),
+            'validation' => 'server',
+            'reset' => false,
+            'force' => false
+        );
+         */
+
+        $this->setupRules($r);
+    }
+
+    //  }}}
+    //  {{{ configureDefaults()
+
+    /**
+     * Form defaults
+     *
+     * @return void
+     * @access public
+     */
+    public function configureDefaults()
+    {
+        if (ctype_digit($_GET['id'])) {
+            $sql = "
+            SELECT *
+              FROM event
+             WHERE id = :id";
+            $stmt = $this->dbh->prepare($sql);
+            $stmt->bindParam(":id", $_GET['id'], PDO::PARAM_INT);
+            $stmt->execute();
+            $d = $stmt->fetch(PDO::FETCH_ASSOC);
+            $d['visable'] = 0;
+            $d['current_file_rmv']
+                = ($d['file'])
+                ? '<a href="' . UPLOADED_FILES . 'original/' . $d['file'] . '">
+                '.$d['file'].'</a>
+                <input type="hidden" name="del_file_rmv" value="0">
+                <input type="checkbox" name="del_file_rmv" value="1">Delete File?'
+                : 'File not yet uploaded';
+            $d['current_img_rmv']
+                = ($d['img'])
+                ? '<img src="' . THUMB . $d['img'] . '">
+                <input type="hidden" name="del_img_rmv" value="0">
+                <input type="checkbox" name="del_img_rmv" value="1">Delete Image?'
+                : 'Image not yet uploaded';
+            $ri = 1;
+            for ($r = 1; $r <= 7; ++$r) {
+                if ($d['daysow']&$ri) {
+                    $daysow[$r] = $ri;
+                }
+                $ri = $ri << 1;
+            }
+            $d['daysow'] = $daysow;
+        } else {
+            $d['current_img_rmv'] = 'Image not yet uploaded';
+            $d['current_file_rmv'] = 'File not yet uploaded';
+        }
+        $d['member_id'] = $GLOBALS['memberAuth']->getAuthData('member_id');
+        $this->setupDefaults($d);
+    }
+
+    //  }}}
+
+    //  {{{ emailOwner()
+
+    /**
+     * Emails the owner the submitted data from the submitted form
+     *
+     * Uses a flexy template to render a nice looking html email.
+     * Fills in the supplied data from the form and doesn't add the
+     * empty fields the user didn't fill in.
+     *
+     * @param string $mailFactory What type of mail factory should we use
+     *
+     * @return boolean result of the mailing
+     * @access protected
+     */
+    protected function emailOwner($mailFactory = 'mail')
+    {
+        if (!$this->email) {
+            return;
+        }
+
+        $topics = $this->getTopicFields();
+        $template = new HTML_Template_Flexy($this->flexyOptions);
+        $page     = new stdClass();
+
+        $page->email_from  = OWNER_EMAIL;
+        $page->subject     = $this->subject;
+        $page->client_info = $this->clientInfo;
+        $page->fname       = $this->_getMemberName();
+        $page->formData    = $this->formData;
+        // setup a link to admin section
+        $page->lname
+            = '<a href="'.MEDIA_BASE_URL.'admin/Events/list_events.phtml?pending=t">Events Admin</a>';
+        unset(
+            $page->formData['img'],
+            $page->formData['member_id'],
+            $page->formData['MAX_FILE_SIZE'],
+            $page->formData['comments'],
+            $page->formData['visable'],
+            $page->formData['id']
+        );
+        if ($page->formData['btime']['element']) {
+            $btime = explode(" / ", $page->formData['btime']['element']);
+            $page->formData['btime']['element']
+                = $btime[0] . ':' . (($btime[1] == '0')?'00':$btime[1]) . ' ' . $btime[2];
+        }
+        if ($page->formData['etime']['element']) {
+            $etime = explode(" / ", $page->formData['etime']['element']);
+            $page->formData['etime']['element']
+                = $etime[0] . ':' . (($etime[1] == '0')?'00':$etime[1]) . ' ' . $etime[2];
+        }
+        if ($page->formData['topicid']['element']) {
+            //  Clean up the mail_ok flag so its human readable
+            $page->formData['topicid']['element']
+                = $topics[$page->formData['topicid']['element']];
+        }
+
+        $template->compile('emailOwner.tpl');
+        $htmlMsg = $template->bufferedOutputObject($page);
+
+        $msg  = "{$page->subject}\n\n";
+        $msg .= "From {$page->fname} {$page->lname}\n\n";
+        $msg .= "Information\n\n";
+        foreach ($page->formData as $i) {
+            $msg .= "{$i['label']}: {$i['element']}\n";
+        }
+
+        $mimeMail = new Mail_mime("\n");
+        $mimeMail->setFrom("Online Form <{$page->email_from}>");
+        $mimeMail->setSubject($this->subject);
+        $mimeMail->setHTMLBody($htmlMsg);
+        $mimeMail->setTXTBody($msg);
+
+        $mail =& Mail::factory($mailFactory);
+        $body = $mimeMail->get();
+
+        $setHeader['Reply-To'] = "{$this->getSubmitValue('fname')} {$this->getSubmitValue('lname')} <{$this->getSubmitValue('email')}>";
+
+        $headers = $mimeMail->headers($setHeader);
+
+        $res = $mail->send($this->email, $headers, $body);
+        if (PEAR::isError($res)) {
+            return Toolkit_Common::handleError($res);
+        } else {
+            return $res;
+        }
+    }
+
+    //  }}}
+
+    // {{{ _getMemberName()
+    /**
+     * return the member name from PDO call
+     *
+     * @access protected
+     * @return string
+     */
+    function _getMemberName()
+    {
+        try {
+            $sql  = "
+            SELECT member_name
+              FROM member
+             WHERE member_id = :mid";
+            $stmt = $this->dbh->prepare($sql);
+            $stmt->bindParam(
+                ":mid",
+                $GLOBALS['memberAuth']->getAuthData('member_id'),
+                PDO::PARAM_INT
+            );
+            $stmt->execute();
+            return $stmt->fetchColumn();
+        } catch(PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+    }
+    // }}}
+    //  {{{ getTopicFields()
+
+    /**
+     * get event topics
+     *
+     * @return array topics
+     * @access protected
+     */
+    protected function getTopicFields()
+    {
+        try {
+            $sql = "
+                SELECT *
+                  FROM topic
+                 ORDER BY descr";
+
+            $topics = array('' => '-- Select --');
+            foreach ($this->dbh->query($sql) as $row) {
+                $topics[$row['id']] = $row['descr'];
+            }
+
+            return $topics;
+        } catch (PDOException $e) {
+            return Toolkit_Common::handleError($e);
+        }
+    }
+
+    //  }}}
+
+    //  {{{ hasEndDate()
+
+    /**
+     * verifies if we have a valid end date to work with
+     *
+     * @param string $d end date
+     *
+     * @return boolean if the end date is
+     */
+    protected function hasEndDate($d)
+    {
+        $pattern = '/([0-9]{2})\/([0-9]{2})\/([0-9]{4})/';
+        if (preg_match($pattern, $d, $m)) {
+            return checkdate((int) $m[1], (int) $m[2], (int) $m[3]);
+        } else {
+            return false;
+        }
+    }
+
+    //  }}}
+
+    //  {{{ formatValue()
+
+    /**
+     * Format an array into an acceptable string
+     *
+     * @param mixed  &$i     array values to format or null value for
+     *                       element that was not filled in
+     * @param string $format string to format values into
+     *
+     * @return string formatted string
+     * @access public
+     */
+    public function formatValue(&$i, $format)
+    {
+        //  Allow for 0 to be not empty.  This allows for minutes in the
+        //  time arrays to be valid if they are on the hour ie. (1:00 pm)
+        $notEmpty = create_function('$v', 'return strlen($v) > 0;');
+        if (is_array($i) && count(array_filter($i, $notEmpty)) == 3) {
+            list($x, $y, $z) = array_values($i);
+            eval("\$i = sprintf('$format', $x, $y, $z);");
+        } else {
+            $i = null;
+        }
+    }
+
+    //  }}}
+
+    //  {{{ insertData()
+
+    /**
+     * Inserts contact data into the contact db
+     *
+     * @param array $values submitted values
+     *
+     * @return object result of db insert query
+     * @access protected
+     */
+    protected function insertData($values)
+    {
+        $values = $this->_geocode($values);
+
+        try {
+            // need to set the dates up first
+            unset($values['id']);
+            $values['visable'] = 0;
+            $sql = Toolkit_Common::createSQLInsert(
+                $this->tableName,
+                array_keys($values)
+            );
+            $sql .= " RETURNING id";
+            $stmt = Toolkit_Common::prepareQuery(
+                $this->dbh,
+                $this->tableName,
+                $sql,
+                $values
+            );
+            $stmt->execute();
+            return $stmt->fetchColumn();
+        } catch (PDOException $e) {
+            return Toolkit_Common::handleError($e);
+        }
+    }
+
+    //  }}}
+
+    private function _geocode(array $values)
+    {
+        $geocoder = new GeocodeYahoo();
+        if (!$values['address'] && !$values['city'] && !$values['state']) {
+            return $values;
+        }
+        $address = array(
+            'city'  => $values['city'],
+            'state' => $values['state'],
+            'zip'   => $values['zip'],
+        );
+        if (!empty($values['address'])) {
+            $address['street'] = $values['address'];
+        }
+        try {
+            $response = $geocoder->geocodeAddress($address);
+            $responseArray = unserialize($response);
+            if ($responseArray['ResultSet']['Result'][0]['Latitude']) {
+                $values['lat'] = $responseArray['ResultSet']['Result'][0]['Latitude'];
+                $values['lon'] = $responseArray['ResultSet']['Result'][0]['Longitude'];
+            } else {
+                $values['lat'] = $responseArray['ResultSet']['Result']['Latitude'];
+                $values['lon'] = $responseArray['ResultSet']['Result']['Longitude'];
+            }
+
+            return $values;
+        } catch (BadMethodCallException $e) {
+            Toolkit_Logger::logException('Invalid Arg', $e);
+        } catch (Exception $e) {
+            Toolkit_Logger::logException('Yahoo GeoCode', $e);
+        }
+
+    }
+
+    //  {{{ processData()
+
+    /**
+     * Handles how to process the form when submitted
+     *
+     * @param array $values Form submitted values
+     *
+     * @return array Result of Insert / Update function
+     * @access protected
+     */
+    public function processData($values)
+    {
+        //  Form data used for the insert/update sql queries and
+        //  the form email.
+        $e = array();
+        $this->setFormData($e);
+
+        unset($values['MAX_FILE_SIZE']);
+        if ($values['del_img_rmv'] && $values['img']) {
+            $is = new Toolkit_Image_Server();
+            $is->imageDelete($values['img']);
+            $values['img'] = '';
+        }
+        if ($values['del_file_rmv'] && $values['file']) {
+            $is = new Toolkit_Image_Server();
+            $is->imageDelete($values['file']);
+            $values['file'] = '';
+        }
+        if ($values['file_rmv']['name']) {
+            $fs = new Toolkit_FileServer_FileAdapter();
+            try {
+                $res = $fs->upload('file_rmv');
+            } catch (Toolkit_FileServer_Exception $e) {
+                Toolkit_Logger::logException('File Server', $e);
+                echo -1;
+                return;
+            }
+            $values['file'] = $res['name'];
+        }
+
+        //  Get rid of any defined un-needed elements.
+        //  un-needed elements after the form is submitted are defined
+        //  by the ending _rmv name.
+        foreach ($values as $k => &$v) {
+            if (!is_array($v)) {
+                $values[$k] = preg_replace("/\r/", "\n", $v);
+            }
+            if (preg_match('/^.+_rmv$/', $k)) {
+                unset($values[$k]);
+            }
+        }
+
+        if ($values['reacur']) {
+            if ($values['dayom']) {
+                $values['weekom'] = null;
+                $values['daysow'] = null;
+            } else {
+                $values['dayom'] = null;
+                $values['daysow'] = array_sum($values['daysow']);
+            }
+
+        } else {
+            $values['dayom']  = null;
+            $values['weekom'] = null;
+            $values['daysow'] = null;
+        }
+        $bdate = $values['bdate'];
+        $edate = $values['edate'];
+        $this->formatValue($values['btime'], '%02d:%02d %s');
+        $this->formatValue($values['etime'], '%02d:%02d %s');
+        if ($values['reacur']) {
+            $recur['dow']        = $values['daysow'];
+            $recur['dom']        = $values['dayom'];
+            if ($recur['dow']) {
+                $recur['recur_week'] = ($values['weekom']) ? $values['weekom'] :null;
+            } else {
+                $recur['recur_week'] = null;
+            }
+        }
+        $eventId = $_REQUEST['id'];
+        if (ctype_digit($eventId)) {
+            $this->updateData($values);
+        } else {
+            $eventId = $this->insertData($values);
+        }
+        $this->insertEventRecur($bdate, $edate, $eventId, $recur);
+        return true;
+    }
+
+    //  }}}
+
+    //  {{{ setupRenderers()
+
+    /**
+     * Custom rendering templates for special fields on the form
+     *
+     * @return void
+     * @access protected
+     */
+    protected function setupRenderers()
+    {
+        parent::setupRenderers();
+        $renderer =& $this->defaultRenderer();
+        $required = '<!-- BEGIN required --><span class="req">*</span>
+        <!-- END required -->';
+        $error    = '<!-- BEGIN error --><div class="req">{error}</div>
+        <!-- END error -->';
+        $recurTmpl = '<tr class="recur-event">
+        <td class="labelcell">'.$required.'<label>{label}</label></td>
+        <td class="fieldcell">'.$error.'{element}</td></tr>';
+        $renderer->setElementTemplate($recurTmpl, 'dayom');
+        $renderer->setElementTemplate($recurTmpl, 'weekom');
+        $renderer->setElementTemplate($recurTmpl, 'daysow');
+        $renderer->setElementTemplate(
+            '<tr><td colspan="2">'.$required.'{label}'
+            .$error.'{element}</td></tr>',
+            'descr'
+        );
+        $renderer->setElementTemplate(
+            '<tr align="center"><td colspan="2">'
+            .$required.'{label}'.$error.'{element}</td></tr>', 'submit_rmv'
+        );
+    }
+
+    //  }}}
+
+    //  {{{ toHtml()
+
+    /**
+     * Handles how to display the current step the user is at in the form
+     *
+     * destroying and resetting the captcha value dis-allows someone from
+     * re-sending a form on a previous captcha.
+     *
+     * @return string form HTML state
+     * @access public
+     */
+    public function toHtml()
+    {
+        $GLOBALS['topScripts'][] = JQUERY_CDN_JS;
+        $baseSecureUrl   = MEDIA_BASE_URL;
+        $appBaseSecueUrl = MEDIA_APP_BASE_URL;
+        $GLOBALS['topScripts'][]
+            = $appBaseSecueUrl
+            . 'libjs/jqueryui/1.8.13/js/jquery-ui-1.8.13.custom.min.js';
+        $GLOBALS['topScripts'][] = CKEDITOR_JS.'';
+
+        $this->setupRenderers();
+        if ($this->validate()) {
+            $this->cleanForm();
+
+            if ($this->process(array(&$this, 'processData'), $this->mergeFiles)) {
+                $this->freeze();
+                $this->emailOwner();
+                $output = $this->successMsg;
+                header(
+                    'Location: ' . MEDIA_BASE_URL . "members-only-area/?rt=Events&page_id=36"
+                );
+                exit;
+            }
+            $this->sent = true;
+        } elseif ($this->isSubmitted()) {
+            $output  = $this->errorMsg;
+            $GLOBALS['topScripts'][]
+                = 'http://maps.googleapis.com/maps/api/js?sensor=true';
+            $GLOBALS['bottomScripts'][]
+                = MEDIA_BASE_URL . 'Toolkit/Maps/geoCoder.js';
+            $GLOBALS['bottomScripts'][]
+                = MEDIA_BASE_URL . 'Toolkit/Events/libjs/edit-event.js';
+            $output .= parent::toHtml();
+        } else {
+            $GLOBALS['topScripts'][]
+                = 'http://maps.googleapis.com/maps/api/js?sensor=true';
+            $GLOBALS['bottomScripts'][]
+                = MEDIA_BASE_URL . 'Toolkit/Maps/geoCoder.js';
+            $GLOBALS['bottomScripts'][]
+                = MEDIA_BASE_URL . 'Toolkit/Events/libjs/edit-event.js';
+            $output = parent::toHtml();
+        }
+        return $output;
+    }
+
+    //  }}}
+    /**
+     * check to see if the address (old on) is different than the one submitted
+     * if the event is not found then return false
+     *
+     * @param array $values The submitted values for the edit event form
+     *
+     * @return boolean
+     */
+    private function _didAddressChange($values)
+    {
+        $didAddressChange = false;
+        if (!$values['id'] && !ctype_digit($values['id'])) {
+            return $didAddressChange;
+        }
+        try {
+            $sql = "
+            SELECT address,city,state,zip
+              FROM event
+             WHERE id = :id";
+            $stmt = $this->dbh->prepare($sql);
+            $stmt->bindParam(':id', $values['id'], PDO::PARAM_INT);
+            $stmt->execute();
+            $address = $stmt->fetch(PDO::FETCH_ASSOC);
+            if (!$address) {
+                return $didAddressChange;
+            } else {
+                if ($address['address'] != $values['address']) {
+                    $didAddressChange = true;
+                }
+                if ($address['city'] != $values['city']) {
+                    $didAddressChange = true;
+                }
+                if ($address['state'] != $values['state']) {
+                    $didAddressChange = true;
+                }
+                if ($address['zip'] != $values['zip']) {
+                    $didAddressChange = true;
+                }
+            }
+        } catch(PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+        return $didAddressChange;
+    }
+    private function _didLatLonChange($values)
+    {
+        $didLatLonChange = false;
+        if (!$values['id'] && !ctype_digit($values['id'])) {
+            return $didLatLonChange;
+        }
+        try {
+            $sql = "
+            SELECT lat,lon
+              FROM event
+             WHERE id = :id";
+            $stmt = $this->dbh->prepare($sql);
+            $stmt->bindParam(':id', $values['id'], PDO::PARAM_INT);
+            $stmt->execute();
+            $location = $stmt->fetch(PDO::FETCH_ASSOC);
+            if (!$location) {
+                return $didLatLonChange;
+            } else {
+                if ($location['lat'] != $values['lat']) {
+                    $didLatLonChange = true;
+                }
+                if ($location['lon'] != $values['lon']) {
+                    $didLatLonChange = true;
+                }
+            }
+        } catch(PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+        return $didLatLonChange;
+    }
+    //  {{{ updateData()
+
+    /**
+     * Inserts contact data into the contact db
+     *
+     * @param array $values submitted values
+     *
+     * @return object result of db insert query
+     * @access protected
+     */
+    protected function updateData($values)
+    {
+        try {
+            // if the address changes then get the lat lon
+            if (  (!$this->_didLatLonChange($values)
+                && $this->_didAddressChange($values))
+                || (!$values['lat'] && !$values['lon'])
+            ) {
+                $values = $this->_geocode($values);
+            }
+
+            $sql = Toolkit_Common::createSQLUpdate(
+                $this->tableName,
+                array_keys($values),
+                array('id = :id')
+            );
+
+            return Toolkit_Common::processQuery(
+                $this->dbh,
+                $this->tableName,
+                $sql,
+                $values
+            );
+        } catch (PDOException $e) {
+            return Toolkit_Common::handleError($e);
+        }
+    }
+
+    //  }}}
+
+    // {{{ getTimeStamp()
+    /**
+    * getTimeStamp
+    *
+    * @param mixed $MySqlDate Description of $MySqlDate
+    *
+    * @access public
+    * @return void
+    */
+    function getTimeStamp($MySqlDate)
+    {
+        $date_array    = explode("-", $MySqlDate); // split the array
+        $var_year      = $date_array[0];
+        $var_month     = $date_array[1];
+        $var_day       = $date_array[2];
+        $var_timestamp = mktime(0, 0, 0, $var_month, $var_day, $var_year);
+        return $var_timestamp; // return it to the user
+    }// }}}
+    // {{{ ordinalDay()
+    /**
+    * ordinalDay
+    *
+    * @param mixed $ord   Ord
+    * @param mixed $day   Day
+    * @param mixed $month Month
+    * @param mixed $year  Year
+    *
+    * @access public
+    * @return void
+    */
+    function ordinalDay($ord, $day, $month, $year)
+    {
+        $firstOfMonth = mktime(0, 0, 30, $month, 1, $year);
+        $lastOfMonth  = $firstOfMonth + date("t", $firstOfMonth) * 86400;
+        $dayOccurs = 0;
+
+        for ($i = $firstOfMonth; $i < $lastOfMonth ; $i += 86400) {
+            if (date("w", $i) == $day) {
+                $dayOccurs++;
+                if ($dayOccurs == $ord) {
+                    $ordDay = $i;
+                }
+            }
+        }
+        return $ordDay;
+    }// }}}
+    // {{{ getEventDates()
+    /**
+    * getEventDates
+    *
+    * @param mixed  $starttime Start time
+    * @param mixed  $endtime   End time
+    * @param mixed  $recur     Recur
+    * @param string $format    Format
+    *
+    * @access public
+    * @return void
+    */
+    function getEventDates($starttime, $endtime, $recur, $format = 'm/d/Y')
+    {
+        if (!is_array($recur)) {
+            return;
+        }
+        if ($starttime == $endtime) {
+            return;
+        }
+        if (is_array($recur['dow'])) {
+            $daysow = array_sum($recur['dow']);
+        }
+        if ($recur['recur_week'] == 9) {
+            $fWeekNum = date("W", $starttime);
+            if (date('w', $starttime) == 0) {
+                $fWeekNum++;
+            }
+            $lWeekNum = date("W", $endtime);
+            if (date('w', $endtime) == 0) {
+                $lWeekNum++;
+            }
+            for ($fi = $fWeekNum; $fi <= $lWeekNum; $fi = $fi + 2) {
+                $eWeeks[] = $fi;
+            }
+        }
+        for ($i = $starttime; $i <= $endtime; $i += 86400) {
+            if ($recur['recur_week'] != '') {
+                if ($daysow) {
+                    $ri = 1;
+                    for ($r=0;$r<7;$r++) {
+                        if ($daysow & $ri) {
+                            $ord = $this->ordinalDay($recur['recur_week'], $r, date('n', $i), date('Y', $i));
+                        }
+                        $ri = $ri << 1;
+                    }
+                } else {
+                    $ord = null;
+                }
+            } else {
+                $ord = null;
+            }
+            if ($recur['dom']) {
+                if (date("j", $i) == $recur['dom']) {
+                    $events[] = date($format, $i);
+                }
+            } elseif (is_array($recur['dow'])) {
+                $cur_dow = date("w", $i);
+                switch ($cur_dow) {
+                case 0:
+                    $cur_dow = 1;
+                    break;
+                case 1:
+                    $cur_dow = 2;
+                    break;
+                case 2:
+                    $cur_dow = 4;
+                    break;
+                case 3:
+                    $cur_dow = 8;
+                    break;
+                case 4:
+                    $cur_dow = 16;
+                    break;
+                case 5:
+                    $cur_dow = 32;
+                    break;
+                case 6:
+                    $cur_dow = 64;
+                    break;
+                }
+                if ((int)$cur_dow & $daysow) {
+                    $cDateWeek = date("W", $i);
+                    if (date('w', $time) == 0) {
+                        $cDateWeek++;
+                    }
+                    if ($recur['recur_week'] == 9) {
+                        if (in_array($cDateWeek, $eWeeks)) {
+                            $events[] = date($format, $i);
+                        }
+                    } elseif ($recur['recur_week'] != '') {
+                        if ($recur['recur_week'] && $i && $ord) {
+                            if ($i == $ord) {
+                                $events[] = date($format, $i);
+                            }
+                        }
+                    } else {
+                        $events[] = date($format, $i);
+                    }
+                }
+            }
+        }
+        //echo '<pre>'.print_r($events, true).'</pre>';exit;
+        return $events;
+    }// }}}
+        // {{{ insertEventRecur()
+    /**
+    * Description of insertEventRecur()
+    *
+    * @param date  $bdate    Description of $bdate
+    * @param date  $edate    Description of $edate
+    * @param int   $event_id Description of $event_id
+    * @param array $recur    Description of $recur
+    *
+    * @return void
+    * @access public
+    */
+    function insertEventRecur($bdate, $edate, $event_id, $recur)
+    {
+        try {
+            $sql = "
+            DELETE
+              FROM event_recur
+             WHERE event_id = :event_id";
+            $del = $this->dbh->prepare($sql);
+            $del->bindParam(":event_id", $event_id, PDO::PARAM_INT);
+            $del->execute();
+        } catch(PDOException $e) {
+            die($e->getMessage());
+        }
+
+        if (isset($recur) && is_array($recur)) {
+            $eventDates = $this->getEventDates(
+                strtotime($bdate),
+                strtotime($edate),
+                $recur
+            );
+            $fields = array('event_id', 'event_day');
+            $sql = Toolkit_Common::createSQLInsert(
+                'event_recur',
+                $fields
+            );
+            try {
+                if (isset($eventDates) && is_array($eventDates)) {
+                    foreach ($eventDates as $eventDay) {
+                        $eventValues = array(
+                            'event_id'  => $event_id,
+                            'event_day' => $eventDay
+                        );
+                        $stmt = Toolkit_Common::prepareQuery(
+                            $this->dbh,
+                            'event_recur',
+                            $sql,
+                            $eventValues
+                        );
+                        $stmt->execute();
+                    }
+                }
+            } catch (PDOException $e) {
+                Toolkit_Common::handleError($e);
+            }
+        }
+    }
+    // }}}
+}
diff --git a/Toolkit/Members/Events/templates/emailOwner.tpl b/Toolkit/Members/Events/templates/emailOwner.tpl
new file mode 100755 (executable)
index 0000000..bebee85
--- /dev/null
@@ -0,0 +1,50 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+       <meta http-equiv="content-type" content="text/html;charset=utf-8">
+       <title>{title:h}</title>
+</head>
+<body>
+       <!-- {{{ body -->
+       <p>
+               <font size="4" face="arial, sans-serif">
+                       <b>{subject:h}</b>
+               </font>
+       </p>
+       <p>
+               <font size="3" face="arial, sans-serif">
+                       <b>From {fname:h} {lname:h}</b>
+               </font>
+       </p>
+       <table cellspacing="0" cellpadding="0" bgcolor="#c0c0c0" border="0">
+               <tr>
+                       <td>
+                               <table cellspacing="1" cellpadding="5" border="0" bgcolor="#c0c0c0" width="400">
+                                       {foreach:formData,v}
+                                               <tr flexy:if="v[element]" bgcolor="#c0c0c0">
+                                                       <td align="right" bgcolor="#ffffff">
+                                                               <font size="2" face="arial, sans-serif">
+                                                                       <b>{v[label]:h}</b>
+                                                               </font>
+                                                       </td>
+                                                       {if:v[nowrap]}
+                                                               <td nowrap bgcolor="#ffffff">
+                                                                       <font size="2" face="arial, sans-serif">{v[element]:h}</font>
+                                                               </td>
+                                                       {else:}
+                                                               <td bgcolor="#ffffff">
+                                                                       <font size="2" face="arial, sans-serif">{v[element]:h}</font>
+                                                               </td>
+                                                       {end:}
+                                               </tr>
+                                       {end:}
+                               </table>
+                       </td>
+               </tr>                                                           
+       </table>
+    <p>A new Event Record has been created from "Add Your Event"</p>
+       <p>The record needs to be approved before changes go live.</p>
+       <p>Goto <a href="{eventAdminURL}">Pending Records</a> to View and Approve their changes.</p>
+       <!-- }}} -->
+</body>
+</html>
diff --git a/Toolkit/Members/Exception.php b/Toolkit/Members/Exception.php
new file mode 100644 (file)
index 0000000..aca5575
--- /dev/null
@@ -0,0 +1,44 @@
+<?php
+/**
+ * BillingController.php
+ * 
+ * PHP version 5.2
+ * 
+ * @category  Toolkit
+ * @package   Members
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @version   SVN: $id$
+ * @link      <>
+ */
+
+/**
+ * Toolkit_Members_Exception
+ * 
+ * Description of Toolkit_Members_Exception
+ * 
+ * @category  Toolkit
+ * @package   Members
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @release   Release: $id$
+ * @link      <>
+ */
+class Toolkit_Members_Exception extends Exception
+{
+    /**
+     * Class constructor
+     * 
+     * @param string $message The exception message
+     * @param mixed  $code    The exception code
+     * 
+     * @access public
+     */
+       public function __construct($message = null, $code = 0)
+       {
+               parent::__construct($message, $code);
+       }
+}
+?>
diff --git a/Toolkit/Members/Exposure.php b/Toolkit/Members/Exposure.php
new file mode 100755 (executable)
index 0000000..6dca33e
--- /dev/null
@@ -0,0 +1,278 @@
+<?php
+/**
+ * Exposure.php
+ *
+ * PHP versions 4 and 5
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2009 Gaslight Media
+ * @license   Gaslight Media
+ * @version   CVS: $Id: Exposure.php,v 1.14 2009/11/10 20:09:02 jamie Exp $
+ * @link      <>
+ */
+
+/**
+ * Toolkit_Members_Exposure
+ *
+ * Exposure Tracking for Member DB
+ * tracking:
+ * list view
+ * detail view
+ * click thru
+ * Tracking record in exposure table are updated if they have
+ * one already for that month.
+ * If they do not then one is added.
+ * NOTE:
+ * A check is done to see if the table exposure is there.
+ * If it is not there then it is created
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2009 Steve Sutton
+ * @license   Gaslight Media
+ * @link      http://pear.php.net/package/Members
+ * @see       References to other sections (if any)...
+ */
+class Toolkit_Members_Exposure implements SplObserver
+{
+    //  {{{ properties
+
+    /**
+     * PDO Object instance
+     * @var    object
+     * @access private
+     */
+    private $_dbh;
+
+    /**
+     * Member's member_id
+     * @var    int
+     * @access private
+     */
+    private $_member_id;
+
+    /**
+     * Original Table name is exposure
+     * can be changed to anything though
+     * @var    string
+     * @access private
+     */
+    private $_tableName = 'exposure';
+    /**
+     * Type of update list,detail,click
+     * @var    string
+     * @access private
+     */
+    private $_type;
+
+    //  }}}
+    // {{{ __construct($member_id, $type = 'list')
+
+    /**
+     * __construct
+     *
+     * @param int     $member_id Member
+     * @param string  $type      Which Type
+     *
+     * @return void
+     * @access public
+     */
+    function __construct($member_id, $type = 'list')
+    {
+        $this->_type      = $type;
+        $this->_member_id = $member_id;
+        if (!$this->checkType()) {
+                       throw new InvalidArgumentException('Invalid type given');
+        }
+        $this->_dbh = Toolkit_Database::getInstance();
+        $this->checkTableInstallation();
+    }
+
+    // }}}
+
+    // {{{ checkTableInstallation()
+
+    /**
+     * checkTableInstallation
+     *
+     * Check to see if $this->_tableName table exists in database
+     *
+     * @return void
+     * @access public
+     * @throws PEAR_Error
+     */
+    function checkTableInstallation()
+    {
+        try {
+            $sql  = "
+                SELECT column_name, data_type
+                  FROM information_schema.columns
+                 WHERE table_name = :tname";
+            $stmt = $this->_dbh->prepare($sql);
+            $stmt->bindParam(':tname', $this->_tableName, PDO::PARAM_STR);
+            $stmt->execute();
+            $row = $stmt->fetch(PDO::FETCH_ASSOC);
+            if (empty($row)) {
+                $this->createTable();
+            }
+        } catch(PDOException $e) {
+            return Toolkit_Common::handleError($e);
+        }
+    }
+    // }}}
+    // {{{ checkType()
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...type
+     *
+     * @return boolean Return description (if any) ...
+     * @access public
+     */
+    function checkType()
+    {
+        return in_array($this->_type, array('list', 'detail', 'click'));
+    }
+
+    // }}}
+    // {{{ createTable()
+
+    /**
+     * createTable
+     *
+     * Creates the $this->_tableName table for tracking views
+     *
+     * @return void
+     * @access public
+     */
+    function createTable()
+    {
+        $sql = "
+        CREATE TABLE {$this->_tableName} (
+            exposure_id SERIAL PRIMARY KEY,
+            list integer DEFAULT 0,
+            click integer DEFAULT 0,
+            detail integer DEFAULT 0,
+            edate date DEFAULT current_date,
+            member_id integer,
+            FOREIGN KEY (member_id) REFERENCES member(member_id) ON DELETE CASCADE
+        );";
+        $this->_dbh->query($sql);
+        $sql = "CREATE INDEX exp_member_id ON {$this->_tableName}(member_id)";
+        $this->_dbh->query($sql);
+        $sql = "CREATE INDEX exp_edate_id ON {$this->_tableName}(edate)";
+        $this->_dbh->query($sql);
+        $sql = "CREATE INDEX exp_edate_month_id ON {$this->_tableName}(date_part('month', edate))";
+        $this->_dbh->query($sql);
+        $sql = "CREATE INDEX exp_edate_year_id ON {$this->_tableName}(date_part('year',edate))";
+        $this->_dbh->query($sql);
+    }
+
+    // }}}
+
+    // {{{ runUpdate()
+
+    /**
+     * runUpdate()
+     *
+     * Based on $this->_type an update is maed if a record exist
+     * for that member for the current month.
+     * If there is no record then one is created.
+     *
+     * @return boolean|void
+     * @access public
+     * @throws PEAR_Error
+     */
+    public function runUpdate()
+    {
+               //      Make sure the member actually exists in the members table
+               //      before we try to manipulate its exposure count. If they don't
+               //      exists, then just get us out of here so we don't throw any errors
+               try {
+                       $sql = "
+                SELECT count(*) AS exists
+                  FROM member
+                 WHERE member_id = :member_id";
+
+                       $stmt = $this->_dbh->prepare($sql);
+                       $stmt->bindParam(':member_id', $this->_member_id, PDO::PARAM_INT);
+                       $stmt->execute();
+                       $stmt->bindColumn('exists', $exists);
+                       $stmt->fetch();
+
+                       if (!$exists) {
+                               return false;
+                       }
+               } catch (PDOException $e) {
+                       return Toolkit_Common::handleError($e);
+               }
+
+        $sql = "
+            SELECT exposure_id
+              FROM {$this->_tableName}
+             WHERE member_id                 = :member_id
+               AND date_part('month', edate) = date_part('month', current_date)
+               AND date_part('year', edate)  = date_part('year', current_date)";
+        try {
+            $stmt = $this->_dbh->prepare($sql);
+            $stmt->bindParam(":member_id", $this->_member_id, PDO::PARAM_INT);
+            $stmt->execute();
+            $exp_data = $stmt->fetch(PDO::FETCH_ASSOC);
+            if ($exp_data['exposure_id']) {
+                // if something is found then update the exposure record
+                $sql = "
+                    UPDATE {$this->_tableName}
+                       SET {$this->_type} = {$this->_type} + 1
+                     WHERE exposure_id = :exposure_id";
+                try {
+                    $stmt2 = $this->_dbh->prepare($sql);
+                    $stmt2->bindParam(
+                        ":exposure_id",
+                        $exp_data['exposure_id'],
+                        PDO::PARAM_INT
+                    );
+                    $stmt2->execute();
+                } catch(PDOException $e) {
+                                       return Toolkit_Common::handleError($e);
+                }
+            } else {
+                // else insert a new exposure record
+                $sql = "
+                                       INSERT INTO {$this->_tableName} ({$this->_type}, member_id)
+                                       VALUES (1, :member_id)";
+                try {
+                    $stmt2 = $this->_dbh->prepare($sql);
+                    $stmt2->bindParam(
+                        ":member_id",
+                        $this->_member_id,
+                        PDO::PARAM_INT
+                    );
+                    $stmt2->execute();
+                } catch(PDOException $e) {
+                                       return Toolkit_Common::handleError($e);
+                }
+            }
+        } catch(PDOException $e) {
+                       return Toolkit_Common::handleError($e);
+        }
+    }
+    // }}}
+
+       //      {{{     update()
+
+    /**
+     * Description for update()
+     * @param SplSubject $subject Description of $subject
+     * @access public
+     */
+       public function update(SplSubject $subject)
+       {
+               $this->runUpdate();
+       }
+
+       //      }}}
+}
diff --git a/Toolkit/Members/ExposureDetailReports.php b/Toolkit/Members/ExposureDetailReports.php
new file mode 100755 (executable)
index 0000000..e80cf0a
--- /dev/null
@@ -0,0 +1,184 @@
+<?php
+/**
+ * ExposureDetailReports.php
+ *
+ * PHP versions 4 and 5
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2009 Gaslight Media
+ * @license   Gaslight Media
+ * @version   CVS: $Id: ExposureDetailReports.php,v 1.12 2010/01/09 19:05:38 jamie Exp $
+ * @link      <>
+ */
+
+
+/**
+ * Short description for class
+ *
+ * Exposuer Report of Member list,click,detail views
+ * for admin and member only area
+ * flag in BASE.setup.phtml of
+ *
+ * @category  MembersD
+ * @package   Toolkit_Members
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2009 Gaslight Media
+ * @license   Gaslight Media
+ * @link      <>
+ */
+class Toolkit_Members_ExposureDetailReports
+       extends Toolkit_FlexyDataGridBuilder
+{
+    // {{{     properties
+
+    /**
+     * Table name for tracking
+     * @var    string
+     * @access protected
+     */
+    protected $tableName = 'exposure';
+
+    /**
+     * Description for protected
+     * @var    array
+     * @access protected
+     */
+    protected $queryParams = array();
+
+    /**
+     * Template File
+     * @var    string
+     * @access protected
+     */
+    protected $template = 'exposureDetail.tpl';
+
+       //      }}}
+    //    {{{ __construct()
+
+    /**
+     * Class constructor
+     *
+     * define where templates for the data grid are at,
+     * then call the parent constructor which will handle
+     * finishing the settings for the datagrid.
+     *
+     * After all settings are finished you can call the toHTML()
+     * function on this object and the datagrid
+     * will be rendered and returned as a string.
+     * Optionally you could call show() and the datagrid would
+     * be rendered and output immediatley to the screen.
+     *
+        * @param PDO     $pdo          PDO object used in the datagrid
+     * @param integer $limit        The number of records to display per page.
+     * @param integer $page         The current page view. In most cases,
+        *                                                              this is useless. Note: if you specify
+        *                                                              this, the "page"GET variable will be ignored.
+     * @param string  $rendererType The type of renderer to use. You may
+        *                                                              prefer to use the $type argument of
+        *                                                              render, fill or getOutput.
+     *
+     * @access public
+     */
+    public function __construct(
+        PDO $pdo,
+        $limit = null,
+        $page = null,
+        $rendererType = null
+    ) {
+        $this->pagerOptions['containerClass'] = 'pages';
+        parent::__construct($pdo, $limit, $page, $rendererType);
+    }
+
+    //    }}}
+
+    //    {{{ configureColumns()
+
+    /**
+     * Configures the columns (fields) that will be used in our datagrid renderer.
+     *
+     * @return void
+     * @access public
+     */
+    protected function configureColumns()
+    {
+        $month = new Structures_DataGrid_Column(
+                       'Month',
+            'month',
+            'month',
+            null,
+            null,
+            array(&$this, 'showMonth')
+               );
+        $this->addColumn($month);
+
+        $memberName = new Structures_DataGrid_Column(
+                       'Member Name',
+            'member_name',
+            'member_name'
+               );
+        $this->addColumn($memberName);
+
+        $list = new Structures_DataGrid_Column(
+                       'List',
+            'list',
+            'list'
+               );
+        $this->addColumn($list);
+
+        $detail = new Structures_DataGrid_Column(
+                       'Detail',
+            'detail',
+            'detail'
+               );
+        $this->addColumn($detail);
+
+        $click = new Structures_DataGrid_Column(
+                       'Click',
+            'click',
+            'click'
+               );
+        $this->addColumn($click);
+    }
+
+    //    }}}
+
+    //  {{{ setQuery()
+
+    /**
+     * Sets the query to use to fetch the datagrid results
+     *
+     * @param integer $mid Member id to fetch reports for
+     *
+     * @return void
+     * @access public
+     */
+    public function setQuery($mid)
+    {
+        $sql = "
+                       SELECT e.*, date_part('epoch', e.edate) AS month, m.member_name
+                         FROM exposure e LEFT OUTER JOIN member m
+                                  ON (m.member_id = e.member_id)
+                        WHERE e.member_id = $mid";
+        parent::setQuery($sql);
+    }
+
+    //  }}}
+    //  {{{ showMonth()
+
+    /**
+     * Returns the monht for a record.
+     *
+     * @param array $data Structure Datagrid
+     *
+     * @access public
+     * @return string
+     */
+    public function showMonth($data)
+    {
+        return date('F, Y', $data['record']['month']);
+    }
+
+    //  }}}
+}
diff --git a/Toolkit/Members/ExposureReports.php b/Toolkit/Members/ExposureReports.php
new file mode 100755 (executable)
index 0000000..6cc2315
--- /dev/null
@@ -0,0 +1,399 @@
+<?php
+/**
+ * ExposureReports.php
+ *
+ * PHP versions 4 and 5
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2009 Gaslight Media
+ * @license   Gaslight Media
+ * @version   CVS: $Id: ExposureReports.php,v 1.15 2010/07/14 23:31:14 jamie Exp $
+ * @link      <>
+ */
+
+
+/**
+ * Toolkit_Members_ExposureReports
+ *
+ * Exposuer Report of Member list,click,detail views
+ * for admin and member only area
+ * flag in BASE.setup.phtml of
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2009 Gaslight Media
+ * @license   Gaslight Media
+ * @link      <>
+ */
+class Toolkit_Members_ExposureReports extends Toolkit_FlexyDataGridBuilder
+{
+    // {{{ properties
+
+    /**
+     * Description for $noRecMessage
+     * @var string
+     * @acess protected
+     */
+    protected $noRecMessage = 'No Data Found';
+
+    /**
+     * Table name for tracking
+     * @var    string
+     * @access protected
+     */
+    protected $tableName = 'exposure';
+
+    /**
+     * Description for protected
+     * @var    array
+     * @access protected
+     */
+    protected $queryParams = array();
+
+    /**
+     * Template File
+     * @var    string
+     * @access protected
+     */
+    protected $template = 'exposureList.tpl';
+
+    // }}}
+    // {{{ __construct()
+
+    /**
+     * Class constructor
+     *
+     * define where templates for the data grid are at,
+     * then call the parent constructor which will handle
+     * finishing the settings for the datagrid.
+     *
+     * After all settings are finished you can call the toHTML()
+     * function on this object and the datagrid
+     * will be rendered and returned as a string.
+     * Optionally you could call show() and the datagrid would
+     * be rendered and output immediatley to the screen.
+     *
+        * @param PDO     $pdo          PDO object used in the datagrid
+     * @param integer $limit        The number of records to display per page.
+     * @param integer $page         The current page view. In most cases,
+        *                                                              this is useless. Note: if you specify
+        *                                                              this, the "page"GET variable will be ignored.
+     * @param string  $rendererType The type of renderer to use. You may
+        *                                                              prefer to use the $type argument of
+        *                                                              render, fill or getOutput.
+     *
+     * @access public
+     */
+    public function __construct(
+        PDO $pdo,
+        $limit = null,
+        $page = null,
+        $rendererType = null
+    ) {
+        $this->dbh = $pdo;
+        $this->checkTableInstallation();
+        $this->pagerOptions['containerClass'] = 'pages';
+        parent::__construct($pdo, $limit, $page, $rendererType);
+    }
+
+    // }}}
+
+    // {{{ createTable()
+
+    /**
+     * createTable
+     *
+     * Creates the $this->tableName table for tracking views
+     *
+     * @return void
+     * @access public
+     */
+    function createTable()
+    {
+        $sql = "
+            CREATE TABLE {$this->tableName} (
+                exposure_id SERIAL PRIMARY KEY,
+                list integer DEFAULT 0,
+                click integer DEFAULT 0,
+                detail integer DEFAULT 0,
+                edate date DEFAULT current_date,
+                member_id integer,
+                FOREIGN KEY (member_id) REFERENCES member(member_id) ON DELETE CASCADE
+            );";
+        $this->dbh->query($sql);
+    }
+
+    // }}}
+    // {{{ checkTableInstallation()
+
+    /**
+     * checkTableInstallation
+     *
+     * Check to see if $this->tableName table exists in database
+     *
+     * @return void
+     * @access public
+     */
+    function checkTableInstallation()
+    {
+        try {
+            $sql  = "
+                SELECT column_name, data_type
+                  FROM information_schema.columns
+                 WHERE table_name = :tname";
+            $stmt = $this->dbh->prepare($sql);
+            $stmt->bindParam(':tname', $this->tableName, PDO::PARAM_STR);
+            $stmt->execute();
+            $row = $stmt->fetch(PDO::FETCH_ASSOC);
+            if (empty($row)) {
+                $this->createTable();
+            }
+        } catch(PDOException $e) {
+            return Toolkit_Common::handleError($e);
+        }
+    }
+
+    // }}}
+    // {{{ configureColumns()
+
+    /**
+     * Configures the columns (fields) that will be used in our datagrid renderer.
+     *
+     * @return void
+     * @access public
+     */
+    protected function configureColumns()
+    {
+        $recordUrl = new Structures_DataGrid_Column(
+                       'DirectURL',
+            'directUrl',
+            'directUrl',
+            null,
+            null,
+            array(&$this, 'recordUrl')
+               );
+        $this->addColumn($recordUrl);
+
+        $memberName = new Structures_DataGrid_Column(
+                       'Member Name',
+            'member_name'
+               );
+        $this->addColumn($memberName);
+
+        $list = new Structures_DataGrid_Column(
+                       'List',
+            'list',
+            'list'
+               );
+        $this->addColumn($list);
+
+        $detail = new Structures_DataGrid_Column(
+                       'Detail',
+            'detail',
+            'detail'
+               );
+        $this->addColumn($detail);
+
+        $click = new Structures_DataGrid_Column(
+                       'Click',
+            'click',
+            'click'
+               );
+        $this->addColumn($click);
+    }
+
+    // }}}
+
+    // {{{ recordUrl()
+
+    /**
+     * Returns the url for a member.
+     *
+     * Used when configuring the columns for the data grid. This
+     * function generates the url to get to the member detail page
+     *
+     * @param array $data Structure_DataGrid
+     *
+     * @return string
+     * @access public
+     */
+    public function recordUrl($data)
+    {
+        $month  = $_GET['reportMonth']
+                       ? "&reportMonth={$_GET['reportMonth']}"
+                       : '';
+        $cat    = $_GET['cat']
+                       ? "&cat={$_GET['cat']}"
+                       : '';
+        $subCat = $_GET['subCat']
+                       ? "&subCat={$_GET['subCat']}"
+                       : '';
+        return "members.php?" .
+                       'rt=Members&ac=memberReports&' .
+            "member_id={$data['record']['member_id']}" .
+            $month .
+            $cat .
+            $subCat;
+    }
+
+    // }}}
+
+    // {{{ setControlObject()
+
+    /**
+     * Sets any control object variables that are going to be used in the template
+     *
+     * @return void
+     * @access public
+     */
+    protected function setControlObject()
+    {
+        try {
+            $letters = array();
+            if ($_REQUEST['reportMonth']) {
+                list($month, $year) = explode("|", $_REQUEST['reportMonth']);
+            } else {
+                $month = date('n');
+                $year  = date('Y');
+            }
+
+            $stime = date('m/d/Y', mktime(0, 0, 0, $month, 1, $year));
+            $etime = date('m/d/Y', mktime(0, 0, 0, $month +1, -1, $year));
+
+            $params = array();
+            if (isset($_GET['name']) && !empty($_GET['name'])) {
+                $noSpaceName = preg_replace('/[^[:alnum:]]/', '', urldecode($_GET['name']));
+                $spaceName = urldecode($_GET['name']);
+                $sanitizedNoSpaceName = $this->dbh->quote($noSpaceName);
+                $sanitizedSpaceName = $this->dbh->quote($spaceName);
+                $params[] = "(regexp_replace(m.member_name, '[^[:alnum:]]', '', 'g') ~* $sanitizedNoSpaceName)";
+            }
+
+            $sql = "
+            SELECT substr(upper(member_name), 1, 1) AS letter
+              FROM exposure e, member m
+                        WHERE e.member_id =  m.member_id
+               AND m.new_member = CAST(0 AS BOOLEAN)
+               AND e.edate     >= '$stime'
+               AND e.edate     <= '$etime'
+             ";
+            if (!empty($params)) {
+                $params = implode(' AND ', $params);
+                $sql    = "{$sql} AND $params";
+            }
+            $sql .= "
+            GROUP BY letter
+            ORDER BY letter";
+
+            foreach ($this->dbh->query($sql) as $row) {
+                $letters[$row['letter']] = $row['letter'];
+            }
+
+                       $addNumeric = false;
+                       while ($i = current($letters)) {
+                               if (is_numeric($i)) {
+                                       $addNumeric = true;
+                                       unset($letters[key($letters)]);
+                               } else {
+                                       break;
+                               }
+                       }
+
+                       if ($addNumeric) {
+                               array_unshift($letters, '0-9');
+                       }
+
+        } catch (PDOException $e) {
+            return Toolkit_Common::handleError($e);
+        }
+
+        //  We only need to show the links of alphabet if we have more than
+        //  one letter to display.
+        if (count($letters) > 1) {
+            foreach ($_GET as $k => $v) {
+                if ($k != 'alpha') {
+                    if (is_array($v)) {
+                        foreach ($v as $i => $j) {
+                            $queryString .= $k . '[' . $i . ']=' . $j . '&';
+                        }
+                    } else {
+                        $queryString .= "$k=$v&";
+                    }
+                }
+            }
+            $links['All']['url'] = MEDIA_BASE_URL . "admin/members.php?{$queryString}";
+            $links['All']['class'] = empty($_GET['alpha']) ? 'curr' : null;
+            foreach ($letters as $v) {
+                $links[$v]['url'] = MEDIA_BASE_URL . "admin/members.php?{$queryString}alpha=$v";
+                if ($_GET['alpha'] == $v) {
+                    $links[$v]['class'] = 'curr';
+                }
+            }
+            $this->ctrlObj['letters'] = $links;
+        }
+    }
+
+    // }}}
+    //  {{{ setQuery()
+
+    /**
+     * Sets the query to use to fetch the datagrid results
+     *
+     * @param array $monthYear Which month to show
+     *
+     * @return void
+     * @access public
+     */
+    public function setQuery()
+    {
+        if ($_REQUEST['reportMonth']) {
+            list($month, $year) = explode("|", $_REQUEST['reportMonth']);
+        } else {
+            $month = date('n');
+            $year  = date('Y');
+        }
+
+        $stime = date('m/d/Y', mktime(0, 0, 0, $month, 1, $year));
+        $etime = date('m/d/Y', mktime(0, 0, 0, $month +1, -1, $year));
+
+        $params = array();
+        //  Limit to members whose first letter starts with the letter
+        //  a user clicked in the narrow alphabetically list.
+               if (isset($_GET['alpha']) && preg_match('/[0-9]/', $_GET['alpha'])) {
+                       $params[] = "substr(m.member_name, 1, 1) ~ '[0-9]'";
+               } elseif (isset($_GET['alpha']) && ctype_alpha($_GET['alpha'])) {
+                       $letter = $_GET['alpha'][0];
+            $params[] = "upper(substr(m.member_name, 1, 1)) = upper(" .
+                               $this->dbh->quote($letter) . ')';
+        }
+        if (isset($_GET['name']) && !empty($_GET['name'])) {
+                       $noSpaceName = preg_replace('/[^[:alnum:]]/', '', urldecode($_GET['name']));
+                       $spaceName = urldecode($_GET['name']);
+                       $sanitizedNoSpaceName = $this->dbh->quote($noSpaceName);
+                       $sanitizedSpaceName = $this->dbh->quote($spaceName);
+            $params[] = "(regexp_replace(m.member_name, '[^[:alnum:]]', '', 'g') ~* $sanitizedNoSpaceName)";
+               }
+
+        $sql   = "
+                       SELECT m.member_name, m.member_id, sum(e.list) AS list,
+                                  sum(e.click) as click, sum(e.detail) AS detail
+                         FROM exposure e, member m
+                        WHERE e.member_id =  m.member_id
+                          AND e.edate     >= '$stime'
+                          AND e.edate     <= '$etime'";
+
+        if (!empty($params)) {
+                       $params = implode(' AND ', $params);
+                       $sql    = "{$sql} AND $params";
+               }
+        $sql .= " GROUP BY m.member_name, m.member_id
+                        ORDER BY m.member_name";
+        parent::setQuery($sql);
+    }
+
+    //  }}}
+}
+?>
diff --git a/Toolkit/Members/FileDownload.php b/Toolkit/Members/FileDownload.php
new file mode 100644 (file)
index 0000000..2670fad
--- /dev/null
@@ -0,0 +1,231 @@
+<?php
+
+/**
+ * File Download manager
+ *
+ * PHP version 5
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @version   CVS: $Id: FileDownload.php,v 1.1 2009/08/21 14:20:56 jamie Exp $
+ * @link      http://demo.gaslightmedia.com
+ */
+
+/**
+ * File Download Manager
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @version   Release: @package_version@
+ * @link      http://demo.gaslightmedia.com
+ */
+class Toolkit_Members_FileDownload
+{
+    //  {{{ properties
+
+
+    /**
+     * Database Handler
+     * @var    object
+     * @access protected
+     */
+    protected $dbh;
+
+    //  }}}
+    //  {{{ __construct()
+
+
+    /**
+     * Constructor
+     *
+     * @param PDO $pdo PHP Data Object to use for dbh
+     *
+     * @return void
+     * @access public
+     */
+    public function __construct(PDO $pdo)
+    {
+        $this->dbh = $pdo;
+    }
+
+    //  }}}
+
+    //  {{{ fileExists()
+
+
+    /**
+     * Determine if the file exists
+     *
+     * The file we are trying to get is determined by the file id passed in
+     * from the url.
+     *
+     * @param integer $fid File id to retrieve
+     *
+     * @return mixed   array of file data if file exists, otherwise false
+     * @access protected
+     */
+    protected function fileExists($fid)
+    {
+        if (!is_numeric($fid) || !ctype_digit((string) $fid)) {
+            return false;
+        }
+
+        try {
+            $sql = "
+                SELECT *
+                  FROM member_files
+                 WHERE member_id = :mid
+                   AND id = :id";
+
+            $stmt = $this->dbh->prepare($sql);
+            $stmt->bindParam(':mid', $this->mid, PDO::PARAM_INT);
+            $stmt->bindParam(':id', $fid, PDO::PARAM_INT);
+            $stmt->execute();
+
+            return $stmt->fetch(PDO::FETCH_ASSOC);
+        } catch (PDOException $e) {
+            return Toolkit_Common::handleError($e);
+        }
+    }
+
+    //  }}}
+
+    //  {{{ getFile()
+
+
+    /**
+     * Get the data about the file we are requesting
+     *
+     * @param integer $mid Member Id
+     * @param integer $fid File Id
+     *
+     * @return mixed   Array of file data if success, otherwise PEAR error
+     * @access public
+     */
+    public function getFile($mid, $fid)
+    {
+        if (!$this->memberExists($mid)) {
+            return PEAR::raiseError('Invalid Member');
+        }
+
+        if (!$file = $this->fileExists($fid)) {
+            return PEAR::raiseError('Invalid File');
+        }
+
+        if (empty($file['file_name'])) {
+            $file['file_name'] = $file['original_name'];
+        }
+
+        return $file;
+    }
+
+    //  }}}
+
+    //  {{{ memberExists()
+
+
+    /**
+     * Determines if the member we are trying to get a file for is a real member
+     *
+     * @param integer $mid member id to check for
+     *
+     * @return boolean True if real member, False if not
+     * @access protected
+     */
+    protected function memberExists($mid)
+    {
+        if (!is_numeric($mid) || !ctype_digit((string) $mid)) {
+            return false;
+        }
+
+        try {
+            $sql = "
+                SELECT count(*) AS total
+                  FROM member
+                 WHERE member_id = :mid";
+
+            $stmt = $this->dbh->prepare($sql);
+            $stmt->bindParam(':mid', $mid, PDO::PARAM_INT);
+            $stmt->execute();
+            $stmt->bindColumn('total', $total);
+            $row = $stmt->fetch();
+
+            if ($total) {
+                $this->mid = $mid;
+                return true;
+            } else {
+                return false;
+            }
+        } catch (PDOException $e) {
+            return Toolkit_Common::handleError($e);
+        }
+    }
+
+    //  }}}
+
+    //  {{{ sendFileToBrowser()
+    //  @codeCoverageIgnoreStart
+
+    /**
+     * Sends the file to the browser via headers
+     *
+     * @param array $file File data to use
+     *
+     * @return void
+     * @access public
+     */
+    public function sendFileToBrowser(array $file)
+    {
+        extract($file);
+        $pattern = '%\.([A-Za-z]{3})$%';
+        preg_match($pattern, $name_on_disk, $matches);
+        $ext = (!empty($matches)) ? $matches[1] : '';
+        switch ($ext) {
+        case 'txt' :
+            $type = 'text/plain';
+            break;
+
+        case 'doc' :
+            $type = 'application/msword';
+            break;
+
+        case 'xls' :
+            $type = 'application/vnd.ms-excel';
+            break;
+
+        case 'pdf' :
+            $type = 'application/pdf';
+            break;
+
+        default :
+            $type = 'application/pdf';
+            break;
+        }
+        //  file we are downloading
+        $ch = curl_init(ORIGINAL . $name_on_disk);
+        curl_setopt($ch, CURLOPT_TIMEOUT, 50);
+        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
+        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
+        $fetchedFile = curl_exec($ch);
+        curl_close($ch);
+
+        // now get the pdf from file and push out to browser
+        // using header()
+        header("Content-Type: $type;");
+        header('Content-Disposition: attachment; filename="'.$name_on_disk.'";');
+        header('Content-Length: '.(string) $size);
+        header('Cache-Control: maxage=3600;'); //Adjust maxage appropriately
+        header('Pragma: public;');
+        echo $fetchedFile;
+        exit();
+    }
+
+    //  @codeCoverageIgnoreEnd
+    //  }}}
+}
diff --git a/Toolkit/Members/FileImporter/index.php b/Toolkit/Members/FileImporter/index.php
new file mode 100644 (file)
index 0000000..4d294fb
--- /dev/null
@@ -0,0 +1,33 @@
+<?php
+require_once '../../../setup.phtml';
+define ('MEMBER_FILES_ORIG_URL', SITE_URL . 'uploads/member_files/');
+
+$dbh = Toolkit_Database::getInstance();
+
+$sql = "
+SELECT *
+  FROM members.member_files
+ WHERE name_on_disk not like 'fs%'
+   AND name_on_disk not like 'is%'";
+
+$stmt = $dbh->query($sql);
+$fs = new Toolkit_FileServer_FileAdapter();
+
+$sql = "
+UPDATE members.member_files
+   SET name_on_disk = :filename
+ WHERE id = :id";
+$updateNameOnDisk = $dbh->prepare($sql);
+while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
+    var_dump($row);
+    $url = MEMBER_FILES_ORIG_URL . urlencode($row['name_on_disk']);
+    var_dump($url);
+    $fileName = $fs->upload($url);
+    if ($fileName['name']) {
+        $fileNameOnDisk = $fileName['name'];
+        var_dump($fileNameOnDisk);
+        $updateNameOnDisk->bindParam(':filename', $fileNameOnDisk);
+        $updateNameOnDisk->bindParam(':id', $row['id'], PDO::PARAM_INT);
+        $updateNameOnDisk->execute();
+    }
+}
diff --git a/Toolkit/Members/Import/ActionDisplay.php b/Toolkit/Members/Import/ActionDisplay.php
new file mode 100644 (file)
index 0000000..1361f2f
--- /dev/null
@@ -0,0 +1,52 @@
+<?php
+
+/**
+ * Controls functionality to build page
+ * 
+ * PHP version 5
+ * 
+ * @category  Members
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @version   CVS: $Id: ActionDisplay.php,v 1.1 2009/10/13 13:23:00 jamie Exp $
+ * @link      http://demo.gaslightmedia.com
+ * @see       References to other sections (if any)...
+ */
+
+/**
+ * Checks to see if the form is built and if not builds it
+ * 
+ * Once the form is built, the appropriate action is taken to display the page
+ * 
+ * @category  Members
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @version   Release: @package_version@
+ * @link      http://demo.gaslightmedia.com
+ * @see       References to other sections (if any)...
+ */
+class Toolkit_Members_Import_ActionDisplay
+    extends HTML_QuickForm_Action_Display
+{
+
+    /**
+     * Short description for function
+     * 
+     * Long description (if any) ...
+     * 
+     * @param  object  &$page      Parameter description (if any) ...
+     * @param  unknown $actionName Parameter description (if any) ...
+     * @return void   
+     * @access public 
+     */
+    public function perform(&$page, $actionName)
+    {
+        $page->isFormBuilt() or $page->buildForm();
+        parent::perform($page, $actionName);
+    }
+}
+?>
diff --git a/Toolkit/Members/Import/ActionUpload.php b/Toolkit/Members/Import/ActionUpload.php
new file mode 100644 (file)
index 0000000..7b79a14
--- /dev/null
@@ -0,0 +1,82 @@
+<?php
+
+/**
+ * Short description for file
+ * 
+ * Long description (if any) ...
+ * 
+ * PHP version 5
+ * 
+ * The license text...
+ * 
+ * @category  Members
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @version   CVS: $Id: ActionUpload.php,v 1.1 2009/10/13 13:23:00 jamie Exp $
+ * @link      http://demo.gaslightmedia.com
+ * @see       References to other sections (if any)...
+ */
+
+/**
+ * Short description for class
+ * 
+ * Long description (if any) ...
+ * 
+ * @category  Members
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @version   Release: @package_version@
+ * @link      http://demo.gaslightmedia.com
+ * @see       References to other sections (if any)...
+ */
+class Toolkit_Members_Import_ActionUpload extends HTML_QuickForm_Action
+{
+
+    /**
+     * Short description for function
+     * 
+     * Long description (if any) ...
+     * 
+     * @param  object  &$page      Parameter description (if any) ...
+     * @param  unknown $actionName Parameter description (if any) ...
+     * @return void   
+     * @access public 
+     */
+    public function perform(&$page, $actionName)
+    {
+        $page->isFormBuilt() or $page->buildForm();
+
+        $pageName = $page->getAttribute('id');
+        $data = $page->controller->container();
+        $data['values'][$pageName] = $page->exportValues();
+        if (PEAR::isError($valid = $page->validate())) {
+            return $valid;
+        }
+        $data['valid'][$pageName] = $valid;
+
+        if (!$data['valid'][$pageName]) {
+            return $page->handle('display');
+        }
+
+        //  Get the element containing the upload
+        $element = $page->getElement('file');
+
+        //  Move the file and store the data
+        if ($element->isUploadedFile()) {
+            $newName = mktime() . '.csv';
+            $element->moveUploadedFile('./uploads', $newName);
+            $value = $element->getValue();
+            $data['_upload'] = basename($newName);
+        }
+
+        // redirect to next page
+        $next = $page->controller->getPage($page->controller->getNextName($pageName));
+        $next->handle('jump');
+
+    }
+}
+?>
diff --git a/Toolkit/Members/Import/AnalyzePage.php b/Toolkit/Members/Import/AnalyzePage.php
new file mode 100644 (file)
index 0000000..908331b
--- /dev/null
@@ -0,0 +1,104 @@
+<?php
+
+/**
+ * Short description for file
+ * 
+ * Long description (if any) ...
+ * 
+ * PHP version 5
+ * 
+ * The license text...
+ * 
+ * @category  Members
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @version   CVS: $Id: AnalyzePage.php,v 1.2 2009/10/14 11:04:16 jamie Exp $
+ * @link      http://demo.gaslightmedia.com
+ * @see       References to other sections (if any)...
+ */
+
+
+/**
+ * Short description for class
+ * 
+ * Long description (if any) ...
+ * 
+ * @category  Members
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @version   Release: @package_version@
+ * @link      http://demo.gaslightmedia.com
+ * @see       References to other sections (if any)...
+ */
+class Toolkit_Members_Import_AnalyzePage
+    extends Toolkit_Members_Import_OpPage
+{
+    //  {{{ buildForm()
+
+
+    /**
+     * Short description for function
+     * 
+     * Long description (if any) ...
+     * 
+     * @return void  
+     * @access public
+     */
+    public function buildForm()
+    {
+        parent::buildForm();
+
+        $this->configureElements();
+        $this->insertData();
+    }
+
+    //  }}}
+    //  {{{ configureElements()
+
+
+    /**
+     * Short description for function
+     * 
+     * Long description (if any) ...
+     * 
+     * @return void     
+     * @access protected
+     */
+    protected function configureElements()
+    {
+        $e = array();
+
+        $prevNext = array();
+        $prevNext[] = array(
+            'type' => 'submit',
+            'req' => false,
+            'name' => $this->getButtonName('back'),
+            'display' => '<< Previous Step'
+        );
+        $prevNext[] = array(
+            'type' => 'submit',
+            'req' => false,
+            'name' => $this->getButtonName('next'),
+            'display' => 'Next Step >>'
+        );
+
+        $e[] = array(
+            'type' => 'group',
+            'req' => false,
+            'name' => 'control_buttons',
+            'group' => $prevNext,
+            'label' => '',
+            'seperator' => '&nbsp;',
+            'appendName' => false
+        );
+        
+        $this->setupElements($e);
+    }
+
+    //  }}}
+}
+?>
diff --git a/Toolkit/Members/Import/ConversionPage.php b/Toolkit/Members/Import/ConversionPage.php
new file mode 100644 (file)
index 0000000..1cc3ffe
--- /dev/null
@@ -0,0 +1,318 @@
+<?php
+
+/**
+ * Short description for file
+ * 
+ * Long description (if any) ...
+ * 
+ * PHP version 5
+ * 
+ * The license text...
+ * 
+ * @category  Members
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @version   CVS: $Id: ConversionPage.php,v 1.1 2009/10/13 13:23:00 jamie Exp $
+ * @link      http://demo.gaslightmedia.com
+ * @see       References to other sections (if any)...
+ */
+
+/**
+ * Description for require_once
+ */
+require_once 'HTML/QuickForm/ElementGrid.php';
+
+/**
+ * Short description for class
+ * 
+ * Long description (if any) ...
+ * 
+ * @category  Members
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @version   Release: @package_version@
+ * @link      http://demo.gaslightmedia.com
+ * @see       References to other sections (if any)...
+ */
+class Toolkit_Members_Import_ConversionPage
+    extends Toolkit_Members_Import_Page
+{
+    //  {{{ buildForm()
+
+
+    /**
+     * Short description for function
+     * 
+     * Long description (if any) ...
+     * 
+     * @return void  
+     * @access public
+     */
+    public function buildForm()
+    {
+        parent::buildForm();
+
+        $this->configureElements();
+        $this->configureFilters();
+
+        $this->setDefaultAction('next');
+    }
+
+    //  }}}
+    //  {{{ configureElements()
+
+
+    /**
+     * Short description for function
+     * 
+     * Long description (if any) ...
+     * 
+     * @return void     
+     * @access protected
+     */
+    protected function configureElements()
+    {
+        $host = $this->controller->exportValue('serverPage', 'host');
+        $db   = $this->controller->exportValue('dbPage', 'database');
+        $cols = $this->controller->exportValue('fieldsPage', 'columns');
+
+        $dbh = new PDO("pgsql:host=$host user=postgres dbname=$db");
+
+        $gridColNames = array(
+            '[Import Field]',
+            'Cnvts To',
+            '[Database Field]',
+        );
+
+        $tables = $columns = $this->getAvailableTables($dbh);
+
+        $columns = array();
+        foreach ($tables as $i) {
+            $columns[$i] = $this->getTableColumns($dbh, $i);
+        }
+
+        $tables = array('' => '-- Select --') + $tables;
+
+        $e = array();
+
+        $prevNext = array();
+        $prevNext[] = array(
+            'type' => 'submit',
+            'req' => false,
+            'name' => $this->getButtonName('back'),
+            'display' => '<< Previous Step'
+        );
+        $prevNext[] = array(
+            'type' => 'submit',
+            'req' => false,
+            'name' => $this->getButtonName('next'),
+            'display' => 'Next Step >>'
+        );
+
+        $e[] = array(
+            'type' => 'header',
+            'req' => false,
+            'name' => 'header_rmv',
+            'display' => 'Select fields to import',
+        );
+        $e[] = array(
+            'type' => 'static',
+            'req' => false,
+            'name' => 'info_rmv',
+            'display' => null,
+            'opts' => 'Select the field(s) you want to import from the file.<br>
+                       To select more than one field, hold down the CTRL key
+                       while selecting items.'
+        );
+        $e[] = array(
+            'type' => 'elementGrid',
+            'req' => false,
+            'name' => 'conversions',
+            'display' => 'Field Conversions',
+            'opts' => array('actAsGroup' => true)
+        );
+        $e[] = array(
+            'type' => 'group',
+            'req' => false,
+            'name' => 'control_buttons',
+            'group' => $prevNext,
+            'label' => '',
+            'seperator' => '&nbsp;',
+            'appendName' => false
+        );
+        
+        $this->setupElements($e);
+        $this->configureElementGrid(
+            'conversions',
+            $gridColNames,
+            $tables,
+            $columns,
+            $cols
+        );
+    }
+
+    //  }}}
+    //  {{{ configureFilters()
+
+
+    /**
+     * Short description for function
+     * 
+     * Long description (if any) ...
+     * 
+     * @return void     
+     * @access protected
+     */
+    protected function configureFilters()
+    {
+        $f = array();
+
+        $f[] = array(
+            'element' => '__ALL__',
+            'filter' => 'trim',
+        );
+
+        $this->setupFilters($f);
+    }
+
+    //  }}}
+    //  {{{ configureElementGrid()
+
+
+    /**
+     * Short description for configureElementGrid()
+     * 
+     * Long description (if any) ...
+     * 
+     * @param unknown $eName    Parameter description (if any) ...
+     * @param unknown $colNames Parameter description (if any) ...
+     * @param unknown $tables   Parameter description (if any) ...
+     * @param unknown $columns  Parameter description (if any) ...
+     * @param array   $cols     Parameter description (if any) ...
+     * 
+     * @return void     
+     * @access protected
+     */
+    protected function configureElementGrid(
+        $eName,
+        $colNames,
+        $tables,
+        $columns,
+        $cols
+    ) {
+        $e =& $this->getElement($eName);
+        $e->setColumnNames($colNames);
+        foreach ($cols as $i) {
+            unset($rec);
+
+            $rec[] =& $this->createElement(
+                'static',
+                null,
+                null,
+                $i
+            );
+
+            $rec[] =& $this->createElement(
+                'static',
+                null,
+                null,
+                ' > '
+            );
+            
+            $select =& $this->createElement(
+                'hierSelect',
+                $i
+            );
+            $select->setOptions(
+                array(
+                    $tables, 
+                    $columns,
+                )
+            );
+            $rec[] = $select;
+
+            $rec[] =& $this->createElement(
+                'text',
+                "{$i}_group"
+            );
+
+            $e->addRow(&$rec, '&nbsp;');
+        }
+    }
+
+    //  }}}
+    //  {{{ getAvailableTables()
+
+
+    /**
+     * Short description for function
+     * 
+     * Long description (if any) ...
+     * 
+     * @param PDO $dbh Databse handler
+     * 
+     * @return array     $tables
+     * @access protected
+     */
+    protected function getAvailableTables(PDO $dbh)
+    {
+        try {
+            $sql = "
+                 SELECT table_name
+                   FROM information_schema.tables
+                  WHERE table_type = 'BASE TABLE'
+                    AND table_schema NOT IN ('pg_catalog',
+                    'information_schema')
+                  ORDER BY table_name";
+
+            $tables = array();
+            foreach ($dbh->query($sql) as $i) {
+                $tables[$i['table_name']] = $i['table_name'];
+            }
+            return $tables;
+        } catch (PDOException $e) {
+            return Toolkit_Common::handleError($e);
+        }
+    }
+
+    //  }}}
+    //  {{{ getTableColumns()
+
+
+    /**
+     * Short description for function
+     * 
+     * Long description (if any) ...
+     * 
+     * @param PDO    $dbh   Database handler
+     * @param string $table Parameter description (if any) ...
+     * 
+     * @return array $cols
+     * @access protected
+     */
+    protected function getTableColumns(PDO $dbh, $table)
+    {
+        try {
+            $sql = "
+                 SELECT column_name
+                   FROM information_schema.columns
+                  WHERE table_name = '$table'
+                  ORDER BY column_name";
+
+            $cols = array('' => '-- Select --');
+            foreach ($dbh->query($sql) as $i) {
+                $cols[$i['column_name']] = $i['column_name'];
+            }
+            return $cols;
+        } catch (PDOException $e) {
+            return Toolkit_Common::handleError($e);
+        }
+    }
+
+    //  }}}
+}
+?>
diff --git a/Toolkit/Members/Import/DBPage.php b/Toolkit/Members/Import/DBPage.php
new file mode 100644 (file)
index 0000000..c8f692a
--- /dev/null
@@ -0,0 +1,241 @@
+<?php
+
+/**
+ * Short description for file
+ * 
+ * Long description (if any) ...
+ * 
+ * PHP version 5
+ * 
+ * The license text...
+ * 
+ * @category  Members
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @version   CVS: $Id: DBPage.php,v 1.1 2009/10/13 13:23:00 jamie Exp $
+ * @link      http://demo.gaslightmedia.com
+ * @see       References to other sections (if any)...
+ */
+
+
+/**
+ * Short description for class
+ * 
+ * Long description (if any) ...
+ * 
+ * @category  Members
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @version   Release: @package_version@
+ * @link      http://demo.gaslightmedia.com
+ * @see       References to other sections (if any)...
+ */
+class Toolkit_Members_Import_DBPage extends Toolkit_Members_Import_Page
+{
+    //  {{{ buildForm()
+
+
+    /**
+     * Short description for function
+     * 
+     * Long description (if any) ...
+     * 
+     * @return void  
+     * @access public
+     */
+    public function buildForm()
+    {
+        parent::buildForm();
+
+        $this->configureElements();
+        $this->configureDefaults();
+        $this->configureFilters();
+
+        $this->setDefaultAction('next');
+    }
+
+    //  }}}
+    //  {{{ getAvailableDatabases()
+
+
+    /**
+     * Short description for function
+     * 
+     * Long description (if any) ...
+     * 
+     * @return array     Return description (if any) ...
+     * @access protected
+     */
+    protected function getAvailableDatabases()
+    {
+        $host = $this->controller->exportValue('serverPage', 'host');
+        try {
+            $sql = '
+                SELECT pgd1.datname AS "name"
+                  FROM pg_database pgd1, pg_user pgu
+                 WHERE pgd1.datdba = pgu.usesysid
+                 UNION 
+                SELECT pgd2.datname AS "name"
+                  FROM pg_database pgd2
+                 WHERE pgd2.datdba NOT IN (
+                        SELECT usesysid
+                  FROM pg_user)
+                 ORDER BY "name"';
+
+            $dbs = array('' => '-- Select --');
+            foreach ($this->dbh->query($sql) as $db) {
+                $dbs[$db['name']] = $db['name'];
+            }
+            return $dbs;
+        } catch (PDOException $e) {
+            return Toolkit_Common::handleError($e);
+        }
+    }
+
+    //  }}}
+    //  {{{ configureElements()
+
+
+    /**
+     * Short description for function
+     * 
+     * Long description (if any) ...
+     * 
+     * @return void     
+     * @access protected
+     */
+    protected function configureElements()
+    {
+        $dbs = $this->getAvailableDatabases();
+        $e = array();
+
+        $existingRecs = array();
+        $existingRecs[] = array(
+            'type' => 'radio',
+            'req' => false,
+            'name' => 'duplicates',
+            'opts' => 'Delete',
+            'att' => 'delete'
+        );
+        $existingRecs[] = array(
+            'type' => 'radio',
+            'req' => false,
+            'name' => 'duplicates',
+            'opts' => 'Keep',
+            'att' => 'keep'
+        );
+
+        $prevNext = array();
+        $prevNext[] = array(
+            'type' => 'submit',
+            'req' => false,
+            'name' => $this->getButtonName('back'),
+            'display' => '<< Previous Step'
+        );
+        $prevNext[] = array(
+            'type' => 'submit',
+            'req' => false,
+            'name' => $this->getButtonName('next'),
+            'display' => 'Next Step >>'
+        );
+
+        $e[] = array(
+            'type' => 'header',
+            'req' => false,
+            'name' => 'header_rmv',
+            'display' => 'Select database to import to',
+        );
+        $e[] = array(
+            'type' => 'select',
+            'req' => true,
+            'name' => 'database',
+            'display' => 'Database',
+            'opts' => $dbs
+        );
+        $e[] = array(
+            'type' => 'header',
+            'req' => false,
+            'name' => 'configure_rmv',
+            'display' => 'Import Configuration',
+        );
+        $e[] = array(
+            'type' => 'static',
+            'req' => false,
+            'name' => 'instructions_rmv',
+            'display' => null,
+            'opts' => 'Existing records will be dictated by email address'
+        );
+        $e[] = array(
+            'type' => 'group',
+            'req' => false,
+            'name' => 'existing',
+            'group' => $existingRecs,
+            'label' => 'Remove prior records from database?',
+            'seperator' => '&nbsp;',
+            'appendName' => false
+        );
+        $e[] = array(
+            'type' => 'group',
+            'req' => false,
+            'name' => 'control_buttons',
+            'group' => $prevNext,
+            'label' => '',
+            'seperator' => '&nbsp;',
+            'appendName' => false
+        );
+        
+        $this->setupElements($e);
+    }
+
+    //  }}}
+    //  {{{ configureFilters()
+
+
+    /**
+     * Short description for function
+     * 
+     * Long description (if any) ...
+     * 
+     * @return void     
+     * @access protected
+     */
+    protected function configureFilters()
+    {
+        $f = array();
+
+        $f[] = array(
+            'element' => '__ALL__',
+            'filter' => 'trim',
+        );
+
+        $this->setupFilters($f);
+    }
+
+    //  }}}
+    //  {{{ configureDefaults()
+
+
+    /**
+     * Short description for function
+     * 
+     * Long description (if any) ...
+     * 
+     * @return void     
+     * @access protected
+     */
+    protected function configureDefaults()
+    {
+        $d = array(
+            'duplicates' => 'keep'
+        );
+
+        $this->setupDefaults($d);
+    }
+
+    //  }}}
+}
+?>
diff --git a/Toolkit/Members/Import/FieldsPage.php b/Toolkit/Members/Import/FieldsPage.php
new file mode 100644 (file)
index 0000000..7e3aaeb
--- /dev/null
@@ -0,0 +1,192 @@
+<?php
+
+/**
+ * Short description for file
+ * 
+ * Long description (if any) ...
+ * 
+ * PHP version 5
+ * 
+ * The license text...
+ * 
+ * @category  Members
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @version   CVS: $Id: FieldsPage.php,v 1.1 2009/10/13 13:23:00 jamie Exp $
+ * @link      http://demo.gaslightmedia.com
+ * @see       References to other sections (if any)...
+ */
+
+/**
+ * Description for require_once
+ */
+require_once 'HTML/QuickForm/advmultiselect.php';
+
+/**
+ * Short description for class
+ * 
+ * Long description (if any) ...
+ * 
+ * @category  Members
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @version   Release: @package_version@
+ * @link      http://demo.gaslightmedia.com
+ * @see       References to other sections (if any)...
+ */
+class Toolkit_Members_Import_FieldsPage extends Toolkit_Members_Import_Page
+{
+    //  {{{ buildForm()
+
+
+    /**
+     * Short description for function
+     * 
+     * Long description (if any) ...
+     * 
+     * @return void  
+     * @access public
+     */
+    public function buildForm()
+    {
+        parent::buildForm();
+
+        $this->configureElements();
+        $this->configureFilters();
+
+        $this->setDefaultAction('next');
+    }
+
+    //  }}}
+    //  {{{ configureElements()
+
+
+    /**
+     * Short description for function
+     * 
+     * Long description (if any) ...
+     * 
+     * @return void     
+     * @access protected
+     */
+    protected function configureElements()
+    {
+        $fields = $this->_getCSVFields();
+        $e = array();
+
+        $prevNext = array();
+        $prevNext[] = array(
+            'type' => 'submit',
+            'req' => false,
+            'name' => $this->getButtonName('back'),
+            'display' => '<< Previous Step'
+        );
+        $prevNext[] = array(
+            'type' => 'submit',
+            'req' => false,
+            'name' => $this->getButtonName('next'),
+            'display' => 'Next Step >>'
+        );
+
+        $e[] = array(
+            'type' => 'header',
+            'req' => false,
+            'name' => 'header_rmv',
+            'display' => 'Select fields to import',
+        );
+        $e[] = array(
+            'type' => 'static',
+            'req' => false,
+            'name' => 'info_rmv',
+            'display' => null,
+            'opts' => 'Select the field(s) you want to import from the file.<br>
+                       To select more than one field, hold down the CTRL key
+                       while selecting items.'
+        );
+        $e[] = array(
+            'type' => 'advmultiselect',
+            'req' => false,
+            'name' => 'columns',
+            'display' => 'Import Fields',
+            'labels' => array('Fields', 'Available', 'Selected'),
+            'opts' => $fields,
+            'att' => array(
+                'multiple' => 'multiple',
+                'size' => 40,
+                'style' => 'width:  200px;'
+            )
+        );
+        $e[] = array(
+            'type' => 'group',
+            'req' => false,
+            'name' => 'control_buttons',
+            'group' => $prevNext,
+            'label' => '',
+            'seperator' => '&nbsp;',
+            'appendName' => false
+        );
+        
+        $this->setupElements($e);
+    }
+
+    //  }}}
+    //  {{{ configureFilters()
+
+
+    /**
+     * Short description for function
+     * 
+     * Long description (if any) ...
+     * 
+     * @return void     
+     * @access protected
+     */
+    protected function configureFilters()
+    {
+        $f = array();
+
+        $f[] = array(
+            'element' => '__ALL__',
+            'filter' => 'trim',
+        );
+
+        $this->setupFilters($f);
+    }
+
+    //  }}}
+    //  {{{ _getCSVFields()
+
+
+    /**
+     * Short description for function
+     * 
+     * Long description (if any) ...
+     * 
+     * @return array   Return description (if any) ...
+     * @access private
+     */
+    private function _getCSVFields()
+    {
+        $data =& $this->controller->container();
+
+        $row = 0;
+        $handle = fopen("./uploads/{$data['_upload']}", 'r');
+        $fields = fgetcsv($handle, 1000, ',');
+        fclose($handle);
+
+        $cleanFields = array();
+        foreach ($fields as $i) {
+            $j = $this->clean($i);
+            $cleanFields[$j] = $j;
+        }
+
+        return $cleanFields;
+    }
+
+    //  }}}
+}
+?>
diff --git a/Toolkit/Members/Import/FilePage.php b/Toolkit/Members/Import/FilePage.php
new file mode 100644 (file)
index 0000000..6121a1b
--- /dev/null
@@ -0,0 +1,173 @@
+<?php
+
+/**
+ * Short description for file
+ * 
+ * Long description (if any) ...
+ * 
+ * PHP version 5
+ * 
+ * The license text...
+ * 
+ * @category  Members
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @version   CVS: $Id: FilePage.php,v 1.1 2009/10/13 13:23:00 jamie Exp $
+ * @link      http://demo.gaslightmedia.com
+ * @see       References to other sections (if any)...
+ */
+
+
+/**
+ * Short description for class
+ * 
+ * Long description (if any) ...
+ * 
+ * @category  Members
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @version   Release: @package_version@
+ * @link      http://demo.gaslightmedia.com
+ * @see       References to other sections (if any)...
+ */
+class Toolkit_Members_Import_FilePage extends Toolkit_Members_Import_Page
+{
+    //  {{{ buildForm()
+
+
+    /**
+     * Short description for function
+     * 
+     * Long description (if any) ...
+     * 
+     * @return void  
+     * @access public
+     */
+    public function buildForm()
+    {
+        parent::buildForm();
+
+        $this->configureElements();
+        $this->configureFilters();
+        $this->configureRules();
+
+        $this->setDefaultAction('upload');
+    }
+
+    //  }}}
+    //  {{{ configureElements()
+
+
+    /**
+     * Short description for function
+     * 
+     * Long description (if any) ...
+     * 
+     * @return void     
+     * @access protected
+     */
+    protected function configureElements()
+    {
+        $e = array();
+
+        $prevNext = array();
+        $prevNext[] = array(
+            'type' => 'submit',
+            'req' => false,
+            'name' => $this->getButtonName('back'),
+            'display' => '<< Previous Step'
+        );
+        $prevNext[] = array(
+            'type' => 'submit',
+            'req' => false,
+            'name' => $this->getButtonName('upload'),
+            'display' => 'Next Step >>'
+        );
+
+        $e[] = array(
+            'type' => 'header',
+            'req' => false,
+            'name' => 'header_rmv',
+            'display' => 'Select CSV file to import',
+        );
+        $e[] = array(
+            'type' => 'file',
+            'req' => true,
+            'name' => 'file',
+            'display' => 'CSV Import File',
+        );
+        $e[] = array(
+            'type' => 'group',
+            'req' => false,
+            'name' => 'control_buttons',
+            'group' => $prevNext,
+            'label' => '',
+            'seperator' => '&nbsp;',
+            'appendName' => false
+        );
+        
+        $this->setupElements($e);
+    }
+
+    //  }}}
+    //  {{{ configureFilters()
+
+
+    /**
+     * Short description for function
+     * 
+     * Long description (if any) ...
+     * 
+     * @return void     
+     * @access protected
+     */
+    protected function configureFilters()
+    {
+        $f = array();
+
+        $f[] = array(
+            'element' => '__ALL__',
+            'filter' => 'trim',
+        );
+
+        $this->setupFilters($f);
+    }
+
+    //  }}}
+    //  {{{ configureRules()
+
+
+    /**
+     * Short description for function
+     * 
+     * Long description (if any) ...
+     * 
+     * @return void     
+     * @access protected
+     */
+    protected function configureRules()
+    {
+        $r = array();
+        $r[] = array(
+            'element' => 'file',
+            'message' => 'ERROR: Missing File!',
+            'type' => 'uploadedfile',
+            'format' => null
+        );
+        $r[] = array(
+            'element' => 'file',
+            'message' => 'ERROR: Must be *.csv!',
+            'type' => 'filename',
+            'format' => '/\.(csv|xls)$/i'
+        );
+            
+        $this->setupRules($r);
+    }
+
+    //  }}}
+}
+?>
diff --git a/Toolkit/Members/Import/ImportPage.php b/Toolkit/Members/Import/ImportPage.php
new file mode 100644 (file)
index 0000000..5d3fee1
--- /dev/null
@@ -0,0 +1,59 @@
+<?php
+
+/**
+ * Short description for file
+ * 
+ * Long description (if any) ...
+ * 
+ * PHP version 5
+ * 
+ * The license text...
+ * 
+ * @category  Members
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @version   CVS: $Id: ImportPage.php,v 1.1 2009/10/13 13:23:00 jamie Exp $
+ * @link      http://demo.gaslightmedia.com
+ * @see       References to other sections (if any)...
+ */
+
+
+/**
+ * Short description for class
+ * 
+ * Long description (if any) ...
+ * 
+ * @category  Members
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @version   Release: @package_version@
+ * @link      http://demo.gaslightmedia.com
+ * @see       References to other sections (if any)...
+ */
+class Toolkit_Members_Import_ImportPage extends Toolkit_Members_Import_OpPage
+{
+    //  {{{ buildForm()
+
+
+    /**
+     * Short description for function
+     * 
+     * Long description (if any) ...
+     * 
+     * @return void  
+     * @access public
+     */
+    public function buildForm()
+    {
+        parent::buildForm();
+
+        $this->insertData($testRun = false);
+    }
+
+    //  }}}
+}
+?>
diff --git a/Toolkit/Members/Import/OpPage.php b/Toolkit/Members/Import/OpPage.php
new file mode 100644 (file)
index 0000000..2e3eaf0
--- /dev/null
@@ -0,0 +1,258 @@
+<?php
+
+/**
+ * Short description for file
+ * 
+ * Long description (if any) ...
+ * 
+ * PHP version 5
+ * 
+ * The license text...
+ * 
+ * @category  Members
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @version   CVS: $Id: OpPage.php,v 1.2 2009/10/14 11:04:40 jamie Exp $
+ * @link      http://demo.gaslightmedia.com
+ * @see       References to other sections (if any)...
+ */
+
+
+/**
+ * Short description for class
+ * 
+ * Long description (if any) ...
+ * 
+ * @category  Members
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @version   Release: @package_version@
+ * @link      http://demo.gaslightmedia.com
+ * @see       References to other sections (if any)...
+ */
+class Toolkit_Members_Import_OpPage extends Toolkit_Members_Import_Page
+{
+    //  {{{ properties
+
+
+    /**
+     * Description for protected
+     * @var    array    
+     * @access protected
+     */
+    protected $data;
+
+    /**
+     * Description for protected
+     * @var    unknown  
+     * @access protected
+     */
+    protected $host;
+
+    /**
+     * Description for protected
+     * @var    unknown  
+     * @access protected
+     */
+    protected $dbName;
+
+    /**
+     * Description for protected
+     * @var    string   
+     * @access protected
+     */
+    protected $existing;
+
+    /**
+     * Description for protected
+     * @var    array    
+     * @access protected
+     */
+    protected $converts;
+
+    /**
+     * Description for protected
+     * @var    unknown  
+     * @access protected
+     */
+    protected $memberName;
+
+    //  }}}
+    //  {{{ buildForm()
+
+
+    /**
+     * Short description for function
+     * 
+     * Long description (if any) ...
+     * 
+     * @return void  
+     * @access public
+     */
+    public function buildForm()
+    {
+        parent::buildForm();
+
+        $this->data     =& $this->controller->container();
+        $this->host     = $this->controller->exportValue('serverPage', 'host');
+        $this->dbName   = $this->controller->exportValue('dbPage', 'database');
+        $this->existing = $this->controller->exportValue('dbPage', 'duplicates');
+        $conversions    = $this->controller->exportValue('conversionPage', 'conversions');
+
+        foreach ($conversions as $i => $j) {
+            //  make sure we aren't dealing w/ the empty option
+            if (!empty($i)) {
+                //  make sure we're dealing w/ a conversion and not a group
+                //  assignment
+                if (substr_compare($i, '_group', -6) != 0) {
+                    $this->converts[$j[0][0]][$j[1][0]] = array(
+                        'field' => $i,
+                        'group' => $conversions["{$i}_group"]
+                    );
+                }
+            }
+        }
+
+        $this->setDefaultAction('next');
+    }
+
+    //  }}}
+
+    //  {{{ insertData()
+
+
+    /**
+     * Short description for function
+     * 
+     * Long description (if any) ...
+     * 
+     * @param  boolean   $testRun Parameter description (if any) ...
+     * @return unknown   Return description (if any) ...
+     * @access protected
+     */
+    protected function insertData($testRun = true)
+    {
+        try {
+            $dsn = "pgsql:host={$this->host} user=postgres dbname={$this->dbName}";
+            $dbh = new PDO($dsn);
+            $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
+
+            $dbh->beginTransaction();
+
+            $dbh->query("SET CLIENT_ENCODING TO 'WIN'");
+            
+            $csvFile = $this->getCSVFile("./uploads/{$this->data['_upload']}");
+
+            foreach ($this->converts as $table => $fields) {
+                if ($this->existing == 'delete') {
+                    $dbh->query("DELETE FROM $table");
+                }
+
+                $sql = $this->getTableQuery($table, $fields);
+                $stmt = $dbh->prepare($sql);
+
+                $success = $failed = 0;
+                //  Loop through all rows of the csv file
+                foreach ($csvFile as $csvRow) {
+                    //  loop through all conversion fields in the table
+                    foreach ($fields as $field => $data) {
+                        $bind = ":$field";
+                        //  if the csv field value contians data
+                        $value = trim($csvRow[$data['field']]);
+                        if (!empty($value)) {
+                            $stmt->bindValue($bind, $value);
+                        } else {
+                            $stmt->bindValue($bind, null, PDO::PARAM_NULL);
+                        }
+                    }
+                    $stmt->execute() ? ++$success : ++$failed;
+                }
+            }
+
+            if ($testRun) {
+                $dbh->rollBack();
+                echo '<div id="failReport">';
+                echo "There will be $success inserts and $failed failed inserts";
+                echo '</div>';
+            } else {
+                $dbh->commit();
+                echo "You succussfully inserted $success new records";
+            }
+        } catch (PDOException $e) {
+            $dbh->rollBack();
+            return Toolkit_Common::handleError($e);
+        }
+    }
+
+    //  }}}
+    //  {{{ getCSVFile()
+
+
+    /**
+     * Short description for function
+     * 
+     * Long description (if any) ...
+     * 
+     * @param  unknown   $file Parameter description (if any) ...
+     * @return array     Return description (if any) ...
+     * @access protected
+     */
+    protected function getCSVFile($file)
+    {
+        //  Read in the CSV file that was uploaded.
+        $row = 0;
+        $handle = fopen($file, 'r');
+        while (false !== ($tiers = fgetcsv($handle, 1000, ','))) {
+            $num = count($tiers);
+            if ($row == 0) {
+                $columns = array();
+                for ($c = 0; $c < $num; ++$c) {
+                    $columns[$c] = $tiers[$c];
+                    $tiers[$c] = array();
+                }
+            } else {
+                for ($c = 0; $c < $num; ++$c) {
+                    $csvFile[$row][$columns[$c]] = $tiers[$c];
+                }
+            }
+            ++$row;
+        }
+
+        return $csvFile;
+    }
+
+    //  }}}
+    //  {{{ getTableQuery()
+
+
+    /**
+     * Short description for function
+     * 
+     * Long description (if any) ...
+     * 
+     * @param  unknown   $table  Parameter description (if any) ...
+     * @param  array     $fields Parameter description (if any) ...
+     * @return string    Return description (if any) ...
+     * @access protected
+     */
+    protected function getTableQuery($table, array $fields)
+    {
+        foreach ($fields as $field => $data) {
+            //  Get the table columns we need to map to
+            $cols[] = $field;
+        }
+
+        //  Create the sql insert query
+        $columns = implode(', ', $cols);
+        $binds   = ':' . implode(', :', $cols);
+
+        return "INSERT INTO $table ($columns) VALUES ($binds)";
+    }
+
+    //  }}}
+}
+?>
diff --git a/Toolkit/Members/Import/Page.php b/Toolkit/Members/Import/Page.php
new file mode 100644 (file)
index 0000000..e1e0afc
--- /dev/null
@@ -0,0 +1,375 @@
+<?php
+
+/**
+ * Short description for file
+ * 
+ * Long description (if any) ...
+ * 
+ * PHP version 5
+ * 
+ * The license text...
+ * 
+ * @category  Members
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @version   CVS: $Id: Page.php,v 1.1 2009/10/13 13:23:00 jamie Exp $
+ * @link      http://demo.gaslightmedia.com
+ * @see       References to other sections (if any)...
+ */
+
+
+/**
+ * Short description for class
+ * 
+ * Long description (if any) ...
+ * 
+ * @category  Members
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @version   Release: @package_version@
+ * @link      http://demo.gaslightmedia.com
+ * @see       References to other sections (if any)...
+ */
+abstract class Toolkit_Members_Import_Page extends HTML_QuickForm_Page
+{
+       //      {{{     __construct()
+
+       /**
+        * Class constructor
+        *
+     * @param PDO    $pdo         PHP Data Object
+     * @param string $formName    Form's name.
+     * @param string $method      (optional)Form's method defaults to 'POST'
+     * @param string $action      (optional)Form's action
+     * @param string $target      (optional)Form's target defaults to '_self'
+     * @param mixed  $attributes  (optional)Extra attributes for <form> tag
+     * @param bool   $trackSubmit (optional)Whether to track if the form was
+        *                                                        submitted by adding a special hidden field
+        * 
+        * @access public
+        */
+       public function __construct(
+        PDO $pdo,
+        $formName,
+        $method = 'post',
+        $action = '',
+        $target = '',
+        $attributes = null,
+        $trackSubmit = false
+    ) {
+               //      T_VARIABLE error when passing this server
+               //      var in on the constructors params.
+        $action = empty($action)
+                 ? MEDIA_BASE_URL . 'Toolkit/Members/Import/index.php'
+                 : $action;
+        $this->HTML_QuickForm(
+            $formName,
+            $method,
+            $action,
+            $target,
+            $attributes,
+            $trackSubmit
+        );
+
+               $this->dbh = $pdo;
+       }
+
+       //      }}}
+       //      {{{ setupElements()
+
+    /**
+     * Sets up all the elements to the form
+     * 
+        * Takes a multi-dimensional array of form elements and uses them
+        * to set up the form objects elements
+     * 
+     * @param  array     $elements Multi-Dimensional array of form elements.
+        *
+     * @return void     
+     * @access protected
+     */
+       protected function setupElements(array $elements)
+       {
+               $this->formElements = $elements;
+               foreach ($elements as $e) {
+                       switch ($e['type']) {
+                       case 'group' :
+                               if (is_array($e['group']) && !empty($e['group'])) {
+                                       //      Special rendering for grouped elements.
+                                       unset($field);
+                                       foreach ($e['group'] as $g) {
+                                               $field[] =& HTML_QuickForm::createElement(
+                            $g['type'],
+                            $g['name'],
+                            $g['display'],
+                            $g['opts'],
+                            $g['att'],
+                            $g['val']
+                        );
+                                       }
+                                       $source =& $this->addGroup(
+                        $field,
+                        $e['name'],
+                        $e['label'],
+                        $e['seperator'],
+                        $e['appendName']
+                    );
+                               }
+                               break;
+
+                       case 'elementGrid' :
+                               $source =& $this->addElement(
+                    $e['type'],
+                    $e['name'],
+                    $e['display'],
+                    $e['opts'],
+                    $e['att'],
+                    $e['val']
+                );
+                               break;
+
+                       default :
+                               //      Render all elements except groups
+                               try {
+                                       $source =& $this->addElement(
+                        $e['type'],
+                        $e['name'],
+                        $e['display'],
+                        $e['opts'],
+                        $e['att'],
+                        $e['val']
+                    );
+
+                                       if (PEAR::isError($source)) {
+                                               throw new Exception ('PEAR QuickForm Element Error');
+                                       }
+                               } catch (HTML_QuickForm_Error $e) {
+                                       Toolkit_Common::dieGracefully(null, $e);
+                               } catch (Exception $e) {
+                                       Toolkit_Common::handleError($e);
+                               }
+
+                               if ($e['type'] == 'advmultiselect') {
+                                       $source->setLabel($e['labels']);
+                               }
+                               if ($e['name'] == 'categories') {
+                                       $res = $source->loadArray($this->categories);
+                                       if (PEAR::isError($res)) {
+                                               Toolkit_Common::dieGracefully(null, $res);
+                                       }
+                               }
+                               if ($e['type'] == 'header') {
+                                       $this->formHeaders[$e['display']] = $e;
+                               }
+                               break;
+                       }
+               }
+       }
+
+       //      }}}
+       //      {{{ setupRules()
+
+       /**
+        * Apply rules to the form
+        *
+        * 100 & 1000 char length limitations are automatically assigned to
+        * text/textarea elements to help reduce load limitations on the server.
+        * -request per Chuck in a conference call on (5/22/2009 @ 12:15pm)
+        *
+        * Applies rules that are defined in child classes to the form elements
+        * group rules can be kind of tricky, since you can't apply a rule
+        * directly to an element inside of a rule you have to define
+        * the rule inside a nest of array's and then add a group rule.
+        * the array will contain all the elements inside the group you wish
+        * to apply rules to.
+        *
+        * You can assign as many rules as you would like to individual elements,
+        * and you aren't required to make the array associative, although it is
+        * easier to see whats going on.
+        *
+        * see: http://pear.activeventure.com/package/package.html.html-quickform.html-quickform.addgrouprule.html
+        * for another example.
+        * <code>
+        * //   Define the rules for each element in the group.
+        * $num_rule = array(
+     *   'ERROR: Must contain a valid decimal number!',
+     *   'numeric'
+     * );
+        * //   Collect together the rules for each element.
+        * $lat_rules = array('lat' => array($num_rule));
+        * $lon_rules = array('lon' => array($num_rule));
+        * $r[] = array(
+     *   'element'    => 'latitude',
+        *   'message'    => 'ERROR:',
+        *   'type'       => 'group',
+        *   'format'     => $lat_rules,
+        *   'validation' => $this->validationType,
+        *   'reset'      => false,
+        *   'force'      => false
+     * );
+        * </code>
+        *
+        * To make a group required but not require every element in the group
+        * you can use the addGroupRule function again
+        * for example:  say you have a group of checkboxes and you only only
+        * require 1 be checked.  a simple group rule such as the following
+        * will handle this.
+        * N.B. notice the extra "howMany" index.
+        * <code>
+        * $r[] = array(
+     *   'element'   => 'element_name',
+        *   'message'   => 'ERROR: Error to display!',
+        *   'type'      => 'required',
+        *   'format'    => null,
+        *   'howMany'   => 1,
+        *   'validation'=> $this->validationType,
+        *   'reset'     => true,
+        *   'force'     => false,
+     * );
+        * </code>
+        *
+        * @param array $rules Multi-Dimensional array of rules for form elements.
+        *
+        * @return void
+        * @access protected
+        */
+       protected function setupRules(array $rules = null)
+       {
+               if (is_array($this->formElements)) {
+                       foreach ($this->formElements as $e) {
+                               if ($this->validationType == 'client') {
+                                       $label = $e['display'];
+                               }
+                               if ($e['req']) {
+                                       if ($e['type'] == 'group') {
+                                               foreach ($e['group'] as $ge) {
+                                                       if ($ge['req']) {
+                                                               $rule[$ge['name']][] = array(
+                                    'ERROR: You must complete this field!',
+                                    'required',
+                                    null,
+                                    $this->validationType
+                                );
+                                                       }
+                                               }
+                                               $this->addGroupRule($e['name'], $rule);
+                                               unset($rule);
+                                       } elseif ($e['type'] == 'date') {
+                        if (!empty($e['error'])) {
+                            //  Custom error message for the date element.
+                            $error = $e['error'];
+                        } else {
+                            //  Default error message for the date element.
+                            $error = 'ERROR: You must enter a date!';
+                        }
+                                               $this->addGroupRule(
+                            $e['name'],
+                            $error,
+                            'required',
+                            3,
+                            $this->validationType
+                        );
+                                               if ($this->autoValidateDateElements) {
+                                                       $this->addRule(
+                                $e['name'],
+                                'ERROR: Date is invalid!',
+                                'callback',
+                                array(&$this, 'checkDateInput')
+                            );
+                                               }
+                                       } else {
+                                               $this->addRule(
+                            $e['name'],
+                            "$label ERROR: You must complete this field!",
+                            'required',
+                            null,
+                            $this->validationType
+                        );
+                                       }
+                               }
+                       }
+               }
+               if (is_array($rules)) {
+                       foreach ($rules as $r) {
+                               if (!is_array($r['element'])) {
+                                       $group = ($this->getElementType($r['element']) == 'group');
+                               }
+                               if ($group) {
+                                       $this->addGroupRule(
+                        $r['element'],
+                        $r['message'],
+                        $r['type'],
+                        $r['format'],
+                        $r['howMany'],
+                        $r['validation'],
+                        $r['reset']
+                    );
+                               }
+                       }
+               }
+       }
+
+       //      }}}
+       //      {{{     setupFilters()
+
+    /**
+     * Sets any filters needed for the form elements when submitting
+     * 
+     * @param  array     $filters Element filters.
+        *
+        * @return void
+     * @access protected
+     */
+       protected function setupFilters($filters)
+       {
+               foreach ($filters as $f) {
+                       $res = $this->applyFilter($f['element'], $f['filter']);
+
+            if (PEAR::isError($res)) {
+                Toolkit_Common::handleError($res);
+            }
+               }
+       }
+
+       //      }}}
+       //      {{{     setupDefaults()
+
+    /**
+     * Sets the form default values
+     * 
+     * @param  array     $defaults Associative array of form default values.
+        *
+     * @return void     
+     * @access protected
+     */
+       protected function setupDefaults($defaults)
+       {
+               $this->setDefaults($defaults);
+       }
+
+       //      }}}
+    //  {{{ clean()
+
+
+    /**
+     * Short description for function
+     * 
+     * Long description (if any) ...
+     * 
+     * @param  unknown   $node Parameter description (if any) ...
+     * @return unknown   Return description (if any) ...
+     * @access protected
+     */
+    protected function clean($node)
+    {
+        $cleanNode = str_replace(' ', '_', $node);
+        return $cleanNode;
+    }
+
+    //  }}}
+}
+?>
diff --git a/Toolkit/Members/Import/ServerPage.php b/Toolkit/Members/Import/ServerPage.php
new file mode 100644 (file)
index 0000000..2e847e4
--- /dev/null
@@ -0,0 +1,133 @@
+<?php
+
+/**
+ * Short description for file
+ * 
+ * Long description (if any) ...
+ * 
+ * PHP version 5
+ * 
+ * The license text...
+ * 
+ * @category  Members
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @version   CVS: $Id: ServerPage.php,v 1.1 2009/10/13 13:23:00 jamie Exp $
+ * @link      http://demo.gaslightmedia.com
+ * @see       References to other sections (if any)...
+ */
+
+
+/**
+ * Short description for class
+ * 
+ * Long description (if any) ...
+ * 
+ * @category  Members
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @version   Release: @package_version@
+ * @link      http://demo.gaslightmedia.com
+ * @see       References to other sections (if any)...
+ */
+class Toolkit_Members_Import_ServerPage extends Toolkit_Members_Import_Page
+{
+    //  {{{ buildForm()
+
+
+    /**
+     * Short description for function
+     * 
+     * Long description (if any) ...
+     * 
+     * @return void  
+     * @access public
+     */
+    public function buildForm()
+    {
+        parent::buildForm();
+
+        $this->configureElements();
+        $this->configureFilters();
+
+        $this->setDefaultAction('next');
+    }
+
+    //  }}}
+    //  {{{ configureElements()
+
+
+    /**
+     * Short description for function
+     * 
+     * Long description (if any) ...
+     * 
+     * @return void     
+     * @access protected
+     */
+    protected function configureElements()
+    {
+        $hosts = array(
+            '' => '-- Select --',
+            'localhost' => 'Localhost',
+            'devsys' => 'Development Server (devsys)',
+            'devsys2' => 'Development Server (devsys2)',
+            'ds1' => 'Live Server (ds1)',
+            'ds3' => 'Live Server (ds3)',
+        );
+
+        $e = array();
+        $e[] = array(
+            'type' => 'header',
+            'req' => false,
+            'name' => 'header_rmv',
+            'display' => 'Upload destination',
+        );
+        $e[] = array(
+            'type' => 'select',
+            'req' => true,
+            'name' => 'host',
+            'display' => 'Select Host',
+            'opts' => $hosts
+        );
+        $e[] = array(
+            'type' => 'submit',
+            'req' => false,
+            'name' => $this->getButtonName('next'),
+            'display' => 'Next Step >>'
+        );
+        
+        $this->setupElements($e);
+    }
+
+    //  }}}
+    //  {{{ configureFilters()
+
+
+    /**
+     * Short description for function
+     * 
+     * Long description (if any) ...
+     * 
+     * @return void     
+     * @access protected
+     */
+    protected function configureFilters()
+    {
+        $f = array();
+
+        $f[] = array(
+            'element' => '__ALL__',
+            'filter' => 'trim',
+        );
+
+        $this->setupFilters($f);
+    }
+
+    //  }}}
+}
+?>
diff --git a/Toolkit/Members/Import/index.php b/Toolkit/Members/Import/index.php
new file mode 100644 (file)
index 0000000..c58ce1e
--- /dev/null
@@ -0,0 +1,97 @@
+<?php
+/**
+ * index.php
+ *
+ * PHP version 5
+ *
+ * @category  Toolkit
+ * @package   Members_Import
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2009 Gaslight Media
+ * @license   Gaslight Media
+ * @version   CVS: $Id$
+ * @link      <>
+ */
+
+require_once '../../../setup.phtml';
+
+HTTP_Session2::useCookies(false);
+HTTP_Session2::start();
+
+$page = new stdClass();
+$page->styles = array(
+    MEDIA_BASE_URL . 'styles.css',
+    MEDIA_BASE_URL . 'forms.css'
+);
+$page->scripts = array(
+    MEDIA_APP_BASE_URL . 'glmPEAR/HTML/QuickForm/qfamsHandler.js'
+);
+
+ob_start();
+$wizard = new HTML_QuickForm_Controller('Wizard');
+$wizard->addPage(
+       new Toolkit_Members_Import_ServerPage(
+               Toolkit_Database::getInstance(),
+               'serverPage'
+       )
+);
+$wizard->addPage(
+       new Toolkit_Members_Import_DBPage(
+               Toolkit_Database::getInstance(),
+               'dbPage'
+       )
+);
+$wizard->addPage(
+       new Toolkit_Members_Import_FilePage(
+               Toolkit_Database::getInstance(),
+               'filePage'
+       )
+);
+$wizard->addPage(
+       new Toolkit_Members_Import_FieldsPage(
+               Toolkit_Database::getInstance(),
+               'fieldsPage'
+       )
+);
+$wizard->addPage(
+       new Toolkit_Members_Import_ConversionPage(
+               Toolkit_Database::getInstance(),
+               'conversionPage'
+       )
+);
+/*
+$wizard->addPage(
+       new Toolkit_Members_Import_ConfirmationPage(
+               Toolkit_Database::getInstance(),
+               'confirmationPage'
+       )
+);
+*/
+$wizard->addPage(
+       new Toolkit_Members_Import_AnalyzePage(
+               Toolkit_Database::getInstance(),
+               'analyzePage'
+       )
+);
+$wizard->addPage(
+       new Toolkit_Members_Import_ImportPage(
+               Toolkit_Database::getInstance(),
+               'importPage'
+       )
+);
+
+$wizard->addAction('upload', new Toolkit_Members_Import_ActionUpload());
+$wizard->addAction('display', new Toolkit_Members_Import_ActionDisplay());
+
+$wizard->run();
+$page->form = ob_get_contents();
+ob_end_clean();
+
+$options = $flexyOptions;
+$options['templateDir'] = dirname(__FILE__) . '/templates';
+$options['compileDir'] = dirname(__FILE__) . '/templates/compiled';
+
+$template = new HTML_Template_Flexy($options);
+$template->compile('template.html');
+$template->outputObject($page);
+?>
diff --git a/Toolkit/Members/Import/templates/template.html b/Toolkit/Members/Import/templates/template.html
new file mode 100644 (file)
index 0000000..1042e48
--- /dev/null
@@ -0,0 +1,28 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
+"http://www.w3.org/TR/html4/strict.dtd">
+<html>
+<head>
+<title>Member CSV File Import</title>
+<meta http-equiv="content-type" content="text/html;charset=utf-8">
+<meta http-equiv="imagetoolbar" content="no">
+<meta http-equiv="imagetoolbar" content="false">
+{foreach:styles,v}
+<link rel="stylesheet" type="text/css" href="{v}">
+{end:}
+{foreach:scripts,v}
+<script type="text/javascript" src="{v}"></script>
+{end:}
+<style type="text/css">
+#failReport {
+    border: 2px solid red;
+    background-color: #ff9999;
+    padding: 10px;
+    margin: 10px auto;
+    text-align: center;
+}
+</style>
+</head>
+<body>
+    {form:h}
+</body>
+</html>
diff --git a/Toolkit/Members/Leads/ExportFileForm.php b/Toolkit/Members/Leads/ExportFileForm.php
new file mode 100644 (file)
index 0000000..246c600
--- /dev/null
@@ -0,0 +1,491 @@
+<?php
+//     vim:set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker syntax=php:
+
+/**
+ * Admin search functionality for memberdb
+ *
+ * PHP version 5
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2009 Gaslight Media
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @version   CVS: $Id: ExportFileForm.php,v 1.3 2010/07/07 12:44:01 matrix Exp $
+ * @link      http://demo.gaslightmedia.com
+ */
+
+/**
+ * Form to search the members database
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2009 Gaslight Media
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @link      http://demo.gaslightmedia.com
+ */
+class Toolkit_Members_Leads_ExportFileForm
+    extends Toolkit_FormBuilder
+{
+    /**
+     * Description of $registeredRules
+     * 
+     * @var array
+     * @access protected
+     */
+       protected $registeredRules = array();
+       //      {{{ configureConstants()
+
+    /**
+     * Form constant definitions
+     *
+     * @return void
+     * @access public
+     */
+       public function configureConstants()
+       {
+               $c = array(
+                       'Action' => $_GET['Actoin'],
+                       'Option' => $_GET['Option'],
+                       'search' => 1,
+               );
+
+               $this->setupConstants($c);
+       }
+
+       //      }}}
+       //      {{{ configureDefaults()
+
+    /**
+     * Form default value definitions
+     *
+     * @return void
+     * @access public
+     */
+       public function configureDefaults()
+       {
+        $storedQuery = $this->getStoredQuery();
+        if ($storedQuery) {
+            $d = array(
+                'start_date' => $storedQuery['start_date'],
+                'end_date'   => $storedQuery['end_date'],
+                'discover'   => $storedQuery['discover'],
+                'interest'   => $storedQuery['interest']
+            );
+        } else {
+            $d = array(
+                'start_date' => date('m/d/Y'),
+                'end_date'   => date('m/d/Y')
+            );
+        }
+
+               $this->setupDefaults($d);
+       }
+
+       //      }}}
+       //      {{{ configureElements()
+
+    /**
+     * Form element definitions
+     *
+     * @return void
+     * @access public
+     */
+       public function configureElements()
+       {
+        $e = array();
+
+               //      All Grouped Elements are created here.
+               //$this->setDiscoveryGroups();
+               //$this->dGroups =& $this->getDiscoveryGroups();
+               $this->setInterestFields();
+               $this->interestsGroups =& $this->getInterestFields();
+
+               //      All Elements are created here.  This includes group element definitions.
+               $e[] = array(
+            'type' => 'hidden',
+            'req'  => false,
+            'name' => 'search'
+        );
+        $e[] = array(
+            'type'    => 'header',
+            'name'    => 'dateHeader',
+            'display' => 'Dates'
+        );
+        $e[] = array(
+            'type' => 'date',
+            'req' => true,
+            'name' => 'start_date',
+            'display' => 'Start Date',
+            'opts' => array(
+                'format'   => 'm / d / Y',
+                'minYear'  => 2010,//date('Y') - 5,
+                'maxYear'  => date('Y'),
+                'addEmptyOption' => true,
+                'emptyOptionValue' => '',
+                'emptyOptionText' => array(
+                    'm' => 'mm',
+                    'd' => 'dd',
+                    'Y' => 'yyyy'
+                ),
+            ),
+            'att' => array('id' =>'sdate'),
+        );
+        $e[] = array(
+            'type' => 'date',
+            'req' => true,
+            'name' => 'end_date',
+            'display' => 'End Date',
+            'opts' => array(
+                'format'   => 'm / d / Y',
+                'minYear'  => 2010,//date('Y') - 5,
+                'maxYear'  => date('Y'),
+                'addEmptyOption' => true,
+                'emptyOptionValue' => '',
+                'emptyOptionText' => array(
+                    'm' => 'mm',
+                    'd' => 'dd',
+                    'Y' => 'yyyy'
+                ),
+            ),
+            'att' => array('id' =>'edate'),
+        );
+        $e[] = array(
+            'type'    => 'header',
+            'name'    => 'interestHeader',
+            'display' => 'Interest'
+        );
+        if (is_array($this->interestsGroups)) {
+            foreach ($this->interestsGroups as $group => $gData) {
+                $this->myGroups[] = $gData;
+                $e[] = array(
+                    'type'       => 'group',
+                    'req'        => false,
+                    'name'       => 'interest['.$group.']',
+                    'group'         => $gData,
+                    'label'      => $group,
+                    'seperator'  => ' ',
+                    'appendName' => true
+                );
+            }
+        }
+        if (is_array($this->dGroups)) {
+            $e[] = array(
+                'type'    => 'header',
+                'name'    => 'howHearHeader',
+                'display' => 'How Did You Hear About Us?'
+            );
+            foreach ($this->dGroups as $i => $j) {
+                $this->myGroups[] = $j;
+                $e[] = array(
+                    'type'       => 'group',
+                    'req'        => false,
+                    'name'       => 'discover['.$i.']',
+                    'group'         => $j,
+                    'label'      => $i,
+                    'seperator'  => ' ',
+                    'appendName' => true
+                );
+            }
+        }
+               $e[] = array(
+            'type'    => 'submit',
+            'req'     => false,
+            'name'    => 'submit',
+            'display' => 'Export Contacts'
+        );
+
+               $this->setupElements($e);
+       }
+
+       //      }}}
+    //  {{{ configureForm()
+
+    /**
+     * Helper function to configure an entire form
+     *
+     * @return void
+     * @access public
+     */
+    public function configureForm()
+    {
+        $this->configureElements();
+        $this->configureConstants();
+        $this->configureDefaults();
+        $this->configureRules();
+    }
+
+    //  }}}
+       //      {{{ configureRules()
+
+    /**
+     * Form rule definitions
+     *
+     * @return void
+     * @access public
+     */
+       public function configureRules()
+       {
+               $r = array();
+
+               $this->setupRules($r);
+       }
+
+       //      }}}
+
+       //      {{{     getDiscoveryGroups()
+
+    /**
+     * Returns the field definitions of the contact db interest fields
+     *
+     * @return array     Group definitions for the interest fields
+     * @access protected
+     */
+       protected function getDiscoveryGroups()
+       {
+               if (is_array($this->discoveryGroups)) {
+            foreach ($this->discoveryGroups as $group => $data) {
+                foreach ($data as $k => $v) {
+                    $interests[$group][] = array('type' => 'checkbox',
+                                             'req' => false,
+                                             'name' => $k,
+                                             'opts' => $v);
+                }
+                       }
+               }
+
+               return $interests;
+       }
+
+       //      }}}
+       //      {{{     getInterestFields()
+
+    /**
+     * Returns the field definitions of the contact db interest fields
+     *
+     * @return array     Group definitions for the interest fields
+     * @access protected
+     */
+       protected function getInterestFields()
+       {
+               if (is_array($this->inquiries)) {
+            foreach ($this->inquiries as $group => $data) {
+                foreach ($data as $k => $v) {
+                    $interests[$group][] = array('type' => 'checkbox',
+                                             'req' => false,
+                                             'name' => $k,
+                                             'opts' => $v);
+                }
+                       }
+               }
+
+               return $interests;
+       }
+
+       //      }}}
+    // {{{ getStoredQuery()
+    
+    /**
+     * Get stored query
+     * 
+     * @return boolean|mixed
+     * @access public
+     * @throws PEAR_Error
+     */
+    function getStoredQuery()
+    {
+               $mid = $GLOBALS['memberAuth']->getAuthData('member_id');
+        try {
+            $sql = "
+            SELECT *
+              FROM member_leads
+             WHERE member_id = :mid";
+            $stmt = $this->dbh->prepare($sql);
+            $stmt->bindParam(":mid", $mid, PDO::PARAM_INT);
+            $stmt->execute();
+            $data = $stmt->fetch(PDO::FETCH_ASSOC);
+            if (!$data) {
+                // if none exists create one
+                $sql = "
+                INSERT INTO member_leads
+                (member_id)
+                VALUES
+                (:mid)";
+                $stmt2 = $this->dbh->prepare($sql);
+                $stmt2->bindParam(":mid", $mid, PDO::PARAM_INT);
+                $stmt2->execute();
+                return false;
+            } else {
+                return unserialize($data['query']);
+            }
+               } catch (PDOException $e) {
+                       Toolkit_Common::handleError($e);
+        }
+    }// }}}
+
+       //      {{{     setDiscoveryGroups()
+
+    /**
+     * Contact DB interests
+     *
+     * @return void
+     * @access protected
+     * @throws PEAR_Error
+     */
+       protected function setDiscoveryGroups()
+       {
+               try {
+                       $sql = "
+                SELECT cd.*, dg.name as group
+                  FROM contact_disc cd LEFT OUTER JOIN discovery_groups dg ON (cd.groupid = dg.id)
+                 ORDER BY groupid, pos";
+
+            $i = array();
+                       foreach ($this->dbh->query($sql) as $row) {
+                               $i[$row['group']][$row['id']] = $row['name'];
+                       }
+
+                       $this->discoveryGroups = $i;
+               } catch (PDOException $e) {
+                       Toolkit_Common::handleError($e);
+               }
+       }
+
+       //      }}}
+       //      {{{     setInterestFields()
+
+    /**
+     * Contact DB interests
+     *
+     * @return void
+     * @access protected
+     * @throws PEAR_Error
+     */
+       protected function setInterestFields()
+       {
+               try {
+            if (get_class($this) != 'Toolkit_Contacts_ContactUs') {
+                $where = 'inq_group.id != 6';
+            } else {
+                $where = 'inq_group.id = 6';
+            }
+                       $sql = "
+                SELECT contact_inq.*, inq_group.name as group
+                  FROM contact_inq LEFT OUTER JOIN inq_group ON (contact_inq.groupid = inq_group.id)
+                 WHERE $where
+                 ORDER BY groupid, pos";
+
+            $i = array();
+                       foreach ($this->dbh->query($sql) as $row) {
+                               $i[$row['group']][$row['id']] = $row['header'];
+                       }
+
+                       $this->inquiries = $i;
+               } catch (PDOException $e) {
+                       Toolkit_Common::handleError($e);
+               }
+       }
+
+       //      }}}
+
+       //      {{{     setupRenderers()
+    //  @codeCoverageIgnoreStart
+
+    /**
+     * Custom rendering templates for special fields on the form
+     *
+     * @return void
+     * @access protected
+     */
+       protected function setupRenderers()
+       {
+               parent::setupRenderers();
+               $renderer =& $this->defaultRenderer();
+               $required = '<!-- BEGIN required --><span class="req"> * </span><!-- END required -->';
+               $error    = '<!-- BEGIN error --><div class="req"> {error} </div><!-- END error -->';
+               $tpl = '<tr align="center"><td colspan="2">{element}</td></tr>';
+        if (is_array($this->interestsGroups)) {
+            foreach ($this->interestsGroups as $group => $gData) {
+                $renderer->setGroupTemplate('<br>{content}', 'interest['.$group.']');
+                $renderer->setGroupElementTemplate('{element}', 'interest['.$group.']');
+                       $renderer->setElementTemplate('<tr><td colspan="2">'.$required.'{label}'.$error.'{element}</td></tr>', 'interest['.$group.']');
+            }
+        }
+        if (is_array($this->dGroups)) {
+            foreach ($this->dGroups as $group => $gData) {
+                $renderer->setGroupTemplate('{content}', 'discover['.$group.']');
+                $renderer->setGroupElementTemplate('{element}', 'discover['.$group.']');
+                       $renderer->setElementTemplate('<tr><td colspan="2">'.$required.'<b>{label}</b>'.$error.'{element}</td></tr>', 'discover['.$group.']');
+            }
+        }
+               $renderer->setElementTemplate($tpl, 'submit');
+       }
+
+    //  @codeCoverageIgnoreEnd
+       //      }}}
+    // {{{ storeQuery()
+    
+    /**
+     * Description for storeQuery()
+     * 
+     * @return void
+     * @access public
+     * @throws PEAR_Error
+     */
+    function storeQuery()
+    {
+               $mid = $GLOBALS['memberAuth']->getAuthData('member_id');
+        try {
+            $query = serialize($_POST);
+            $sql = "
+            UPDATE member_leads
+               SET query = :query
+             WHERE member_id = :mid";
+            $stmt2 = $this->dbh->prepare($sql);
+            $stmt2->bindParam(":mid", $mid, PDO::PARAM_INT);
+            $stmt2->bindParam(":query", $query, PDO::PARAM_STR);
+            $stmt2->execute();
+               } catch (PDOException $e) {
+                       Toolkit_Common::handleError($e);
+        }
+    }// }}}
+       //      {{{     toHtml()
+
+       /**
+        * Call the rendering function to get the form in a string
+     * 
+     * @param PDO $dbh PDO
+        *
+        * @access public
+        * @return string $file The Form to be rendered or success msg.
+        */
+       public function toHtml(PDO $dbh)
+       {
+               $this->setupRenderers();
+        $GLOBALS['styleSheets'][] = MEDIA_APP_BASE_URL . 'libjs/Jscal/system.css';
+        $GLOBALS['bottomScripts'][] = MEDIA_APP_BASE_URL . 'libjs/Jscal/utils.js';
+        $GLOBALS['bottomScripts'][] = MEDIA_APP_BASE_URL . 'libjs/Jscal/calendar.js';
+        $GLOBALS['bottomScripts'][] = MEDIA_APP_BASE_URL . 'libjs/Jscal/calendar-en.js';
+        $GLOBALS['bottomScripts'][] = MEDIA_APP_BASE_URL . 'libjs/Jscal/calendar-setup.js';
+        $GLOBALS['bottomScripts'][] = MEDIA_BASE_URL . 'Toolkit/Members/Leads/libjs/visitorLeads.js';
+
+               if ($this->validate()) {
+                       $export = new Toolkit_Members_Leads_ExportLeads($dbh);
+                       $export->setQuery();
+            $this->storeQuery();
+                       $export->setDefaultSort(array('sort_field' => 'ASC'));
+                       $file = $export->exportAsFile($dbh);
+            if ($file) {
+                return $file;
+            } else {
+                return '<div id="form-warning-top">Nothing Matched your Search. Try again.</div>'
+                    . parent::toHTML();
+            }
+               }
+               return parent::toHTML();
+       }
+
+       //      }}}
+}
+?>
diff --git a/Toolkit/Members/Leads/ExportLeads.php b/Toolkit/Members/Leads/ExportLeads.php
new file mode 100644 (file)
index 0000000..32da058
--- /dev/null
@@ -0,0 +1,700 @@
+<?php
+//     vim:set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker syntax=php:
+
+/**
+ * ExportMembers.php
+ *
+ * PHP version 5
+ *
+ * The license text...
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2009 Gaslight Media
+ * @license   Gaslight Media
+ * @version   CVS: $Id: ExportLeads.php,v 1.7 2010/07/21 19:39:01 jamie Exp $
+ * @link      <>
+ */
+
+/**
+ * using our extension of the Pear Structures Datagrid set the
+ * renderer to CSV so we can send it out as a file.
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2009 Gaslight Media
+ * @license   Gaslight Media
+ * @version   Release: @package_version@
+ * @link      <>
+ */
+class Toolkit_Members_Leads_ExportLeads extends Toolkit_DataGridBuilder
+{
+       //      {{{ properties
+
+    /**
+     * Options to pass to DataGrid
+        *
+     * @var    array
+     * @access protected
+     */
+       protected $options;
+
+    /**
+     * Rendering options for DataGrid
+        *
+     * @var    array
+     * @access protected
+     */
+       protected $rendererOptions = array(
+               'buildFooter' => true,
+               'buildHeader' => true,
+               'delimiter' => ',',
+               'enclosure' => '"',
+               'encoding' => 'ISO-8859-1',
+               'filename' => false,
+               'fillWithEmpytRows' => false,
+               'lineBreak' => "\n",
+               'numberAlign' => true,
+               'saveToFile' => false,
+               'useQuotes' => true,
+       );
+
+    /**
+     * SQL query used to obtain the DataGrid
+        *
+     * @var    string
+     * @access protected
+     */
+       protected $sql;
+
+    /**
+     * How many records must exist in the Datagrid before the sort form shows up
+        *
+     * @var    integer
+     * @access protected
+     */
+       protected $sortableAfter = 10;
+
+    /**
+     * The HTML table id of the DataGrid
+        *
+     * @var    string
+     * @access protected
+     */
+       protected $tableId = 'dataGrid';
+
+    /**
+     * The HTML class name of the DataGrid
+        *
+     * @var    string
+     * @access protected
+     */
+       protected $tableClass = 'dataGrid';
+
+    /**
+     * The HTML id of the DataGrid sorting form (when present)
+        *
+     * @var    string
+     * @access protected
+     */
+       protected $sortFormId = 'gridSorter';
+
+       /**
+        * Message to display to users if no records were found
+        *
+        * @var         String
+        * @access      Protected
+        * @see         Toolkit_DataGridBuilder::setNoRecordMessage()
+        */
+       protected $noRecMessage = 'No Records';
+    /**
+     * Path to create the export file 
+     * 
+     * @var string
+     * @access protected
+     */
+    protected $csvDirPath = '';
+
+       //      }}}
+       //      {{{ configureColumns()
+
+       /**
+        * Configures the columns (fields) that will be used in our datagrid renderer.
+        *
+        * @return void
+        * @access public
+        */
+    protected function configureColumns()
+       {
+        //  get reference to [controlledCities] section of config file
+               $memberName = new Structures_DataGrid_Column(
+                   'Member Name',
+            'member_name'
+        );
+               $this->addColumn($memberName);
+
+               $active = new Structures_DataGrid_Column(
+                   'Active',
+            'active'
+        );
+               $this->addColumn($active);
+
+               $street = new Structures_DataGrid_Column(
+                   'Street Address',
+            'street'
+        );
+               $this->addColumn($street);
+
+        if ($ctrlCities) {
+                       $city = new Structures_DataGrid_Column(
+                               'City',
+                               'city_name'
+                       );
+                       $this->addColumn($city);
+        } else {
+                       $city = new Structures_DataGrid_Column(
+                               'City',
+                               'city'
+                       );
+                       $this->addColumn($city);
+               }
+
+               $state = new Structures_DataGrid_Column(
+                       'State',
+                       'state_name'
+               );
+               $this->addColumn($state);
+
+               $zip = new Structures_DataGrid_Column(
+                       'Zip',
+                       'zip'
+               );
+               $this->addColumn($zip);
+
+               $phone = new Structures_DataGrid_Column(
+                       'Phone',
+                       'phone'
+               );
+               $this->addColumn($phone);
+
+               $fax = new Structures_DataGrid_Column(
+                       'Fax',
+                       'fax'
+               );
+               $this->addColumn($fax);
+
+               $tollFree = new Structures_DataGrid_Column(
+                       'Toll Free Phone Number',
+                       'toll_free'
+               );
+               $this->addColumn($tollFree);
+
+               $url = new Structures_DataGrid_Column(
+                       'Website Address',
+                       'url'
+               );
+               $this->addColumn($url);
+
+               $primaryContactFname = new Structures_DataGrid_Column(
+                       'Primary Contact First Name',
+                       'primary_contact_fname'
+               );
+               $this->addColumn($primaryContactFname);
+
+               $primaryContactLname = new Structures_DataGrid_Column(
+                       'Primary Contact Last Name',
+                       'primary_contact_lname'
+               );
+               $this->addColumn($primaryContactLname);
+
+               $primaryContactEmail = new Structures_DataGrid_Column(
+                       'Primary Contact Email',
+                       'process_email'
+               );
+               $this->addColumn($primaryContactEmail);
+
+               $emailAddress = new Structures_DataGrid_Column(
+                       'Email on Website',
+                       'member_contact_email'
+               );
+               $this->addColumn($primaryContactEmail);
+
+        if ($ctrlCities) {
+                       $mailingCity = new Structures_DataGrid_Column(
+                               'Mailing City Address',
+                               'mailing_city_name'
+                       );
+                       $this->addColumn($mailingCity);
+        } else {
+                       $mailingCity = new Structures_DataGrid_Column(
+                               'Mailing City Address',
+                               'mailing_city'
+                       );
+                       $this->addColumn($mailingCity);
+               }
+
+               $mailingState = new Structures_DataGrid_Column(
+                       'Mailing State',
+                       'mailing_state_name'
+               );
+               $this->addColumn($mailingState);
+
+               $mailingZip = new Structures_DataGrid_Column(
+                       'Mailing Zip',
+                       'mailing_zip'
+               );
+               $this->addColumn($mailingZip);
+
+               $joinDate = new Structures_DataGrid_Column(
+                       'Join Date',
+                       'join_date'
+               );
+               $this->addColumn($joinDate);
+       }
+
+       //      }}}
+    // {{{ exportAsFile()
+
+    /**
+     * throws out headers to the browser so the file can be downloaded
+     * there's a case for IE browsers which may change in future depending on
+     * Microsoft Whim:(
+     *
+     * @param PDO $dbh PDO
+     * 
+     * @access public
+     * @return boolean
+     */
+    public function exportAsFile(PDO $dbh)
+    {
+        // if the file is other than csv then set delimeter
+        if ($_REQUEST['file_type']) {
+            switch ($_REQUEST['file_type']) {
+            case "pipe":
+                $delimiter = "|";
+                break;
+            case "tab":
+                $delimiter = "\t";
+                break;
+            default:
+                $delimiter = ",";
+                break;
+            }
+            $this->rendererOptions['delimiter'] = $delimiter;
+        }
+        $fileOut = $this->toCSV($dbh);
+        if ($fileOut) {
+            if (ini_get('zlib.output_compression')) {
+                 ini_set('zlib.output_compression', 'Off');
+            }
+            header("Content-Type: application/force-download\n");
+            /* Correction for the stupid MSIE thing */
+            if (strstr(getenv('HTTP_USER_AGENT'), 'MSIE')) {
+                header("Content-Disposition: inline; filename=\"Member-Export.csv\"");
+            } else {
+                header("Content-Disposition: attachment; filename=\"Member-Export.csv\"");
+            }
+                       header('Content-Length: ' . filesize($fileOut));
+                       $fp = fopen($fileOut, 'rb');
+                       if (fpassthru($fp)) {
+                               unlink($fileOut);
+                       }
+            exit();
+        } else {
+            return false;
+        }
+    }
+    // }}}
+
+    // {{{ getDiscovery()
+    
+    /**
+     * Description for getDiscovery()
+     * 
+     * @return array|mixed $return or Toolkit_Common
+     * @access public 
+     */
+    function getDiscovery()
+    {
+        try {
+            $dbh = $GLOBALS['dbh'];
+            $sql = "
+              SELECT id, name
+                FROM contact_disc
+               ORDER BY groupid, pos";
+            $stmt = $dbh->query($sql);
+            while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                $return[$row['id']] = $row['name'];
+            }
+            return $return;
+               } catch (PDOException $e) {
+                       return Toolkit_Common::handleError($e);
+        }
+    }// }}}
+    // {{{ getInterest()
+    
+    /**
+     * Description for getInterest()
+     * 
+     * @param PDO $dbh PDO
+     * 
+     * @return array|mixed $return or Toolkit_Common 
+     * @access public
+     */
+    function getInterest(PDO $dbh)
+    {
+        try {
+            $sql = "
+              SELECT id, header as name
+                FROM contact_inq
+               ORDER BY groupid, pos";
+            $stmt = $dbh->query($sql);
+            while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                $return[$row['id']] = $row['name'];
+            }
+            return $return;
+               } catch (PDOException $e) {
+                       return Toolkit_Common::handleError($e);
+        }
+    }// }}}
+
+       //      {{{ setQuery()
+
+    /**
+     * Sets the sql query to use in the DataGrid to get the results
+     *
+     * @return void
+     * @access public
+     */
+       public function setQuery()
+       {
+        $params = $params2 = array();
+        $sql = "
+                       SELECT id, create_date, fname, lname, company,
+                              address, address2, city, state, zip, phone, fax, email,
+                                  discover, interest
+              FROM contact";
+        if (   $_REQUEST['interest']
+                       && is_array($_REQUEST['interest'])
+                       && !empty($_REQUEST['interest'])
+               ) {
+            // code to get interest out
+            foreach ($_REQUEST['interest'] as $groupName => $gData) {
+                foreach ($gData as $intId => $boolVal) {
+                    $params2[] = "interest like '%:{$intId}:%'";
+                }
+            }
+        }
+        if (   $_REQUEST['discover']
+                       && is_array($_REQUEST['discover'])
+                       && !empty($_REQUEST['discover'])
+               ) {
+            // code to get interest out
+            foreach ($_REQUEST['discover'] as $groupName => $gData) {
+                foreach ($gData as $intId => $boolVal) {
+                    $params2[] = "discover like '%:{$intId}:%'";
+                }
+            }
+        }
+        // for now the start and end date are required
+        if (   is_array($_REQUEST['start_date'])
+                       && !empty($_REQUEST['start_date'])
+            && is_array($_REQUEST['end_date'])
+                       && !empty($_REQUEST['end_date'])
+        ) {
+            $start_date = implode("/", $_REQUEST['start_date']);
+            $end_date = implode("/", $_REQUEST['end_date']);
+            $params[] = "create_date BETWEEN '{$start_date}'::date AND '{$end_date}'::date";
+        }
+        $params[] = "members = true";
+        if (!empty($params)) {
+            $sql .= " WHERE " . implode(" AND ", $params);
+        }
+        if (!empty($params2)) {
+            $sql .= " AND (" . implode(" OR ", $params2). ")";
+        }
+        $sql .= " ORDER BY id";
+        //echo '<p>'.$sql.'</p>';exit;
+        parent::setQuery($sql);
+       }
+
+       //      }}}
+       //      {{{     _getDate()
+
+    /**
+     * Description for _getDate()
+     * 
+     * @param mixed $key Description for key
+     * 
+     * @return string
+     * @throws RuntimeException 
+     * @access private
+     */
+       private function _getDate($key)
+       {
+               if (   filter_var($_POST[$key]['m'], FILTER_VALIDATE_INT)
+                       && filter_var($_POST[$key]['d'], FILTER_VALIDATE_INT)
+                       && filter_var($_POST[$key]['Y'], FILTER_VALIDATE_INT)
+               ) {
+                       return $_POST[$key]['Y'] . $_POST[$key]['m'] . $_POST[$key]['d'];
+               } else {
+
+                       throw new RuntimeException("Invalid date values for `$key`");
+               }
+       }
+
+       //      }}}
+       //      {{{     _getFieldIds()
+
+    /**
+     * Description for _getFieldIds()
+     * 
+     * @param mixed $key Key
+     * 
+     * @return string|void 
+     * @access private
+     */
+       private function _getFieldIds($key)
+       {
+               if (!isset($_POST[$key])) {
+                       return;
+               } elseif (!is_array($_POST[$key])) {
+                       return;
+               } else {
+                       $identifier = '';
+                       foreach ($_POST[$key] as $group => $values) {
+                               $identifier .= "$group-";
+                               foreach ($values as $id => $selected) {
+                                       if ($selected == '1') {
+                                               $identifier .= $id;
+                                       }
+                               }
+                       }
+                       return $identifier;
+               }
+       }
+
+       //      }}}
+       //      {{{     _getContactDiscoverFields()
+
+    /**
+     * Get contact discover fields
+     * 
+     * @param int   $total   Description of $total
+     * @param array $contact Description of $contact
+     * 
+     * @return array $discoverFields
+     * @access private
+     */
+       private function _getContactDiscoverFields($total, $contact)
+       {
+               $discoverFields = array();
+               for ($i = 0, $j = 1; $i < $total; ++$i, ++$j) {
+                       $discoverFields["How did you hear field $j"]
+                               = $contact[$i] ? '"' . $contact[$i] .'"' : null;
+               }
+
+               return $discoverFields;
+       }
+
+       //      }}}
+       //      {{{     _getContactInterestFields()
+
+    /**
+     * Get contact interest fields
+     * 
+     * @param int   $total   Description for $total
+     * @param array $contact Description for $contact
+     * 
+     * @return array
+     * @access private 
+     */
+       private function _getContactInterestFields($total, $contact)
+       {
+               $interestFields = array();
+               for ($i = 0, $j = 1; $i < $total; ++$i, ++$j) {
+                       $interestFields["Interest field $j"]
+                               = $contact[$i] ? '"' . $contact[$i] . '"' : null;
+               }
+               return $interestFields;
+       }
+
+       //      }}}
+       //      {{{     _getContactDiscoverValues()
+
+    /**
+     * Get contact discover values
+     * 
+     * @param array $contact         Contact array
+     * @param array $discover        Discover array
+     * @param array &$discoverValues Discover values
+     * 
+     * @return void 
+     * @access private
+     */
+       private function _getContactDiscoverValues(
+               array $contact,
+               array $discover,
+               array &$discoverValues
+       ) {
+               $discoverKeys = array_filter(explode(':', $contact['discover']));
+               foreach ($discoverKeys as $key) {
+                       $discoverValues[$contact['id']][] = $discover[$key];
+               }
+               return;
+       }
+
+       //      }}}
+       //      {{{     _getContactInterestValues()
+
+    /**
+     * Get contact interest values
+     * 
+     * @param array $contact         Contact array
+     * @param array $interest        Interest array
+     * @param array &$interestValues Interest values array
+     * 
+     * @return void
+     * @access private
+     */
+       private function _getContactInterestValues(
+               array $contact,
+               array $interest,
+               array &$interestValues
+       ) {
+               $interestKeys = array_filter(explode(':', $contact['interest']));
+               foreach ($interestKeys as $key) {
+                       $interestValues[$contact['id']][] = $interest[$key];
+               }
+               return;
+       }
+
+       //      }}}
+    
+    /**
+     * Set CSV path
+     * 
+     * @return void
+     * @access public 
+     */
+    function setCsvPath()
+    {
+        $this->csvDirPath = BASE . 'Toolkit/Members/Leads/csv_leads';
+        if (!is_dir($this->csvDirPath)) {
+            $curMask = umask(0);
+            mkdir($this->csvDirPath, 0775, true);
+            umask($curMask);
+        }
+    }
+       //      {{{ toCSV()
+
+    /**
+     * returns a CSV file of the datagrid
+     * 
+     * @param PDO $dbh PDO
+     *
+     * @return string|boolean|mixed Mixed from Toolkit_Common
+     * @access public
+     */
+       public function toCSV(PDO $dbh)
+       {
+        $this->setCsvPath();
+        //$discover = $this->getDiscovery();
+        $interest = $this->getInterest($dbh);
+        $discoverValues = $interestValues = array();
+        try {
+            $totalDisc = $totalInt = 0;
+            $stmt = $dbh->query($this->sql);
+            while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                // discovery part
+                if (!is_null($row['discover'])) {
+                                       $this->_getContactDiscoverValues(
+                                               $row,
+                                               $discover,
+                                               $discoverValues
+                                       );
+                                       $numOfContactDisc = count($discoverValues[$row['id']]);
+                                       if ($numOfContactDisc > $totalDisc) {
+                                               $totalDisc = $numOfContactDisc;
+                                       }
+                }
+                               if (!is_null($row['interest'])) {
+                                       $this->_getContactInterestValues(
+                                               $row,
+                                               $interest,
+                                               $interestValues
+                                       );
+                                       $numOfContactInt = count($interestValues[$row['id']]);
+                                       if ($numOfContactInt > $totalInt) {
+                                               $totalInt = $numOfContactInt;
+                                       }
+                               }
+                $data[$row['id']] = $row;
+            }
+                       $sdate = $this->_getDate('start_date');
+                       $edate = $this->_getDate('end_date');
+                       $interests = $this->_getFieldIds('interest');
+                       $discover = $this->_getFieldIds('discover');
+
+                       $hash = md5($sdate . $edate . $interests . $discover);
+                       $fname = $this->csvDirPath . "/$hash-" . time();
+
+                       if (!$fh = fopen($fname, 'w')) {
+                               throw new RuntimeException('Unable to create CSV file');
+                       }
+
+            $fileContents = array();
+            if (isset($data) && !empty($data)) {
+                $fileHeaders = false;
+                foreach ($data as $contactId => $row) {
+                    unset(
+                        $row['id'],
+                        $row['discover'],
+                        $row['interest']
+                    );
+
+                                       if (!$fileHeaders) {
+                                               $fileHeaders = true;
+                                               $contactDiscoverHeaders = $this->_getContactDiscoverFields(
+                                                       $totalDisc,
+                                                       $discoverValues[$contactId]
+                                               );
+                                               $contactInterestHeaders = $this->_getContactInterestFields(
+                                                       $totalInt,
+                                                       $interestValues[$contactId]
+                                               );
+
+                                               fwrite($fh, '"' . implode('","', array_keys($row)) . '"');
+                                               fwrite($fh, ',"' . implode('","', array_keys($contactDiscoverHeaders)) . '"');
+                                               fwrite($fh, ',"' . implode('","', array_keys($contactInterestHeaders)) . '"');
+                                               fwrite($fh, "\n");
+                                       }
+
+                                       fwrite($fh, '"' . implode('","', $row) . '"');
+                                       $contactDiscoverValues = $this->_getContactDiscoverFields(
+                                               $totalDisc,
+                                               $discoverValues[$contactId]
+                                       );
+                                       fwrite($fh, ',' . implode(',', $contactDiscoverValues));
+                                       $contactInterestValues = $this->_getContactInterestFields(
+                                               $totalInt,
+                                               $interestValues[$contactId]
+                                       );
+                                       fwrite($fh, ',' . implode(',', $contactInterestValues));
+                                       fwrite($fh, "\n");
+                }
+                               fclose($fh);
+                               return $fname;
+            } else {
+                return false;
+            }
+               } catch (PDOException $e) {
+                       return Toolkit_Common::handleError($e);
+        }
+       }
+
+       //      }}}
+}
+?>
diff --git a/Toolkit/Members/Leads/libjs/visitorLeads.js b/Toolkit/Members/Leads/libjs/visitorLeads.js
new file mode 100644 (file)
index 0000000..cf6bb34
--- /dev/null
@@ -0,0 +1,75 @@
+var Event =
+{
+       calendar: '//app.gaslightmedia.com/assets/icons/calendar.png',
+
+       init: function()
+       {
+               if ($('#sdate1').is('select')) {
+                       Event.setupSDateCalendar();
+                       var fromCal = new Zapatec.Calendar.setup({
+                               weekNumbers             : false,
+                               ifFormat                : '%m/%d/%Y',
+                               button                  : 'startcal',
+                               onUpdate                : Event.updateFrom,
+                               showsTime               : false
+                       });
+               }
+
+               if ($('#edate1').is('select')) {
+                       Event.setupEDateCalendar();
+                       var toCal = new Zapatec.Calendar.setup({
+                               weekNumbers             : false,
+                               ifFormat                : '%m/%d/%Y',
+                               button                  : 'endcal',
+                               onUpdate                : Event.updateTo,
+                               showsTime               : false
+                       });
+               }
+       },
+
+       updateFrom: function(cal)
+       {
+               var date = cal.date;
+               var month = date.getMonth() + 1;
+               var day = date.getDate();
+               var year = date.getFullYear();
+
+               $("#sdate1 option[value='"+month+"']").attr('selected', 'selected');
+               $("#sdate2 option[value='"+day+"']").attr('selected', 'selected');
+               $("#sdate3 option[value='"+year+"']").attr('selected', 'selected');
+        Event.updateTo(cal);
+       },
+
+       updateTo: function(cal)
+       {
+               var date = cal.date;
+               var month = date.getMonth() + 1;
+               var day = date.getDate();
+               var year = date.getFullYear();
+
+               $("#edate1 option[value='"+month+"']").attr('selected', 'selected');
+               $("#edate2 option[value='"+day+"']").attr('selected', 'selected');
+               $("#edate3 option[value='"+year+"']").attr('selected', 'selected');
+       },
+
+       setupSDateCalendar: function()
+       {
+               var img = '&nbsp;<img id="startcal" height="16" width="16" ' +
+                                               'style="float:none;display:inline;vertical-align: middle" ' +
+                                               'src="' + Event.calendar + '">';
+               $('#sdate3').after(img);
+        //$("#startcal").css('display', 'inline');
+       },
+
+       setupEDateCalendar: function()
+       {
+               var img = '&nbsp;<img id="endcal" height="16" width="16" ' +
+                                               'style="float:none;display:inline;vertical-align: middle" ' +
+                                               'src="' + Event.calendar + '">';
+               $('#edate3').after(img);
+        //$("#endcal").css('display', 'inline');
+       }
+
+};
+
+$(document).ready(Event.init);
diff --git a/Toolkit/Members/Map.php b/Toolkit/Members/Map.php
new file mode 100644 (file)
index 0000000..a2c92d5
--- /dev/null
@@ -0,0 +1,100 @@
+<?php
+/**
+ * Map.php
+ *
+ * PHP version 5
+ *
+ * @category  Toolkit
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2009 Gaslight Media
+ * @license   Gaslight Media
+ * @version   CVS: $Id$
+ * @link      <>
+ */
+
+/**
+ * Short description for class
+ *
+ * Long description (if any) ...
+ *
+ * @category  Toolkit
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2009 Gaslight Media
+ * @license   Gaslight Media
+ * @version   Release: @package_version@
+ * @link      <>
+ */
+class Toolkit_Members_Map
+{
+    //    {{{    __construct()
+
+    /**
+     * Class constructor
+     *
+     * @access public
+     */
+    public function __construct()
+    {
+    }
+
+    //    }}}
+
+    //    {{{    getMemberXML()
+
+    /**
+     * Description of getMemberXML
+     *
+     * @param array $members Members array
+     *
+     * @return DOMDocument
+     * @access public
+     */
+    public function getMemberXML(array $members)
+    {
+        $dom     = new DOMDocument("1.0");
+
+        $node    = $dom->createElement("markers");
+        $parNode = $dom->appendChild($node);
+
+        foreach ($members as $i) {
+            $node    = $dom->createElement("marker");
+            $newNode = $parNode->appendChild($node);
+
+            foreach ($i as $k => $v) {
+                $v = iconv(mb_detect_encoding($v, mb_detect_order(), true), "UTF-8", $v);
+                $newNode->setAttribute($k, $v);
+            }
+        }
+
+        return $dom->saveXML();
+    }
+
+    //    }}}
+
+    //    {{{    toHtml()
+
+    /**
+     * Description for toHtml
+     *
+     * @param string $id Description of $id
+     * @param string $w  Description of $w
+     * @param string $h  Description of $h
+     *
+     * @return string
+     */
+    public function toHtml($id = "map-canvas", $w = "100%", $h = "300px")
+    {
+        $GLOBALS['bottomScripts'][]
+            = MEDIA_BASE_URL . 'Toolkit/Members/libjs/google-map.js';
+        $GLOBALS['bottomScripts'][]
+            = 'http://maps.google.com/maps/api/js?sensor=false';
+
+        $format = '<div id="%s" style="width: %s;height: %s"></div>';
+
+        return sprintf($format, $id, $w, $h);
+    }
+
+    //    }}}
+}
diff --git a/Toolkit/Members/Member.php b/Toolkit/Members/Member.php
new file mode 100644 (file)
index 0000000..3707ea0
--- /dev/null
@@ -0,0 +1,105 @@
+<?php
+//     vim:set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker syntax=php:
+
+/**
+ * Handles member interactions
+ *
+ * PHP version 5
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @version   CVS: $Id: Member.php,v 1.2 2009/09/16 00:31:53 jamie Exp $
+ * @link      http://demo.gaslightmedia.com
+ */
+
+/**
+ * Handle member record interaction
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @version   Release: @package_version@
+ * @link      http://demo.gaslightmedia.com
+ */
+class Toolkit_Members_Member
+{
+    //  {{{ properties
+
+
+    /**
+     * Description for protected
+     * @var    unknown
+     * @access protected
+     */
+    protected $pdo;
+
+    //  /// }}}
+    //  {{{ __construct()
+    //  @codeCoverageIgnoreStart
+
+    /**
+     * Constructor
+     *
+     * @param PDO              $pdo PHP Data Object
+     * @param Config_Container $c   Application configuration
+     *
+     * @return void
+     * @access public
+     */
+    public function __construct(PDO $pdo, Config_Container $c)
+    {
+        $this->dbh = $pdo;
+        $this->config = $c;
+    }
+
+    //  @codeCoverageIgnoreEnd
+    //  }}}
+
+    //  {{{ canAddPhotos()
+
+    /**
+     * calculate if a member can add more photos to their record
+     *
+     * @param Toolkit_Members_Photos $ll Linked List of member photos
+     *
+     * @return boolean
+     * @access public
+     */
+    public function canAddPhotos(Toolkit_Members_Photos $ll)
+    {
+        //  get reference to [conf] section of config file
+        $section =& $this->config->getItem('section', 'photos');
+        $maxPhotos =& $section->getItem('directive', 'maxPhotos');
+
+               return ($ll->getListSize() < $maxPhotos->getContent());
+    }
+
+    //  }}}
+
+    //  {{{ hasUploadedPhotos()
+
+    /**
+     * Deterimines of the member has any photos uploaded to their record
+     *
+     * @param Toolkit_Members_Photos $ll   Linked List of member photos
+     * @param boolean                $fltr Filter pending when calculating
+     *
+     * @return boolean
+     * @access public
+     */
+    public function hasUploadedPhotos(
+        Toolkit_Members_Photos $ll,
+        $fltr = false
+    ) {
+        $size = $ll->getListSize($fltr);
+
+        return ($size > 0);
+    }
+
+    //  }}}
+}
diff --git a/Toolkit/Members/MemberImport/Category.php b/Toolkit/Members/MemberImport/Category.php
new file mode 100644 (file)
index 0000000..0336b12
--- /dev/null
@@ -0,0 +1,179 @@
+<?php
+/**
+ * Category.php
+ *
+ * PHP version 5
+ *
+ * @category Toolkit
+ * @package  Toolkit
+ * @author   Steve Sutton <steve@gaslightmedia.com>
+ * @license  http://www.gaslightmedia.com Gaslightmedia
+ * @link     <>
+ */
+
+/**
+ * Toolkit_Members_MemberImport_Category
+ * 
+ * Category class for members
+ *
+ * @category Toolkit
+ * @package  Toolkit
+ * @author   Steve Sutton <steve@gaslightmedia.com>
+ * @license  http://www.gaslightmedia.com Gaslightmedia
+ * @link     <>
+ */
+class Toolkit_Members_MemberImport_Category
+{
+    /**
+     * Description of $category_id
+     * @var integer
+     * @access protected 
+     */
+    protected $category_id;
+    
+    /**
+     * Description of $name
+     * @var string
+     * @access protected
+     */
+    protected $name;
+    
+    /**
+     * Description of $parent_id
+     * @var integer
+     * @access protected
+     */
+    protected $parent_id = 0;
+
+    /**
+     * Helps to create functions for any class variable if the variable does
+     * exists
+     *
+     * @param String $name Name of variable
+     * @param mixed  $args Argument for the varable (if set)
+     *
+     * @return Toolkit_Table
+     */
+    public function __call($name, $args)
+    {
+        if (preg_match('/^(get|set)(\w+)/', strtolower($name), $match)
+            && $attribute = $this->validateAttribute($match[2])
+        ) {
+            if ('get' == $match[1]) {
+                return $this->$attribute;
+            } else {
+                $this->$attribute = $args[0];
+            }
+        } else {
+            throw new Exception(
+                'Call to undefined method Member::' . $name
+            );
+        }
+    }
+    /**
+     * insert the object
+     *
+     * @param PDO $dbh Database Connection
+     *
+     * @return Toolkit_Table
+     */
+    private function _insert(PDO $dbh)
+    {
+        $classData = get_object_vars($this);
+        unset($classData['category_id']);
+        //echo '<pre>'.print_r($classData, true).'</pre>';
+        //exit;
+        $sql = Toolkit_Common::createSQLInsert(
+            'category',
+            array_keys($classData)
+        );
+        $sql .= " RETURNING category_id";
+        $stmt = Toolkit_Common::prepareQuery(
+            $dbh,
+            'category',
+            $sql,
+            $classData
+        );
+        $stmt->execute();
+        $this->setCategoryId($stmt->fetchColumn());
+    }
+    /**
+     * given a category name return id create category if needed
+     *
+     * @param PDO    $dbh  Database connection
+     * @param String $name Category Name
+     *
+     * @return Object
+     */
+    function fetchByName(PDO $dbh, $name)
+    {
+        $sql = "
+        SELECT " . implode(',', $this->getClassVars())."
+          FROM category
+         WHERE name = :name";
+        $stmt = $dbh->prepare($sql);
+        $stmt->bindParam(":name", $name, PDO::PARAM_STR);
+        $stmt->execute();
+        $row = $stmt->fetch(PDO::FETCH_ASSOC);
+        if ($row) {
+            $category = new Toolkit_Members_MemberImport_Category();
+            $category->setCategoryId($row['category_id']);
+            $category->setName($row['name']);
+            $category->setParent_id($row['parent_id']);
+            return $category;
+        } else {
+            return false;
+        }
+    }
+    /**
+     * get all clas properties as an array (just names)
+     *
+     * @return Array
+     */
+    public function getClassVars()
+    {
+        return array_keys(get_class_vars(get_class($this)));
+    }
+    /**
+     * set category id
+     *
+     * @param Int $id Category id
+     *
+     * @return void
+     */
+    public function setCategoryId($id)
+    {
+        if (!$this->category_id) {
+            $this->category_id = $id;
+        }
+    }
+    /**
+     * Checks the id of the object if it is set then calls update othervise
+     * calls insert function
+     *
+     * @param PDO $dbh Database connection
+     *
+     * @return viod
+     */
+    public function save(PDO $dbh)
+    {
+        if ($this->category_id) {
+            $this->_update($dbh);
+        } else {
+            $this->_insert($dbh);
+        }
+    }
+    /**
+     * Validates the property
+     *
+     * @param String $name property name
+     *
+     * @return String
+     */
+    protected function validateAttribute($name)
+    {
+        if (property_exists(get_class($this), $name)) {
+            return strtolower($name);
+        }
+    }
+}
diff --git a/Toolkit/Members/MemberImport/City.php b/Toolkit/Members/MemberImport/City.php
new file mode 100644 (file)
index 0000000..d48d8b2
--- /dev/null
@@ -0,0 +1,135 @@
+<?php
+/**
+ * City.php
+ *
+ * PHP version 5
+ *
+ * @category Toolkit
+ * @package  Members
+ * @author   Steve Sutton <steve@gaslightmedia.com>
+ * @license  http://www.gaslightmedia.com Gaslightmedia
+ * @link     <>
+ */
+
+/**
+ * Toolkit_Members_MemberImport_City
+ *
+ * member city table class
+ *
+ * @category Toolkit
+ * @package  Members
+ * @author   Steve Sutton <steve@gaslightmedia.com>
+ * @license  http://www.gaslightmedia.com Gaslightmedia
+ * @link     <>
+ */
+class Toolkit_Members_MemberImport_City
+{
+    /**
+     * Description of $city_id
+     * @var integer 
+     * @access protected
+     */
+    protected $city_id;
+    
+    /**
+     * Description of $city_name
+     * @var string
+     * @access protected
+     */
+    protected $city_name;
+    
+    /**
+     * Description of $state_id
+     * @var integer
+     * @access protected
+     */
+    protected $state_id;
+
+    /**
+     * Helps to create functions for any class variable if the variable does
+     * exists
+     *
+     * @param String $name Name of variable
+     * @param mixed  $args Argument for the varable (if set)
+     *
+     * @return Toolkit_Table
+     */
+    public function __call($name, $args)
+    {
+        if (preg_match('/^(get|set)(\w+)/', strtolower($name), $match)
+            && $attribute = $this->validateAttribute($match[2])
+        ) {
+            if ('get' == $match[1]) {
+                return $this->$attribute;
+            } else {
+                $this->$attribute = $args[0];
+            }
+        } else {
+            throw new Exception(
+                'Call to undefined method Member::' . $name
+            );
+        }
+    }
+    /**
+     * given a city name search for and get id create city if needed
+     *
+     * @param PDO    $dbh  Database connection
+     * @param String $name City Name
+     *
+     * @return Object
+     */
+    public function fetchByName(PDO $dbh, $name)
+    {
+        $sql = "
+        SELECT city_id,city_name,state_id
+          FROM city
+         WHERE lower(city_name) = :city_name";
+        $stmt = $dbh->prepare($sql);
+        $stmt->setFetchMode(
+            PDO::FETCH_CLASS, 
+            'Toolkit_Members_MemberImport_City'
+        );
+        $stmt->bindParam(":city_name", strtolower($name));
+        $stmt->execute();
+        return $stmt->fetch(PDO::FETCH_CLASS);
+    }
+    /**
+     * Checks the id of the object if it is set then calls update othervise
+     * calls insert function
+     *
+     * @param PDO $dbh Database connection
+     *
+     * @return viod
+     */
+    public function save(PDO $dbh)
+    {
+        $classData = get_object_vars($this);
+        unset($classData['city_id']);
+        $sql = Toolkit_Common::createSQLInsert(
+            'city',
+            array_keys($classData)
+        );
+        $sql .= " RETURNING city_id";
+        $stmt = Toolkit_Common::prepareQuery(
+            $dbh,
+            'city',
+            $sql,
+            $classData
+        );
+        $stmt->execute();
+        $this->setCity_id($stmt->fetchColumn());
+    }
+    /**
+     * Validates the property
+     *
+     * @param String $name property name
+     *
+     * @return String
+     */
+    protected function validateAttribute($name)
+    {
+        if (property_exists(get_class($this), $name)) {
+            return strtolower($name);
+        }
+    }
+}
diff --git a/Toolkit/Members/MemberImport/FileForm.php b/Toolkit/Members/MemberImport/FileForm.php
new file mode 100644 (file)
index 0000000..73fcc0b
--- /dev/null
@@ -0,0 +1,149 @@
+<?php
+/**
+ * FileForm.php
+ *
+ * PHP Version 5
+ *
+ * @category Toolkit_Members
+ * @package  Members
+ * @author   Steve Sutton <steve@gaslightmedia.com>
+ * @license  Gaslight Media
+ * @link     <>
+ */
+/**
+ * Toolkit_Members_MemberImport_FileForm
+ *
+ * Build the form for uploading the file
+ *
+ * @category Toolkit_Members
+ * @package  Members
+ * @author   Steve Sutton <steve@gaslightmedia.com>
+ * @license  Gaslight Media
+ * @link     <>
+ */
+class Toolkit_Members_MemberImport_FileForm 
+    extends Toolkit_FormBuilder implements Toolkit_Form
+{
+    /**
+     * Description of $uploadDir
+     * @var string
+     * @access protected
+     */
+    protected $uploadDir = './upload/';
+    
+    /**
+     * Description of $successMessage
+     * @var string
+     * @access protected
+     */
+    protected $successMsg = '<div>file uploaded now processing</div>';
+
+    /**
+     * Form element definitions
+     *
+     * @return void
+     * @access public
+     */
+    public function configureElements()
+    {
+        $e = array();
+
+        // All Elements are created here.
+        // This includes group element definitions.
+        $e[] = array(
+            'type'    => 'file',
+            'req'     =>  true,
+            'name'    => 'file_upload',
+            'display' => 'File Upload'
+        );
+        $e[] = array(
+            'type'    => 'submit',
+            'req'     => false,
+            'name'    => 'submit_rmv',
+            'display' => 'Submit Form'
+        );
+
+        $this->setupElements($e);
+    }
+
+    /**
+     * Helper function, configures the entire form
+     *
+     * @return void
+     * @access public
+     */
+    public function configureForm()
+    {
+        $this->configureElements();
+        $this->configureRules();
+    }
+
+    /**
+     * Form rule definitions
+     *
+     * Adds validation rules for the given fields
+     *
+     * @return void
+     * @access public
+     */
+    public function configureRules()
+    {
+        $r = array();
+        $this->setupRules($r);
+    }
+
+    /**
+     * Process the form
+     *
+     * @param Array $values Array of values from the form (filtered)
+     *
+     * @return Boolean
+     */
+    function processForm($values)
+    {
+        $uploadedFile = move_uploaded_file(
+            $values['file_upload']['tmp_name'],
+            $this->uploadDir . 'members.csv'
+        );
+        if ($uploadedFile) {
+            $import = new Toolkit_Members_MemberImport_Import(
+                Toolkit_Database::getInstance()
+            );
+            $data = $import->readFile($this->uploadDir . 'members.csv');
+            $import->createTmpTable();
+            $import->importFile();
+            return true;
+        } else {
+            echo 'failed';
+        }
+    }
+
+    /**
+     * Handles how to display the current step the user is at in the form
+     *
+     * destroying and resetting the captcha value dis-allows someone from
+     * re-sending a form on a previous captcha.
+     *
+     * @return string form HTML state
+     * @access public
+     */
+    public function toHtml()
+    {
+        $this->fileImported = false;
+        if ($this->validate()) {
+            $this->cleanForm();
+
+            if ($this->process(array(&$this, 'processForm'), $this->mergeFiles)) {
+                $this->freeze();
+                $output = $this->successMsg;
+                $this->fileImported = true;
+            }
+        } elseif ($this->isSubmitted()) {
+            $output  = $this->errorMsg;
+            $output .= parent::toHTML();
+        } else {
+            $output = parent::toHTML();
+        }
+        return $output;
+    }
+}
diff --git a/Toolkit/Members/MemberImport/Import.php b/Toolkit/Members/MemberImport/Import.php
new file mode 100644 (file)
index 0000000..788ebd9
--- /dev/null
@@ -0,0 +1,181 @@
+<?php
+/**
+ * Import.php
+ *
+ * PHP Version 5
+ *
+ * @category Toolkit_Members
+ * @package  Members
+ * @author   Steve Sutton <steve@gaslightmedia.com>
+ * @license  Gaslight Media
+ * @link     <>
+ */
+/**
+ * Toolkit_Members_MemberImport_Import
+ *
+ * handles the import of the members from a temp table to the actual members
+ * table
+ *
+ * @category Toolkit_Members
+ * @package  Members
+ * @author   Steve Sutton <steve@gaslightmedia.com>
+ * @license  Gaslight Media
+ * @link     <>
+ */
+class Toolkit_Members_MemberImport_Import
+{
+    
+    /**
+     * Description for $fieldNames
+     * @var array
+     * @access public
+     */
+    public $fieldNames = array();
+    
+    /**
+     * Description for $_dropEnd
+     * @var boolean
+     * @access private
+     */
+    private $_dropEnd = false;
+    
+    /**
+     * Description for $_headCount
+     * @var integer
+     * @access private
+     */
+    private $_headCount = 0;
+    
+    /**
+     * Description for $tmpTableName
+     * @var string
+     * @access protected
+     */
+    protected $tmpTableName = 'tmp_members';
+    
+    /**
+     * Description for $headerFile
+     * @var string
+     * @access protected
+     */
+    protected $headerFile = 'fileHeaders.txt';
+    
+    /**
+     * Description for $dbh
+     * @var PDO
+     * @access protected
+     */
+    protected $dbh;
+    
+    /**
+     * Description for $data
+     * @var array
+     * @access protected
+     */
+    protected $data = array();
+    
+    /**
+     * constructor of class Sets the pdo to protected $dbh variable
+     *
+     * @param PDO $pdo Database connection
+     *
+     * @return void
+     * @access public
+     */
+    public function __construct(PDO $pdo)
+    {
+        $this->dbh = $pdo;
+    }
+    /**
+     * readFile
+     * 
+     * Read in the file uploaded and return an array of values from each row 
+     *
+     * @param String $file Name of the file uploaded
+     *
+     * @throws Exception If file is not found
+     * @return Array
+     * @access public
+     */
+    public function readFile($file)
+    {
+        if (!is_file($file)) {
+            throw new Exception("File $file not found!");
+        }
+        if (($handle = fopen($file, 'r')) !== false) {
+            while (($line = fgetcsv($handle, 1000, ",")) !== false) {
+                $this->data[] = $line;
+            }
+        }
+        return $this->data;
+    }
+    /**
+     * Create a tmp_member table
+     *
+     * @return void
+     * @access public
+     */
+    public function createTmpTable()
+    {
+        $this->dbh->query("DROP TABLE IF EXISTS {$this->tmpTableName}");
+        // get the first row of the csv file
+        $headers = array_values($this->data[0]);
+        $headers = array_filter($headers);
+        $this->fieldNames = array_map(array($this, "_cleanFieldName"), $headers);
+        $this->_headCount = count($headers);
+        file_put_contents($this->headerFile, serialize($this->fieldNames));
+        $sql = "
+        CREATE TABLE {$this->tmpTableName}(
+        ".implode(" TEXT,", $this->fieldNames)." TEXT
+        )";
+        $this->dbh->query($sql);
+    }
+    /**
+     * Clean the field name for creation of a tmp_member table with the 
+     * first header row as the field names
+     *
+     * @param String $name Name of the field
+     *
+     * @return String
+     * @access private
+     */
+    private function _cleanFieldName($name)
+    {
+        $name = str_replace("-", "_", $name);
+        return strtolower(str_replace(" ", "_", $name));
+    }
+    /**
+     * importFile
+     *
+     * Takes the file uploaded and imports the rows into a tmp_member table
+     *
+     * @return void
+     * @access public
+     */
+    public function importFile()
+    {
+        $sql = Toolkit_Common::createSQLInsert(
+            $this->tmpTableName,
+            $this->fieldNames
+        );
+        try {
+            foreach ($this->data as $key => $row) {
+                // skip first row
+                if ($key == 0) {
+                    continue;
+                }
+                // only get part of the row Length of headers
+                $row = array_slice($row, 0, $this->_headCount);
+                $values = array_combine($this->fieldNames, $row);
+                Toolkit_Common::processQuery(
+                    $this->dbh,
+                    $this->tmpTableName,
+                    $sql,
+                    $values
+                );
+            }
+        } catch(PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+    }
+}
diff --git a/Toolkit/Members/MemberImport/ImportForm.php b/Toolkit/Members/MemberImport/ImportForm.php
new file mode 100644 (file)
index 0000000..1d63d02
--- /dev/null
@@ -0,0 +1,234 @@
+<?php
+/**
+ * ImportForm.php
+ *
+ * PHP Version 5
+ *
+ * @category Toolkit
+ * @package  Members_MemberImport
+ * @author   Steve Sutton <steve@gaslightmedia.com>
+ * @license  Gaslight Media
+ * @link     <>
+ */
+/**
+ * Toolkit_Members_MemberImport_ImportForm
+ *
+ * Build the form for defining whihc fields can be imported from the file
+ * and to which field they get imported to
+ *
+ * @category Toolkit
+ * @package  Members_MemberImport
+ * @author   Steve Sutton <steve@gaslightmedia.com>
+ * @license  Gaslight Media
+ * @link     <>
+ */
+class Toolkit_Members_MemberImport_ImportForm 
+    extends Toolkit_FormBuilder
+    implements Toolkit_Form
+{
+    
+    /**
+     * Description for $tmpTableName
+     * @var string
+     * @access protected
+     */
+    protected $tmpTableName = 'tmp_members';
+    
+    /**
+     * Description for $headerFile
+     * @var string
+     * @access protected
+     */
+    protected $headerFile = 'fileHeaders.txt';
+    
+    /**
+     * Description for $successMsg
+     * @var string
+     * @access protected
+     */
+    protected $successMsg = '<div>Inserting new members</div>';
+
+    /**
+     * _cleanFieldName
+     *
+     * Cleans the fieldname takes out the spaces and puts in underscore
+     *
+     * @param String $name Field name to be cleaned
+     *
+     * @return String
+     */
+    private function _cleanFieldName($name)
+    {
+        return ucwords(str_replace("_", " ", $name));
+    }
+
+    /**
+     * Form element definitions
+     *
+     * @return void
+     * @access public
+     */
+    public function configureElements()
+    {
+        $e = array();
+        $member = new Toolkit_Members_MemberImport_Member();
+        $flds = $member->getClassVars();
+        $fieldOption = array(
+            '' => 'Ignore',
+            'member_category' => 'Member Category'
+        );
+        foreach ($flds as $fieldName) {
+            if ($fieldName != 'member_id') {
+                $fieldOption[$fieldName] = $this->_cleanFieldName($fieldName);
+            }
+        }
+
+        //     All Elements are created here.  This includes group element definitions.
+        $headers = unserialize(file_get_contents($this->headerFile));
+        foreach ($headers as $field) {
+            $title = ucwords(str_replace("_", " ", $field));
+            $e[] = array(
+                'type'    => 'select',
+                'req'     =>  false,
+                'name'    => $field,
+                'display' => $title,
+                'opts'    => $fieldOption
+            );
+        }
+        $e[] = array(
+            'type'    => 'submit',
+            'req'     => false,
+            'name'    => 'submit_rmv',
+            'display' => 'Submit Form'
+        );
+
+        $this->setupElements($e);
+    }
+
+    /**
+     * Helper function, configures the entire form
+     *
+     * @return void
+     * @access public
+     */
+    public function configureForm()
+    {
+        $this->configureElements();
+        $this->configureRules();
+    }
+
+    /**
+     * Form rule definitions
+     *
+     * Adds validation rules for the given fields
+     *
+     * @return void
+     * @access public
+     */
+    public function configureRules()
+    {
+        $r = array();
+        $this->setupRules($r);
+    }
+
+    /**
+     * Process the form and import members according to their placement
+     *
+     * @param Array $values Array of values from the form (filtered)
+     *
+     * @return Boolean
+     */
+    function processForm($values)
+    {
+        unset($values['submit_rmv']);
+        $memberCategoryFields = array();
+        foreach ($values as $fromField => $toField) {
+            if ($toField && $toField != 'member_categories') {
+                $fromFieldArray[] = $fromField;
+            } else if ($toField == 'member_categories') {
+                $fromFieldArray[] = $fromField;
+                $memberCategoryFields[] = $fromField;
+            }
+        }
+        $ignore = array(
+            'city',
+            'state',
+            'mailing_city',
+            'mailing_state',
+            'member_category'
+        );
+        $sql = "
+        SELECT ".implode(',', $fromFieldArray)."
+          FROM {$this->tmpTableName}";
+        $stmt = $this->dbh->query($sql);
+        while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
+            $member = new Toolkit_Members_MemberImport_Member();
+            $member_categories = array();
+            foreach ($row as $prop => $val) {
+                if (in_array($prop, $memberCategoryFields)) {
+                    $member_categories[] = $val;
+                    continue;
+                }
+                // we need to get the state done first before the city 
+                if (!in_array(strtolower($prop), $ignore)) {
+                    if ($val != '') {
+                        $propertyName = $values[$prop];
+                        $setprop = "set$propertyName";
+                        $member->$setprop($val);
+                    }
+                } else {
+                    if (strtolower($prop) == 'city') {
+                        $city = $val;
+                    }
+                    if (strtolower($prop) == 'state') {
+                        $state = $val;
+                    }
+                    if (strtolower($prop) == 'mailing_city') {
+                        $mailing_city = $val;
+                    }
+                    if (strtolower($prop) == 'mailing_state') {
+                        $mailing_state = $val;
+                    }
+                }
+            }
+            $member->setState($this->dbh, $state);
+            $member->setCity($this->dbh, $city);
+            $member->setMailingState($this->dbh, $mailing_state);
+            $member->setMailingCity($this->dbh, $mailing_city);
+            if (!empty($member_categories)) {
+                $member->setMember_categories($member_categories);
+            }
+            $member->save($this->dbh);
+            $this->members[] = $member;
+        }
+        echo '<pre>'.print_r($this->members, true).'</pre>';
+        return true;
+    }
+
+    /**
+     * Handles how to display the current step the user is at in the form
+     *
+     * destroying and resetting the captcha value dis-allows someone from
+     * re-sending a form on a previous captcha.
+     *
+     * @return string form HTML state
+     * @access public
+     */
+    public function toHtml()
+    {
+        if ($this->validate()) {
+            $this->cleanForm();
+
+            if ($this->process(array(&$this, 'processForm'), $this->mergeFiles)) {
+                $this->freeze();
+                $output = $this->successMsg;
+            }
+        } elseif ($this->isSubmitted()) {
+            $output  = $this->errorMsg;
+            $output .= parent::toHTML();
+        } else {
+            $output = parent::toHTML();
+        }
+        return $output;
+    }
+}
diff --git a/Toolkit/Members/MemberImport/Member.php b/Toolkit/Members/MemberImport/Member.php
new file mode 100644 (file)
index 0000000..54d4516
--- /dev/null
@@ -0,0 +1,667 @@
+<?php
+/**
+ * Member.php
+ *
+ * PHP Version 5
+ *
+ * @category Toolkit_Members
+ * @package  Members
+ * @author   Jamie Kahgee <steve@gaslightmedia.com>
+ * @license  Gaslight Media
+ * @link     <>
+ */
+/**
+ * Toolkit_Members_MemberImport_Member
+ *
+ * Member class for importing member
+ *
+ * @category Toolkit_Members
+ * @package  Members
+ * @author   Jamie Kahgee <steve@gaslightmedia.com>
+ * @license  Gaslight Media
+ * @link     <>
+ */
+class Toolkit_Members_MemberImport_Member
+{
+    
+    /**
+     * Description for $active
+     * @var boolean
+     * @access protected
+     */
+    protected $active = true;
+    
+    /**
+     * Description for $join_date
+     * @var date
+     * @access protected
+     */
+    protected $join_date;
+    
+    /**
+     * Description for $member_id
+     * @var integer
+     * @access protected
+     */
+    protected $member_id;
+    
+    /**
+     * Description for $member_name
+     * @var string
+     * @access protected
+     */
+    protected $member_name;
+    
+    /**
+     * Description for $street
+     * @var string
+     * @access protected
+     */
+    protected $street;
+    
+    /**
+     * Description for $country
+     * @var string
+     * @access protected
+     */
+    protected $country;
+    
+    /**
+     * Description for $phone_area
+     * @var string
+     * @access protected
+     */
+    protected $phone_area; // not real field
+    
+    /**
+     * Description for $phone
+     * @var string
+     * @access protected
+     */
+    protected $phone;
+    
+    /**
+     * Description for $fax_area
+     * @var string
+     * @access protected
+     */
+    protected $fax_area;    // not real field
+    
+    /**
+     * Description for $fax
+     * @var string
+     * @access protected
+     */
+    protected $fax;
+    
+    /**
+     * Description for $process_email
+     * @var string
+     * @access protected
+     */
+    protected $process_email;
+    
+    /**
+     * Description for $url
+     * @var string
+     * @access protected
+     */
+    protected $url;
+    
+    /**
+     * Description for $city
+     * @var string
+     * @access protected
+     */
+    protected $city;
+    
+    /**
+     * Description for $city_id
+     * @var integer
+     * @access protected
+     */
+    protected $city_id;
+    
+    /**
+     * Description for $state
+     * @var string
+     * @access protected
+     */
+    protected $state;
+    
+    /**
+     * Description for $state_id
+     * @var integer
+     * @access protected
+     */
+    protected $state_id;
+    
+    /**
+     * Description for $description
+     * @var integer
+     * @access protected
+     */
+    protected $description;
+    
+    /**
+     * Description for $zip
+     * @var string
+     * @access protected
+     */
+    protected $zip;
+    
+    /**
+     * Description for $toll_free_area
+     * @var string
+     * @access protected
+     */
+    protected $toll_free_area; // not real field
+    
+    /**
+     * Description for $toll_free
+     * @var string
+     * @access protected
+     */
+    protected $toll_free;
+    
+    /**
+     * Description for $member_contact_email
+     * @var string
+     * @access protected
+     */
+    protected $member_contact_email;
+    
+    /**
+     * Description for $mailing_address
+     * @var string
+     * @access protected
+     */
+    protected $mailing_address;
+    
+    /**
+     * Description for $primary_contact
+     * @var string
+     * @access protected
+     */
+    protected $primary_contact;
+    
+    /**
+     * Description for $primary_contact_fname
+     * @var string
+     * @access protected
+     */
+    protected $primary_contact_fname;
+    
+    /**
+     * Description for $primary_contact_lname
+     * @var string
+     * @access protected
+     */
+    protected $primary_contact_lname;
+    
+    /**
+     * Description for $mailing_state
+     * @var string
+     * @access protected
+     */
+    protected $mailing_state;
+    
+    /**
+     * Description for $mailing_state_id
+     * @var integer
+     * @access protected
+     */
+    protected $mailing_state_id;
+    
+    /**
+     * Description for $mailing_city
+     * @var string
+     * @access protected
+     */
+    protected $mailing_city;
+    
+    /**
+     * Description for $mailing_city_id
+     * @var integer
+     * @access protected
+     */
+    protected $mailing_city_id;
+    
+    /**
+     * Description for $mailing_zip
+     * @var string
+     * @access protected
+     */
+    protected $mailing_zip;
+    
+    /**
+     * Description for $lat
+     * @var double
+     * @access protected
+     */
+    protected $lat;
+    
+    /**
+     * Description for $lon
+     * @var double
+     * @access protected
+     */
+    protected $lon;
+    
+    /**
+     * Description for $member_categories
+     * @var array
+     * @access protected
+     */
+    protected $member_categories = array(); // not real field
+
+    /**
+     * created setters and getters for the class properties
+     * checks for existance of the property before running getter/setter
+     *
+     * @param String $name Function name
+     * @param Mixed  $args if set the arg to set
+     *
+     * @return mixed
+     * @throws Exception
+     * @access public
+     */
+    public function __call($name, $args)
+    {
+        if (preg_match('/^(get|set)(\w+)/', strtolower($name), $match)
+            && $attribute = $this->validateAttribute($match[2])
+        ) {
+            if ('get' == $match[1]) {
+                return $this->$attribute;
+            } else {
+                $this->$attribute = $args[0];
+            }
+        } else {
+            throw new Exception(
+                'Call to undefined method Member::' . $name
+            );
+        }
+    }
+    /**
+     * _insert
+     *
+     * create the record in database
+     *
+     * @param PDO $dbh Databese connection
+     *
+     * @return void
+     * @access public
+     */
+    private function _insert(PDO $dbh)
+    {
+        $classData = get_object_vars($this);
+        unset(
+            $classData['member_id'],
+            $classData['member_categories'],
+            $classData['phone_area'],
+            $classData['fax_area'],
+            $classData['toll_free_area'],
+            $classData['mailing_state']
+        );
+        $sql = Toolkit_Common::createSQLInsert(
+            'member',
+            array_keys($classData)
+        );
+        $sql .= " RETURNING member_id";
+        $stmt = Toolkit_Common::prepareQuery(
+            $dbh,
+            'member',
+            $sql,
+            $classData
+        );
+        $stmt->execute();
+        $this->setMemberId($stmt->fetchColumn());
+    }
+    /**
+     * getCLassVars
+     *
+     * get all class properties
+     *
+     * @return Array Keys of the class properties
+     * @access public
+     */
+    public function getClassVars()
+    {
+        return array_keys(get_class_vars(get_class($this)));
+    }
+    /**
+     * check the date given to see if its a valid date first before setting it
+     *
+     * @param String $date date
+     *
+     * @return void
+     * @access public
+     */
+    public function setJoinDate($date)
+    {
+        if (strtotime($date)) {
+            $this->join_date = $date;
+        }
+    }
+    /**
+     * check the number given to make sure it is numeric
+     *
+     * @param String $lat latitude
+     *
+     * @return void
+     * @access public
+     */
+    public function setLat($lat)
+    {
+        if (is_numeric($lat)) {
+            $this->lat = $lat;
+        }
+    }
+    /**
+     * check the number given to make sure it is numeric
+     *
+     * @param String $lon longitude
+     *
+     * @return void
+     * @access public
+     */
+    public function setLon($lon)
+    {
+        if (is_numeric($lon)) {
+            $this->lon = $lon;
+        }
+    }
+    /**
+     * check the number given to make sure it is numeric
+     *
+     * @param Int $id member id must be numeric
+     *
+     * @throws Exception if not numeric
+     * @return void
+     * @access public
+     */
+    public function setMemberId($id)
+    {
+        if (!is_numeric($id)) {
+            throw new Exception('Id must be numeric');
+        }
+        if (!$this->member_id) {
+            $this->member_id = $id;
+        }
+    }
+    /**
+     * create the city if not found then set member property
+     *
+     * @param PDO    $dbh      Database connection
+     * @param String $cityName City Name
+     *
+     * @return Int
+     * @access public
+     */
+    public function setCity(PDO $dbh, $cityName)
+    {
+        if (!$cityName) {
+            return false;
+        }
+        // update the city name so each first letter is upper case
+        $cityName = ucwords(strtolower($cityName));
+        $city = new Toolkit_Members_MemberImport_City();
+        $newCity = $city->fetchByName($dbh, $cityName);
+        if ($newCity) {
+            $cityId = $newCity->getCity_id();
+            $this->setCity_id($cityId);
+        } else {
+            // need to create new city
+            $city->setCity_name($cityName);
+            $city->setState_id($this->getState_id());
+            $city->save($dbh);
+            $this->setCity_id($city->getCity_id());
+        }
+    }
+    /**
+     * create the city if not found then set member property
+     *
+     * @param PDO    $dbh      Database connection
+     * @param String $cityName City Name
+     *
+     * @return boolean
+     * @access public
+     */
+    public function setMailingCity(PDO $dbh, $cityName)
+    {
+        if (!$cityName) {
+            return false;
+        }
+        $city = new Toolkit_Members_MemberImport_City();
+        $newCity = $city->fetchByName($dbh, $cityName);
+        if ($newCity) {
+            $cityId = $newCity->getCity_id();
+            $this->setMailing_city_id($cityId);
+        } else {
+            // need to create new city
+            $city->setCity_name($cityName);
+            $city->setState_id($this->getMailing_state_id());
+            $city->save($dbh);
+            $this->setMailing_city_id($city->getCity_id);
+        }
+    }
+    /**
+     * create the state if not found then set member property
+     *
+     * @param PDO    $dbh      Database connection
+     * @param String $stateAbb State Abbreviation
+     *
+     * @return boolean
+     * @access public
+     */
+    public function setMailingState(PDO $dbh, $stateAbb)
+    {
+        if (!$stateAbb) {
+            return false;
+        }
+        $state = new Toolkit_Members_MemberImport_State();
+        $newState = $state->fetchByAbb($dbh, $stateAbb);
+        if (is_object($newState)) {
+            if ($stateId = $newState->getState_id()) {
+                $this->setMailing_state_id($stateId);
+            } else {
+                // ?
+            }
+        } else {
+            var_dump($stateId);
+            var_dump($stateAbb);
+        }
+    }
+    /**
+     * also set the member_contact_email at same time
+     *
+     * @param String $email Email address
+     *
+     * @return void
+     * @access public
+     */
+    public function setProcessEmail($email)
+    {
+        if ($email != '') {
+            $this->process_email        = $email;
+            $this->member_contact_email = $email;
+        }
+    }
+    /**
+     * phone field may come in two seperate fields from the file
+     * here it's appending the fields area and
+     *
+     * @param <type> $phone_area phone area code
+     *
+     * @return void
+     * @access public
+     */
+    public function setPhoneArea($phone_area)
+    {
+        $this->phone = $phone_area . ' ';
+    }
+    /**
+     * set phone
+     *
+     * @param <type> $phone phone number
+     *
+     * @return void
+     * @access public
+     */
+    public function setPhone($phone)
+    {
+        $this->phone .= $phone;
+        $this->phone = trim($this->phone);
+    }
+    /**
+     * set fax
+     *
+     * @param String $fax_area fax area code part
+     *
+     * @return void
+     * @access public
+     */
+    public function setFaxArea($fax_area)
+    {
+        $this->fax = $fax_area. ' ';
+    }
+    /**
+     * set fax number
+     *
+     * @param String $fax fax number part
+     *
+     * @return void
+     * @access public
+     */
+    public function setFax($fax)
+    {
+        $this->fax .= $fax;
+        $this->fax = trim($this->fax);
+    }
+    /**
+     * set toll free area code
+     *
+     * @param String $toll_free_area toll free area code
+     *
+     * @return void
+     * @access public
+     */
+    public function setTollFreeArea($toll_free_area)
+    {
+        $this->toll_free = $toll_free_area . ' ';
+    }
+    /**
+     * set toll free number
+     *
+     * @param String $toll_free Toll free number
+     *
+     * @return void
+     * @access public
+     */
+    public function setTollFree($toll_free)
+    {
+        $this->toll_free .= $toll_free;
+        $this->toll_free = trim($this->toll_free);
+    }
+    /**
+     * create the state if not found then set member property
+     *
+     * @param PDO    $dbh      Database connection
+     * @param String $stateAbb State Abbreviation
+     *
+     * @return boolean
+     * @access public
+     */
+    public function setState(PDO $dbh, $stateAbb)
+    {
+        if (!$stateAbb) {
+            return false;
+        }
+        $state = new Toolkit_Members_MemberImport_State();
+        $state = new Toolkit_Members_MemberImport_State();
+        $newState = $state->fetchByAbb($dbh, $stateAbb);
+        if ($stateId = $newState->getState_id()) {
+            $this->setState_id($stateId);
+        } else {
+            // ?
+        }
+    }
+    /**
+     * save
+     *
+     * check to see if the id is set if it is then update else call insert
+     * also set the member categories
+     *
+     * @param PDO $dbh Database connection
+     *
+     * @return void
+     * @access public
+     */
+    public function save(PDO $dbh)
+    {
+        if ($this->member_id) {
+            $this->_update($dbh);
+        } else {
+            $this->_insert($dbh);
+        }
+        // now we can add categories
+        if (!empty($this->member_categories)) {
+            $addMemberCatSql = "
+            INSERT INTO member_category
+            (member_id, category_id)
+            VALUES
+            (:member_id, :category_id)";
+            $addMemberCat = $dbh->prepare($addMemberCatSql);
+            foreach ($this->member_categories as $catName) {
+                $category = new Toolkit_Members_MemberImport_Category();
+                if ($catName
+                    && $oldCategory = $category->fetchByName($dbh, $catName)
+                ) {
+                    // can now add this category to this member
+                    $addMemberCat->bindParam(
+                        ":member_id",
+                        $this->getMember_id(),
+                        PDO::PARAM_INT
+                    );
+                    $addMemberCat->bindParam(
+                        ":category_id",
+                        $oldCategory->getCategory_id(),
+                        PDO::PARAM_INT
+                    );
+                    $addMemberCat->execute();
+                } else if ($catName) {
+                    // not found must add 
+                    $category->setName($catName);
+                    $category->save($dbh);
+                    $addMemberCat->bindParam(
+                        ":member_id",
+                        $this->getMember_id(),
+                        PDO::PARAM_INT
+                    );
+                    $addMemberCat->bindParam(
+                        ":category_id",
+                        $category->getCategory_id(),
+                        PDO::PARAM_INT
+                    );
+                    $addMemberCat->execute();
+                }
+            }
+        }
+    }
+    /**
+     * validateAttribute
+     *
+     * checks to see if the properties exists
+     *
+     * @param String $name property name
+     * 
+     * @return String
+     * @access public
+     */
+    protected function validateAttribute($name)
+    {
+        if (property_exists(get_class($this), $name)) {
+            return strtolower($name);
+        }
+    }
+}
diff --git a/Toolkit/Members/MemberImport/State.php b/Toolkit/Members/MemberImport/State.php
new file mode 100644 (file)
index 0000000..ac1bf38
--- /dev/null
@@ -0,0 +1,106 @@
+<?php
+/**
+ * State.php
+ *
+ * PHP version 5
+ *
+ * @category Toolkit
+ * @package  Toolkit
+ * @author   Steve Sutton <steve@gaslightmedia.com>
+ * @license  http://www.gaslightmedia.com Gaslightmedia
+ * @link     <>
+ */
+
+/**
+ * Toolkit_Members_MemberImport_State
+ * 
+ * member state class
+ *
+ * @category Toolkit
+ * @package  Toolkit
+ * @author   Steve Sutton <steve@gaslightmedia.com>
+ * @license  http://www.gaslightmedia.com Gaslightmedia
+ * @link     <>
+ */
+class Toolkit_Members_MemberImport_State
+{
+    /**
+     * Description of $state_abb
+     * @var string 
+     * @access protected
+     */
+    protected $state_abb;
+    
+    /**
+     * Description of $city_name
+     * @var string
+     * @access protected
+     */
+    protected $state_name;
+    
+    /**
+     * Description of $state_id
+     * @var integer
+     * @access protected
+     */
+    protected $state_id;
+
+    /**
+     * Helps to create functions for any class variable if the variable does
+     * exists
+     *
+     * @param String $name Name of variable
+     * @param mixed  $args Argument for the varable (if set)
+     *
+     * @return Toolkit_Table
+     */
+    public function __call($name, $args)
+    {
+        if (preg_match('/^(get|set)(\w+)/', strtolower($name), $match)
+            && $attribute = $this->validateAttribute($match[2])
+        ) {
+            if ('get' == $match[1]) {
+                return $this->$attribute;
+            } else {
+                $this->$attribute = $args[0];
+            }
+        } else {
+            throw new Exception(
+                'Call to undefined method Member::' . $name
+            );
+        }
+    }
+    /**
+     * given a state abbreviation search for the state id
+     *
+     * @param PDO    $dbh Database connection
+     * @param String $abb State Abbreviation
+     *
+     * @return Object
+     */
+    public function fetchByAbb(PDO $dbh, $abb)
+    {
+        $sql = "
+        SELECT state_id,state_name,state_abb
+          FROM state
+         WHERE state_abb = :abb";
+        $stmt = $dbh->prepare($sql);
+        $stmt->setFetchMode(PDO::FETCH_CLASS, 'Toolkit_Members_MemberImport_State');
+        $stmt->bindParam(":abb", $abb);
+        $stmt->execute();
+        return $stmt->fetch(PDO::FETCH_CLASS);
+    }
+    /**
+     * Validates the property
+     *
+     * @param String $name property name
+     *
+     * @return String
+     */
+    protected function validateAttribute($name)
+    {
+        if (property_exists(get_class($this), $name)) {
+            return strtolower($name);
+        }
+    }
+}
diff --git a/Toolkit/Members/MemberImport/index.php b/Toolkit/Members/MemberImport/index.php
new file mode 100644 (file)
index 0000000..2930626
--- /dev/null
@@ -0,0 +1,44 @@
+<?php
+/**
+ * index.php
+ *
+ * PHP Version 5
+ *
+ * @category Toolkit_Members
+ * @package  Members
+ * @author   Steve Sutton <steve@gaslightmedia.com>
+ * @license  Gaslight Media
+ * @link     <>
+ */
+require_once '../../../setup.phtml';
+if (!isset($_REQUEST['_qf__import-members']) && (!$_GET['skip'] == 't')) {
+    $form = new Toolkit_Members_MemberImport_FileForm(
+        'import-file',
+        'post',
+        'index.php',
+        null,
+        null,
+        true
+    );
+    $form->configureForm();
+    if (is_file('./upload/members.csv')) {
+        echo '<a href="index.php?skip=t">Use Current Uploaded File</a><br />';
+    }
+    echo $form->toHtml();
+}
+if (isset($_REQUEST['_qf__import-file'])
+    || isset($_REQUEST['_qf__import-members'])
+    || $_GET['skip'] == 't'
+) {
+    // next page
+    $form2 = new Toolkit_Members_MemberImport_ImportForm(
+        'import-members',
+        'post',
+        'index.php',
+        null,
+        null,
+        true
+    );
+    $form2->configureForm();
+    echo $form2->toHtml();
+}
diff --git a/Toolkit/Members/MembersOnly/BreadCrumbs.php b/Toolkit/Members/MembersOnly/BreadCrumbs.php
new file mode 100644 (file)
index 0000000..b06c37c
--- /dev/null
@@ -0,0 +1,65 @@
+<?php
+/**
+ * BreadCrumbs.php
+ * 
+ * PHP Version 5.2
+ * 
+ * @category  Toolkit
+ * @package   Members_MembersOnly
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @version   SVN: $id$
+ * @link      <>
+ *  
+ */
+
+/**
+ * Toolkit_Members_MembersOnly_BreadCrumbs
+ * 
+ * Description of Toolkit_Members_MembersOnly_BreadCrumbs
+ * 
+ * @category  Toolkit
+ * @package   Members_MembersOnly
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @release   Release: $id$
+ * @link      <>
+ */
+class Toolkit_Members_MembersOnly_BreadCrumbs
+       extends Toolkit_Template_BreadCrumbs
+{
+    /**
+     * Description of getBreadCrumbsArray()
+     * 
+     * @param int $id ID
+     * 
+     * @return array 
+     * @access protected
+     */
+       protected function getBreadCrumbsArray($id)
+       {
+               $stack = array();
+               $page  = $this->pageGateway->find($id);
+               array_unshift($stack, $page['navigation_name']);
+               $parent = $page['parent'];
+
+               while ($parent != 0) {
+                       $page = $this->pageGateway->find($parent);
+                       $seoUrl = Toolkit_Template_Page::getSeoUrl(
+                               $this->pageGateway,
+                               $parent
+                       );
+                       $anchor = '<a href="'.$seoUrl.'">'.$page['navigation_name'].'</a>';
+                       array_unshift($stack, $anchor);
+                       $parent = $page['parent'];
+               }
+
+               $anchor = '<a href="' . MEDIA_BASE_URL . 'index.php">Home</a>';
+               array_unshift($stack, $anchor);
+
+               return $stack;
+       }
+}
+?>
diff --git a/Toolkit/Members/MembersOnly/CommonEventsController.php b/Toolkit/Members/MembersOnly/CommonEventsController.php
new file mode 100644 (file)
index 0000000..75c343a
--- /dev/null
@@ -0,0 +1,193 @@
+<?php
+/**
+ * EventsController.php
+ *
+ * PHP Version 5.2
+ *
+ * @category  Toolkit
+ * @package   Members_MembersOnly
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @version   SVN: $id$
+ * @link      <>
+ */
+if (!defined('COMMON_APP_BASE')) {
+    define('COMMON_APP_BASE', '/var/www/server/CommonApps/');
+}
+require_once COMMON_APP_BASE . 'EventCalendar/V1/models/EventMapper.php';
+/**
+ * Toolkit_Members_MembersOnly_EventsController
+ *
+ * Description of Toolkit_Members_MembersOnly_EventsController
+ *
+ * @category  Toolkit
+ * @package   Members_MembersOnly
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @release   Release: $id$
+ * @link      <>
+ */
+
+class Toolkit_Members_MembersOnly_CommonEventsController
+    extends Toolkit_BaseControllerAbstract
+    implements Toolkit_IController
+{
+    //  {{{ _creatPage()
+
+    /**
+     * Description of _createPage()
+     *
+     * @param string $eventContent Description of $eventcontent
+     *
+     * @return void
+     * @access private
+     */
+    private function _createPage($eventContent)
+    {
+        $breadCrumbsFactory = new Toolkit_BreadCrumbsFactory(
+            new Toolkit_Toolbox_PageGatewayPublishFactory(
+                $this->registry->dbh
+            )
+        );
+        $keywordReplacement = new Toolkit_Template_KeywordReplacement(
+            new Toolkit_Toolbox_PageGatewayPublish(
+                $this->registry->dbh
+            )
+        );
+        $tEngine = new HTML_Template_Flexy($this->registry->flexyOptions);
+        $glmPage = new Toolkit_Page(
+            new Toolkit_Template_Page(),
+            $breadCrumbsFactory,
+            new Toolkit_Toolbox_PageGatewayPublishFactory($this->registry->dbh),
+            new Toolkit_Toolbox_ParagraphGatewayPublishFactory($this->registry->dbh),
+            new Toolkit_Members_MembersOnly_Navigation_Factory(),
+            $keywordReplacement,
+            MEMBERS_EVENTS_PAGE
+        );
+
+        $glmPage->fetchPage();
+
+        $glmPage->toolboxContent .= $eventContent;
+        $baseSecureUrl
+            = ($_SERVER['HTTPS'] == 'on')
+            ? BASE_SECURE_URL
+            : MEDIA_BASE_URL;
+        $appBaseSecueUrl
+            = ($_SERVER['HTTPS'] == 'on')
+            ? GLM_APP_BASE_SECURE_URL
+            : MEDIA_APP_BASE_URL;
+        $GLOBALS['styleSheets'][]
+            = $baseSecureUrl . 'css/contactform.css';
+        $GLOBALS['styleSheets'][]
+            = $baseSecureUrl . 'Toolkit/Members/css/member-admin.css';
+        $GLOBALS['styleSheets'][]
+            = $appBaseSecueUrl
+            . 'libjs/jqueryui/1.8.13/development-bundle/themes/base/jquery.ui.all.css';
+
+        $glmPage->topScripts    = Toolkit_Common::getScripts($GLOBALS['topScripts']);
+        $glmPage->bottomScripts = Toolkit_Common::getScripts($GLOBALS['bottomScripts']);
+        $glmPage->styles        = Toolkit_Common::getStyleSheets();
+
+        $tEngine->compile('template.html');
+        $tEngine->outputObject($glmPage);
+
+    }
+
+    //  }}}
+
+    //  {{{ indexAction()
+
+    /**
+     * Description for indexAction()
+     *
+     * @return void
+     * @throws Toolkit_Members_Exception
+     * @access public
+     */
+    public function indexAction()
+    {
+        $eventMapper = new EventMapper(Toolkit_Database::getInstance());
+        $data = $eventMapper->fetchAllMemberEvents(
+            $this->registry->memberId
+        );
+        $html = '';
+        if (is_array($data) && !empty($data)) {
+            $html .= '<table style="float:left;" class="member-admin-table">';
+            $html .= '<tr>
+                <th>&nbsp;</th>
+                <th>Event</th>
+                <th>Status</th>
+                <th>Start</th>
+                <th>End</th>
+                    </tr>';
+            foreach( $data as $row ){
+                $html .= '<tr>
+                    <td>
+                        <div class="buttons">
+                            <a href="'.$this->page.'?rt=CommonEvents&amp;ac=addEvent&amp;id='.$row->getId().'">
+                            Edit
+                        </a>
+                        </div>
+                    </td>
+                    <td>
+                        '.$row->getHeader().'
+                    </td>
+                    <td>
+                        '.( ( $row->getActive() == 't' ) ? 'Active' : 'Pending').'
+                    </td>
+                    <td>
+                        '.$row->getStarting().'
+                    </td>
+                    <td>
+                        '.$row->getEnding().'
+                    </td>
+                </tr>';
+            }
+            $html .= '</table>';
+        }
+
+        $this->_createPage($html);
+    }
+
+    //  }}}
+    //  {{{ addEventAction()
+    /**
+     * Description of addEventAction()
+     *
+     * @return void
+     * @access public
+     */
+    public function addEventAction()
+    {
+        // get code for edit event here
+        //  application configuration
+        $conf = new Config;
+        $root =& $conf->parseConfig(
+            BASE . 'Toolkit/Members/config.ini',
+            'IniFile'
+        );
+        $eventMapper = new EventMapper(Toolkit_Database::getInstance());
+        $form = new Toolkit_Members_Events_CommonEditEvent(
+            $this->registry->dbh,
+            'edit_event',
+            $method = 'post',
+            '',
+            '',
+            null,
+            false,
+            $eventMapper
+        );
+
+        $form->configureForm();
+        $html = $form->toHtml(
+            Toolkit_Database::getInstance(),
+            new Toolkit_Image_Server()
+        );
+
+        $this->_createPage($html);
+    }
+
+    //  }}}
+}
diff --git a/Toolkit/Members/MembersOnly/Controller.php b/Toolkit/Members/MembersOnly/Controller.php
new file mode 100644 (file)
index 0000000..3f06b0e
--- /dev/null
@@ -0,0 +1,290 @@
+<?php
+/**
+ * Controller.php
+ *
+ * PHP Version 5.2
+ *
+ * @category  Toolkit
+ * @package   Members_MembersOnly
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @version   SVN: $id$
+ * @link      <>
+ */
+
+/**
+ * Toolkit_Members_MembersOnly_Controller
+ *
+ * Description of Toolkit_Members_MembersOnly_Controller
+ *
+ * @category  Toolkit
+ * @package   Members_MembersOnly
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @release   Release: $id$
+ * @link      <>
+ */
+class Toolkit_Members_MembersOnly_Controller
+{
+       //      {{{     properties
+
+    /**
+     * Member ID
+     * @var int
+     * @access private
+     */
+       private $_mid;
+
+       //      }}}
+
+       //      {{{     __construct()
+
+    /**
+     * Class constructor
+     *
+     * @param integer $mid
+     *
+     * @throws InvalidArgumentException
+     * @access public
+     */
+       public function __construct($mid)
+       {
+               if (!ctype_digit((string)$mid)) {
+                       throw new InvalidArgumentException(
+                               '$mid must be an integer'
+                       );
+               }
+
+               $this->_mid = $mid;
+               $_GET['catid'] = $page_id = ($_REQUEST['page_id'])
+                       ? $_REQUEST['page_id']
+                       : MEMBERS_ONLY_CATEGORY;
+       }
+
+       //      }}}
+
+       //      {{{     getPage()
+
+
+    /**
+     * Description of getPage()
+     *
+     * @param PDO                                    $dbh      Database handler
+     * @param Config_Container                       $config   Config COntainer
+     * @param Toolkit_Members_MembersOnly_Navigation $nav      Description of $nav..
+     * @param string                                 $template Description of $template..
+     *
+     * @return string|mixed Mixed only from error
+     * @access public
+     */
+       public function getPage(
+               PDO $dbh,
+               Config_Container $config,
+               Toolkit_Members_MembersOnly_Navigation $nav,
+               $template
+       ) {
+               $toolbox = new GLM_TEMPLATE($_GET['catid']);
+               $tEngine =  new HTML_Template_Flexy($GLOBALS['flexyOptions']);
+               $page    =  new Toolkit_Page($toolbox);
+
+               $navArray      = $nav->getNavStructure($dbh, $config);
+               $page->sideNav = $nav->renderPageNav($navArray, 'tree');
+               $page->title   = $toolbox->title();
+
+               $page->toolboxContent = '';
+
+               if (isset($_GET['Option']) && isset($_GET['Action'])) {
+                       if (   $_GET['Option'] == 'Member'
+                               && $_GET['Action'] == 'Edit'
+                       ) {
+                               $toolbox->set_catid(MEMBERS_PROFILE_FORM_PAGE);
+                               $page->toolboxContent = $toolbox->get_page();
+                       } elseif (   $_GET['Option'] == 'Coupons'
+                                         && $_GET['Action'] == 'List'
+                       ) {
+                               $toolbox->set_catid(MEMBERS_COUPONS_PAGE);
+                               $page->toolboxContent = $toolbox->get_page();
+                       } elseif (   $_GET['Option'] == 'Events'
+                                         && $_GET['Action'] == 'List'
+                       ) {
+                               $toolbox->set_catid(MEMBERS_EVENTS_PAGE);
+                               $page->toolboxContent = $toolbox->get_page();
+                       } elseif (   $_GET['Option'] == 'Reports'
+                                     && $_GET['Action'] == 'List'
+                       ) {
+                               $toolbox->set_catid(MEMBERS_REPORTS_PAGE);
+                               $page->toolboxContent = $toolbox->get_page();
+                       }
+               }
+
+               try {
+                       $method = $this->_getMethod();
+               } catch (BadMethodCallException $e) {
+                       return Toolkit_Common::handleError($e);
+               }
+
+               if ($method = $this->_getMethod()) {
+                       $page->toolboxContent .= $this->$method();
+               } else {
+                       $page->toolboxContent .= $toolbox->get_page();
+               }
+
+               $page->bottomScripts = Toolkit_Common::getScripts($GLOBALS['bottomScripts']);
+               $page->styles        = Toolkit_Common::getStyleSheets();
+
+               $tEngine->compile($template);
+               return $tEngine->bufferedOutputObject($page);
+       }
+
+       //      }}}
+       //      {{{     _getMethod()
+
+    /**
+     * Description of _getMethod
+     *
+     * @return boolean|string
+     * @access private
+     * @throws BadMethodCallException
+     */
+       private function _getMethod()
+       {
+               $act = strtolower($_GET['Action']);
+               $opt = ucfirst($_GET['Option']);
+
+               if (!empty($opt) && !empty($act)) {
+                       $methodName =  "_{$act}{$opt}";
+
+                       if (!method_exists($this, $methodName)) {
+                               throw new BadMethodCallException(
+                                       "Invalid method $methodName"
+                               );
+                       }
+
+                       return $methodName;
+               } else {
+                       //      no method to call
+                       return false;
+               }
+       }
+
+       //      }}}
+    // {{{ _deleteEvents()
+    /**
+     * description for _deleteEvents()
+     *
+     * @return string
+     * @access public
+     */
+    function _deleteEvents()
+    {
+        $event = new Toolkit_Event(
+            Toolkit_Database::getInstance()
+        );
+        if (ctype_digit($_REQUEST['event_id'])) {
+            $event->fetch($_REQUEST['event_id']);
+            $event->delete();
+        }
+        $out = '<p>Event Succesfully Deleted!</p>';
+        return $out . $this->_listEvents();
+
+    }// }}}
+
+       //      {{{     _editMember()
+
+    /**
+     *  Description for _editMember()
+     *
+     * @return string
+     * @access private
+     */
+       private function _editMember()
+       {
+               //      need to work at removing this
+               $_REQUEST['id'] = $_GET['id'] = $this->_mid;
+
+               $dbh = Toolkit_Database::getInstance();
+               $member = new Toolkit_Membersonly();
+               return $member->toHtml($dbh, $this->_mid);
+       }
+
+       //      }}}
+
+       //      {{{     _listReports()
+
+    /**
+     * Description for _listReports
+     *
+     * @return string
+     * @access private
+     */
+       private function _listReports()
+       {
+               $dReport = new Toolkit_Members_ExposureDetailReports(
+                       Toolkit_Database::getInstance()
+               );
+               $dReport->setQuery($this->_mid);
+               $dReport->setDefaultSort(array('month' => 'desc'));
+
+               $rEngine = new Structures_DataGrid_Renderer_Flexy();
+               $tplOpts = Toolkit_Members::getFlexyOptions();
+               $tEngine = new HTML_Template_Flexy($tplOpts);
+               $rEngine->setContainer($tEngine);
+
+               $out = $dReport->toHtml($rEngine);
+
+               if (   defined('EXPOSURE_REPORTS_LIST')
+                       && EXPOSURE_REPORTS_LIST
+               ) {
+                       $eReports = new Toolkit_Members_ExposureReports(
+                               Toolkit_Database::getInstance()
+                       );
+                       $eReports->setQuery($_REQUEST['reportMonth']);
+
+                       $out .= $eReports->toHtml($rEngine);
+               } else {
+                       $out .= "<h1>Exposure Reports</h1>\n";
+                       $out .= "<p>\n";
+                       $out .= "Exposure Reports - Check the statistics being compiled that include:\n";
+                       $out .= "<ul>\n";
+                       $out .= "\t<li>Listed - Number of times your member record was viewed in search result page.</li>\n";
+                       $out .= "\t<li>Details - Number of times your member profile page was visited.</li>\n";
+                       $out .= "\t<li>Clicks - Number of times your web site address was clicks on.</li>\n";
+                       $out .= "</ul\n";
+                       $out .= "</p>\n";
+                       $out .= "<br clear=\"all\"></hr>\n";
+               }
+
+               return $out;
+       }
+
+       //      }}}
+
+       //      {{{     _pageToolbox()
+
+    /**
+     * Description of _pageToolbox
+     *
+     * @return string
+     * @throws RuntimeException
+     * @access private
+     */
+       private function _pageToolbox()
+       {
+               if (!ctype_digit($_GET['page_id'])) {
+                       throw new RuntimeException(
+                               '$_GET[\'page_id\'] must be an integer'
+                       );
+               }
+
+               $toolbox = new GLM_TEMPLATE($_GET['page_id']);
+               $out  = $toolbox->get_category();
+               $out .= $toolbox->get_listings();
+
+               return $out;
+       }
+
+       //      }}}
+}
+?>
diff --git a/Toolkit/Members/MembersOnly/CouponsController.php b/Toolkit/Members/MembersOnly/CouponsController.php
new file mode 100644 (file)
index 0000000..2bf4094
--- /dev/null
@@ -0,0 +1,216 @@
+<?php
+/**
+ * CouponsController.php
+ *
+ * PHP Version 5.2
+ *
+ * @category  Toolkit
+ * @package   Members_MembersOnly
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @version   SVN: $id$
+ * @link      <>
+ */
+
+/**
+ * Toolkit_Members_MembersOnly_IndexController
+ *
+ * Description of Toolkit_Members_MembersOnly_IndexController
+ *
+ * @category  Toolkit
+ * @package   Members_MembersOnly
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @release   Release: $id$
+ * @link      <>
+ */
+class Toolkit_Members_MembersOnly_CouponsController
+       extends Toolkit_BaseControllerAbstract implements Toolkit_IController
+{
+       //      {{{     _creatPage()
+
+    /**
+     * Description for _createPage()
+     *
+     * @param string $couponContent
+     *
+     * @return void
+     * @access private
+     */
+       private function _createPage($couponContent)
+       {
+               $breadCrumbsFactory = new Toolkit_BreadCrumbsFactory(
+                       new Toolkit_Toolbox_PageGatewayPublishFactory(
+                               $this->registry->dbh
+                       )
+               );
+               $keywordReplacement = new Toolkit_Template_KeywordReplacement(
+                       new Toolkit_Toolbox_PageGatewayPublish(
+                               $this->registry->dbh
+                       )
+               );
+               $tEngine = new HTML_Template_Flexy($this->registry->flexyOptions);
+               $glmPage = new Toolkit_Page(
+                       new Toolkit_Template_Page(),
+                       $breadCrumbsFactory,
+                       new Toolkit_Toolbox_PageGatewayPublishFactory($this->registry->dbh),
+                       new Toolkit_Toolbox_ParagraphGatewayPublishFactory($this->registry->dbh),
+                       new Toolkit_Members_MembersOnly_Navigation_Factory(),
+                       $keywordReplacement,
+                       MEMBERS_COUPONS_PAGE
+               );
+
+               //$glmPage->title = $toolbox->title();
+
+               $glmPage->fetchPage();
+
+               $glmPage->toolboxContent .= $couponContent;
+        $baseSecureUrl
+            = ($_SERVER['HTTPS'] == 'on')
+            ? BASE_SECURE_URL
+            : MEDIA_BASE_URL;
+        $appBaseSecueUrl
+            = ($_SERVER['HTTPS'] == 'on')
+            ? GLM_APP_BASE_SECURE_URL
+            : MEDIA_APP_BASE_URL;
+        $GLOBALS['styleSheets'][]
+            = $baseSecureUrl . 'css/contactform.css';
+        $GLOBALS['styleSheets'][]
+            = $baseSecureUrl . 'Toolkit/Members/css/member-admin.css';
+
+               $glmPage->topScripts    = Toolkit_Common::getScripts($GLOBALS['topScripts']);
+               $glmPage->bottomScripts = Toolkit_Common::getScripts($GLOBALS['bottomScripts']);
+               $glmPage->styles        = Toolkit_Common::getStyleSheets();
+
+               $tEngine->compile('template.html');
+               $tEngine->outputObject($glmPage);
+
+       }
+
+       //      }}}
+
+       //      {{{     indexAction()
+
+    /**
+     * Description for indexAction()
+     *
+     * @return void
+     * @access public
+     * @throws Toolkit_Members_Exception
+     */
+       public function indexAction()
+       {
+               try {
+                       $sql = "
+                               SELECT *
+                                 FROM coupons
+                                WHERE member = :mid
+                                ORDER BY sdate desc, edate desc, title";
+
+                       $stmt = $this->registry->dbh->prepare($sql);
+                       $stmt->bindParam(
+                               ':mid',
+                               $this->registry->memberId,
+                               PDO::PARAM_INT
+                       );
+                       $stmt->execute();
+
+                       $data = $stmt->fetchAll(PDO::FETCH_ASSOC);
+
+                       $html = '';
+                       if (is_array($data) && !empty($data)) {
+                               $format = '<a href="%s">Edit</a>';
+                               $html  = "<table style=\"float: left;\" class=\"member-admin-table\">\n";
+                               $html .= "<tr>\n";
+                               $html .= "\t<th>&nbsp;</th>\n";
+                               $html .= "\t<th>Coupons</th>\n";
+                               $html .= "\t<th>Status</th>\n";
+                               $html .= "\t<th>Start</th>\n";
+                               $html .= "\t<th>End</th>\n";
+                               $html .= "\t<th>Expire</th>\n";
+                               $html .= "</tr>\n";
+                               foreach ($data as $row) {
+                                       $link = sprintf(
+                                               $format,
+                                               MEDIA_BASE_URL . "members-only-area/?rt=Coupons&ac=addCoupon&page_id={$_REQUEST['page_id']}&id={$row['id']}"
+                                       );
+                                       if ($row['pending']) {
+                                               $status = 'Pending';
+                                       } elseif ($row['active']) {
+                                               $status = 'Active';
+                                       } else {
+                                               $status = 'Disabled';
+                                       }
+                                       $html .= "<tr>\n";
+                                       $html .= "\t<td>$link</td>\n";
+                                       $html .= "\t<td>{$row['title']}</td>\n";
+                                       $html .= "\t<td>$status</td>\n";
+                                       $html .= "\t<td>{$row['sdate']}</td>\n";
+                                       $html .= "\t<td>{$row['edate']}</td>\n";
+                                       $html .= "\t<td>{$row['expiration']}</td>\n";
+                                       $html .= "</tr>\n";
+                               }
+                               $html .= "</table>\n";
+                       }
+               } catch (PDOException $e) {
+                       Toolkit_Logger::logException('DB Error', $e);
+                       throw new Toolkit_Members_Exception(
+                               'Error fetching member coupons list'
+                       );
+               }
+
+               $this->_createPage($html);
+       }
+
+       //      }}}
+       //      {{{     addCouponAction()
+
+    /**
+     * Description of addCouponAction()
+     *
+     * @return void
+     * @access public
+     */
+       public function addCouponAction()
+       {
+        //  application configuration
+        $conf = new Config;
+        $root =& $conf->parseConfig(
+                       BASE . 'Toolkit/Members/config.ini',
+                       'IniFile'
+               );
+
+               $couponMailerTemplateData = new stdClass();
+               $couponMailerTemplateData->memberName = $this->registry->memberName;
+
+               $flexyOptions = $this->registry->flexyOptions;
+               $flexyOptions['templateDir'] = BASE . 'Toolkit/Members/templates';
+               $flexyOptions['compileDir'] = BASE . 'Toolkit/Members/templates/compiled';
+               $tEngine = new HTML_Template_Flexy($flexyOptions);
+               $tEngine->compile('editCouponEmail.tpl');
+               $mailer = new Toolkit_Members_Coupons_Mailer(
+                       $tEngine,
+                       new Mail_mime("\n"),
+                       Mail::factory('mail')
+               );
+
+               $form = new Toolkit_Members_Coupons_EditCouponForm(
+                       'edit_coupon'
+               );
+
+               $form->configureForm($this->registry->dbh, $root);
+               $html = $form->toHtml(
+                       Toolkit_Database::getInstance(),
+            new Toolkit_Image_Server(),
+                       new Toolkit_Coupons_MemberCouponFactory(),
+                       $mailer,
+                       $couponMailerTemplateData
+               );
+
+               $this->_createPage($html);
+       }
+
+       //      }}}
+}
diff --git a/Toolkit/Members/MembersOnly/EditProfileController.php b/Toolkit/Members/MembersOnly/EditProfileController.php
new file mode 100644 (file)
index 0000000..96fe87c
--- /dev/null
@@ -0,0 +1,86 @@
+<?php
+/**
+ * EditProfileController.php
+ * 
+ * PHP Version 5.2
+ * 
+ * @category  Toolkit
+ * @package   Members_MembersOnly
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @version   SVN: $id$
+ * @link      <>
+ */
+
+/**
+ * Toolkit_Members_MembersOnly_EditProfileController
+ * 
+ * Description of Toolkit_Members_MembersOnly_EditProfileController
+ * 
+ * @category  Toolkit
+ * @package   Members_MembersOnly
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @release   Release: $id$
+ * @link      <>
+ */
+class Toolkit_Members_MembersOnly_EditProfileController
+       extends Toolkit_BaseControllerAbstract implements Toolkit_IController
+{
+       //      {{{     indexAction()
+
+    /**
+     * Description of indexAction()
+     * 
+     * @return void
+     * @access public 
+     */
+       public function indexAction()
+       {
+               $breadCrumbsFactory = new Toolkit_BreadCrumbsFactory(
+                       new Toolkit_Toolbox_PageGatewayPublishFactory(
+                               $this->registry->dbh
+                       )
+               );
+               $keywordReplacement = new Toolkit_Template_KeywordReplacement(
+                       new Toolkit_Toolbox_PageGatewayPublish(
+                               $this->registry->dbh
+                       )
+               );
+               $tEngine = new HTML_Template_Flexy($this->registry->flexyOptions);
+               $glmPage = new Toolkit_Page(
+                       new Toolkit_Template_Page(),
+                       $breadCrumbsFactory,
+                       new Toolkit_Toolbox_PageGatewayPublishFactory($this->registry->dbh),
+                       new Toolkit_Toolbox_ParagraphGatewayPublishFactory($this->registry->dbh),
+                       new Toolkit_Members_MembersOnly_Navigation_Factory(),
+                       $keywordReplacement,
+                       MEMBERS_PROFILE_FORM_PAGE
+               );
+
+               //$glmPage->title = $toolbox->title();
+
+               $glmPage->fetchPage();
+
+               //      need to work at removing this
+               $_REQUEST['id'] = $_GET['id'] = $this->registry->memberId;
+
+               $member = new Toolkit_Membersonly();
+               $glmPage->toolboxContent .= $member->toHtml(
+                       $this->registry->dbh,
+                       $this->registry->memberId
+               );
+
+               $glmPage->topScripts    = Toolkit_Common::getScripts($GLOBALS['topScripts']);
+               $glmPage->bottomScripts = Toolkit_Common::getScripts($GLOBALS['bottomScripts']);
+               $glmPage->styles        = Toolkit_Common::getStyleSheets();
+
+               $tEngine->compile('template.html');
+               $tEngine->outputObject($glmPage);
+       }
+
+       //      }}}
+}
+?>
diff --git a/Toolkit/Members/MembersOnly/EventsController.php b/Toolkit/Members/MembersOnly/EventsController.php
new file mode 100644 (file)
index 0000000..ac1ea5e
--- /dev/null
@@ -0,0 +1,200 @@
+<?php
+/**
+ * EventsController.php
+ *
+ * PHP Version 5.2
+ *
+ * @category  Toolkit
+ * @package   Members_MembersOnly
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @version   SVN: $id$
+ * @link      <>
+ */
+
+/**
+ * Toolkit_Members_MembersOnly_EventsController
+ *
+ * Description of Toolkit_Members_MembersOnly_EventsController
+ *
+ * @category  Toolkit
+ * @package   Members_MembersOnly
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @release   Release: $id$
+ * @link      <>
+ */
+
+class Toolkit_Members_MembersOnly_EventsController
+       extends Toolkit_BaseControllerAbstract
+    implements Toolkit_IController
+{
+       //      {{{     _creatPage()
+
+    /**
+     * Description of _createPage()
+     *
+     * @param string $eventContent Description of $eventcontent
+     *
+     * @return void
+     * @access private
+     */
+       private function _createPage($eventContent)
+       {
+               $breadCrumbsFactory = new Toolkit_BreadCrumbsFactory(
+                       new Toolkit_Toolbox_PageGatewayPublishFactory(
+                               $this->registry->dbh
+                       )
+               );
+               $keywordReplacement = new Toolkit_Template_KeywordReplacement(
+                       new Toolkit_Toolbox_PageGatewayPublish(
+                               $this->registry->dbh
+                       )
+               );
+               $tEngine = new HTML_Template_Flexy($this->registry->flexyOptions);
+               $glmPage = new Toolkit_Page(
+                       new Toolkit_Template_Page(),
+                       $breadCrumbsFactory,
+                       new Toolkit_Toolbox_PageGatewayPublishFactory($this->registry->dbh),
+                       new Toolkit_Toolbox_ParagraphGatewayPublishFactory($this->registry->dbh),
+                       new Toolkit_Members_MembersOnly_Navigation_Factory(),
+                       $keywordReplacement,
+                       MEMBERS_EVENTS_PAGE
+               );
+
+               $glmPage->fetchPage();
+
+               $glmPage->toolboxContent .= $eventContent;
+        $baseSecureUrl
+            = ($_SERVER['HTTPS'] == 'on')
+            ? BASE_SECURE_URL
+            : MEDIA_BASE_URL;
+        $appBaseSecueUrl
+            = ($_SERVER['HTTPS'] == 'on')
+            ? GLM_APP_BASE_SECURE_URL
+            : MEDIA_APP_BASE_URL;
+        $GLOBALS['styleSheets'][]
+            = $baseSecureUrl . 'css/contactform.css';
+        $GLOBALS['styleSheets'][]
+            = $baseSecureUrl . 'Toolkit/Members/css/member-admin.css';
+        $GLOBALS['styleSheets'][]
+            = $appBaseSecueUrl
+            . 'libjs/jqueryui/1.8.13/development-bundle/themes/base/jquery.ui.all.css';
+
+               $glmPage->topScripts    = Toolkit_Common::getScripts($GLOBALS['topScripts']);
+               $glmPage->bottomScripts = Toolkit_Common::getScripts($GLOBALS['bottomScripts']);
+               $glmPage->styles        = Toolkit_Common::getStyleSheets();
+
+               $tEngine->compile('template.html');
+               $tEngine->outputObject($glmPage);
+
+       }
+
+       //      }}}
+
+       //      {{{     indexAction()
+
+    /**
+     * Description for indexAction()
+     *
+     * @return void
+     * @throws Toolkit_Members_Exception
+     * @access public
+     */
+       public function indexAction()
+       {
+               try {
+            // get code for list events
+            $sql = "
+              SELECT *
+                FROM event
+               WHERE member_id = :mid
+            ORDER BY bdate DESC,edate DESC,header";
+                       $stmt = $this->registry->dbh->prepare($sql);
+                       $stmt->bindParam(
+                               ':mid',
+                               $this->registry->memberId,
+                               PDO::PARAM_INT
+                       );
+                       $stmt->execute();
+
+                       $data = $stmt->fetchAll(PDO::FETCH_ASSOC);
+                       $html = '';
+                       if (is_array($data) && !empty($data)) {
+                $html .= '<table style="float:left;" class="member-admin-table">';
+                $html .= '<tr>
+                    <th>&nbsp;</th>
+                    <th>Event</th>
+                    <th>Status</th>
+                    <th>Start</th>
+                    <th>End</th>
+                        </tr>';
+                foreach( $data as $row ){
+                    $html .= '<tr>
+                        <td>
+                            <div class="buttons">
+                                <a href="'.$this->page.'?rt=Events&amp;ac=addEvent&amp;id='.$row["id"].'">
+                                Edit
+                            </a>
+                            </div>
+                        </td>
+                        <td>
+                            '.$row["header"].'
+                        </td>
+                        <td>
+                            '.( ( $row['visable'] == 't' ) ? 'Active' : 'Pending').'
+                        </td>
+                        <td>
+                            '.$row["bdate"].'
+                        </td>
+                        <td>
+                            '.$row["edate"].'
+                        </td>
+                    </tr>';
+                }
+                $html .= '</table>';
+            }
+               } catch (PDOException $e) {
+                       Toolkit_Logger::logException('DB Error', $e);
+                       throw new Toolkit_Members_Exception(
+                               'Error fetching member Events list'
+                       );
+               }
+               $this->_createPage($html);
+       }
+
+       //      }}}
+       //      {{{     addEventAction()
+    /**
+     * Description of addEventAction()
+     *
+     * @return void
+     * @access public
+     */
+       public function addEventAction()
+       {
+        // get code for edit event here
+        //  application configuration
+        $conf = new Config;
+        $root =& $conf->parseConfig(
+                       BASE . 'Toolkit/Members/config.ini',
+                       'IniFile'
+               );
+        $form = new Toolkit_Members_Events_EditEvent(
+            $this->registry->dbh,
+                       'edit_event'
+               );
+
+               $form->configureForm();
+               $html = $form->toHtml(
+                       Toolkit_Database::getInstance(),
+                       new Toolkit_Image_Server()
+               );
+
+               $this->_createPage($html);
+       }
+
+       //      }}}
+}
diff --git a/Toolkit/Members/MembersOnly/IndexController.php b/Toolkit/Members/MembersOnly/IndexController.php
new file mode 100644 (file)
index 0000000..c67095c
--- /dev/null
@@ -0,0 +1,76 @@
+<?php
+/**
+ * IndexController.php
+ * 
+ * PHP Version 5.2
+ * 
+ * @category  Toolkit
+ * @package   Members_MembersOnly
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @version   SVN: $id$
+ * @link      <>
+ */
+
+/**
+ * Toolkit_Members_MembersOnly_IndexController
+ * 
+ * Description of Toolkit_Members_MembersOnly_IndexController
+ * 
+ * @category  Toolkit
+ * @package   Members_MembersOnly
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @release   Release: $id$
+ * @link      <>
+ */
+class Toolkit_Members_MembersOnly_IndexController
+       extends Toolkit_BaseControllerAbstract implements Toolkit_IController
+{
+       //      {{{     indexAction()
+
+    /**
+     * Description of indexAction()
+     * 
+     * @return void
+     * @access public 
+     */
+       public function indexAction()
+       {
+               $breadCrumbsFactory = new Toolkit_BreadCrumbsFactory(
+                       new Toolkit_Toolbox_PageGatewayPublishFactory(
+                               $this->registry->dbh
+                       )
+               );
+               $keywordReplacement = new Toolkit_Template_KeywordReplacement(
+                       new Toolkit_Toolbox_PageGatewayPublish(
+                               $this->registry->dbh
+                       )
+               );
+               $tEngine = new HTML_Template_Flexy($this->registry->flexyOptions);
+               $glmPage = new Toolkit_Page(
+                       new Toolkit_Template_Page(),
+                       $breadCrumbsFactory,
+                       new Toolkit_Toolbox_PageGatewayPublishFactory($this->registry->dbh),
+                       new Toolkit_Toolbox_ParagraphGatewayPublishFactory($this->registry->dbh),
+                       new Toolkit_Members_MembersOnly_Navigation_Factory(),
+                       $keywordReplacement,
+                       $this->registry->pageid
+               );
+
+               //$glmPage->title = $toolbox->title();
+
+               $glmPage->fetchPage();
+
+               $glmPage->topScripts    = Toolkit_Common::getScripts($GLOBALS['topScripts']);
+               $glmPage->bottomScripts = Toolkit_Common::getScripts($GLOBALS['bottomScripts']);
+               $glmPage->styles        = Toolkit_Common::getStyleSheets();
+
+               $tEngine->compile('template.html');
+               $tEngine->outputObject($glmPage);
+       }
+
+       //      }}}
+}
diff --git a/Toolkit/Members/MembersOnly/LeadsController.php b/Toolkit/Members/MembersOnly/LeadsController.php
new file mode 100644 (file)
index 0000000..f5ceb24
--- /dev/null
@@ -0,0 +1,88 @@
+<?php
+/**
+ * LeadsController.php
+ * 
+ * PHP Version 5.2
+ * 
+ * @category  Toolkit
+ * @package   Members_MembersOnly
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @version   SVN: $id$
+ * @link      <>
+ *  
+ */
+
+/**
+ * Toolkit_Members_MembersOnly_LeadsController
+ * 
+ * Description of Toolkit_Members_MembersOnly_LeadsController
+ * 
+ * @category  Toolkit
+ * @package   Members_MembersOnly
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @release   Release: $id$
+ * @link      <>
+ */
+class Toolkit_Members_MembersOnly_LeadsController
+       extends Toolkit_BaseControllerAbstract implements Toolkit_IController
+{
+       //      {{{     indexAction()
+
+    /**
+     * Description of indexAction()
+     * 
+     * @return void
+     * @access public 
+     */
+       public function indexAction()
+       {
+               $breadCrumbsFactory = new Toolkit_BreadCrumbsFactory(
+                       new Toolkit_Toolbox_PageGatewayPublishFactory(
+                               $this->registry->dbh
+                       )
+               );
+               $keywordReplacement = new Toolkit_Template_KeywordReplacement(
+                       new Toolkit_Toolbox_PageGatewayPublish(
+                               $this->registry->dbh
+                       )
+               );
+               $tEngine = new HTML_Template_Flexy($this->registry->flexyOptions);
+               $glmPage = new Toolkit_Page(
+                       new Toolkit_Template_Page(),
+                       $breadCrumbsFactory,
+                       new Toolkit_Toolbox_PageGatewayPublishFactory($this->registry->dbh),
+                       new Toolkit_Toolbox_ParagraphGatewayPublishFactory($this->registry->dbh),
+                       new Toolkit_Members_MembersOnly_Navigation_Factory(),
+                       $keywordReplacement,
+                       MEMBERS_LEADS_PAGE
+               );
+
+               $glmPage->fetchPage();
+
+        // Export File Form
+        $export = new Toolkit_Members_Leads_ExportFileForm(
+            'file_export',
+            'POST',
+            MEDIA_BASE_URL . 'members-only-area/?rt=Leads&page_id='.MEMBERS_LEADS_PAGE,
+            '',
+            null,
+            true
+        );
+
+        $export->configureForm();
+               $glmPage->toolboxContent .= $export->toHtml($this->registry->dbh);
+
+               $glmPage->topScripts    = Toolkit_Common::getScripts($GLOBALS['topScripts']);
+               $glmPage->bottomScripts = Toolkit_Common::getScripts($GLOBALS['bottomScripts']);
+               $glmPage->styles        = Toolkit_Common::getStyleSheets();
+
+               $tEngine->compile('template.html');
+               $tEngine->outputObject($glmPage);
+       }
+
+       //      }}}
+}
diff --git a/Toolkit/Members/MembersOnly/Navigation/Factory.php b/Toolkit/Members/MembersOnly/Navigation/Factory.php
new file mode 100644 (file)
index 0000000..e5a95dc
--- /dev/null
@@ -0,0 +1,89 @@
+<?php
+/**
+ * Factory.php
+ * 
+ * PHP Version 5.2
+ * 
+ * @category  Toolkit
+ * @package   Members_MembersOnly
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @version   SVN: $id$
+ * @link      <>
+ */
+
+/**
+ * Toolkit_Members_MembersOnly_Navigation_Factory
+ * 
+ * Description of Toolkit_Members_MembersOnly_Navigation_Factory
+ * 
+ * @category  Toolkit
+ * @package   Members_MembersOnly
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @release   Release: $id$
+ * @link      <>
+ */
+class Toolkit_Members_MembersOnly_Navigation_Factory
+       extends Toolkit_NavigationFactoryAbstract
+{
+    /**
+     * Description of $_gateway
+     * @var Toolkit_Toolbox_PageGatewayAbstract
+     * @access private 
+     */
+       private $_gateway;
+
+    /**
+     * Description of setGateway()
+     *
+     * @param Toolkit_Toolbox_PageGatewayAbstract $gateway Toolbox Gateway
+     * 
+     * @return void
+     * @access public
+     */
+       public function setGateway(Toolkit_Toolbox_PageGatewayAbstract $gateway)
+       {
+               $this->_gateway = $gateway;
+       }
+
+    /**
+     * Description of createSideNav()
+     * 
+     * @return \Toolkit_Members_MembersOnly_Navigation_Side 
+     * @access public
+     */
+       public function createSideNav()
+       {
+               $nav = new Toolkit_Members_MembersOnly_Navigation_Side(
+                       new HTML_Menu(),
+                       new HTML_Menu_DirectTreeRenderer()
+               );
+
+               $conf = new Config;
+               $root =& $conf->parseConfig(
+                       BASE . 'Toolkit/Members/config.ini',
+                       'IniFile'
+               );
+               $nav->setConfig($root);
+
+               return $nav;
+       }
+
+    /**
+     * Description for createMainNav()
+     * 
+     * @return \Toolkit_Template_Navigation_MainNavigationStatic 
+     * @access public
+     */
+       public function createMainNav()
+       {
+               return new Toolkit_Template_Navigation_MainNavigationStatic(
+                       new HTML_Menu(),
+                       new Toolkit_Template_Navigation_Renderer_DirectTreeLastLi('lastli')
+               );
+       }
+}
+?>
diff --git a/Toolkit/Members/MembersOnly/Navigation/Side.php b/Toolkit/Members/MembersOnly/Navigation/Side.php
new file mode 100644 (file)
index 0000000..249d968
--- /dev/null
@@ -0,0 +1,289 @@
+<?php
+/**
+ * Side.php
+ *
+ * PHP Version 5.2
+ *
+ * @category  Toolkit
+ * @package   Members_MembersOnly
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @version   SVN: $id$
+ * @link      <>
+ */
+
+/**
+ * Toolkit_Members_MembersOnly_Navigation_Side
+ *
+ * Description of Toolkit_Members_MembersOnly_Navigation_Side
+ *
+ * @category  Toolkit
+ * @package   Members_MembersOnly
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @release   Release: $id$
+ * @link      <>
+ */
+class Toolkit_Members_MembersOnly_Navigation_Side extends Toolkit_NavigationAbstract
+    implements Toolkit_INavigation
+{
+    //  {{{ properties
+
+    /**
+     * Description for $_config
+     * @var Config_Container
+     * @access private
+     */
+    private $_config = null;
+
+    //  }}}
+    //  {{{ __construct()
+
+    /**
+     * Class constructor
+     *
+     * @param HTML_Menu          $menu    Description for HTML_Menu
+     * @param HTML_Menu_Renderer $rEngine Description for HTML Menu Renderer
+     *
+     * @access public
+     */
+    public function __construct(
+        HTML_Menu $menu,
+        HTML_Menu_Renderer $rEngine
+    ) {
+        $this->menu      = $menu;
+        $this->rEngine   = $rEngine;
+        $this->currIndex = 'home';
+    }
+
+    //  }}}
+
+    //  {{{ setConfig()
+
+    /**
+     * Description for setConfig
+     *
+     * @param Config_Container $config Config container
+     *
+     * @return void
+     * @access public
+     */
+    public function setConfig(Config_Container $config)
+    {
+        $this->_config = $config;
+    }
+
+    //  }}}
+    //  {{{ getNavSructure()
+    //  @codeCoverageIgnoreStart
+
+    /**
+     * Sets up a multi dimensional array used for the nav structure
+     *
+     * @param Toolkit_Toolbox_GatewayAbstract $gateway Toolbox gateway
+     * @param integer                         $id      id of page we're on
+     *
+     * @return array navigation structure
+     * @access public
+     */
+    public function getNavStructure(
+        Toolkit_Toolbox_GatewayAbstract $gateway,
+        $id
+    ) {
+        $nav = $this->_getToolboxNav($gateway, $id);
+        $nav['travel'] = array(
+            'title' => 'Travel Reservation System',
+            'url' => 'http://travel.gaslightmedia.com/members/'
+        );
+        $nav['logout'] = array(
+            'title' => 'Log out',
+            'url' => MEDIA_BASE_URL . 'members-only-area/?logout=',
+        );
+
+        $hasTravelSystem
+            = $this->_config->getItem('section', 'conf')
+                ->getItem('directive', 'glmReservations')
+                ->getContent();
+
+        if (!$hasTravelSystem) {
+            unset($nav['travel']);
+        }
+
+        $hasCoupons
+            = $this->_config->getItem('section', 'members only')
+                ->getItem('directive', 'coupons')
+                ->getContent();
+
+        if (!$hasCoupons) {
+            unset($nav[MEMBER_COUPONS_PAGE]);
+        }
+
+        $hasEvents
+            = $this->_config->getItem('section', 'members only')
+                ->getItem('directive', 'events')
+                ->getContent();
+
+        if (!$hasEvents) {
+            unset($nav['events']);
+        }
+
+        return $nav;
+    }
+
+    //  @codeCoverageIgnoreEnd
+    //  }}}
+    //  {{{ _getToolboxNav()
+
+    /**
+     * Description for _getToolboxNav()
+     *
+     * @param Toolkit_Toolbox_GatewayAbstract $gateway  Toolbox gateway
+     * @param integer                         $id       ID of page we're on
+     * @param array                           $tree     Description for $tree
+     * @param unknown                         $appendTo Description for $appendTo
+     *
+     * @return array
+     * @access private
+     */
+    private function _getToolboxNav(
+        Toolkit_Toolbox_GatewayAbstract $gateway,
+        $id,
+        array $tree = null,
+        $appendTo = null
+    ) {
+        $currentPage = $gateway->find($id);
+        $subPages    = $gateway->findAllByParent($currentPage['parent']);
+
+        $nav = array();
+        foreach ($subPages as $subPage) {
+            if ($subPage['active']) {
+                if (   defined('MEMBERS_PROFILE_FORM_PAGE')
+                    && $subPage['id'] == MEMBERS_PROFILE_FORM_PAGE
+                ) {
+                    $nav[$subPage['id']] = array(
+                        'title' => strip_tags($subPage['navigation_name']),
+                        'url' => MEDIA_BASE_URL . 'members-only-area/?rt=EditProfile&tab=info'
+                    );
+                } elseif (   defined('MEMBERS_COUPONS_PAGE')
+                          && $subPage['id'] == MEMBERS_COUPONS_PAGE
+                ) {
+                    $nav[$subPage['id']] = array(
+                        'title' => strip_tags($subPage['navigation_name']),
+                        'url' => MEDIA_BASE_URL . 'members-only-area/?rt=Coupons&page_id=' . $subPage['id'],
+                        'sub' => array(
+                            'listCoupons' => array(
+                                'title' => 'List Coupons',
+                                'url' => MEDIA_BASE_URL . 'members-only-area/?rt=Coupons&page_id=' . $subPage['id'],
+                            ),
+                            'addCoupon' => array(
+                                'title' => 'Add Coupon',
+                                'url' => MEDIA_BASE_URL . 'members-only-area/?rt=Coupons&ac=addCoupon&page_id=' . $subPage['id'],
+                            )
+                        )
+                    );
+                } elseif (   defined('MEMBERS_EVENTS_PAGE')
+                          && $subPage['id'] == MEMBERS_EVENTS_PAGE
+                ) {
+                    $routerCalled
+                        = (defined('COMMON_EVENTS') && COMMON_EVENTS)
+                        ? 'CommonEvents'
+                        : 'Events';
+                    $nav[$subPage['id']] = array(
+                        'title' => strip_tags($subPage['navigation_name']),
+                        'url' => MEDIA_BASE_URL . 'members-only-area/?rt='.$routerCalled.'&page_id=' . $subPage['id'],
+                        'sub' => array(
+                            'listEvents' => array(
+                                'title' => 'List Events',
+                                'url' => MEDIA_BASE_URL . 'members-only-area/?rt='.$routerCalled.'&page_id=' . $subPage['id'],
+                            ),
+                            'addEvent' => array(
+                                'title' => 'Add Event',
+                                'url' => MEDIA_BASE_URL . 'members-only-area/?rt='.$routerCalled.'&ac=addEvent&page_id=' . $subPage['id'],
+                            )
+                        )
+                    );
+                } elseif (   defined('MEMBERS_REPORTS_PAGE')
+                          && $subPage['id'] == MEMBERS_REPORTS_PAGE
+                ) {
+                    $nav[$subPage['id']] = array(
+                        'title' => strip_tags($subPage['navigation_name']),
+                        'url' => MEDIA_BASE_URL . 'members-only-area/?rt=Reports&page_id=' . $subPage['id']
+                    );
+                } elseif (   defined('MEMBERS_LEADS_PAGE')
+                          && $subPage['id'] == MEMBERS_LEADS_PAGE
+                ) {
+                    $nav[$subPage['id']] = array(
+                        'title' => strip_tags($subPage['navigation_name']),
+                        'url' => MEDIA_BASE_URL . 'members-only-area/?rt=Leads&page_id=' . $subPage['id']
+                    );
+                } else {
+                    $nav[$subPage['id']] = array(
+                        'title' => strip_tags($subPage['navigation_name']),
+                        'url' => MEDIA_BASE_URL . 'members-only-area/?page_id=' . $subPage['id']
+                    );
+                }
+            }
+        }
+
+        if (is_array($tree) && array_key_exists($appendTo, $nav)) {
+            $nav[$appendTo]['sub'] = $tree;
+        }
+
+        if ($currentPage['parent'] != MEMBERS_CATEGORY) {
+            return $this->_getToolboxNav(
+                $gateway,
+                $currentPage['parent'],
+                $nav,
+                $id
+            );
+        } else {
+            //var_dump($id, $nav);die;
+            return $nav;
+        }
+    }
+
+    //  }}}
+
+    //  {{{ setCurrentIndex()
+
+    /**
+     * Description for setCurrentIndex
+     *
+     * @return void
+     * @access protected
+     */
+    protected function setCurrentIndex()
+    {
+        $this->menu->forceCurrentIndex($_GET['page_id']);
+    }
+
+    //  }}}
+    //  {{{ setNavTemplates()
+
+    /**
+     * Description of setNavTemplates
+     *
+     * @return void
+     * @access protected
+     */
+    protected function setNavTemplates()
+    {
+        $tpl = '<a class="%s" href="%s">{title}</a>';
+        $this->rEngine->setEntryTemplate(
+            HTML_MENU_ENTRY_INACTIVE,
+            sprintf($tpl, '{class}', '{url}', '{desc}', '{title}')
+        );
+        $this->rEngine->setEntryTemplate(
+            HTML_MENU_ENTRY_ACTIVE,
+            sprintf($tpl, 'active {class}', '{url}', '{desc}', '{title}')
+        );
+        $this->rEngine->setEntryTemplate(
+            HTML_MENU_ENTRY_ACTIVEPATH,
+            sprintf($tpl, 'active {class}', '{url}', '{desc}', '{title}')
+        );
+    }
+
+    //  }}}
+}
diff --git a/Toolkit/Members/MembersOnly/ReportsController.php b/Toolkit/Members/MembersOnly/ReportsController.php
new file mode 100644 (file)
index 0000000..5261544
--- /dev/null
@@ -0,0 +1,113 @@
+<?php
+/**
+ * ReportsController.php
+ *
+ * PHP Version 5.2
+ *
+ * @category  Toolkit
+ * @package   Members_MembersOnly
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @version   SVN: $id$
+ * @link      <>
+ */
+
+/**
+ * Toolkit_Members_MembersOnly_ReportsController
+ *
+ * Description of Toolkit_Members_MembersOnly_ReportsController
+ *
+ * @category  Toolkit
+ * @package   Members_MembersOnly
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @release   Release: $id$
+ * @link      <>
+ */
+class Toolkit_Members_MembersOnly_ReportsController
+       extends Toolkit_BaseControllerAbstract implements Toolkit_IController
+{
+       //      {{{     indexAction()
+
+    /**
+     * Desciption of indexAction()
+     *
+     * @return void
+     * @access public
+     */
+       public function indexAction()
+       {
+               $breadCrumbsFactory = new Toolkit_BreadCrumbsFactory(
+                       new Toolkit_Toolbox_PageGatewayPublishFactory(
+                               $this->registry->dbh
+                       )
+               );
+               $keywordReplacement = new Toolkit_Template_KeywordReplacement(
+                       new Toolkit_Toolbox_PageGatewayPublish(
+                               $this->registry->dbh
+                       )
+               );
+               $tEngine = new HTML_Template_Flexy($this->registry->flexyOptions);
+               $glmPage = new Toolkit_Page(
+                       new Toolkit_Template_Page(),
+                       $breadCrumbsFactory,
+                       new Toolkit_Toolbox_PageGatewayPublishFactory($this->registry->dbh),
+                       new Toolkit_Toolbox_ParagraphGatewayPublishFactory($this->registry->dbh),
+                       new Toolkit_Members_MembersOnly_Navigation_Factory(),
+                       $keywordReplacement,
+                       MEMBERS_REPORTS_PAGE
+               );
+
+               //$glmPage->title = $toolbox->title();
+
+               $glmPage->fetchPage();
+
+               if (   defined('EXPOSURE_REPORTS_LIST')
+                       && EXPOSURE_REPORTS_LIST
+               ) {
+                       $eReports = new Toolkit_Members_ExposureReports(
+                               $this->registry->dbh
+                       );
+                       $eReports->setQuery($_REQUEST['reportMonth']);
+
+                       $out = $eReports->toHtml($rEngine);
+               } else {
+                       $out = "<h1>Exposure Reports</h1>\n";
+                       $out .= "<p>\n";
+                       $out .= "Exposure Reports - Check the statistics being compiled that include:\n";
+                       $out .= "<ul>\n";
+                       $out .= "\t<li>Listed - Number of times your member record was viewed in search result page.</li>\n";
+                       $out .= "\t<li>Details - Number of times your member profile page was visited.</li>\n";
+                       $out .= "\t<li>Clicks - Number of times your web site address was clicks on.</li>\n";
+                       $out .= "</ul\n";
+                       $out .= "</p>\n";
+                       $out .= "<br clear=\"all\"></hr>\n";
+               }
+
+        $dReport = new Toolkit_Members_ExposureDetailReports(
+                       $this->registry->dbh
+               );
+               $dReport->setQuery($this->registry->memberId);
+               $dReport->setDefaultSort(array('month' => 'desc'));
+
+               $rEngine        = new Structures_DataGrid_Renderer_Flexy();
+               $tplOpts        = Toolkit_Members::getFlexyOptions();
+               $tReportsEngine = new HTML_Template_Flexy($tplOpts);
+               $rEngine->setContainer($tReportsEngine);
+
+               $out .= $dReport->toHtml($rEngine);
+
+               $glmPage->toolboxContent .= $out;
+
+               $glmPage->topScripts    = Toolkit_Common::getScripts($GLOBALS['topScripts']);
+               $glmPage->bottomScripts = Toolkit_Common::getScripts($GLOBALS['bottomScripts']);
+               $glmPage->styles        = Toolkit_Common::getStyleSheets();
+
+               $tEngine->compile('template.html');
+               $tEngine->outputObject($glmPage);
+       }
+
+       //      }}}
+}
diff --git a/Toolkit/Members/MembersOnly/index.php b/Toolkit/Members/MembersOnly/index.php
new file mode 100644 (file)
index 0000000..b150e2e
--- /dev/null
@@ -0,0 +1,91 @@
+<?php
+/**
+ * index.php
+ * 
+ * PHP Version 5.2
+ * 
+ * @category  Toolkit
+ * @package   Members_MembersOnly
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @version   SVN: $id$
+ * @link      <>
+ */
+require_once '../../../setup.phtml';
+HTTP_Session2::useCookies(false);
+HTTP_Session2::start();
+//  application configuration
+$conf = new Config;
+$root =& $conf->parseConfig(BASE . 'Toolkit/Members/config.ini', 'IniFile');
+
+$authContainer = new Toolkit_Members_AuthContainer(
+       Toolkit_Database::getInstance(),
+       array(
+               'table'       => 'member',
+               'usernamecol' => 'member_login',
+               'passwordcol' => 'member_passwd',
+               'db_fields'   => array('member_id', 'member_name'),
+               'cryptType'   => 'none',
+               'db_where'    => 'new_member <> true AND active = true',
+       )
+);
+
+$memberAuth = new Toolkit_Members_Auth(
+       $root,
+       $authContainer,
+       '',
+       false
+);
+$memberAuth->setIdle();
+$memberAuth->start();
+
+if (isset($_GET['logout'])) {
+       $memberAuth->logout();
+}
+
+if (!$memberAuth->checkAuth()) {
+       //      Manually adjust the authentication status for empty credentials
+       if (empty($_POST['username']) || empty($_POST['password'])) {
+               $status = -3;
+       }
+       $status = $memberAuth->getStatus();
+       header('Location: ' . MEDIA_BASE_URL . 'index.php?catid=' . MEMBERS_CATEGORY . '&status=' . $status);
+} else {
+    if (!Toolkit_Membersonly::checkMemberExists(
+        Toolkit_Database::getInstance(),
+        $memberAuth->getAuthData('member_id')
+    )) {
+        $memberAuth->logout();
+        $status = $memberAuth->getStatus();
+        header('Location: ' . MEDIA_BASE_URL . 'index.php?catid=' . MEMBERS_CATEGORY . '&status=' . $status);
+    }
+       //      Carry over from old code
+       //      @todo: work to remove
+       $pageId = filter_var($_REQUEST['page_id']);
+    if ($pageId) {
+        $_GET['catid']   = $pageId;
+        $_GET['page_id'] = $pageId;
+    }
+       $_GET['catid'] = ctype_digit($_REQUEST['page_id'])
+               ? $_REQUEST['page_id']
+               : MEMBERS_ONLY_HOME_PAGE;
+
+       //      Create a new registry so we don't pollute the global namespace
+       $registry = new Toolkit_Registry;
+
+       $registry->cacheOptions = $GLOBALS['cacheOptions'];
+       $registry->flexyOptions = $GLOBALS['flexyOptions'];
+       $registry->memberId     = $memberAuth->getAuthData('member_id');
+       $registry->memberName   = $memberAuth->getAuthData('member_name');
+       $registry->logger       = Toolkit_Logger::getLogger();
+       $registry->pageid       = $_GET['catid'];
+       $registry->dbh          = Toolkit_Database::getInstance();
+
+       //      Create a router so we can get where we need to be.
+       $registry->router = new Toolkit_Router($registry);
+       $registry->router->setPath(BASE . 'Toolkit/Members/MembersOnly');
+       $registry->router->setApplication('Members/MembersOnly');
+
+       $registry->router->loader();
+}
diff --git a/Toolkit/Members/PackageList.php b/Toolkit/Members/PackageList.php
new file mode 100644 (file)
index 0000000..1870d19
--- /dev/null
@@ -0,0 +1,315 @@
+<?php
+
+/**
+ * File Doc Comment
+ *
+ * PHP version 5
+ *
+ * @category MembersDB
+ * @package  Toolkit_Members
+ * @author   Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @license     http://www.gaslightmedia.com Gaslightmedia
+ * @release  CVS: $Id: SearchList.php,v 1.47 2010/07/14 23:31:14 jamie Exp $
+ * @link     http://demo.gaslightmedia.com
+ */
+
+/**
+ * Datagrid of search results of member db
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @link      http://demo.gaslightmedia.com
+ */
+class Toolkit_Members_PackageList
+    extends Toolkit_FlexyDataGridBuilder
+{
+   /**
+     * Description for protected
+     * @var    string
+     * @access protected
+     */
+    protected $tableName = 'member_packages';
+
+    /**
+     * Description for protected
+     * @var    string
+     * @access protected
+     */
+    protected $template = 'packageList.tpl';
+    protected $members = array();
+    public $type;
+
+    /**
+     * Class constructor
+     *
+     * define where templates for the data grid are at, then call the parent constructor which will handle
+     * finishing the settings for the datagrid.
+     *
+     * After all settings are finished you can call the toHTML() function on this object and the datagrid
+     * will be rendered and returned as a string.  Optionally you could call show() and the datagrid would
+     * be rendered and output immediatley to the screen.
+     *
+     * @param PDO     $pdo           PHP Data Object to use for DB calls
+     * @param string  $limit         The number of records to display per page.
+     * @param int     $page             The current page viewed.
+     *                               In most cases, this is useless.
+     *                               Note: if you specify this, the "page" GET
+     *                               variable will be ignored.
+     * @param string  $rendererType  The type of renderer to use.
+     *                               You may prefer to use the $type argument
+     *                               of {@link render}, {@link fill} or
+     *                               {@link getOutput}
+     * @param boolean $showSearchBox If we should allow searching for members
+     * @param array   $sections      specific members we should show
+     *
+     * @return void
+     * @access public
+     */
+    public function __construct(
+        PDO $pdo,
+        $limit = null,
+        $page = null,
+        $rendererType = null,
+        $showSearchBox = true,
+        array $sections = null
+    ) {
+        $GLOBALS['styleSheets'][] = BASE_URL . 'Toolkit/Members/css/member.css';
+        parent::__construct($pdo, $limit, $page, $rendererType);
+        $GLOBALS['bottomScripts'][]
+            = BASE_URL . 'Toolkit/Members/libjs/business-search.js';
+        if (defined("MEMBER_SESSION_LIST") && MEMBER_SESSION_LIST) {
+            $GLOBALS['bottomScripts'][]
+                = BASE_URL . 'Toolkit/Members/libjs/travel-list.js';
+        }
+        $this->setPackageType();
+    }
+
+    /**
+     * Configures the columns that will be used in our datagrid renderer.
+     *
+     * @return void
+     * @access protected
+     */
+    protected function configureColumns()
+    {
+        $this->addColumn(
+            new Structures_DataGrid_Column(
+                'Member Id',
+                'member_id',
+                'member_id'
+            )
+        );
+
+        $this->addColumn(new Structures_DataGrid_Column(
+            'URL',
+            'url',
+            'url',
+            null,
+            null,
+            array(&$this, 'url')
+        ));
+        $this->addColumn(
+            new Structures_DataGrid_Column(
+                'Member Name',
+                'member_name',
+                'member_name'
+            )
+        );
+
+        $this->addColumn(
+            new Structures_DataGrid_Column(
+                'Street',
+                'street',
+                'street'
+            )
+        );
+
+        $this->addColumn(
+            new Structures_DataGrid_Column(
+                'City',
+                'city',
+                'city'
+            )
+        );
+
+        $this->addColumn(
+            new Structures_DataGrid_Column(
+                'State',
+                'state',
+                'state'
+            )
+        );
+
+        $this->addColumn(
+            new Structures_DataGrid_Column(
+                'State Abb',
+                'state_abbr',
+                'state_abbr'
+            )
+        );
+
+        $this->addColumn(
+            new Structures_DataGrid_Column(
+                'Zip',
+                'zip',
+                'zip'
+            )
+        );
+
+        $this->addColumn(
+            new Structures_DataGrid_Column(
+                'Phone',
+                'phone',
+                'phone'
+            )
+        );
+
+        $this->addColumn(
+            new Structures_DataGrid_Column(
+                'member_contact_email',
+                'member_contact_email',
+                'member_contact_email'
+            )
+        );
+
+        $this->addColumn(
+            new Structures_DataGrid_Column(
+                'website',
+                'website',
+                'website',
+                null,
+                null,
+                array($this, 'website')
+            )
+        );
+        $this->addColumn(new Structures_DataGrid_Column(
+            'Address',
+            'address',
+            'address',
+            null,
+            null,
+            array($this, 'drivingDirections')
+        ));
+
+        $this->addColumn(
+            new Structures_DataGrid_Column(
+                'Packages',
+                'packages',
+                null,
+                null,
+                null,
+                array($this, 'getPackages')
+            )
+        );
+
+        $this->addColumn(
+            new Structures_DataGrid_Column(
+            'Logo',
+            'logo',
+            'logo',
+            null,
+            null,
+            array(&$this, 'logo')
+        ));
+    }
+    /**
+     * Returns the logo for a member.
+     *
+     * @param array $data tuple record from db
+     *
+     * @return false when empty, otherwise logo path for member
+     * @access
+     */
+    public function logo($data)
+    {
+        extract($data['record']);
+        return empty($logo) ? false : MEMBER_PHOTOS . $logo;
+    }
+
+    public function url($data)
+    {
+        extract($data['record']);
+        $exposure = new Toolkit_Members_Exposure($member_id, 'list');
+        $exposure->runUpdate();
+        if ($pageId = filter_var($_REQUEST['page_id'], FILTER_VALIDATE_INT)) {
+            return MEDIA_BASE_URL . "members-only-area/?page_id={$pageId}&member_id={$member_id}";
+        }
+        //return MEDIA_BASE_URL . "member-profile/{$_GET['catid']}/$member_id/";
+        $memberName = str_replace(' ', '-', $member_name);
+        $pattern = '/[\/#&?\'"]|amp;/';
+        $name = preg_replace(
+            $pattern,
+            '',
+            strip_tags(strtolower(trim($memberName)))
+        );
+        return MEDIA_BASE_URL . "memberProfiles/" . htmlspecialchars($name) . "-{$member_id}.html";
+    }
+
+    public function renderIntro($data)
+    {
+        return nl2br($data['record']['intro']);
+    }
+    public function setImg($data)
+    {
+        extract($data['record']);
+        return empty($image) ? false : MEMBER_PHOTOS . $image;
+    }
+
+    public function getPackages($data)
+    {
+        if ($this->type) {
+            $typeWhere = " AND {$this->type}";
+        }
+        $packages = array();
+        extract($data['record']);
+        try {
+            $dbh = Toolkit_Database::getInstance();
+            $sql = "
+              SELECT *
+                FROM member_packages
+               WHERE pending <> true
+                 AND member_id = :member_id
+                 AND current_date BETWEEN sdate and edate
+               $typeWhere
+            ORDER BY title";
+
+            $stmt = $dbh->prepare($sql);
+            $stmt->bindParam(':member_id', $member_id, PDO::PARAM_INT);
+            $stmt->execute();
+            while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                if ($row['image']) {
+                    $row['image'] = MEMBER_PHOTOS . $row['image'];
+                }
+                $packages[] = $row;
+            }
+        } catch (PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+        return $packages;
+    }
+
+    public function setPackageType()
+    {
+        $sql = "
+        SELECT m.*,c.city_name as city,s.state_abb as state,
+               m.url as website,m.logo as logo
+          FROM member m, city c, state s
+         WHERE m.active
+           AND s.state_id = m.state_id
+           AND c.city_id = m.city_id
+           AND member_id IN (
+               SELECT member_id
+                 FROM member_packages
+                WHERE current_date BETWEEN sdate and edate)";
+        parent::setQuery($sql);
+    }
+
+    protected function setControlObject()
+    {
+        $this->ctrlObj['base_url'] = BASE_URL;
+        $this->ctrlObj['catid']    = $_REQUEST['catid'];
+    }
+}
diff --git a/Toolkit/Members/Packages.php b/Toolkit/Members/Packages.php
new file mode 100644 (file)
index 0000000..a31a93b
--- /dev/null
@@ -0,0 +1,359 @@
+<?php
+//     vim:set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker syntax=php:
+
+/**
+ * Linked list (LL) implementation of member packages
+ *
+ * File contains the class that gives the ability to create a linked list
+ * of member packages.  You can iterate through manipulate and control every
+ * aspect of the LL.  The best part is, by keeping the LL in order it
+ * automatically maintains the DB for you.
+ *
+ * PHP version 5
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @version   CVS: $Id: Packages.php,v 1.9 2010/05/25 14:01:16 jamie Exp $
+ * @link      http://demo.gaslightmedia.com
+ * @see       Structures/LinkedList/Double.php
+ *            Toolkit/Members/Packages/Package.php
+ */
+
+/**
+ * Class implementation of a linked list
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @link      http://demo.gaslightmedia.com
+ * @see       Structures/LinkedList/Double.php
+ */
+class Toolkit_Members_Packages extends Structures_LinkedList_Double
+{
+       //      {{{     properties
+
+    /**
+     * Database handler object
+     * @var    pdo
+     * @access public
+     */
+       public $dbh;
+
+    /**
+     * DB id of member whos packages we are manipulating with this linked list
+     * @var    integer
+     * @access protected
+     */
+       protected $mid;
+
+       //      }}}
+       //      {{{     __construct()
+
+    /**
+     * Constructor
+     *
+     * @param Structures_LinkedList_DoubleNode $root Linked List node object
+     * @param int                              $mid  DB id of member we want to prepare the list for
+        *
+     * @access public
+     */
+       function __construct(
+        Structures_LinkedList_DoubleNode $root = null,
+        $mid = null
+    ) {
+               parent::__construct($root);
+               $this->setMember($mid);
+       }
+
+       //      }}}
+
+       //      {{{     createMemberList()
+
+    /**
+     * Creates a linked list from all the packages a member has in the db.
+     *
+     * @param string $order The order to make the linked list in
+        *
+     * @return boolean false on error
+     * @access public
+     */
+       public function createMemberList($order = 'pos')
+       {
+               if (!is_numeric($this->mid)) {
+                       return;
+               }
+
+               $safeOrder = array('pos', 'id');
+               if (!in_array($order, $safeOrder)) {
+                       $order = 'pos';
+               }
+               try {
+                       //      Get each package tuple from the member_packages table.
+                       $sql = "
+                               SELECT *
+                                 FROM member_packages
+                                WHERE member_id = :member_id
+                                ORDER BY $order";
+
+                       $stmt = $this->dbh->prepare($sql);
+                       $stmt->bindParam(':member_id', $this->mid, PDO::PARAM_INT);
+                       $stmt->execute();
+
+                       while ($row = $stmt->fetch()) {
+                               //      Create a new node w/ all the data we just extracted.
+                               $node = $this->createNode($row);
+                $node->setDbh($this->dbh);
+                               //      Sets up any pending data the package might currently have.
+                               $node->setPendingData();
+                               //      Add the package node to the end of the linked list.
+                               $res = $this->appendNode($node);
+                       }
+
+               } catch (PDOException $e) {
+                       return Toolkit_Common::handleError($e);
+               }
+       }
+
+       //      }}}
+       //      {{{     createNode()
+
+    /**
+     * Creates a new linked list node object
+     *
+     * @param array $data Data to populate the new node with
+        *
+     * @access public
+     * @return object newly created linked list node
+     */
+       public function &createNode(array $data)
+       {
+               $node = new Toolkit_Members_Packages_Package($data);
+        $node->setDbh($this->dbh);
+
+        return $node;
+       }
+
+       //      }}}
+
+       //      {{{     getListSize()
+
+    /**
+     * Get how many nodes are currently in the linked list
+     *
+     * @param boolean $filterPending Include pending packages in the count
+        *
+     * @return integer number of nodes in the linked list
+     * @access public
+     */
+       public function getListSize($filterPending = false)
+       {
+               $size = 0;
+               $cur  = $this->rewind();
+
+               //      If the current node is null, then we have no nodes
+               //      in the linked list.  return the empty size.
+               //      Else we have at least one node in the linked list.
+               if (is_null($cur)) {
+                       return $size;
+               }
+
+               //      Add one to the size of the list for every
+               //      node we encounter.
+               do {
+                       if ($filterPending) {
+                               if (!$cur->getPending()) {
+                                       ++$size;
+                               }
+                       } else {
+                               ++$size;
+                       }
+               } while ($cur = $this->next());
+
+               return $size;
+       }
+
+       //      }}}
+       //      {{{     getMemberId()
+
+    /**
+     * Get which member this linked list is for
+     *
+     * @return integer id of member we are dealing with
+     * @access public
+     */
+       public function getMemberId()
+       {
+               return $this->mid;
+       }
+
+       //      }}}
+
+       //      {{{     findNode()
+
+    /**
+     * find a node from the linked list
+     *
+     * @param integer $target Id of package you are searching for
+        *
+     * @return mixed Toolkit_Members_Packages_Package if present, else false
+     * @access public
+     */
+       public function &findNode($target)
+       {
+               $this->end();
+               $startingId = $this->current->getId();
+
+               if ($target == $startingId) {
+                       return $this->current;
+               }
+
+               while ($curr = $this->previous()) {
+                       if ($target == $curr->getId()) {
+                               return $curr;
+                       }
+               }
+
+               return false;
+       }
+
+       //      }}}
+
+       //      {{{     moveNode()
+
+    /**
+     * Adjust nodes position in the linked list
+     *
+     * @param integer $pid id of package you want to move
+     * @param integer $pos new position you want to move to
+        *
+     * @access public
+     * @return void
+     */
+       public function moveNode($pid, $pos)
+       {
+               $node   = $this->findNode($pid);
+               $oldPos = $node->getPosition();
+
+               if ($this->getListSize()) {
+                       $cur = $this->rewind();
+                       do {
+                               if ($cur->getId() == $pid) {
+                                       $cur->setPosition($pos);
+                               } else {
+                                       $curPos = $cur->getPosition();
+                                       if ($pos > $oldPos) {
+                                               if ($curPos <= $pos && $curPos > $oldPos) {
+                                                       $cur->setPosition($curPos - 1);
+                                               }
+                                       } elseif ($pos < $oldPos) {
+                                               if ($curPos >= $pos && $curPos < $oldPos) {
+                                                       $cur->setPosition($curPos + 1);
+                                               }
+                                       }
+                               }
+                       } while ($cur = $this->next());
+               }
+       }
+
+       //      }}}
+
+       //      {{{     removeNode()
+
+    /**
+     * Removes a node from the linked list
+     *
+     * @param Toolkit_Image_Server $is     Image Server Object
+     * @param integer              $target Id of package you want to remove
+        *
+     * @return boolean false on error
+     * @access public
+     */
+       public function removeNode(Toolkit_Image_Server $is, $target)
+       {
+               $node = $this->findNode($target);
+               if (!$node) {
+                       return false;
+               }
+
+               $node->remove($is);
+               $remNodePos = $node->getPosition();
+
+               //      only try and update positions in the linked list if     there are nodes that
+               //      will still exists after this node is removed ie.  size is greater than 1
+               if ($this->getListSize() > 1) {
+                       $cur = $this->rewind();
+                       do {
+                               if ($cur->getPosition() > $remNodePos) {
+                                       $cur->setPosition($cur->getPosition() - 1);
+                               }
+                       } while ($cur = $this->next());
+               }
+
+               $this->deleteNode($node);
+       }
+
+       //      }}}
+
+    //  {{{ setDbh()
+
+    /**
+     * Set the Database Handler
+     *
+     * @param PDO $pdo PHP Data Object to use
+     *
+     * @return void
+     * @access public
+     */
+    public function setDbh(PDO $pdo)
+    {
+        $this->dbh = $pdo;
+    }
+
+    //  }}}
+       //      {{{     setMember()
+
+    /**
+     * Sets which member we are working with
+     *
+     * @param integer $mid id of member
+        *
+     * @access public
+     * @return PEAR error on invalid member id
+     */
+       public function setMember($mid)
+       {
+        if (is_integer($mid)) {
+            $this->mid = $mid;
+        } elseif (is_numeric($mid) && ctype_digit($mid)) {
+                       $this->mid = $mid;
+               } else {
+            return PEAR::raiseError('invalid member id');
+        }
+       }
+
+       //      }}}
+
+       //      {{{     emptyList()
+
+    /**
+     * Deletes all nodes from a list
+     *
+     * @access public
+     * @return void
+     */
+       public function emptyList()
+       {
+               $this->end();
+               while ($cur = $this->current()) {
+                       $this->deleteNode($cur);
+               }
+       }
+
+       //      }}}
+}
+?>
diff --git a/Toolkit/Members/Packages/Package.php b/Toolkit/Members/Packages/Package.php
new file mode 100644 (file)
index 0000000..b4a49ae
--- /dev/null
@@ -0,0 +1,377 @@
+<?php
+//     vim:set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker syntax=php:
+
+/**
+ * Node class for the member packages linked list implementation
+ *
+ * PHP version 5
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @version   CVS: $Id: Package.php,v 1.7 2010/05/25 14:01:22 jamie Exp $
+ * @link      <>
+ * @see       Structures/LinkedList/Double.php
+ *                               Toolkit/Image/Server.php
+ */
+
+/**
+ * Linked list node class
+ *
+ * Handles controlling all functionality for a linked list node
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @link      <>
+ * @see       Structures_LinkedList_DoubleNode
+ */
+class Toolkit_Members_Packages_Package extends Structures_LinkedList_DoubleNode
+{
+       //      {{{     properties
+
+    /**
+     * Database handler object
+     * @var    object
+     * @access public
+     */
+       public $dbh;
+
+    /**
+     * Tuple id of package in db
+     * @var    integer
+     * @access protected
+     */
+       protected $id;
+
+    /**
+     * Image name on image server
+     * @var    string
+     * @access protected
+     */
+       protected $image;
+
+    /**
+     * Packages position in the linked list / db
+     * @var    integer
+     * @access protected
+     */
+       protected $pos;
+
+    /**
+     * If the package is new and is still pending
+     * @var    boolean
+     * @access protected
+     */
+       protected $pending;
+
+    /**
+     * Member id this package belongs to
+     * @var    integer
+     * @access protected
+     */
+       protected $mid;
+
+    /**
+     * DB table name where package data is stored
+     * @var    string
+     * @access protected
+     */
+       protected $tableName = 'member_packages';
+
+       //      }}}
+       //      {{{     __construct()
+
+    /**
+     * Class Constructor
+     *
+     * @param array $data new node data
+        *
+     * @return void
+     * @access public
+     */
+       public function __construct(array $data)
+       {
+               $this->id      = $data['id'];
+               $this->image   = $data['image'];
+               $this->title   = $data['title'];
+               $this->pos     = $data['pos'];
+               $this->pending = $data['pending'];
+               $this->mid     = $data['member_id'];
+       }
+
+       //      }}}
+
+       //      {{{     setFauxPending()
+
+    /**
+     * Make the package pending, but don't set it to pending in the DB
+     *
+        * This is useful in the members only area when only part of a
+        * package is pending but you need to show the entire package in a pending
+        * state.  ie (titles can be pending)
+        *
+     * @param boolean $pending what to set the pending status to
+        *
+     * @return void
+     * @access public
+     */
+       public function setFauxPending($pending)
+       {
+               $this->pending = $pending;
+       }
+
+       //      }}}
+
+       //      {{{     getTitle()
+
+    /**
+     * Get the package title
+     *
+     * @return string packages title
+     * @access public
+     */
+       public function getTitle()
+       {
+               return $this->title;
+       }
+
+       //      }}}
+       //      {{{     getPendingTitle()
+
+    /**
+     * Get the packages pending title (if set)
+     *
+     * @return string packages pending title
+     * @access public
+     */
+       public function getPendingTitle()
+       {
+               return $this->pendingTitle;
+       }
+
+       //      }}}
+       //      {{{     getId()
+
+    /**
+     * Get the package id
+     *
+     * @return integer packages db id
+     * @access public
+     */
+       public function getId()
+       {
+               return $this->id;
+       }
+
+       //      }}}
+       //      {{{     getImage()
+
+    /**
+     * Get the package image name
+     *
+     * @return string file name of image stored on image server
+     * @access public
+     */
+       public function getImage()
+       {
+               return $this->image;
+       }
+
+       //      }}}
+       //      {{{     getPending()
+
+    /**
+     * Get the pending status of the package
+     *
+     * @return boolean if the package is pending or not
+     * @access public
+     */
+       public function getPending()
+       {
+               return $this->pending;
+       }
+
+       //      }}}
+       //      {{{     getPosition()
+
+    /**
+     * Get the position of the package
+     *
+     * @return integer current position in the linked list / db of the package
+     * @access public
+     */
+       public function getPosition()
+       {
+               return $this->pos;
+       }
+
+       //      }}}
+
+       //      {{{     remove()
+
+    /**
+     * Remove a package from the db
+     *
+        * Handles deleting all data from the member_packages table and any
+        * data that might currently be pending for the package as well.
+     *
+     * @param Toolkit_Image_Server $is Image Server object
+     *
+     * @return boolean false on error
+     * @access public
+     */
+       public function remove(Toolkit_Image_Server $is)
+       {
+               try {
+                       $is->imageDelete($this->image);
+                       //      Get rid of the package in the member_packages table.
+                       $sql = "
+                DELETE FROM {$this->tableName}
+                 WHERE id = ?";
+
+                       $stmt = $this->dbh->prepare($sql);
+                       $stmt->execute(array($this->id));
+
+                       //      Get rid of any updates to the package so we don't have
+                       //      orphaned data lying around.
+                       $sql = "
+                DELETE FROM " . Toolkit_Membersonly::PENDING_TABLE . "
+                 WHERE db_table    = '{$this->tableName}'
+                   AND member_id   = :member_id
+                   AND foreign_key = :id";
+
+                       $stmt = $this->dbh->prepare($sql);
+                       $stmt->bindParam(':member_id', $_GET['id'], PDO::PARAM_INT);
+                       $stmt->bindParam(':id', $this->id, PDO::PARAM_INT);
+                       $stmt->execute();
+               } catch (PDOException $e) {
+                       return Toolkit_Common::handleError($e);
+               }
+       }
+
+       //      }}}
+
+    //  {{{ setDbh()
+
+    /**
+     * Set the Database handler object
+     *
+     * @param PDO $pdo PHP Data Object
+     *
+     * @return void
+     * @access public
+     */
+    public function setDbh(PDO $pdo)
+    {
+        $this->dbh = $pdo;
+    }
+
+    //  }}}
+       //      {{{     setPending()
+
+    /**
+     * Set the pending status for the package
+     *
+     * @param boolean $pending If the package is pending or not
+        *
+     * @return boolean result of db update
+     * @access public
+     */
+       public function setPending($pending)
+       {
+               $this->pending = $pending;
+               try {
+                       $sql = "
+                UPDATE {$this->tableName}
+                   SET pending   = ?
+                 WHERE member_id = ?
+                   AND id        = ?";
+
+                       $stmt = $this->dbh->prepare($sql);
+                       return $stmt->execute(array($pending, $this->mid, $this->id));
+               } catch (PDOException $e) {
+                       return Toolkit_Common::handleError($e);
+               }
+       }
+
+       //      }}}
+       //      {{{     setPendingData()
+
+    /**
+     * Set the content of any data that might be pending
+     *
+     * Currently this only supports pending title
+     *
+     * @return bolean false on error
+     * @access public
+     */
+       public function setPendingData()
+       {
+               try {
+                       //      Get any updates for that package that are still in
+                       //      a pending status.
+                       $sql = "
+                               SELECT *
+                                 FROM " . Toolkit_Membersonly::PENDING_TABLE . "
+                                WHERE id in (
+                                       SELECT max(id)
+                                         FROM " . Toolkit_Membersonly::PENDING_TABLE . "
+                                        WHERE foreign_key = :foreign_key
+                                          AND db_table = 'member_packages'
+                                        GROUP BY field)";
+
+                       $stmt = $this->dbh->prepare($sql);
+                       $stmt->execute(array($this->id));
+                       $row = $stmt->fetch();
+
+                       if ($row['field'] == 'title') {
+                               $this->pendingCaption = $row['update'];
+                       }
+               } catch (PDOException $e) {
+                       return Toolkit_Common::handleError($e);
+               }
+       }
+
+       //      }}}
+       //      {{{     setPosition()
+
+    /**
+     * Set a new position for a node
+     *
+     * Handle updating the nodes position in the linked list as well as db.
+     *
+     * @param integer $pos new position number for node
+        *
+     * @return boolean result of position update
+     * @access public
+     */
+       public function setPosition($pos)
+       {
+               //      Not updating anything so just return.
+               if ($pos == $this->pos) {
+                       return true;
+               }
+               try {
+                       $this->pos = $pos;
+
+                       $sql = "
+                UPDATE {$this->tableName}
+                   SET pos       = ?
+                 WHERE member_id = ?
+                   AND id        = ?";
+
+                       $stmt = $this->dbh->prepare($sql);
+                       return $stmt->execute(array($pos, $this->mid, $this->id));
+               } catch (PDOException $e) {
+                       return Toolkit_Common::handleError($e);
+               }
+       }
+
+       //      }}}
+}
+?>
diff --git a/Toolkit/Members/Photos.php b/Toolkit/Members/Photos.php
new file mode 100644 (file)
index 0000000..c5cfb42
--- /dev/null
@@ -0,0 +1,349 @@
+<?php
+//     vim:set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker syntax=php:
+
+/**
+ * Linked list (LL) implementation of member photos
+ *
+ * File contains the class that gives the ability to create a linked list
+ * of member photos.  You can iterate through manipulate and control every
+ * aspect of the LL.  The best part is, by keeping the LL in order it
+ * automatically maintains the DB for you.
+ *
+ * PHP version 5
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @version   CVS: $Id: Photos.php,v 1.13 2010/05/25 14:01:18 jamie Exp $
+ * @link      http://demo.gaslightmedia.com
+ * @see       Structures/LinkedList/Double.php
+ *                       Toolkit/Members/Photos/Photo.php
+ */
+
+/**
+ * Class implementation of a linked list
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @link      http://demo.gaslightmedia.com
+ * @see       Structures/LinkedList/Double.php
+ */
+class Toolkit_Members_Photos extends Structures_LinkedList_Double
+{
+       //      {{{     properties
+
+    /**
+     * DB id of member whos photos we are manipulating with this linked list
+     * @var    integer
+     * @access protected
+     */
+       protected $mid;
+
+       //      }}}
+       //      {{{     __construct()
+
+    /**
+     * Constructor
+     *
+     * @param Structures_LinkedList_DoubleNode $root Linked List node object
+     * @param integer                          $mid  id of member we want
+     *                                               to prepare the list for
+        *
+     * @access public
+     * @return void
+     */
+       function __construct(
+        Structures_LinkedList_DoubleNode $root = null,
+        $mid = null
+    ) {
+               parent::__construct($root);
+               $this->setMember($mid);
+        /*
+               if (is_numeric($mid)) {
+                       $this->createMemberList($mid);
+               }
+        */
+       }
+
+       //      }}}
+
+       //      {{{     createMemberList()
+
+    /**
+     * Creates a linked list from all the photos a member has in the db.
+     *
+     * @param PDO              $dbh   PHP Data Object
+     * @param Config_Container $c     Member configuration
+     * @param string           $order The order to make the linked list in
+        *
+     * @return boolean false on error
+     * @access public
+     */
+       public function createMemberList(
+        PDO $dbh,
+        Config_Container $c,
+        $order = 'pos'
+    ) {
+               if (!ctype_digit((string) $this->mid)) {
+                       return;
+               }
+
+        //  Make sure the list is actually empty before you recreate it
+        //  otherwise you can end up w/ duplicate values in the LL
+        $this->emptyList();
+
+               $safeOrder = array('pos', 'id');
+               if (!in_array($order, $safeOrder)) {
+                       $order = 'pos';
+               }
+               try {
+                       //      Get each photo tuple from the member_photos table.
+                       $sql = "
+                               SELECT *
+                                 FROM member_photos
+                                WHERE member_id = :member_id
+                                ORDER BY $order";
+
+                       $stmt = $dbh->prepare($sql);
+                       $stmt->bindParam(':member_id', $this->mid, PDO::PARAM_INT);
+                       $stmt->execute();
+
+                       while ($row = $stmt->fetch()) {
+                               //      Create a new node w/ all the data we just extracted.
+                               $node = $this->createNode($row);
+                $node->setDbh($dbh);
+                               //      Sets up any pending data the photo might currently have.
+                               $node->setPendingData($c);
+                               //      Add the photo node to the end of the linked list.
+                               $res = $this->appendNode($node);
+                       }
+
+               } catch (PDOException $e) {
+                       return Toolkit_Common::handleError($e);
+               }
+       }
+
+       //      }}}
+       //      {{{     createNode()
+
+    /**
+     * Creates a new linked list node object
+     *
+     * @param array $data Data to populate the new node with
+        *
+     * @return Toolkit_Members_Photos_Photo linked list node
+     * @access public
+     */
+       public function &createNode(array $data)
+       {
+               return new Toolkit_Members_Photos_Photo($data);
+       }
+
+       //      }}}
+
+       //      {{{     getListSize()
+
+    /**
+     * Get how many nodes are currently in the linked list
+     *
+     * @param boolean $filterPending Include pending photos in the count
+        *
+     * @return integer number of nodes in the linked list
+     * @access public
+     */
+       public function getListSize($filterPending = false)
+       {
+               $size = 0;
+               $cur  = $this->rewind();
+
+               //      If the current node is null, then we have no nodes
+               //      in the linked list.  return the empty size.
+               //      Else we have at least one node in the linked list.
+               if (is_null($cur)) {
+                       return $size;
+               }
+
+               //      Add one to the size of the list for every
+               //      node we encounter.
+               do {
+                       if ($filterPending) {
+                               if (!$cur->getPending()) {
+                                       ++$size;
+                               }
+                       } else {
+                               ++$size;
+                       }
+               } while ($cur = $this->next());
+
+               return $size;
+       }
+
+       //      }}}
+       //      {{{     getMemberId()
+
+    /**
+     * Get which member this linked list is for
+     *
+     * @return integer id of member we are dealing with
+     * @access public
+     */
+       public function getMemberId()
+       {
+               return $this->mid;
+       }
+
+       //      }}}
+
+       //      {{{     findNode()
+
+    /**
+     * find a node from the linked list
+     *
+     * @param integer $target Id of photo you are searching for
+        *
+     * @return mixed Toolkit_Members_Photos_Photo object if present, else false
+     * @access public
+     */
+       public function &findNode($target)
+       {
+               $this->end();
+               $startingId = $this->current->getId();
+
+               if ($target == $startingId) {
+                       return $this->current;
+               }
+
+               while ($curr = $this->previous()) {
+                       if ($target == $curr->getId()) {
+                               return $curr;
+                       }
+               }
+
+               return false;
+       }
+
+       //      }}}
+
+       //      {{{     moveNode()
+
+    /**
+     * Adjust nodes position in the linked list
+     *
+     * @param integer $pid id of photo you want to move
+     * @param integer $pos new position you want to move to
+        *
+     * @return void
+     * @access public
+     */
+       public function moveNode($pid, $pos)
+       {
+               $node   = $this->findNode($pid);
+               $oldPos = $node->getPosition();
+
+               if ($this->getListSize()) {
+                       $cur = $this->rewind();
+                       do {
+                               if ($cur->getId() == $pid) {
+                                       $cur->setPosition($pos);
+                               } else {
+                                       $curPos = $cur->getPosition();
+                                       if ($pos > $oldPos) {
+                                               if ($curPos <= $pos && $curPos > $oldPos) {
+                                                       $cur->setPosition($curPos - 1);
+                                               }
+                                       } elseif ($pos < $oldPos) {
+                                               if ($curPos >= $pos && $curPos < $oldPos) {
+                                                       $cur->setPosition($curPos + 1);
+                                               }
+                                       }
+                               }
+                       } while ($cur = $this->next());
+               }
+       }
+
+       //      }}}
+
+       //      {{{     removeNode()
+
+    /**
+     * Removes a node from the linked list
+     *
+     * @param Toolkit_Image_Server $is     Image Server Object
+     * @param Config_Container     $c      Member configuration
+     * @param integer              $target Id of photo you want to remove
+        *
+     * @return boolean false on error
+     * @access public
+     */
+       public function removeNode(
+        Toolkit_Image_Server $is,
+        Config_Container $c,
+        $target
+    ) {
+               $node = $this->findNode($target);
+               if (!$node) {
+                       return false;
+               }
+
+               $node->remove($is, $c);
+               $remNodePos = $node->getPosition();
+
+               //      only try and update positions in the linked list if     there are nodes that
+               //      will still exists after this node is removed ie.  size is greater than 1
+               if ($this->getListSize() > 1) {
+                       $cur = $this->rewind();
+                       do {
+                               if ($cur->getPosition() > $remNodePos) {
+                                       $cur->setPosition($cur->getPosition() - 1);
+                               }
+                       } while ($cur = $this->next());
+               }
+
+               $this->deleteNode($node);
+       }
+
+       //      }}}
+
+       //      {{{     setMember()
+
+    /**
+     * Sets which member we are working with
+     *
+     * @param integer $mid id of member
+        *
+     * @access public
+     * @return void
+     */
+       public function setMember($mid)
+       {
+               if (ctype_digit((string) $mid)) {
+                       $this->mid = $mid;
+               }
+       }
+
+       //      }}}
+
+       //      {{{     emptyList()
+
+    /**
+     * Deletes all nodes from a list
+     *
+     * @access public
+     * @return void
+     */
+       public function emptyList()
+       {
+               $this->end();
+               while ($cur = $this->current()) {
+                       $this->deleteNode($cur);
+               }
+       }
+
+       //      }}}
+}
+?>
diff --git a/Toolkit/Members/Photos/Photo.php b/Toolkit/Members/Photos/Photo.php
new file mode 100644 (file)
index 0000000..cdf46a6
--- /dev/null
@@ -0,0 +1,440 @@
+<?php
+//     vim:set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker syntax=php:
+
+/**
+ * Node class for the member photos linked list implementation
+ * 
+ * PHP version 5
+ * 
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @version   CVS: $Id: Photo.php,v 1.9 2009/09/16 10:48:10 jamie Exp $
+ * @link      http://demo.gaslightmedia.com
+ * @see       Structures/LinkedList/Double.php
+ *                       Toolkit/Image/Server.php
+ */
+
+/**
+ * PEAR linked list library
+ */
+require_once 'Structures/LinkedList/Double.php';
+
+/**
+ * Class to handle uploading and processing of images on the image server
+ */
+require_once BASE . 'Toolkit/Image/Server.php';
+
+/**
+ * Linked list node class
+ * 
+ * Handles controlling all functionality for a linked list node
+ * 
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @link      http://demo.gaslightmedia.com
+ * @see       Structures_LinkedList_DoubleNode
+ */
+class Toolkit_Members_Photos_Photo extends Structures_LinkedList_DoubleNode
+{
+       //      {{{     properties
+
+    /**
+     * Database handler object
+     * @var    object
+     * @access public
+     */
+       public $dbh;
+
+    /**
+     * Tuple id of photo in db
+     * @var    integer
+     * @access protected
+     */
+       protected $id;
+
+    /**
+     * Image name on image server
+     * @var    string
+     * @access protected
+     */
+       protected $image;
+
+    /**
+     * Photo caption
+     * @var    string
+     * @access protected
+     */
+       protected $caption;
+
+    /**
+     * Photo pending caption (if set)
+     * @var    string
+     * @access protected
+     */
+       protected $pendingCaption;
+
+    /**
+     * Photos position in the linked list / db
+     * @var    integer
+     * @access protected
+     */
+       protected $pos;
+
+    /**
+     * If the photo is new and is still pending
+     * @var    boolean
+     * @access protected
+     */
+       protected $pending;
+
+    /**
+     * Member id this photo belongs to
+     * @var    integer
+     * @access protected
+     */
+       protected $mid;
+
+    /**
+     * DB table name where photo data is stored
+     * @var    string   
+     * @access protected
+     */
+       protected $tableName = 'member_photos';
+
+       //      }}}
+       //      {{{     __construct()
+
+    /**
+     * Class Constructor
+     * 
+     * @param array $data new node data
+        *
+     * @return void  
+     * @access public
+     */
+       public function __construct(array $data)
+       {
+               $this->id      = $data['id'];
+               $this->image   = $data['image'];
+               $this->caption = $data['caption'];
+               $this->pos     = $data['pos'];
+               $this->pending = $data['pending'];
+               $this->mid     = $data['member_id'];
+       }
+
+       //      }}}
+
+       //      {{{     setFauxPending()
+
+    /**
+     * Make the photo pending, but don't set it to pending in the DB
+     * 
+        * This is useful in the members only area when only part of a
+        * photo is pending but you need to show the entire photo in a pending
+        * state.  ie (captions can be pending)
+        *
+     * @param boolean $pending what to set the pending status to
+        *
+     * @return void   
+     * @access public 
+     */
+       public function setFauxPending($pending)
+       {
+               $this->pending = $pending;
+       }
+
+       //      }}}
+
+       //      {{{     getCaption()
+
+    /**
+     * Get the photo caption
+     * 
+     * @return string photos caption
+     * @access public 
+     */
+       public function getCaption()
+       {
+               return $this->caption;
+       }
+       
+       //      }}}
+       //      {{{     getPendingCaption()
+
+    /**
+     * Get the photos pending caption (if set)
+     * 
+     * @return string photos pending caption
+     * @access public 
+     */
+       public function getPendingCaption()
+       {
+               return $this->pendingCaption;
+       }
+       
+       //      }}}
+       //      {{{     getId()
+
+    /**
+     * Get the photo id
+     * 
+     * @return integer photos db id
+     * @access public 
+     */
+       public function getId()
+       {
+               return $this->id;
+       }
+
+       //      }}}
+       //      {{{     getImage()
+
+    /**
+     * Get the photo image name
+     * 
+     * @return string file name of image stored on image server
+     * @access public 
+     */
+       public function getImage()
+       {
+               return $this->image;
+       }
+
+       //      }}}
+       //      {{{     getPending()
+
+    /**
+     * Get the pending status of the photo
+     * 
+     * @return boolean if the photo is pending or not
+     * @access public 
+     */
+       public function getPending()
+       {
+               return $this->pending;
+       }
+
+       //      }}}
+       //      {{{     getPosition()
+
+    /**
+     * Get the position of the photo
+     * 
+     * @return integer current position in the linked list / db of the photo
+     * @access public 
+     */
+       public function getPosition()
+       {
+               return $this->pos;
+       }
+
+       //      }}}
+
+       //      {{{     remove()
+
+    /**
+     * Remove a photo from the db
+     * 
+        * Handles deleting all data from the member_photos table and any
+        * data that might currently be pending for the photo as well.
+     * 
+     * @param Toolkit_Image_Server $is Image Server object
+     * @param Config_Container     $c  Member Configuration
+     *
+     * @return boolean false on error
+     * @access public 
+     */
+       public function remove(Toolkit_Image_Server $is, Config_Container $c)
+       {
+               try {
+                       $is->imageDelete($this->image);
+                       //      Get rid of the photo in the member_photos table.
+                       $sql = "
+                DELETE FROM {$this->tableName}
+                 WHERE id = ?";
+
+                       $stmt = $this->dbh->prepare($sql);
+                       $stmt->execute(array($this->id));
+
+            $config = $c->getItem('section', 'tables');
+            $pendingTable = $config->getItem('directive', 'pendingTable');
+
+                       //      Get rid of any updates to the photo so we don't have
+                       //      orphaned data lying around.
+                       $sql = "
+                DELETE FROM {$pendingTable->getContent()}
+                 WHERE db_table    = '{$this->tableName}'
+                   AND member_id   = :member_id
+                   AND foreign_key = :id";
+
+                       $stmt = $this->dbh->prepare($sql);
+                       $stmt->bindParam(':member_id', $_GET['id'], PDO::PARAM_INT);
+                       $stmt->bindParam(':id', $this->id, PDO::PARAM_INT);
+                       return $stmt->execute();
+               } catch (PDOException $e) {
+                       return Toolkit_Common::handleError($e);
+               }
+       }
+
+       //      }}}
+
+       //      {{{     setCaption()
+
+    /**
+     * Set the caption for the photo
+     * 
+     * @param string $caption new caption text
+        *
+     * @return boolean result of db update
+     * @access public 
+     */
+       public function setCaption($caption)
+       {
+               $this->caption = $caption;
+               try {
+                       $sql = "
+                UPDATE {$this->tableName}
+                   SET caption   = ?
+                 WHERE member_id = ?
+                   AND id        = ?";
+
+                       $stmt = $this->dbh->prepare($sql);
+                       return $stmt->execute(array($caption, $this->mid, $this->id));
+               } catch (PDOException $e) {
+                       return Toolkit_Common::handleError($e);
+               }
+       }
+
+       //      }}}
+    //  {{{ setDbh()
+
+    /**
+     * sets the database handler object
+     *
+     * using dependecy injection to help testing this application
+     *
+     * @param PDO $pdo PHP Data Object used for DB calls.
+     *
+     * @return void
+     * @access public
+     */
+    public function setDbh(PDO $pdo)
+    {
+        $this->dbh = $pdo;
+    }
+
+    //  }}}
+       //      {{{     setPending()
+
+    /**
+     * Set the pending status for the photo
+     * 
+     * @param boolean $pending If the photo is pending or not
+        *
+     * @return boolean result of db update
+     * @access public 
+     */
+       public function setPending($pending)
+       {
+               $this->pending = $pending;
+               try {
+                       $sql = "
+                UPDATE {$this->tableName}
+                   SET pending   = ?
+                 WHERE member_id = ?
+                   AND id        = ?";
+
+                       $stmt = $this->dbh->prepare($sql);
+                       return $stmt->execute(array($pending, $this->mid, $this->id));
+               } catch (PDOException $e) {
+                       return Toolkit_Common::handleError($e);
+               }
+       }
+
+       //      }}}
+       //      {{{     setPendingData()
+
+    /**
+     * Set the content of any data that might be pending
+     * 
+     * Currently this only supports pending caption
+     *
+     * @param Config_Container $c Member configuration
+     * 
+     * @return bolean false on error
+     * @access public 
+     */
+       public function setPendingData(Config_Container $c)
+       {
+        $config = $c->getItem('section', 'tables');
+        $pendingTable = $config->getItem('directive', 'pendingTable');
+
+               try {
+                       //      Get any updates for that photo that are still in 
+                       //      a pending status.
+                       $sql = "
+                               SELECT *
+                                 FROM {$pendingTable->getContent()}
+                                WHERE id in (
+                                       SELECT max(id)
+                                         FROM {$pendingTable->getContent()}
+                                        WHERE foreign_key = :foreign_key
+                                          AND db_table = 'member_photos'
+                                        GROUP BY field)";
+
+                       $stmt = $this->dbh->prepare($sql);
+                       $stmt->execute(array($this->id));
+                       $row = $stmt->fetch();
+
+                       if ($row['field'] == 'caption') {
+                               $this->pendingCaption = $row['update'];
+                       }
+               } catch (PDOException $e) {
+                       return Toolkit_Common::handleError($e);
+               }
+       }
+
+       //      }}}
+       //      {{{     setPosition()
+
+    /**
+     * Set a new position for a node
+     * 
+     * Handle updating the nodes position in the linked list as well as db.
+     * 
+     * @param integer $pos new position number for node
+        *
+     * @return boolean result of position update
+     * @access public 
+     */
+       public function setPosition($pos)
+       {
+               //      Not updating anything so just return.
+               if ($pos == $this->pos) {
+                       return true;
+               }
+               try {
+                       $this->pos = $pos;
+
+                       $sql = "
+                UPDATE {$this->tableName}
+                   SET pos       = ?
+                 WHERE member_id = ?
+                   AND id        = ?";
+
+                       $stmt = $this->dbh->prepare($sql);
+                       return $stmt->execute(array($pos, $this->mid, $this->id));
+               } catch (PDOException $e) {
+                       return Toolkit_Common::handleError($e);
+               }
+       }
+
+       //      }}}
+}
+?>
diff --git a/Toolkit/Members/ProfilePage.php b/Toolkit/Members/ProfilePage.php
new file mode 100644 (file)
index 0000000..7107e45
--- /dev/null
@@ -0,0 +1,861 @@
+<?php
+//  vim:set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker syntax=php:
+
+/**
+ * Member Profile Page
+ *
+ * PHP version 5
+ *
+ * @category  MemberDB
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @version   CVS: $Id: ProfilePage.php,v 1.17 2010/08/15 19:34:33 jamie Exp $
+ * @link      http://demo.gaslightmedia.com
+ */
+
+/**
+ * Generates Member profile pages
+ *
+ * @category  MemberDB
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @version   Release: @package_version@
+ * @link      http://demo.gaslightmedia.com
+ */
+class Toolkit_Members_ProfilePage
+{
+    //  {{{ properties
+
+    /**
+     * Database handler
+     * @var    object
+     * @access protected
+     */
+    protected $dbh;
+
+    /**
+     * Page catid
+     * @var    integer
+     * @access private
+     */
+    private $_catid;
+
+    /**
+     * Member Id to get profile for
+     * @var    integer
+     * @access private
+     */
+    private $_mid;
+
+    /**
+     * Toolbox page gateway
+     * @var    integer
+     * @access private
+     */
+    private $_pageGateway;
+
+    //  }}}
+    //  {{{ __construct()
+
+    /**
+     * constructor
+     *
+     * @param PDO                                 $pdo         Database handler
+     * @param Toolkit_Toolbox_PageGatewayAbstract $pageGateway DB page gateway
+     * @param integer                             $mid         Member to get profile for
+     *
+     * @return void
+     * @access public
+     */
+    public function __construct(
+        PDO $pdo,
+        Toolkit_Toolbox_PageGatewayAbstract $pageGateway,
+        $mid
+    ) {
+        $this->dbh          = $pdo;
+        $this->_pageGateway = $pageGateway;
+        $this->setMemberId($mid);
+    }
+
+    //  }}}
+
+    //  {{{ _isNewMember()
+
+    /**
+     * Determines if we are dealing w/ a new member from the addyourbusiness form
+     *
+     * @return boolean
+     * @access private
+     */
+    private function _isNewMember()
+    {
+        try {
+            $sql = "
+                SELECT new_member
+                  FROM member
+                 WHERE member_id = :mid";
+
+            $stmt = $this->dbh->prepare($sql);
+            $stmt->bindParam(':mid', $this->_mid, PDO::PARAM_INT);
+            $stmt->execute();
+            $stmt->bindColumn('new_member', $isNew);
+            $stmt->fetch();
+
+            return (bool) $isNew;
+        } catch (PDOException $e) {
+            return Toolkit_Common::handleError($e);
+        }
+    }
+
+    //  }}}
+
+    //  {{{ getMemberId()
+
+    /**
+     * Description for getMemberId()
+     *
+     * @return integer
+     */
+    public function getMemberId()
+    {
+        return $this->_mid;
+    }
+
+    //  }}}
+    //  {{{ memberActive()
+
+    /**
+     * Check to make sure the member is active in the DB.
+     *
+     * @return boolean true or false if the member is active
+     * @access private
+     */
+    private function _memberActive()
+    {
+        if (!filter_var($this->_mid, FILTER_VALIDATE_INT)) {
+            return false;
+        }
+
+        try {
+            $sql = "
+                SELECT CASE
+                       WHEN active THEN 1
+                       ELSE 0
+                       END AS active
+                  FROM member
+                 WHERE member_id = :member_id";
+
+            $stmt = $this->dbh->prepare($sql);
+            $stmt->bindParam(':member_id', $this->_mid, PDO::PARAM_INT);
+            $stmt->execute();
+            $stmt->bindColumn('active', $active);
+            $stmt->fetch();
+
+            return (bool) $active;
+        } catch (PDOException $e) {
+            return Toolkit_Common::handleError($e);
+        }
+    }
+
+    //  }}}
+    //  {{{ memberExists()
+
+    /**
+     * Check to make sure the member exists in the database
+     *
+     * @return boolean true or false if the member exists
+     * @access private
+     */
+    private function _memberExists()
+    {
+        if (!filter_var($this->_mid, FILTER_VALIDATE_INT)) {
+            return false;
+        }
+
+        try {
+            $sql = "
+                SELECT count(*) AS total
+                  FROM member
+                 WHERE member_id = :member_id";
+
+            $stmt = $this->dbh->prepare($sql);
+            $stmt->bindParam(':member_id', $this->_mid, PDO::PARAM_INT);
+            $stmt->execute();
+            $stmt->bindColumn('total', $total);
+            $stmt->fetch();
+
+            return (bool) $total;
+        } catch (PDOException $e) {
+            return Toolkit_Common::handleError($e);
+        }
+    }
+
+    //  }}}
+
+    //  {{{ setCatId()
+
+    /**
+     * Set the catid for the page
+     *
+     * @param integer $catid page id from db
+     *
+     * @return void
+     * @access public
+     * @throws InvalidArgumentException on invalid member id
+     */
+    public function setCatId($catid)
+    {
+        if (filter_var($catid, FILTER_VALIDATE_INT)) {
+            $this->_catid = $catid;
+        } else {
+            $this->_catid = null;
+            throw new InvalidArgumentException("Invalid catid `$catid`");
+        }
+    }
+
+    //  }}}
+    //  {{{ setConfig()
+
+    /**
+     * Sets the query to use to fetch the datagrid results
+     *
+     * @param Config_Container $c Configuration object
+     *
+     * @return void
+     * @access public
+     */
+    public function setConfig(Config_Container $c)
+    {
+        $this->config = $c;
+    }
+
+    //  }}}
+    //  {{{ setImagePath()
+
+    /**
+     * Sets the path to use for images
+     *
+     * @param string $imagePath File server image path url
+     *
+     * @return void
+     * @access public
+     */
+    public function setImagePath($imagePath)
+    {
+        $this->imagePath = $imagePath;
+    }
+
+    //  }}}
+    //  {{{ setMemberAccommodations()
+
+    /**
+     * Get all the member accommodation info
+     *
+     * @param object &$page The object you want to use with the flexy template
+     *
+     * @return void
+     * @access protected
+     */
+    protected function setMemberAccommodations(&$page)
+    {
+        try {
+            //  Check to see if there are any rows in the
+            //  member_accommodations table that are linked to this
+            //  member. If there are, then we know this member
+            //  needs the hotel section
+            $sql = "
+                SELECT reservation_url, reservation_id, num_rooms, year_round
+                  FROM member_accommodations
+                 WHERE member_id = :mid";
+
+            $stmt = $this->dbh->prepare($sql);
+            $stmt->bindParam(':mid', $this->_mid, PDO::PARAM_INT);
+            $stmt->execute();
+
+            if ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                $accommodations = array_filter($row);
+            }
+
+            $page->has_accommodations    = !empty($accommodations);
+            $page->does_online_reservations
+                = (   !is_null($accommodations['reservation_id'])
+                   || !is_null($accommodations['reservation_url'])
+                  );
+            $page->reservation_id        = $accommodations['reservation_id'];
+            $page->reservation_url       = $accommodations['reservation_url'];
+            $page->lists_number_of_rooms = !is_null($accommodations['num_rooms']);
+            $page->num_rooms             = $accommodations['num_rooms'];
+            $page->year_round            = $accommodations['year_round'];
+        } catch (PDOException $e) {
+            return Toolkit_Common::handleError($e);
+        }
+    }
+
+    //  }}}
+    //  {{{ setMemberAmenities()
+
+    /**
+     * Get all the member amenity info
+     *
+     * @param object &$page The object you want to use with the flexy template
+     *
+     * @return void
+     * @access protected
+     */
+    protected function setMemberAmenities(&$page)
+    {
+        try {
+            $sql = "
+                SELECT *
+                  FROM amenity
+                 WHERE amenity_id in (
+                        SELECT amenity_id
+                          FROM member_amenity
+                         WHERE member_id = :mid)";
+
+            $stmt = $this->dbh->prepare($sql);
+            $stmt->bindParam(':mid', $this->_mid, PDO::PARAM_INT);
+            $stmt->execute();
+
+            while ($row = $stmt->fetch()) {
+                $page->amenities[] = $row['amenity_name'];
+            }
+        } catch (PDOException $e) {
+            return Toolkit_Common::handleError($e);
+        }
+    }
+
+    //  }}}
+    //  {{{ setMemberCCards()
+
+    /**
+     * Get all the member Credit Card info
+     *
+     * @param object &$page The object you want to use with the flexy template
+     *
+     * @return void
+     * @access protected
+     */
+    protected function setMemberCCards(&$page)
+    {
+        try {
+            $sql = "
+                SELECT *
+                  FROM ccard_type
+                 WHERE ccard_type_id in (
+                        SELECT ccard_type_id
+                          FROM member_ccard_type
+                         WHERE member_id = :mid)";
+
+            $stmt = $this->dbh->prepare($sql);
+            $stmt->bindParam(':mid', $this->_mid, PDO::PARAM_INT);
+            $stmt->execute();
+
+            while ($row = $stmt->fetch()) {
+                $page->ccards[] = $row['ccard_type_name'];
+            }
+        } catch (PDOException $e) {
+            return Toolkit_Common::handleError($e);
+        }
+    }
+
+    //  }}}
+    //  {{{ setMemberDetail()
+
+    /**
+     * Get the member profile page
+     *
+     * @param stdClass $page controller object used with the template
+     *
+     * @return string html string of member profile page
+     * @access public
+     */
+    public function setMemberDetail(stdClass $page)
+    {
+        if (!$this->_validMember()) {
+            return false;
+        }
+
+        $this->profile =& $page;
+
+        $page->image_path   = $this->imagePath;
+        $page->base_url     = MEDIA_BASE_URL;
+        $page->glm_base_url = MEDIA_APP_BASE_URL;
+
+        $this->setMemberInfo($page);
+        $this->setMemberPhotos($page);
+        $this->setMemberAccommodations($page);
+        $this->setMemberAmenities($page);
+        $this->setMemberCCards($page);
+        $this->setMemberSocialMedia($page);
+        $this->setMemberFiles($page);
+        $this->setMemberPackages($page);
+        $this->setMemberGolf($page);
+        $this->setMemberRestaurant($page);
+
+        $page->show_attributes = (   $page->has_accommodations
+                                  || $page->has_social_media
+                                  || is_array($page->ccards));
+    }
+
+    //  }}}
+    //  {{{ setMemberFiles()
+
+    /**
+     * Get all the file data for the the member profile being looked at
+     *
+     * @param object &$page The object you want to use with the flexy template
+     *
+     * @return void
+     * @access protected
+     */
+    protected function setMemberFiles(&$page)
+    {
+        try {
+            $sql = "
+                SELECT *
+                  FROM member_files
+                 WHERE member_id = :mid
+                   AND NOT pending";
+
+            $stmt = $this->dbh->prepare($sql);
+            $stmt->bindParam(':mid', $this->_mid, PDO::PARAM_INT);
+            $stmt->execute();
+
+            $count = 0;
+            while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                $page->files[$count]['href']
+                    = MEDIA_BASE_URL . "member-file/{$this->_mid}/{$row['id']}/";
+                $page->files[$count]['name']
+                    = empty($row['file_name']) ?
+                    $row['original_name'] :
+                    $row['file_name'];
+                ++$count;
+            }
+        } catch (PDOException $e) {
+            return Toolkit_Common::handleError($e);
+        }
+    }
+
+    //  }}}
+    //  {{{ setMemberGolf()
+
+    /**
+     * Get all the golf data for the the member profile being looked at
+     *
+     * @param object &$page The object you want to use with the flexy template
+     *
+     * @return void
+     * @access protected
+     */
+    protected function setMemberGolf(&$page)
+    {
+        try {
+            $sql = "
+                SELECT column_name, data_type
+                  FROM information_schema.columns
+                 WHERE table_name = 'member_golf'";
+
+            $tableData = $this->dbh->query($sql)->fetchAll();
+
+            $sql = "
+                SELECT *
+                  FROM member_golf
+                 WHERE member_id = :mid";
+
+            $stmt = $this->dbh->prepare($sql);
+            $stmt->bindParam(':mid', $this->_mid, PDO::PARAM_INT);
+            $stmt->execute();
+
+            if ($row = $stmt->fetch()) {
+                $show = false;
+                foreach ($tableData as $td) {
+                    switch ($td['data_type']) {
+                    case 'radio' :
+                    case 'boolean' :
+                        $golf[$td['column_name']] = ($row[$td['column_name']] == 't') ? 'Yes': 'No';
+                        break;
+
+                    case 'integer' :
+                        break;
+
+                    case 'text' :
+                    default :
+                        $golf[$td['column_name']] = $row[$td['column_name']];
+                        if (!empty($row[$td['column_name']])) {
+                            $show = true;
+                        }
+                        break;
+                    }
+                }
+
+                $page->golf_info      = $show;
+                $page->res_url        = $golf['res_url'];
+                $page->par            = $golf['par'];
+                $page->yardage        = $golf['yardage'];
+                $page->course_rating  = $golf['course_rating'];
+                $page->slope_rating   = $golf['slope_rating'];
+                $page->walking_course = $golf['walking_course'];
+                $page->holes18        = $golf['holes18'];
+                $page->holes9         = $golf['holes9'];
+            }
+        } catch (PDOException $e) {
+            return Toolkit_Common::handleError($e);
+        }
+    }
+
+    //  }}}
+    //  {{{ setMemberId()
+
+    /**
+     * Set a new member id
+     *
+     * @param integer $mid member id to set
+     *
+     * @return void
+     * @access public
+     * @throws PEAR_Exception Error on invalid member id
+     */
+    public function setMemberId($mid)
+    {
+        if ($mid = filter_var($mid, FILTER_VALIDATE_INT)) {
+            $this->_mid = $mid;
+        } else {
+            $this->_mid = null;
+            throw new PEAR_Exception('Invalid Member Id');
+        }
+    }
+
+    //  }}}
+    //  {{{ setMemberInfo()
+
+    /**
+     * Set the record data stored in the database for the template
+     *
+     * @param object &$page The object you want to use with the flexy template
+     *
+     * @return void
+     * @access protected
+     */
+    protected function setMemberInfo(&$page)
+    {
+        try {
+            $sql = "
+                SELECT m.*, c.city_name, s.state_name,
+                       s.state_abb AS state_abbr
+                  FROM member m JOIN city c USING (city_id), state s
+                 WHERE m.member_id = :mid
+                   AND m.state_id  = s.state_id";
+
+            $stmt = $this->dbh->prepare($sql);
+            $stmt->bindParam(':mid', $this->_mid, PDO::PARAM_INT);
+            $stmt->execute();
+            $row = $stmt->fetch();
+
+            if (!empty($row['logo'])) {
+                $page->logo = $row['logo'];
+            }
+
+            $page->member_name = $row['member_name'];
+            $page->street      = $row['street'];
+            //  get reference to [listing type] section of config file
+            $config =& $this->config->getItem('section', 'conf');
+            $ctrlCtyD =& $config->getItem('directive', 'controlledCities');
+            if ($ctrlCtyD->getContent()) {
+                $page->city = $row['city_name'];
+            } else {
+                $page->city = $row['city'];
+            }
+            $page->state_name  = $row['state_name'];
+            $page->state_abbr  = $row['state_abbr'];
+            $page->zip         = $row['zip'];
+            $page->phone       = $row['phone'];
+            $page->toll_free   = $row['toll_free'];
+            $page->fax         = $row['fax'];
+            $page->email       = $row['member_contact_email'];
+            if (($row['url']) && strpos($row['url'], 'http://') === false) {
+                $row['url'] = 'http://' . $row['url'];
+            }
+            $page->url         = $row['url'];
+
+            //  handle if we should be showing mailing addresses
+            //  on the member profile or not.
+            $mailingAddyD
+                =& $config->getItem('directive', 'showMailingAddress');
+            if ($mailingAddyD->getContent()) {
+                $sql = "
+                    SELECT c.city_name AS mailing_city_name, s.state_name AS
+                           mailing_state_name, s.state_abb AS mailing_state_abbr
+                      FROM member m, city c, state s
+                     WHERE m.member_id        = :mid
+                       AND m.mailing_state_id = s.state_id
+                       AND m.mailing_city_id  = c.city_id";
+
+                $stmt = $this->dbh->prepare($sql);
+                $stmt->bindParam(':mid', $this->_mid, PDO::PARAM_INT);
+                $stmt->execute();
+                $mailingAddyRow = $stmt->fetch(PDO::FETCH_ASSOC);
+
+                if ($mailingAddyRow) {
+                    $page->hasMailingAddy = true;
+                    $page->mailing_address
+                        = $row['mailing_address'];
+                    $page->mailing_city
+                        = $mailingAddyRow['mailing_city_name'];
+                    $page->mailing_state
+                        = $mailingAddyRow['mailing_state_name'];
+                    $page->mailing_state_abbr
+                        = $mailingAddyRow['mailing_state_abbr'];
+                    $page->mailing_zip = $row['mailing_zip'];
+                }
+            }
+
+            if (!empty($row['lat']) && !empty($row['lon'])) {
+                $fromAddress  = trim(urlencode(str_replace("\n", "", strip_tags($row['member_name']))));
+                $fromAddress .= "@{$row['lat']},{$row['lon']}";
+            } else {
+                $fromAddress = trim(urlencode(str_replace("\n", "", strip_tags($row['street']))))
+                .','.trim(urlencode(str_replace("\n", "", strip_tags($row['city']))))
+                .','.trim(urlencode(str_replace("\n", "", strip_tags($row['state_name']))));
+            }
+
+            $page->daddr = $fromAddress;
+            $page->description = $row['description'];
+
+            $page->member_id = $row['member_id'];
+            if (defined("MEMBER_SESSION_LIST") && MEMBER_SESSION_LIST) {
+                $page->catid           = $_GET['catid'];
+                $page->trip_planner_id = MEMBER_SESSION_PAGE;
+                if ($_SESSION['wish_list'][$this->_mid]) {
+                    $page->addToLink = false;
+                    $page->plink     = Toolkit_Template_Page::getSeoUrl(
+                        $this->_pageGateway,
+                        MEMBER_SESSION_PAGE
+                    );
+                } else {
+                    $page->addToLink = true;
+                    $page->plink = MEDIA_BASE_URL . "Toolkit/Members/TripPlanner/"
+                    . "wish-list.php?catid={$this->_catid}"
+                    . "&amp;member_id={$row['member_id']}&amp;detail=1";
+                }
+            } else {
+                $page->plink = null;
+            }
+        } catch (PDOException $e) {
+            return Toolkit_Common::handleError($e);
+        }
+    }
+
+    //  }}}
+    //  {{{ setMemberPackages()
+
+    /**
+     * Get all the file data for the the member profile being looked at
+     *
+     * @param object &$page The object you want to use with the flexy template
+     *
+     * @return void
+     * @access protected
+     */
+    protected function setMemberPackages(&$page)
+    {
+        try {
+            $sql = "
+              SELECT id, title, description, image
+                FROM member_packages
+               WHERE member_id = :mid
+                 AND NOT pending
+                 AND CURRENT_DATE BETWEEN sdate AND edate
+            ORDER BY id";
+
+            $stmt = $this->dbh->prepare($sql);
+            $stmt->bindParam(':mid', $this->_mid, PDO::PARAM_INT);
+            $stmt->execute();
+
+            $count = 0;
+            while ($row = $stmt->fetch()) {
+                $page->packages[$count]['title'] = $row['title'];
+                $page->packages[$count]['id']    = $row['id'];
+                if (!empty($row['image'])) {
+                    $img = $page->image_path . $row['image'];
+                    $page->packages[$count]['image'] = $img;
+                }
+                if (!empty($row['description'])) {
+                    $page->packages[$count]['description'] = $row['description'];
+                }
+                ++$count;
+            }
+        } catch (PDOException $e) {
+            return Toolkit_Common::handleError($e);
+        }
+    }
+
+    //  }}}
+    //  {{{ setMemberPhotos()
+
+    /**
+     * set all the non-pending photos that a member has
+     *
+     * @param object &$page The object you want to use with the flexy template
+     *
+     * @return void
+     * @access protected
+     */
+    protected function setMemberPhotos(&$page)
+    {
+        try {
+            $sql = "
+                SELECT *
+                  FROM member_photos
+                 WHERE member_id = :mid
+                   AND (NOT pending OR pending IS NULL)
+                 ORDER BY pos";
+
+            $stmt = $this->dbh->prepare($sql);
+            $stmt->bindParam(':mid', $this->_mid, PDO::PARAM_INT);
+            $stmt->execute();
+
+            $count = 0;
+            while ($row = $stmt->fetch()) {
+                $images[$count]['alt'] = $row['caption'];
+                $images[$count]['id']  = PHOTO_LARGE_URL . $row['image'];
+                $images[$count]['src'] = PHOTO_SMALL_URL . $row['image'];
+                $images[$count]['img'] = $row['image'];
+                ++$count;
+            }
+
+            $page->photos = !empty($images) ? $images : false;
+        } catch (PDOException $e) {
+            return Toolkit_Common::handleError($e);
+        }
+    }
+
+    //  }}}
+    //  {{{ setMemberRestaurant()
+
+    /**
+     * Get all the restaurant data for the the member profile being looked at
+     *
+     * @param object &$page The object you want to use with the flexy template
+     *
+     * @return void
+     * @access protected
+     */
+    protected function setMemberRestaurant(&$page)
+    {
+        try {
+            $sql = "
+                SELECT column_name, data_type
+                  FROM information_schema.columns
+                 WHERE table_name = 'member_restaurants'";
+
+            $tableData = $this->dbh->query($sql)->fetchAll();
+
+            $sql = "
+                SELECT *
+                  FROM member_restaurants
+                 WHERE member_id = :mid";
+
+            $stmt = $this->dbh->prepare($sql);
+            $stmt->bindParam(':mid', $this->_mid, PDO::PARAM_INT);
+            $stmt->execute();
+
+            if ($row = $stmt->fetch()) {
+                $show = false;
+                foreach ($tableData as $td) {
+                    switch ($td['data_type']) {
+                    case 'radio' :
+                    case 'boolean' :
+                        $restaurant[$td['column_name']]
+                            = ($row[$td['column_name']] == 't') ? 'Yes': 'No';
+                        if ($row[$td['column_name']] == 't') {
+                            $show = true;
+                        }
+                        break;
+
+                    default :
+                        $restaurant[$td['column_name']] = $row[$td['column_name']];
+                        break;
+                    }
+                }
+
+                $page->restaurant_info = $show;
+                $page->breakfast       = $restaurant['breakfast'];
+                $page->brunch          = $restaurant['brunch'];
+                $page->lunch           = $restaurant['lunch'];
+                $page->dinner          = $restaurant['dinner'];
+                $page->alcohol         = $restaurant['alcohol'];
+            }
+        } catch (PDOException $e) {
+            return Toolkit_Common::handleError($e);
+        }
+    }
+
+    //  }}}
+    //  {{{ setMemberSocialMedia()
+
+    /**
+     * Gets all the members social media links
+     *
+     * @param object &$page The object you want to use with the flexy template
+     *
+     * @return mixed array if the member has any links otherwise false
+     * @access protected
+     */
+    protected function setMemberSocialMedia(&$page)
+    {
+        try {
+            $sql = "
+                SELECT facebook, twitter, myspace, linkedin, blog,
+                       youtube, pinterest, instagram, google_plus
+                  FROM member
+                 WHERE member_id = :mid";
+
+            $stmt = $this->dbh->prepare($sql);
+            $stmt->bindParam(':mid', $this->_mid, PDO::PARAM_INT);
+            $stmt->execute();
+
+            $row = $stmt->fetch(PDO::FETCH_ASSOC);
+            $media = array_filter($row);
+
+            if (!empty($media)) {
+                $page->has_social_media = true;
+                foreach ($media as $k => $v) {
+                    $page->$k = $v;
+                }
+            }
+        } catch (PDOException $e) {
+            return Toolkit_Common::handleError($e);
+        }
+    }
+
+    //  }}}
+
+    //  {{{ _validMember()
+
+    /**
+     * Verify the member if valid
+     *
+     * check that the member exists in the DB and that they are active
+     *
+     * @return boolean true if valid, false if not
+     * @access private
+     */
+    private function _validMember()
+    {
+        if (   !$this->_memberExists()
+            || !$this->_memberActive()
+            || $this->_isNewMember()
+        ) {
+            return false;
+        }
+
+        return true;
+    }
+
+    //  }}}
+}
diff --git a/Toolkit/Members/ProfileWriter.php b/Toolkit/Members/ProfileWriter.php
new file mode 100644 (file)
index 0000000..d193f64
--- /dev/null
@@ -0,0 +1,240 @@
+<?php
+
+/**
+ * Member profile writer
+ *
+ * PHP version 5
+ *
+ * The license text...
+ *
+ * @category  Members
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <jamie@gaslightmedia.com>
+ * @copyright 2010 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com/ Gaslightmedia
+ * @version   CVS: $Id:$
+ * @link      <>
+ * @see       References to other sections (if any)...
+ */
+
+/**
+ * Writes a profile to HTML
+ *
+ * Uses server side caching to increase page return speed.  If a cache exists
+ * for a member - the cache will be returned.  If the member profile template
+ * has been changed after the cache file was written, all the cached
+ * profiles will be cleaned so they can be rewritten on the next pull
+ * using the new template.
+ *
+ * @category  Members
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <jamie@gaslightmedia.com>
+ * @copyright 2010 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com/ Gaslightmedia
+ * @version   Release: @package_version@
+ * @link      <>
+ * @see       References to other sections (if any)...
+ */
+class Toolkit_Members_ProfileWriter implements SplSubject
+{
+       //      {{{     properties
+
+       /**
+        * Observers for the observer pattern
+        * @var    SplSubject
+        * @access private
+        */
+       private $_observers = array();
+
+    /**
+     * Cache
+     * @var    Cache_Lite
+     * @access private
+     */
+       private $_cache;
+
+    /**
+     * Template Engine
+     * @var    HTML_Template_Flexy
+     * @access private
+     */
+       private $_tEngine;
+
+       //      }}}
+       //      {{{     __construct()
+
+
+    /**
+     * Constructor
+     *
+     * @param Cache_Lite          $cache   Caching object
+     * @param HTML_Template_Flexy $tEngine Template engine
+        *
+     * @return void
+     * @access public
+     */
+       public function __construct(
+               Cache_Lite $cache,
+               HTML_Template_Flexy $tEngine
+       ) {
+               $this->_cache   = $cache;
+               $this->_tEngine = $tEngine;
+       }
+
+       //      }}}
+
+       //  {{{ attach()
+
+
+    /**
+     * Attach observer to the subject
+     *
+     * @param SplObserver $observer Observer to attach
+        *
+     * @return void
+     * @access public
+     */
+       public function attach(SplObserver $observer)
+       {
+               $this->_observers[] = $observer;
+       }
+
+       //  }}}
+       //  {{{ detach()
+
+
+    /**
+     * Detach observer form subject
+     *
+     * @param SplObserver $observer Observer to detach
+        *
+     * @return void
+     * @access public
+     */
+       public function detach(SplObserver $observer)
+       {
+               if ($idx = array_search($observer, $this->_observers, true)) {
+                       unset($this->_observers[$idx]);
+               }
+       }
+
+       //  }}}
+       //  {{{ notify()
+
+
+    /**
+     * Notify observers of action
+     *
+     * @return void
+     * @access public
+     */
+       public function notify()
+       {
+               foreach ($this->_observers as $observer) {
+                       $observer->update($this);
+               }
+       }
+
+       //  }}}
+
+       //      {{{     setTemplate()
+
+       /**
+        * Set the template to use with the template engine
+        *
+        * @param string Path to template file
+        *
+        * @return void
+        * @access public
+        */
+       public function setTemplate($template)
+       {
+               $this->_template = $template;
+       }
+
+       //      }}}
+       //      {{{     _isTemplateNewer()
+
+       /**
+        * Check to see if a template has been updated after a cache was written
+        *
+        * @param string $tplFile   Path to template file
+        * @param string $cacheFile Path to cache file
+        *
+        * @return boolean If template mod time is LTE cache mod time
+        * @access private
+        */
+       private function _isTemplateNewer($tplFile, $cacheFile)
+       {
+               $tplPath = $this->_tEngine->options['templateDir'][0];
+               $strlen = strlen($tplPath);
+               if ($tplPath[$strlen - 1] !== DIRECTORY_SEPARATOR) {
+                       $tplPath .= DIRECTORY_SEPARATOR;
+               }
+
+               if (file_exists($cacheFile)) {
+                       //      If the template was modified after the cache was
+                       //      written the mod time will be greater
+                       return (filemtime($cacheFile) <= filemtime($tplPath . $tplFile));
+               }
+       }
+
+       //      }}}
+       //      {{{     toHtml()
+
+
+    /**
+     * Gets an HTML respresentation of the profile
+     *
+        * If profile cache is available - fetch cache to bypass compile time.
+        * If template has been updated after cache was written - clean cache files
+        * and re-create cache to include updates.
+        *
+        * send out notification to observers.
+     *
+     * @param Toolkit_Members_ProfilePage $profile Profile Page Object
+        *
+     * @return boolean Return description (if any) ...
+     * @access public
+     */
+       public function toHtml(Toolkit_Members_ProfilePage $profile)
+       {
+               $GLOBALS['bottomScripts'][]
+                       = MEDIA_APP_BASE_URL . 'gallery/thickbox-3.1.1.js';
+               $GLOBALS['bottomScripts'][]
+                       = MEDIA_BASE_URL . 'Toolkit/Members/libjs/business-detail.js';
+
+               $GLOBALS['styleSheets'][] = MEDIA_BASE_URL . 'Toolkit/Members/css/member.css';
+               $GLOBALS['styleSheets'][] = MEDIA_APP_BASE_URL . 'gallery/gallery.css';
+               $GLOBALS['styleSheets'][] = MEDIA_APP_BASE_URL . 'gallery/thickbox.css';
+
+               $profileCache = "Member-{$profile->getMemberId()}";
+               $out = $this->_cache->get($profileCache, 'Profile');
+               $cacheFile = $this->_cache->_cacheDir . "cache_Profile_$profileCache";
+
+               if ($this->_isTemplateNewer($this->_template, $cacheFile)) {
+                       $this->_cache->clean('Profile');
+                       $out = false;
+               }
+
+               if (!$out) {
+                       $this->_tEngine->compile($this->_template);
+                       $profile->setMemberDetail(new stdClass());
+                       $out = $this->_tEngine->bufferedOutputObject($profile->profile);
+
+                       if ($out === false) {
+                               include BASE . '404.html';
+                               exit();
+                       } elseif (PEAR::isError($this->_cache->save($out))) {
+                               $logger =& Toolkit_Logger::getLogger();
+                               $logger->emerg($res->getMessage());
+                       }
+               }
+
+               $this->notify();
+
+               return $out;
+       }
+
+       //      }}}
+}
diff --git a/Toolkit/Members/RecordNavigation.php b/Toolkit/Members/RecordNavigation.php
new file mode 100644 (file)
index 0000000..102b9c3
--- /dev/null
@@ -0,0 +1,317 @@
+<?php
+//     vim:set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker syntax=php:
+
+/**
+ * Navigation class for editing member records
+ *
+ * PHP version 5
+ *
+ * @category MembersDB
+ * @package  Toolkit_Members
+ * @author   Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @license     http://www.gaslightmedia.com Gaslightmedia
+ * @version  CVS: $Id: RecordNavigation.php,v 1.7 2010/07/14 23:31:14 jamie Exp $
+ * @link     http://demo.gaslightmedia.com
+ */
+
+/**
+ * Base class for the memberdb
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2008 Gaslight Media
+ * @license      http://www.gaslightmedia.com Gaslightmedia
+ * @link      http://demo.gaslightmedia.com
+ */
+class Toolkit_Members_RecordNavigation
+{
+    //  {{{ __construct()
+
+    /**
+     * Constructor
+     *
+     * @param Config_Container $c Configuration object
+     *
+     * @return void
+     * @access public
+     */
+    public function __construct(Config_Container $c)
+    {
+        $this->config = $c;
+    }
+
+    //  }}}
+       //      {{{     getPageNav()
+
+    /**
+     * Render the page navigation that is defined in the navStructure
+     *
+     * @return string Page navigation structure
+     * @access public
+     */
+       public function getPageNav()
+       {
+               $menu = new HTML_Menu($this->navStructure, 'rows');
+        $menu->forceCurrentIndex($_GET['tab']);
+
+               $renderer = new HTML_Menu_DirectRenderer($tpl);
+               $renderer->setEntryTemplate(
+                       HTML_MENU_ENTRY_INACTIVE,
+                       '<li><a href="{url}" title="{desc}">{title}</a></li>'
+               );
+               $renderer->setEntryTemplate(
+                       HTML_MENU_ENTRY_ACTIVE,
+                       '<li><a class="current" href="{url}" title="{desc}">{title}</a></li>'
+               );
+               $renderer->setEntryTemplate(
+                       HTML_MENU_ENTRY_ACTIVEPATH,
+                       '<li><a class="current" href="{url}" title="{desc}">{title}</a></li>'
+               );
+               $renderer->setMenuTemplate('', '');
+               $renderer->setRowTemplate('<ul>', '</ul>');
+
+               $menu->render($renderer);
+               return $renderer->toHtml();
+       }
+
+       //      }}}
+
+       //      {{{     setupAdminNavStructure()
+
+    /**
+     * Sets up a multi dimensional array used for the subnav structure
+     *
+     * @return mixed false on sql error. otherwise void
+     * @access public
+     */
+       public function setupAdminNavStructure()
+       {
+        $singularType = $this->config
+                       ->getItem('section', 'listing type')
+                       ->getItem('directive', 'singular')
+                       ->getContent();
+               $pluralType = $this->config
+                       ->getItem('section', 'listing type')
+                       ->getItem('directive', 'plural')
+                       ->getContent();
+
+        //  Default URI parameters that will get us back to the
+        //  page were we can edit member data.
+               $params = "rt=Members&amp;ac=editMember";
+
+        //  If we are editing an existing member, then we need to
+        //  add their id into the URI so we know who we are editing
+        if (is_numeric($_GET['id'])) {
+            $params .= "&amp;id={$_GET['id']}";
+        }
+
+        //  We always show the member info tab.
+        //  whether we are adding a member or editing a member
+               $nav = array(
+                       'info' => array(
+                               'title' => "$singularType Info",
+                               'url' => "/members.php?$params",
+                               'desc' => "View and edit $singularType in the database",
+                       )
+        );
+        if (is_numeric($_GET['id'])) {
+            $nav['photos'] = array(
+                               'title' => 'Photos',
+                               'url' => "/members.php?$params",
+                               'desc' => "View and edit $pluralType in the database",
+                       );
+            $nav['packages'] = array(
+                               'title' => 'Packages',
+                               'url' => "/members.php?$params",
+                               'desc' => "View and edit $singularType categories",
+                       );
+                       $nav['amenities'] = array(
+                               'title' => 'Amenities',
+                               'url' => "/members.php?$params",
+                               'desc' => "View and edit $singularType amenities",
+                       );
+                       $nav['files'] = array(
+                               'title' => 'Files',
+                               'url' => "/members.php?$params",
+                               'desc' => "View and edit $singularType regions",
+                       );
+                       $nav['contacts'] = array(
+                               'title' => 'Contacts',
+                               'url' => "/members.php?$params",
+                               'desc' => "View and edit $singularType cities",
+                       );
+            $billingModule = $this->config->getItem('section', 'admin')
+                ->getItem('directive', 'billing')
+                ->getContent();
+
+
+            if ($billingModule) {
+                $billingNav = new Toolkit_Members_Billing_RecordNavigation();
+                $nav = $billingNav->getNavigationArray($params, $nav);
+            }
+        }
+
+               $hasPhotos = $this->config
+                       ->getItem('section', 'admin')
+                       ->getItem('directive', 'photos')
+                       ->getContent();
+
+               if (!$hasPhotos) {
+                       unset($nav['photos']);
+               }
+
+               $hasPackages = $this->config
+                       ->getItem('section', 'admin')
+                       ->getItem('directive', 'packages')
+                       ->getContent();
+
+               if (!$hasPackages) {
+                       unset($nav['packages']);
+               }
+
+               $hasAmenities = $this->config
+                       ->getItem('section', 'admin')
+                       ->getItem('directive', 'amenities')
+                       ->getContent();
+
+               if (!$hasAmenities) {
+                       unset($nav['amenities']);
+               }
+
+               $hasFiles = $this->config
+                       ->getItem('section', 'admin')
+                       ->getItem('directive', 'files')
+                       ->getContent();
+
+               if (!$hasFiles) {
+                       unset($nav['files']);
+               }
+
+               $hasContacts = $this->config
+                       ->getItem('section', 'admin')
+                       ->getItem('directive', 'contacts')
+                       ->getContent();
+
+               if (!$hasContacts) {
+                       unset($nav['contacts']);
+               }
+
+        //  make full URI's and attach which tab we are working with
+        foreach ($nav as $i => &$j) {
+            $j['url'] =  MEDIA_BASE_URL . 'admin' . $j['url'] . "&amp;tab=$i";
+        }
+
+               $this->navStructure = $nav;
+       }
+
+       //      }}}
+       //      {{{     setupUserNavSructure()
+
+    /**
+     * Sets up a multi dimensional array used for the subnav structure
+     *
+     * @return mixed  false on sql error. otherwise void
+     * @access public
+     */
+       public function setupUserNavStructure()
+       {
+        $singularType = $this->config
+                       ->getItem('section', 'listing type')
+                       ->getItem('directive', 'singular')
+                       ->getContent();
+               $pluralType = $this->config
+                       ->getItem('section', 'listing type')
+                       ->getItem('directive', 'plural')
+                       ->getContent();
+
+               $params = "rt=EditProfile";
+               $nav = array(
+                       'info' => array(
+                               'title' => "$singularType Info",
+                               'url' => "/?$params",
+                               'desc' => "View and edit $singularType in the database",
+                       ),
+                       'photos' => array(
+                               'title' => 'Photos',
+                               'url' => "/?$params",
+                               'desc' => "View and edit $pluralType in the database",
+                       ),
+                       'packages' => array(
+                               'title' => 'Packages',
+                               'url' => "/?$params",
+                               'desc' => "View and edit $singularType categories",
+                       ),
+                       'amenities' => array(
+                               'title' => 'Amenities',
+                               'url' => "/?$params",
+                               'desc' => "View and edit $singularType amenities",
+                       ),
+                       'files' => array(
+                               'title' => 'Files',
+                               'url' => "/?$params",
+                               'desc' => "View and edit $singularType regions",
+                       ),
+                       'contacts' => array(
+                               'title' => 'Contacts',
+                               'url' => "/?$params",
+                               'desc' => "View and edit $singularType cities",
+                       ),
+               );
+
+               $hasPhotos = $this->config
+                       ->getItem('section', 'members only')
+                       ->getItem('directive', 'photos')
+                       ->getContent();
+
+               if (!$hasPhotos) {
+                       unset($nav['photos']);
+               }
+
+               $hasPackages = $this->config
+                       ->getItem('section', 'members only')
+                       ->getItem('directive', 'packages')
+                       ->getContent();
+
+               if (!$hasPackages) {
+                       unset($nav['packages']);
+               }
+
+               $hasAmenities = $this->config
+                       ->getItem('section', 'members only')
+                       ->getItem('directive', 'amenities')
+                       ->getContent();
+
+               if (!$hasAmenities) {
+                       unset($nav['amenities']);
+               }
+
+               $hasFiles = $this->config
+                       ->getItem('section', 'members only')
+                       ->getItem('directive', 'files')
+                       ->getContent();
+
+               if (!$hasFiles) {
+                       unset($nav['files']);
+               }
+
+               $hasContacts = $this->config
+                       ->getItem('section', 'members only')
+                       ->getItem('directive', 'contacts')
+                       ->getContent();
+
+               if (!$hasContacts) {
+                       unset($nav['contacts']);
+               }
+
+        //  make full URI's and attach which tab we are working with
+        foreach ($nav as $i => &$j) {
+            $j['url'] =  MEDIA_BASE_URL . 'members-only-area' . $j['url'] . "&amp;tab=$i";
+        }
+
+               $this->navStructure = $nav;
+       }
+
+       //      }}}
+}
+?>
diff --git a/Toolkit/Members/Rules/DuplicateMember.php b/Toolkit/Members/Rules/DuplicateMember.php
new file mode 100644 (file)
index 0000000..ba030ee
--- /dev/null
@@ -0,0 +1,99 @@
+<?php
+
+/**
+ * validate if a member name is being duplicated or not
+ *
+ * PHP version 5
+ *
+ * The license text...
+ *
+ * @category  Members
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <jamie@gaslightmedia.com>
+ * @copyright 2010 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com/ Gaslightmedia
+ * @version   CVS: $Id: DuplicateMember.php,v 1.2 2010/05/15 15:14:54 jamie Exp $
+ * @link      <>
+ * @see       References to other sections (if any)...
+ */
+
+
+/**
+ * Validate if a member name is being duplicated or not
+ *
+ * Check to see if the member name exists already in the database.
+ * If it does not, then it is a valid name. Else it is invalid.
+ *
+ * @category  Members
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <jamie@gaslightmedia.com>
+ * @copyright 2010 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com/ Gaslightmedia
+ * @version   Release: @package_version@
+ * @link      <>
+ * @see       References to other sections (if any)...
+ */
+class Toolkit_Members_Rules_DuplicateMember extends HTML_QuickForm_Rule
+{
+       //      {{{     validate()
+
+       /**
+        * Validate if a member already exists
+        *
+        * @param string $value the date array to validate
+        * @param PDO    $dbh   Database handler used to check the DB
+
+        * @return bool if the string could correctly be validated as a date.
+        * @access      public
+        * @see         Validate::date()
+        */
+       function validate($value, PDO $dbh)
+       {
+               try {
+                       $sql = "
+                               SELECT count(*) AS total
+                                 FROM member
+                                WHERE member_name = :name";
+
+                       $stmt = $dbh->prepare($sql);
+                       $stmt->bindParam(':name', $value, PDO::PARAM_STR);
+                       $stmt->execute();
+                       $stmt->bindColumn('total', $isValid);
+                       $stmt->fetch();
+               } catch (PDOException $e) {
+                       Toolkit_Logger::logException('DB Error', $e);
+                       throw new Toolkit_Members_Exception(
+                               'Error checking for duplicate member names'
+                       );
+               }
+
+               return (bool) !$isValid;
+       }
+
+       //      }}}
+       //      {{{ getValidationScript()
+
+    /**
+     * Cannot be validated via JS
+     *
+        * Could set up an AJAX call to perform this.  but haven't done it yet
+     *
+     * @param  unknown $options Parameter description (if any) ...
+     * @return mixed   Return description (if any) ...
+     * @access public
+     */
+       function getValidationScript($options = null)
+       {
+               return array('', false);
+       }
+
+       //      }}}
+}
+
+HTML_QuickForm::registerRule(
+       'DuplicateMember',
+       'rule',
+       'Toolkit_Members_Rules_DuplicateMember',
+       BASE . 'Toolkit/Members/Rules/DuplicateMember.php'
+);
+?>
diff --git a/Toolkit/Members/Rules/MemberFile.php b/Toolkit/Members/Rules/MemberFile.php
new file mode 100644 (file)
index 0000000..c093416
--- /dev/null
@@ -0,0 +1,159 @@
+<?php
+/**
+ * MemberFile.php
+ *
+ * PHP Version 5
+ *
+ * @category Toolkit
+ * @package  Members_Rules
+ * @author   Steve Sutton <steve@gaslightmedia.com>
+ * @license  Gaslight Media
+ * @link     <>
+ */
+/**
+ * Toolkit_Members_Rules_MemberFile
+ *
+ * Description for Toolkit_Members_Rules_MemberFile
+ *
+ * @category Toolkit
+ * @package  Members_Rules
+ * @author   Steve Sutton <steve@gaslightmedia.com>
+ * @license  Gaslight Media
+ * @link     <>
+ */
+class Toolkit_Members_Rules_MemberFile extends HTML_QuickForm_Rule
+{
+       //      {{{     fileUpload()
+
+       /**
+        * Upload a file into its proper spot
+        *
+        * Uploads a file with a unique filename to a target dir by:
+        * 1. Remove illegal characters from the filename.
+        * 2. Check if a file in the target dir exists with that name
+        * 3. If a file does exist, append a unix timestamp to the name to make it
+        *              a unique filename.
+        * 4. Copy the file in the tmp dir to its target dir under its new name.
+        * 5. Verify the new file exists.
+        * 6. If it does make it readable.
+        *
+        * @param string $tmpFile   The name of the file in the tmp dir
+        *                                                  As given by the $_FILES array.
+        * @param string $fileName  The name of the new file, as given by the
+        *                                                  $_FILES array.
+        * @param string $targetDir     The path of the dir where the file should go.
+     * 
+        * @author Jamie Kahgee <steve@gaslightmedia.com
+        *
+        * @return mixed True if successfull, otherwise PEAR error.
+        * @access protected
+        */
+       protected function fileUpload($tmpFile, $fileName, $targetDir)
+       {
+               $fileName = preg_replace('/[!@#$%^&()+={};:\'\"\/ ]/', '-', $fileName);
+               $nameInUse = (file_exists($targetDir . $fileName));
+               if ($nameInUse) {
+                       $fileName = mktime() . $fileName;
+               }
+               $newLocation = $targetDir . $fileName;
+               //      Copy the tmp file into its new spot under its new name.
+               if (!copy($tmpFile, $newLocation)) {
+                       return PEAR::raiseError(
+                               'Unable to copy uploaded file into its target destination.'
+                       );
+               }
+               //      Make sure the file exists
+               if (!is_file($newLocation)) {
+                       return PEAR::raiseError('New file does not exist!');
+               }
+
+               //      Make sure the file is readable.
+               chmod($newLocation, 0666);
+
+               return $fileName;
+       }
+
+       //      }}}
+       //      {{{     getValidationScript()
+
+    /**
+     * Description of getValidationScript
+     * 
+     * @param array $options Options
+     * 
+     * @return void
+     * @access public
+     */
+       function getValidationScript($options = null)
+       {
+       }
+
+       //      }}}
+       //      {{{     validate()
+
+    /**
+     * Description of validate()
+     * 
+     * @param array $value    Value array
+     * @param array &$options Options array
+     * 
+     * @global unknown $form description of $form
+     * 
+     * @return boolean 
+     * @access public
+     */
+       function validate($value, &$options = null)
+       {
+               if ($value['error'] != UPLOAD_ERR_OK) {
+                       return false;
+               }
+               //      Ewwwwww GLOBALS....I know they suck, but unavoidable in this case.
+               if (is_null($options)) {
+                       global $form;
+               } elseif (is_array($options)) {
+                   //  an array of info is passed along the options.
+                       $form =& $options[0];
+                       $required = $options[1];
+               } else {
+                   //  No array is passed along. just the form object.
+            // image upload is then required.
+                       $form =& $options;
+                       $required = true;
+               }
+
+               //      A file was uploaded from the file element.
+               //      process the file
+               //      insert it into the forms submit values
+               if (!empty($value['size'])) {
+                       $fileName = $this->fileUpload(
+                               $value['tmp_name'],
+                               $value['name'],
+                               MEMBER_UPLOADED_FILES
+                       );
+                       if (PEAR::isError($fileName)) {
+                               echo 'File Upload Error: ' . $fileName->getMessage();
+                               return false;
+                       }
+
+                       $element = array_keys(
+                               Toolkit_Common::multiDimArrayLocate($_FILES, $value['name'])
+                       );
+
+                       $origName    = $element[0];
+                       $elementName = "uploaded_{$element[0]}";
+
+                       $form->_submitValues[$elementName] = $fileName;
+               }
+               return true;
+       }
+
+       //      }}}
+}
+
+HTML_QuickForm::registerRule(
+       'MemberFile',
+       'rule',
+       'Toolkit_Members_Rules_MemberFile',
+       BASE . 'Toolkit/Members/Rules/MemberFile.php'
+);
+?>
diff --git a/Toolkit/Members/Rules/MemberLogin.php b/Toolkit/Members/Rules/MemberLogin.php
new file mode 100644 (file)
index 0000000..f4be360
--- /dev/null
@@ -0,0 +1,99 @@
+<?php
+
+/**
+ * validate if a member login is being duplicated or not
+ *
+ * PHP version 5
+ *
+ * The license text...
+ *
+ * @category  Members
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <jamie@gaslightmedia.com>
+ * @copyright 2010 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com/ Gaslightmedia
+ * @version   CVS: $Id: MemberLogin.php,v 1.1 2010/05/16 01:51:13 jamie Exp $
+ * @link      <>
+ * @see       References to other sections (if any)...
+ */
+
+
+/**
+ * Validate if a member login is being duplicated or not
+ *
+ * Check to see if the member login exists already in the database.
+ * If it does not, then it is a valid login . Else it is invalid.
+ *
+ * @category  Members
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <jamie@gaslightmedia.com>
+ * @copyright 2010 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com/ Gaslightmedia
+ * @version   Release: @package_version@
+ * @link      <>
+ * @see       References to other sections (if any)...
+ */
+class Toolkit_Members_Rules_MemberLogin extends HTML_QuickForm_Rule
+{
+       //      {{{     validate()
+
+       /**
+        * Validate if a member already exists
+        *
+        * @param string $value the date array to validate
+        * @param PDO    $dbh   Database handler used to check the DB
+
+        * @return bool if the string could correctly be validated as a date.
+        * @access      public
+        * @see         Validate::date()
+        */
+       function validate($value, PDO $dbh)
+       {
+               try {
+                       $sql = "
+                               SELECT count(*) AS total
+                                 FROM member
+                                WHERE member_login = :name";
+
+                       $stmt = $dbh->prepare($sql);
+                       $stmt->bindParam(':name', $value, PDO::PARAM_STR);
+                       $stmt->execute();
+                       $stmt->bindColumn('total', $isValid);
+                       $stmt->fetch();
+               } catch (PDOException $e) {
+                       Toolkit_Logger::logException('DB Error', $e);
+                       throw new Toolkit_Members_Exception(
+                               'Error checking for duplicate member names'
+                       );
+               }
+
+               return (bool) !$isValid;
+       }
+
+       //      }}}
+       //      {{{ getValidationScript()
+
+    /**
+     * Cannot be validated via JS
+     *
+        * Could set up an AJAX call to perform this.  but haven't done it yet
+     *
+     * @param  unknown $options Parameter description (if any) ...
+     * @return mixed   Return description (if any) ...
+     * @access public
+     */
+       function getValidationScript($options = null)
+       {
+               return array('', false);
+       }
+
+       //      }}}
+}
+
+HTML_QuickForm::registerRule(
+       'MemberLogin',
+       'rule',
+       'Toolkit_Members_Rules_MemberLogin',
+       BASE . 'Toolkit/Members/Rules/MemberLogin.php'
+);
+?>
diff --git a/Toolkit/Members/SearchList.php b/Toolkit/Members/SearchList.php
new file mode 100644 (file)
index 0000000..bf0ceb5
--- /dev/null
@@ -0,0 +1,824 @@
+<?php
+
+/**
+ * SearchList.php
+ *
+ * PHP version 5
+ *
+ * @category Toolkit
+ * @package  Members
+ * @author   Jamie Kahgee <steve@gaslightmedia.com>
+ * @license     http://www.gaslightmedia.com Gaslightmedia
+ * @release  CVS: $Id: SearchList.php,v 1.47 2010/07/14 23:31:14 jamie Exp $
+ * @link     http://demo.gaslightmedia.com
+ */
+
+/**
+ * Datagrid of search results of member db
+ *
+ * @category  Toolkit
+ * @package   Members
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @link      http://demo.gaslightmedia.com
+ */
+class Toolkit_Members_SearchList extends Toolkit_FlexyDataGridBuilder
+{
+    //    {{{    properties
+
+    /**
+     * Description for protected
+     * @var    string
+     * @access protected
+     */
+    protected $tableName = 'member';
+
+    /**
+     * Description for protected
+     * @var    array
+     * @access protected
+     */
+    protected $queryParams = array();
+
+    /**
+     * Description for protected
+     * @var    unknown
+     * @access protected
+     */
+    protected $showSearchBox;
+
+    /**
+     * Description for protected
+     * @var    string
+     * @access protected
+     */
+    protected $template = 'membersList.tpl';
+
+    /**
+     * Whether subcats should be included in the search results
+     *
+     * If this is turned on, the subcategories beneath all categories assigned
+     * to the page will be included in the search results.
+     *
+     * Example Category Tree:
+     *
+     * Parent
+     *   |_ Child
+     *   |    |_ GrandChild
+     *   |
+     *   |_ Sibling
+     *
+     * If set to true and only "Parent" is assigned to a page from the toolbox,
+     * then members with all categories (parent, child, grandchild, sibling) will
+     * be included in the search results.
+     *
+     * If set to false and only Child is assigned to a page, then only members
+     * that have the category "Child" assigned to them will be included in the
+     * search Results.
+     *
+     * @var boolean
+     * @access protected
+     */
+    protected $includeSubCats = true;
+
+    /**
+     * map icon numbers only get displayed if the map is there
+     *
+     * @var boolean
+     * @access protected
+     */
+    protected $memberMap = false;
+
+    //    }}}
+    //    {{{ __construct()
+
+    /**
+     * Class constructor
+     *
+     * define where templates for the data grid are at, then call the parent constructor which will handle
+     * finishing the settings for the datagrid.
+     *
+     * After all settings are finished you can call the toHTML() function on this object and the datagrid
+     * will be rendered and returned as a string.  Optionally you could call show() and the datagrid would
+     * be rendered and output immediatley to the screen.
+     *
+     * @param PDO     $pdo           PHP Data Object to use for DB calls
+     * @param string  $limit         The number of records to display per page.
+     * @param int     $page             The current page viewed.
+     *                               In most cases, this is useless.
+     *                               Note: if you specify this, the "page" GET
+     *                               variable will be ignored.
+     * @param string  $rendererType  The type of renderer to use.
+     *                               You may prefer to use the $type argument
+     *                               of {@link render}, {@link fill} or
+     *                               {@link getOutput}
+     * @param boolean $showSearchBox If we should allow searching for members
+     * @param array   $sections      specific members we should show
+     *
+     * @return void
+     * @access public
+     */
+    public function __construct(
+        PDO $pdo,
+        $limit = null,
+        $page = null,
+        $rendererType = null,
+        $showSearchBox = true,
+        array $sections = null
+    ) {
+        $GLOBALS['styleSheets'][] = MEDIA_BASE_URL . 'Toolkit/Members/css/member.css';
+        $GLOBALS['bottomScripts'][]
+            = MEDIA_BASE_URL . 'Toolkit/Members/libjs/business-search.js';
+        if (defined("MEMBER_SESSION_LIST") && MEMBER_SESSION_LIST) {
+            $GLOBALS['bottomScripts'][]
+                = MEDIA_BASE_URL . 'Toolkit/Members/libjs/travel-list.js';
+        }
+
+        $this->showSearchBox = $showSearchBox;
+        $this->pagerOptions['containerClass'] = 'pages';
+        parent::__construct($pdo, $limit, $page, $rendererType);
+    }
+
+    //    }}}
+
+    //    {{{ city()
+
+    /**
+     * Returns the city for a member.
+     *
+     * If this memberdb is using controlled cities then we need to extract
+     * the city name via the city_id
+     *
+     * @param array $data tuple record from db
+     *
+     * @return string City name for the record
+     * @access public
+     */
+    public function city($data)
+    {
+        //  get reference to [conf] section of config file
+        $ctrlCtyD =& $this->config
+            ->getItem('section', 'conf')
+            ->getItem('directive', 'controlledCities');
+
+        if ($ctrlCtyD->getContent()) {
+            return $data['record']['city_name'];
+        } else {
+            return $data['record']['city'];
+        }
+    }
+
+    //    }}}
+    //    {{{ configureColumns()
+
+    /**
+     * Configures the columns that will be used in our datagrid renderer.
+     *
+     * @return void
+     * @access protected
+     */
+    protected function configureColumns()
+    {
+        $this->addColumn(new Structures_DataGrid_Column(
+            'Logo',
+            'logo',
+            'logo',
+            null,
+            null,
+            array(&$this, 'logo')
+        ));
+
+        $this->addColumn(new Structures_DataGrid_Column(
+            'Member Name',
+            'member_name',
+            'member_name'
+        ));
+
+        $this->addColumn(new Structures_DataGrid_Column(
+            'Email',
+            'member_contact_email',
+            'member_contact_email'
+        ));
+
+        $this->addColumn(new Structures_DataGrid_Column(
+            'Address',
+            'address',
+            'address',
+            null,
+            null,
+            array(&$this, 'drivingDirections')
+        ));
+
+        $this->addColumn(new Structures_DataGrid_Column(
+            'Street',
+            'street',
+            'street'
+        ));
+
+        $this->addColumn(new Structures_DataGrid_Column(
+            'City',
+            'city',
+            'city',
+            null,
+            null,
+            array(&$this, 'city')
+        ));
+
+        $this->addColumn(new Structures_DataGrid_Column(
+            'State',
+            'state',
+            'state'
+        ));
+
+        $this->addColumn(new Structures_DataGrid_Column(
+            'State Abbr',
+            'state_abbr',
+            'state_abbr'
+        ));
+
+        $this->addColumn(new Structures_DataGrid_Column(
+            'Zip',
+            'zip',
+            'zip'
+        ));
+
+        $this->addColumn(new Structures_DataGrid_Column(
+            'Phone',
+            'phone',
+            'phone',
+            null,
+            null,
+            array(&$this, 'phone')
+        ));
+
+        $this->addColumn(new Structures_DataGrid_Column(
+            'URL',
+            'url',
+            'url',
+            null,
+            null,
+            array(&$this, 'url')
+        ));
+
+        $this->addColumn(new Structures_DataGrid_Column(
+            'Reservation Id',
+            'reservation_id',
+            'reservation_id'
+        ));
+
+        $this->addColumn(new Structures_DataGrid_Column(
+            'Number of Rooms',
+            'num_rooms',
+            'num_rooms'
+        ));
+
+        $this->addColumn(new Structures_DataGrid_Column(
+            'Year Round',
+            'year_round',
+            'year_round',
+            null,
+            null,
+            array(&$this, 'yearRound')
+        ));
+
+        $this->addColumn(new Structures_DataGrid_Column(
+            null,
+            'has_hotel_info',
+            'has_hotel_info',
+            null,
+            null,
+            array(&$this, 'hasHotelInfo')
+        ));
+
+        $this->addColumn(new Structures_DataGrid_Column(
+            null,
+            'has_member_packages',
+            'has_member_packages',
+            null,
+            null,
+            array(&$this, 'hasMemberPackages')
+        ));
+
+        $this->addColumn(new Structures_DataGrid_Column(
+            null,
+            'res_url',
+            'res_url',
+            null,
+            null,
+            array(&$this, 'teeTimeUrl')
+        ));
+        if (defined("MEMBER_SESSION_LIST") && MEMBER_SESSION_LIST) {
+            $this->addColumn(
+                new Structures_DataGrid_Column(
+                    'plink',
+                    'plink',
+                    'plink',
+                    null,
+                    null,
+                    array(&$this, 'listUrl')
+                )
+            );
+        }
+
+        $this->addColumn(
+            new Structures_DataGrid_Column(
+                'addToLink',
+                'addToLink',
+                'addToLink',
+                null,
+                null,
+                array(&$this, 'inList')
+            )
+        );
+
+        $this->addColumn(new Structures_DataGrid_Column(
+            'member_id',
+            'member_id',
+            'member_id'
+        ));
+
+        $this->addColumn(new Structures_DataGrid_Column(
+            'Website',
+            'website',
+            'website',
+            null,
+            null,
+            array(&$this, 'website')
+        ));
+
+        $this->addColumn(new Structures_DataGrid_Column(
+            'map_icon',
+            'map_icon',
+            'map_icon',
+            null,
+            null,
+            array(&$this, 'mapIcon')
+        ));
+
+    }
+
+    public function hasMemberPackages($data)
+    {
+        extract($data['record']);
+        try {
+            $sql = "
+            SELECT count(*) as count
+              FROM member_packages
+             WHERE member_id = :member_id
+               AND current_date BETWEEN sdate AND edate";
+            $stmt = $this->dbh->prepare($sql);
+            $stmt->bindParam(':member_id', $member_id, PDO::PARAM_INT);
+            $stmt->execute();
+            return $stmt->fetchColumn();
+        } catch(PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+        return !(empty($reservation_id) && empty($num_rooms) && empty($year_round));
+    }
+
+    public function teeTimeUrl($data)
+    {
+        extract($data['record']);
+        try {
+            $sql = "
+            SELECT res_url
+              FROM member_golf
+             WHERE member_id = :member_id";
+            $stmt = $this->dbh->prepare($sql);
+            $stmt->bindParam(':member_id', $member_id, PDO::PARAM_INT);
+            $stmt->execute();
+            return $stmt->fetchColumn();
+        } catch (PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+    }
+
+    //    }}}
+
+    //    {{{    drivingDirections()
+
+    /**
+     * Get the html link for driving directions to google
+     *
+     * @param array $data tuple record from db
+     *
+     * @return string html string for link to google maps for driving directions
+     * @access public
+     */
+    public function drivingDirections($data)
+    {
+        extract($data['record']);
+        $format = 'http://maps.google.com/maps?daddr=%s@%s,%s';
+        return sprintf(
+            $format,
+            urlencode($member_name),
+            $lat,
+            $lon
+        );
+    }
+
+    //    }}}
+
+    //  {{{ getAlphaList()
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @return array     Return description (if any) ...
+     *
+     * @access protected
+     */
+    protected function getAlphaList()
+    {
+        $pattern = "/ (\s*?AND\s*?)?upper\(substr\(m.member_name, 1, 1\)\) = upper\('.'\)/i";
+        $replacement = '';
+        $sql = preg_replace($pattern, $replacement, $this->sql);
+        try {
+            $letters = array();
+            $stmt = $this->dbh->prepare($sql);
+            $stmt->execute();
+
+            $stmt->bindColumn('member_name', $name);
+
+            while ($row = $stmt->fetch()) {
+                $letters[] = strtoupper($name{0});
+            }
+
+            $letters = array_unique($letters);
+            sort($letters);
+
+            $addNumeric = false;
+            while ($i = current($letters)) {
+                if (is_numeric($i)) {
+                    $addNumeric = true;
+                    unset($letters[key($letters)]);
+                } else {
+                    break;
+                }
+            }
+
+            if ($addNumeric) {
+                array_unshift($letters, '0-9');
+            }
+
+            //    We only need to show the links of alphabet if we have more than
+            //    one letter to display.
+            if (count($letters) > 1) {
+                foreach ($_GET as $k => $v) {
+                    if ($k != 'alpha') {
+                        if (is_array($v)) {
+                            foreach ($v as $i => $j) {
+                                $queryString .= $k . '[' . $i . ']=' . $j . '&';
+                            }
+                        } else {
+                            $queryString .= "$k=$v&";
+                        }
+                    }
+                }
+                $links['All']['url']
+                    = ($_GET['page_id'])
+                    ? MEDIA_BASE_URL . "members-only-area/?{$queryString}"
+                    : MEDIA_BASE_URL . "index.php?{$queryString}";
+                $links['All']['class'] = empty($_GET['alpha']) ? 'curr' : null;
+                foreach ($letters as $v) {
+                    $links[$v]['url']
+                        = ($_GET['page_id'])
+                        ? MEDIA_BASE_URL . "members-only-area/?{$queryString}alpha=$v"
+                        : MEDIA_BASE_URL . "index.php?{$queryString}alpha=$v";
+                    if ($_GET['alpha'] == $v) {
+                        $links[$v]['class'] = 'curr';
+                    }
+                }
+                return $links;
+            }
+        } catch (PDOException $e) {
+            return Toolkit_Common::handleError($e);
+        }
+    }
+
+    //    }}}
+
+    //    {{{ hasHotelInfo()
+
+    /**
+     * Determines if the record has any available hotel info to display
+     *
+     * @param array $data record data
+     *
+     * @return boolean true if any data to display, otherwise false
+     * @access public
+     */
+    public function hasHotelInfo($data)
+    {
+        extract($data['record']);
+        return !(empty($reservation_id) && empty($num_rooms) && empty($year_round));
+    }
+
+    //    }}}
+
+    // {{{ inList()
+
+    /**
+     * Returns the bool for a member in session (list)
+     *
+     * Used when configuring the columns for the data grid. This
+     * function returns true or false if the member is in not session
+     * then true
+     *
+     * @param array $data tuple record from db
+     *
+     * @return boolean true if member is not in $_SESSION['wish_list']
+     * @access public
+     */
+    public function inList($data)
+    {
+        return !($_SESSION['wish_list'][$data['record']['member_id']]);
+    }
+
+    // }}}
+
+    //    {{{ listUrl()
+
+    /**
+     * Returns the url for a member.
+     *
+     * Used when configuring the columns for the data grid. This
+     * function generates the url to get to the member detail page
+     *
+     * @param array $data tuple record from db
+     *
+     * @access    Public
+     * @version    Release: @package_version@
+     * @return    url for the add to or remove from $_SESSION['wish_list']
+     */
+    public function listUrl($data)
+    {
+        if (defined("MEMBER_SESSION_LIST") && MEMBER_SESSION_LIST) {
+            if ($_SESSION['wish_list'][$data['record']['member_id']]) {
+                $url = Toolkit_Template_Page::getSeoUrl($this->_pageGateway, MEMBER_SESSION_PAGE);
+            } else {
+                $url = MEDIA_BASE_URL . "Toolkit/Members/TripPlanner/"
+                . "wish-list.php?catid={$_REQUEST['catid']}"
+                . "&amp;member_id={$data['record']['member_id']}";
+            }
+        } else {
+            $url = false;
+        }
+        return $url;
+    }
+
+    //    }}}
+    //    {{{ logo()
+
+    /**
+     * Returns the logo for a member.
+     *
+     * @param array $data tuple record from db
+     *
+     * @return false when empty, otherwise logo path for member
+     * @access
+     */
+    public function logo($data)
+    {
+        extract($data['record']);
+        return empty($logo) ? false : MEMBER_PHOTOS . $logo;
+    }
+
+    //    }}}
+
+    //    {{{ phone()
+
+    /**
+     * Returns the phone # for a member.
+     *
+     * Used when configuring the columns for the data grid. This
+     * function generates the phone for the member
+     *
+     * @param array $data tuple record from db
+     *
+     * @return mixed false when empty, member phone number if not empty
+     * @access public
+     */
+    public function phone($data)
+    {
+        extract($data['record']);
+        return empty($phone) ? false : $phone;
+    }
+
+    //    }}}
+
+    //  {{{ setConfig()
+
+    /**
+     * Sets the query to use to fetch the datagrid results
+     *
+     * @param Config_Container $c Configuration object
+     *
+     * @return void
+     * @access public
+     */
+    public function setConfig(Config_Container $c)
+    {
+        $this->config = $c;
+    }
+
+    //  }}}
+    //  {{{ setMemberMap()
+
+    /**
+     * Sets the memberMap on if there's a map displayed on page
+     *
+     * @param unknown $memberMap Description for $memberMap
+     *
+     * @return void
+     * @access public
+     */
+    public function setMemberMap($memberMap)
+    {
+        $this->memberMap = $memberMap;
+    }
+
+    //  }}}
+    //  {{{ setPageGateway()
+
+    /**
+     * Sets the page gateway
+     *
+     * @param Toolkit_Toolbox_PageGatewayAbstract $pageGateway gateway obj
+     *
+     * @return void
+     * @access public
+     */
+    public function setPageGateway(
+        Toolkit_Toolbox_PageGatewayAbstract $pageGateway
+    ) {
+        $this->_pageGateway= $pageGateway;
+    }
+
+    //  }}}
+    //    {{{    setControlObject()
+
+    /**
+     * These are the objects that will be inserted into the template.
+     *
+     * @return void
+     * @access protected
+     */
+    protected function setControlObject()
+    {
+        $searchMapIconActive =& $this->config
+            ->getItem('section', 'conf')
+            ->getItem('directive', 'searchMapIconActive')
+            ->getContent();
+        $this->ctrlObj['base_url']            = MEDIA_BASE_URL;
+        $this->ctrlObj['catid']               = $_REQUEST['catid'];
+        $this->ctrlObj['trip_planner_id']     = MEMBER_SESSION_PAGE;
+        $this->ctrlObj['letters']             = $this->getAlphaList();
+        $this->ctrlObj['searchMapIconActive'] = $searchMapIconActive;
+
+        if (isset($_GET['search']) && $this->showSearchBox) {
+            $action = MEDIA_BASE_URL . "index.php?{$_SERVER['QUERY_STRING']}";
+            $form = new Toolkit_Members_UserSearchForm(
+                'SearchForm',
+                'get',
+                $action,
+                null,
+                null,
+                true
+            );
+            $res = $form->setCatId($_GET['catid']);
+
+            if (PEAR::isError($res)) {
+                return Toolkit_Common::handleError($res);
+            } else {
+                $form->setPageMemberCategories($this->_pageGateway);
+                $form->setPageMemberRegions($this->_pageGateway);
+                $form->configureForm($this->dbh, $this->config);
+                $this->ctrlObj['member_search_form'] = $form->toHtml(
+                    Toolkit_Members::getFlexyOptions(),
+                    $this->_pageGateway
+                );
+            }
+        }
+    }
+
+    //    }}}
+
+    //    {{{ url()
+
+    /**
+     * Returns the url for a member.
+     *
+     * Used when configuring the columns for the data grid. This
+     * function generates the url to get to the member detail page
+     *
+     * @param array $data tuple record from db
+     *
+     * @return string uri for exposure record
+     * @access public
+     */
+    public function url($data)
+    {
+        extract($data['record']);
+        $exposure = new Toolkit_Members_Exposure($member_id, 'list');
+        $exposure->runUpdate();
+        if ($pageId = filter_var($_REQUEST['page_id'], FILTER_VALIDATE_INT)) {
+            return MEDIA_BASE_URL . "members-only-area/?page_id={$pageId}&member_id={$member_id}";
+        }
+        $memberName = str_replace(' ', '-', $member_name);
+        $pattern = '/[\/#&?\'"]|amp;/';
+        $name = preg_replace(
+            $pattern,
+            '',
+            strip_tags(strtolower(trim($memberName)))
+        );
+        return MEDIA_BASE_URL . "memberProfiles/" . htmlspecialchars($name) . "-{$member_id}.html";
+    }
+
+    //    }}}
+
+    //    {{{    website()
+
+    /**
+     * Returns html link to members website w/ clickthru tracking
+     *
+     * @param array $data tuple record from db
+     *
+     * @return string html string for member website w/ clickthru tracking
+     * @access public
+     */
+    public function website($data)
+    {
+        extract($data['record']);
+        if (!$url) {
+            return false;
+        }
+        // make sure the url has http://
+        if (strpos($url, 'http://') === false) {
+            $url = 'http://' . $url;
+        }
+        return $url;
+    }
+
+    //    }}}
+
+    //    {{{    mapIcon()
+
+    /**
+     * Returns html link to members website w/ clickthru tracking
+     *
+     * @param array $data tuple record from db
+     *
+     * @return string html string for member website w/ clickthru tracking
+     * @access public
+     */
+    public function mapIcon($data)
+    {
+        $searchResultNumbered =& $this->config
+            ->getItem('section', 'conf')
+            ->getItem('directive', 'searchResultNumbered')
+            ->getContent();
+        extract($data['record']);
+        if ($searchResultNumbered && $this->memberMap) {
+            $format = MEDIA_BASE_URL . 'map/5680FC/%s.png';
+        }
+
+        if (empty($format)) {
+            return false;
+        }
+        $currentRecordNumber
+            = $data['currRow'] + $this->getCurrentRecordNumberStart();
+        return array(
+            'id' => $currentRecordNumber - 1,
+            'icon'   => sprintf(
+                $format,
+                $currentRecordNumber
+            )
+        );
+    }
+
+    //    }}}
+
+    //    {{{ yearRound()
+
+    /**
+     * Returns the year round for a member w/ accommodations.
+     *
+     * Used when configuring the columns for the data grid. This
+     * function generates the year round for the member detail page
+     *
+     * @param array $data tuple record from db
+     *
+     * @return string if the accommodations is offered year round
+     * @access public
+     */
+    public function yearRound($data)
+    {
+        return ($data['record']['year_round'] == 't') ? 'Yes' : 'No';
+    }
+
+    //    }}}
+}
diff --git a/Toolkit/Members/SearchQueryGenerator.php b/Toolkit/Members/SearchQueryGenerator.php
new file mode 100644 (file)
index 0000000..31c1931
--- /dev/null
@@ -0,0 +1,389 @@
+<?php
+/**
+ * SearchQueryGenerator.php
+ *
+ * PHP Version 5
+ *
+ * @category Toolkit
+ * @package  Members
+ * @author   Steve Sutton <steve@gaslightmedia.com>
+ * @license  Gaslight Media
+ * @link     <>
+ */
+/**
+ * Toolkit_Members_SearchQueryGenerator
+ *
+ * Description for Toolkit_Members_SearchQueryGenerator
+ *
+ * @category Toolkit
+ * @package  Members
+ * @author   Steve Sutton <steve@gaslightmedia.com>
+ * @license  Gaslight Media
+ * @link     <>
+ */
+class Toolkit_Members_SearchQueryGenerator
+{
+    //    {{{    properties
+
+    /**
+     * Description of $_includeSubCats
+     * @var unknown
+     * @access private
+     */
+    private $_includeSubCats;
+
+    /**
+     * Description of $_config
+     * @var Config_Container
+     * @access private
+     */
+    private $_config;
+
+    //    }}}
+
+    //    {{{ __construct()
+
+    /**
+     * Class constructor
+     *
+     * @param unknown          $includeSubCats Include sub categories
+     * @param Config_Container $root           Config container
+     *
+     * @access public
+     */
+    public function __construct($includeSubCats, Config_Container $root)
+    {
+        $this->_includeSubCats = $includeSubCats;
+        $this->_config = $root;
+    }
+
+    //    }}}
+
+    //    {{{    _getMemberCategoriesForPage()
+
+    /**
+     * Gets the member categories that are assigned to the page
+     *
+     * @param PDO $dbh Database handler
+     *
+     * @return mixed false if not categories, else array of cats
+     * @access private
+     */
+    private function _getMemberCategoriesForPage(PDO $dbh)
+    {
+        try {
+            $memberCategories = array();
+            $sql =  "
+                SELECT category
+                  FROM member_categories2toolbox_pages
+                 WHERE page = :catid";
+
+            $stmt = $dbh->prepare($sql);
+            $stmt->bindParam(':catid', $_GET['catid'], PDO::PARAM_INT);
+            $stmt->execute();
+            $stmt->bindColumn('category', $category);
+
+            while ($row = $stmt->fetch()) {
+                $memberCategories[] = $category;
+            }
+
+            //    If we should include all the sub categories beneath a category.
+            if ($this->_includeSubCats) {
+                //    For each of the categories assigned to the page,
+                //    we need to get all sub-categories.  This way the user
+                //    can assign a top level category as a balloon to cover all the
+                //    subcategories w/out individually assigning them as well.
+                foreach ($memberCategories as $v) {
+                    $catTree = Toolkit_Common::getHierarchicalTreeStructure(
+                        $dbh,
+                        'category',
+                        'category_id',
+                        'parent_id',
+                        'pos',
+                        $v
+                    );
+
+                    foreach ($catTree as $i => $j) {
+                        $memberCategories[] = $i;
+                    }
+                }
+            }
+
+            return empty($memberCategories) ? false : $memberCategories;
+        } catch (PDOException $e) {
+            return Toolkit_Common::handleError($e);
+        }
+    }
+
+    //    }}}
+    //    {{{    _getMemberRegionsForPage()
+
+    /**
+     * Gets the member regions that are assigned to the page
+     *
+     * @param PDO $dbh Database handler
+     *
+     * @return mixed false if not regions, else array of regions
+     * @access private
+     */
+    private function _getMemberRegionsForPage(PDO $dbh)
+    {
+        try {
+            $memberRegions = array();
+            $sql =  "
+                SELECT region
+                  FROM member_regions2toolbox_pages
+                 WHERE page = :catid";
+
+            $stmt = $dbh->prepare($sql);
+            $stmt->bindParam(':catid', $_GET['catid'], PDO::PARAM_INT);
+            $stmt->execute();
+            $stmt->bindColumn('region', $region);
+
+            while ($row = $stmt->fetch()) {
+                $memberRegions[] = $region;
+            }
+
+            return empty($memberRegions) ? false : $memberRegions;
+        } catch (PDOException $e) {
+            return Toolkit_Common::handleError($e);
+        }
+    }
+
+    //    }}}
+
+    //  {{{ getQuery()
+
+    /**
+     * Gets the query to use to fetch the datagrid results
+     *
+     * @param PDO   $dbh      Database handler
+     * @param array $sections Sections array
+     *
+     * @return void
+     * @access public
+     */
+    public function getQuery(PDO $dbh, array $sections = null)
+    {
+        $sql = "
+               SELECT distinct m.*, c.city_name, s.state_name AS state, s.state_abb
+                      AS state_abbr, ma.reservation_id, ma.num_rooms,
+                      ma.year_round,
+                      CASE
+                        WHEN m.non_member IS NULL
+                        THEN 0
+                        WHEN m.non_member = false
+                        THEN 0
+                        ELSE 1
+                      END AS nonmember
+                 FROM member m
+              NATURAL LEFT JOIN member_accommodations ma
+                 JOIN state s ON m.state_id = s.state_id
+                 JOIN city c ON m.city_id  = c.city_id,
+                 exploded_members_name emn";
+
+        $params = array(
+            "new_member <> '1'",
+            "active = '1'",
+            'emn.mid = m.member_id');
+        if (defined('EVENT_MANAGEMENT') && EVENT_MANAGEMENT) {
+            $params[] = "(events_only IS NULL OR events_only = false)";
+        }
+        //  Limit to members whose first letter starts with the letter
+        //  a user clicked in the narrow alphabetically list.
+        if (isset($_GET['alpha']) && $_GET['alpha'] == '0-9') {
+            $params[] = "substr(m.member_name, 1, 1) ~ '[0-9]'";
+        } elseif (isset($_GET['alpha']) && ctype_alpha($_GET['alpha'])) {
+            $letter = $_GET['alpha'][0];
+            $params[] = "upper(substr(m.member_name, 1, 1)) = upper(" .
+                $dbh->quote($letter) . ')';
+        }
+
+        //  Limit to members whose name contains the string a user
+        //  entered into the member name box. + Phonetics search matches.
+        if (isset($_GET['member_name']) && !empty($_GET['member_name'])) {
+            $noSpaceName = preg_replace(
+                '/\W/',
+                '',
+                urldecode($_GET['member_name'])
+            );
+            $spaceName = urldecode($_GET['member_name']);
+            $memberName = filter_var(
+                $_REQUEST['member_name'],
+                FILTER_SANITIZE_STRING
+            );
+            $sanitizedNoSpaceName = $dbh->quote($noSpaceName);
+            $sanitizedSpaceName = $dbh->quote($spaceName);
+            $params[] = "member_name ilike " . $dbh->quote('%'.$memberName.'%');
+            /*
+            $params[] = "
+            (
+                (   regexp_replace(member_name, '[^[:alnum:]]', '', 'g') ~* $sanitizedNoSpaceName
+                 OR metaphone(member_name, 4) = metaphone($sanitizedSpaceName, 4)
+                )
+             OR (
+                     m.member_id = emn.mid
+                 AND (
+                         $sanitizedNoSpaceName ~* regexp_replace(emn.part, '[^[:alnum:]]', '', 'g')
+                      OR (metaphone($sanitizedSpaceName, 4) = metaphone(emn.part, 4))
+                     )
+                 AND char_length(emn.part) > 1
+                )
+            )";
+             */
+        }
+
+        //  Using controlled cities and a search was performed on a city
+        //  get reference to [conf] section of config file
+        $ctrlCtyD =& $this->_config
+            ->getItem('section', 'conf')
+            ->getItem('directive', 'controlledCities');
+        $hasRegions =& $this->_config
+            ->getItem('section', 'conf')
+            ->getItem('directive', 'regions')
+            ->getContent();
+        if (ctype_digit($_GET['region_id'])) {
+            $params[] = " (m.region = " . $dbh->quote($_GET['region_id']) .")";
+        } elseif ($hasRegions && $regions = $this->_getMemberRegionsForPage($dbh)) {
+            $params[] = '
+             (m.region in ('.implode(', ', $regions).') OR m.region IS NULL)';
+        }
+
+        //  Limit to members who have the category assigned to them
+        //  that a user selected from the member type box.
+        if (   isset($_GET['sub_category_id'])
+            && ctype_digit($_GET['sub_category_id'])
+        ) {
+            $subCategoryId = filter_input(
+                INPUT_GET,
+                'sub_category_id',
+                FILTER_SANITIZE_NUMBER_INT
+            );
+            $params[] = "
+                m.member_id IN (
+                    SELECT member_id
+                      FROM member_category
+                     WHERE category_id = " .
+                    $dbh->quote($subCategoryId) . ')';
+        } elseif (   isset($_GET['category_id'])
+                  && ctype_digit($_GET['category_id'])
+        ) {
+            unset($subCats);
+            if ($this->_includeSubCats) {
+                //  Members who are assigned specifically to this category
+                //  OR
+                //  Members who have a category assigned to them that is a child
+                //  of the parent category.
+                $catTree = Toolkit_Common::getHierarchicalTreeStructure(
+                    $dbh,
+                    'category',
+                    'category_id',
+                    'parent_id',
+                    'pos',
+                    $_GET['category_id']
+                );
+                $categories = $this->_getMemberCategoriesForPage($dbh);
+
+                // need to find out if the categories returned here are actually
+                // included on the page first before including to the search params
+
+
+                if (!empty($categories) && !empty($catTree)) {
+                    foreach ($catTree as $ctId => $testCat) {
+                        if (!in_array($ctId, $categories)) {
+                            unset($catTree[$ctId]);
+                        }
+                    }
+                    if (!empty($catTree)) {
+                        $subCats = "
+                        OR category_id IN (".implode(', ', array_keys($catTree)).")";
+                    }
+                }
+            }
+            if (   (   isset($_GET['category_id'])
+                && ctype_digit($_GET['category']))
+                || isset($subCats)
+            ) {
+                $params[] = "
+                    m.member_id IN (
+                        SELECT member_id
+                        FROM member_category
+                        WHERE category_id = {$_GET['category_id']}
+                            $subCats)";
+            }
+        } elseif ($categories = $this->_getMemberCategoriesForPage($dbh)) {
+            $params[] = '
+                 m.member_id in (
+                    SELECT member_id
+                      FROM member_category
+                     WHERE category_id in ('.implode(', ', $categories).'))';
+        }
+
+        //    Limit to members who have the amenities that a user
+        //    checked off.
+        if (isset($_GET['amenities']) && !empty($_GET['amenities'])) {
+            if (   isset($_GET['search_all_amenity'])
+                && isset($_GET['amenities'])
+                && is_array($_GET['amenities'])
+                && !empty($_GET['amenities'])
+            ) {
+                //    Member must have ALL of the selected amenities.
+                $innerSql = array();
+                foreach ($_GET['amenities'] as $k => $v) {
+                    if (ctype_digit((string)$k)) {
+                        $innerSql[] = "
+                            SELECT member_id
+                              FROM member_amenity
+                             WHERE amenity_id = $k";
+                    }
+                }
+
+                $innerSql = implode(' INTERSECT ', $innerSql);
+                $innerSql = "m.member_id in ($innerSql)";
+
+                $params[] = $innerSql;
+            } elseif (is_array($_GET['amenities'])) {
+                $addParam = true;
+                foreach ($_GET['amenities'] as $key => $value) {
+                    if (!filter_var($key, FILTER_VALIDATE_INT)) {
+                        $addParam = false;
+                    }
+                }
+                if ($addParam) {
+                    //    Member can have ANY of the selected amenities
+                    $params[] = "
+                        member_id in (
+                            SELECT member_id
+                              FROM member_amenity
+                             WHERE amenity_id in(".implode(', ', array_keys($_GET['amenities']))."))";
+                }
+            }
+        }
+
+        if (!is_null($sections)) {
+            foreach ($sections as $v) {
+                switch ($v) {
+                case 'packages' :
+                    $params[] = "
+                        member_id IN (
+                            SELECT member_id
+                              FROM member_packages
+                             WHERE NOT pending
+                               AND CURRENT_DATE BETWEEN sdate AND edate)";
+                    break;
+
+                default :
+                    break;
+                }
+            }
+        }
+
+        if (!empty($params)) {
+            $sql .= ' WHERE ' . implode(' AND ', $params);
+        }
+
+        return $sql;
+    }
+
+    //  }}}
+}
diff --git a/Toolkit/Members/StreamSend.php b/Toolkit/Members/StreamSend.php
new file mode 100644 (file)
index 0000000..af9ad33
--- /dev/null
@@ -0,0 +1,526 @@
+<?php
+/**
+ * StreamSend.php
+ *
+ * PHP Version 5
+ *
+ * @category Toolkit
+ * @package  Members
+ * @author   Jamie Kahgee <steve@gaslightmedia.com>
+ * @license  Gaslight Media
+ * @link     <>
+ */
+
+/**
+ * Default parameters for contact create operations.
+ * Note that these are strings for use in XML data not true/false values.
+ * If false, the person will be created with a status of pending
+ */
+define('STREAMSEND_DEFAULT_ACTIVATE', 'true');
+/**
+  * If activate is false, setting this to true will trigger the sending of the built-in
+  * activation notification; if activate is true, this setting has no effect
+  */
+define('STREAMSEND_DEFAULT_DELIVER_ACTIVATION', 'false');
+/**
+  * If activate is true, setting this to true will trigger the sending of the built-in
+  * welcome notification; if activate is false, this setting has no effect
+  */
+define('STREAMSEND_DEFAULT_DELIVER_WELCOME', 'false');
+
+require_once GLM_APP_BASE . 'StreamSend/Member.php';
+
+/**
+ * Toolkit_Members_StreamSend
+ *
+ * Description for Toolkit_Members_StreamSend
+ *
+ * @category Toolkit
+ * @package  Members
+ * @author   Jamie Kahgee <steve@gaslightmedia.com>
+ * @license  Gaslight Media
+ * @link     <>
+ */
+class Toolkit_Members_StreamSend
+{
+    /**
+     * Database handler
+     * @var PDO
+     * @access protected
+     */
+    protected $dbh;
+
+    /**
+     * Description of $streamSendMember
+     * @var StreamSend_Member()
+     * @access protected
+     */
+    protected $streamSendMember;
+
+    /**
+     * Class constructor
+     *
+     * @param PDO $dbh Database handler
+     *
+     * @access public
+     */
+    public function __construct(PDO $dbh)
+    {
+        $this->dbh = $dbh;
+        $this->streamSendMember = new StreamSend_Member();
+    }
+
+    /**
+     * Description of addAllMembers
+     *
+     * @return void
+     * @access protected
+     */
+    protected function addAllMembers()
+    {
+        $this->streamSendMember->setStreamSend();
+        $this->streamSendMember->setMemberCategories($this->dbh);
+
+        $members = $this->getAllMembers();
+        var_dump($members);
+        $ret = $this->streamSendMember->importMembers($members);
+        var_dump($ret);
+    }
+
+    /**
+     * Description of firstRun()
+     *
+     * @return void
+     * @access public
+     */
+    public function firstRun()
+    {
+        $this->streamSendMember->initializeMemberStreamSend(
+            Toolkit_Database::getInstance()
+        );
+        // now everything is setup import all members and member contacts
+        $this->addAllMembers();
+    }
+
+    /**
+     * Description of getAllMembers()
+     *
+     * @return array $members
+     * @access protected
+     */
+    protected function getAllMembers()
+    {
+        $memberMapper = new StreamSend_DB_MemberMapper();
+        $memberMapper->setMemberCategories(
+            $this->streamSendMember->memberCategories
+        );
+        // for the import we need to translate the &amp; back to &
+        $memberMapper->setMemberCategoriesForImport();
+        try {
+            $members = array();
+            $sql = "
+            SELECT member_id
+              FROM member
+             WHERE process_email != ''
+               AND process_email IS NOT NULL";
+            $stmt = $this->dbh->query($sql);
+            while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                $members[] = $memberMapper->findById($this->dbh, $row['member_id']);
+                $memberContacts
+                    = $this->getAllMemberContacts($row['member_id']);
+                if ($memberContacts) {
+                    foreach ($memberContacts as $contact) {
+                        $members[] = $contact;
+                    }
+                }
+            }
+        } catch(PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+        return $members;
+    }
+
+    /**
+     * Description of getAllMemberContacts()
+     *
+     * @param integer $memberId memberId
+     *
+     * @return unknown
+     * @access protected
+     */
+    protected function getAllMemberContacts($memberId)
+    {
+        $memberContactMapper = new StreamSend_DB_MemberContactMapper();
+        $memberContactMapper->setMemberCategories(
+            $this->streamSendMember->memberCategories
+        );
+        return $memberContactMapper->findById(
+            $this->dbh,
+            $memberId
+        );
+    }
+
+    /**
+     * Description for SendMemberById
+     *
+     * @param integer $memberId Member ID
+     *
+     * @return void
+     * @access public
+     */
+    public function sendMemberById($memberId)
+    {
+        $this->streamSendMember->setStreamSend();
+        $this->streamSendMember->setMemberCategories($this->dbh);
+
+        $memberMapper = new StreamSend_DB_MemberMapper();
+        $memberMapper->setMemberCategories(
+            $this->streamSendMember->memberCategories
+        );
+
+        $member = $memberMapper->findById($this->dbh, $memberId);
+        if ($member->getEmail_address()) {
+               $this->streamSendMember->addContactToStreamSend($member);
+        }
+    }
+
+    /**
+     * Description for SendMemberContactsByMemberId
+     *
+     * @param int $memberId Member ID
+     *
+     * @return void
+     * @access public
+     */
+    public function sendMemberContactsByMemberId($memberId)
+    {
+        $this->streamSendMember->debug = true;
+        $this->streamSendMember->setStreamSend();
+        $this->streamSendMember->setMemberCategories($this->dbh);
+
+        $memberContactMapper = new StreamSend_DB_MemberContactMapper();
+        $memberContactMapper->setMemberCategories(
+            $this->streamSendMember->memberCategories
+        );
+        $memberContacts = $memberContactMapper->findById(
+            $this->dbh,
+            $memberId
+        );
+        foreach ($memberContacts as $contact) {
+            $this->streamSendMember->addContactToStreamSend($contact);
+        }
+    }
+
+    /**
+     * Given a Member Category id and name update the streamsend option
+     * for the field 'Member Category'
+     * This also update the streamsend table with correct data
+     * if the category parent is 0 or the category is deleted streamsend table
+     * is updated and the field option is destroyed
+     *
+     * @param integer $categoryId category_id field table category
+     *
+     * @return void
+     */
+    public function updateOptionByCategoryId($categoryId)
+    {
+        $this->streamSendMember->setStreamSend();
+        $this->streamSendMember->setMemberCategories($this->dbh);
+        if (!is_numeric($categoryId)) {
+            throw new InvalidArgumentException(
+                'CategoryId supplied must be numeric'
+            );
+        }
+        try {
+            // for getting the categories parent name (needed for option name)
+            $sql = "
+            SELECT name
+              FROM category
+             WHERE category_id = :parent_id";
+            $getParentId = $this->dbh->prepare($sql);
+            // get data from our cache table of member category options
+            $sql = "
+            SELECT *
+              FROM streamsend
+             WHERE category_id = :category_id";
+            $stmt = $this->dbh->prepare($sql);
+            $stmt->bindParam(
+                ':category_id',
+                $categoryId,
+                PDO::PARAM_INT
+            );
+            $stmt->execute();
+            // $currentFieldData is from the streamsend table
+            $currentFieldData = $stmt->fetch(PDO::FETCH_ASSOC);
+            $sql = "
+            SELECT *
+              FROM category
+             WHERE category_id = :category_id";
+            $stmt = $this->dbh->prepare($sql);
+            $stmt->bindParam(
+                ':category_id',
+                $categoryId,
+                PDO::PARAM_INT
+            );
+            $stmt->execute();
+            // $data will be the array from category table
+            $data = $stmt->fetch(PDO::FETCH_ASSOC);
+            if ($data['parent_id']) {
+                $getParentId->bindParam(
+                    ':parent_id',
+                    $data['parent_id'],
+                    PDO::PARAM_INT
+                );
+                $getParentId->execute();
+                $parentName = $getParentId->fetchColumn();
+                $newOptionName = $parentName . '/' . $data['name'];
+                $newOptionName = $this->streamSendMember->cleanName(
+                    $newOptionName
+                );
+            }
+            if ($currentFieldData) {
+                if (!$data['parent_id'] || $_REQUEST['delete']) {
+                    $this->streamSendFieldOptionDelete(
+                        $currentFieldData,
+                        $categoryId
+                    );
+                    return true;
+                }
+                if ($newOptionName != $currentFieldData['option_name']) {
+                    // we'll need to see if the category is going to parent 0
+                    // if it is then it'll need to be deleted
+                    // if not then it'll be updated
+                    if ($data['parent_id']) {
+                        $this->streamSendFieldOptionUpdate(
+                            $currentFieldData['field_id'],
+                            $currentFieldData['option_id'],
+                            $newOptionName,
+                            $categoryId
+                        );
+                    }
+                }
+            } else {
+                if (!$_REQUEST['delete'] && $data['parent_id']) {
+                    $this->streamSendFieldOptionCreate(
+                        $data,
+                        $newOptionName
+                    );
+                }
+            }
+            return true;
+        } catch(PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+    }
+
+    /**
+     * Create a new stream send field option
+     * gets the fieldId from feild_id in 'Member Category' (streamsend table)
+     * function must setup the dabase entry for field option and submit to
+     * streamsend the new field option
+     *
+     * @param array  $data          Array from the category table
+     * @param string $newOptionName option name 'parent/subcategory'
+     *
+     * @return boolean|void
+     * @throws InvalidArgumentException
+     * @access protected
+     */
+    protected function streamSendFieldOptionCreate($data, $newOptionName)
+    {
+        if (!is_numeric($data['category_id'])) {
+            throw new InvalidArgumentException(
+                '$data["category_id"] must be numeric'
+            );
+        }
+        if (!is_numeric($data['parent_id'])) {
+            throw new InvalidArgumentException(
+                '$data["parent_id"] must be numeric'
+            );
+        }
+        if (!isset($newOptionName) || !strstr($newOptionName, '/')) {
+            throw new InvalidArgumentException(
+                'Must have option name with a slash in it'
+            );
+        }
+        $sql = "
+        SELECT field_id
+          FROM streamsend
+         WHERE field_name = 'Member Category'
+         LIMIT 1
+        OFFSET 0";
+        $fieldId = $this->dbh->query($sql)->fetchColumn();
+        $fieldName = 'Member Category';
+        // for a new field option create a record in cache table
+        $sql = "
+        INSERT INTO streamsend
+        (field_id, field_name, option_id, option_name, category_id, parent)
+        VALUES
+        (:field_id, :field_name, :option_id, :option_name, :category_id, :parent)";
+        $stmt = $this->dbh->prepare($sql);
+        $optionId = $this->streamSendMember->createFieldOption(
+            $fieldId,
+            $newOptionName
+        );
+        if (!$optionId) {
+            // if we can't find it here then look into the member category array
+            if (is_array($this->streamSendMember->memberCategories)) {
+                foreach ($this->streamSendMember->memberCategories as $optId => $name) {
+                    if ($newOptionName == $name) {
+                        $optionId = $optId;
+                        continue;
+                    }
+                }
+            }
+        }
+        if (!$optionId) {
+            return true;
+        }
+        // now insert into the streamsend table
+        $stmt->bindParam(
+            ':field_id',
+            $fieldId,
+            PDO::PARAM_INT
+        );
+        $stmt->bindParam(
+            ':field_name',
+            $fieldName,
+            PDO::PARAM_STR
+        );
+        $stmt->bindParam(
+            ':option_id',
+            $optionId,
+            PDO::PARAM_INT
+        );
+        $stmt->bindParam(
+            ':category_id',
+            $data['category_id'],
+            PDO::PARAM_INT
+        );
+        $stmt->bindParam(
+            ':option_name',
+            $newOptionName,
+            PDO::PARAM_STR
+        );
+        $stmt->bindParam(
+            ':parent',
+            $data['parent_id'],
+            PDO::PARAM_INT
+        );
+        $stmt->execute();
+    }
+
+    /**
+     * delete the field option from streamsend
+     * and delete the streamsend record for it
+     * This has to be done before the actual delete of the category
+     * or it won't find the field_id and option_id so it can be deleted
+     * from streamsend
+     *
+     * reamsend the new field option
+     *
+     * @param array  $data       Array from the category table
+     * @param string $categoryId Category ID
+     *
+     * @return void
+     * @throws InvalidArgumentException
+     * @access protected
+     */
+    protected function streamSendFieldOptionDelete($data, $categoryId)
+    {
+        if (!is_numeric($data['field_id'])) {
+            throw new InvalidArgumentException(
+                '$data["field_id"] must be numeric'
+            );
+        }
+        if (!is_numeric($data['option_id'])) {
+            throw new InvalidArgumentException(
+                '$data["option_id"] must be numeric'
+            );
+        }
+        if (!is_numeric($categoryId)) {
+            throw new InvalidArgumentException(
+                '$categoryId must be numeric'
+            );
+        }
+        $this->streamSendMember->deleteFieldOption(
+            $data['field_id'],
+            $data['option_id']
+        );
+        try {
+            $sql = "
+            DELETE FROM streamsend
+             WHERE category_id = :category_id";
+            $stmt = $this->dbh->prepare($sql);
+            $stmt->bindParam(
+                ':category_id',
+                $categoryId,
+                PDO::PARAM_INT
+            );
+            $stmt->execute();
+        } catch(PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+    }
+
+    /**
+     * update the streamsend account with new name for an option
+     * and update our streamsend table with new name
+     *
+     * @param integer $fieldId       field_id of streamsend field
+     * @param integer $optionId      option_id of streamsend field
+     * @param string  $newOptionName new name
+     * @param integer $categoryId    category_id for streamsend table
+     *
+     * @return void
+     * @access protected
+     * @throws InvalidArgumentException
+     */
+    protected function streamSendFieldOptionUpdate(
+        $fieldId,
+        $optionId,
+        $newOptionName,
+        $categoryId
+    ) {
+        if (!is_numeric($fieldId)) {
+            throw new InvalidArgumentException(
+                '$fieldId must be numeric'
+            );
+        }
+        if (!is_numeric($optionId)) {
+            throw new InvalidArgumentException(
+                '$optionId must be numeric'
+            );
+        }
+        if (!isset($newOptionName) || !strstr($newOptionName, '/')) {
+            throw new InvalidArgumentException(
+                'Must have option name with a slash in it'
+            );
+        }
+        $this->streamSendMember->updateFieldOption(
+            $fieldId,
+            $optionId,
+            $newOptionName
+        );
+        try {
+            $sql = "
+            UPDATE streamsend
+               SET option_name = :option_name
+             WHERE category_id = :category_id";
+            $stmt = $this->dbh->prepare($sql);
+            $stmt->bindParam(
+                ':category_id',
+                $categoryId,
+                PDO::PARAM_INT
+            );
+            $stmt->bindParam(
+                ':option_name',
+                $newOptionName,
+                PDO::PARAM_STR
+            );
+            $stmt->execute();
+        } catch(PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+    }
+}
diff --git a/Toolkit/Members/TripPlanner/AuthContainer.php b/Toolkit/Members/TripPlanner/AuthContainer.php
new file mode 100644 (file)
index 0000000..cc7c55b
--- /dev/null
@@ -0,0 +1,196 @@
+<?php
+
+/**
+ * Authentication container for members trip planner
+ *
+ * Custom container which allows us to utilize our PDO Singleton which
+ * takes advantage of schema based partitioning of our tables
+ *
+ * PHP version 5
+ *
+ * The license text...
+ *
+ * @category  Members
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2010 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com/ Gaslightmedia
+ * @version   CVS: $Id: AuthContainer.php,v 1.1 2010/06/22 11:45:34 jamie Exp $
+ * @link      <>
+ * @see       References to other sections (if any)...
+ */
+
+/**
+ * Authentication container for members trip planner
+ *
+ * Custom container which allows us to utilize our PDO Singleton which
+ * takes advantage of schema based partitioning of our tables
+ *
+ * @category  Members
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2010 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com/ Gaslightmedia
+ * @version   Release: @package_version@
+ * @link      <>
+ * @see       References to other sections (if any)...
+ */
+class Toolkit_Members_TripPlanner_AuthContainer extends Auth_Container
+{
+       //      {{{     properties
+
+    /**
+     * Database handler
+     * @var    PDO
+     * @access private
+     */
+       private $_dbh;
+
+       /**
+        * Addition options for the storage container
+        * @var array
+        * @access private
+        */
+       private $_options = array();
+
+       //      }}}
+       //      {{{     __construct()
+
+    /**
+     * Constructor
+     *
+     * @param PDO   $dbh     Database handler
+     * @param array $options Options array
+        *
+     * @return void
+     * @access public
+     */
+       public function __construct(PDO $dbh, array $options = null)
+       {
+               $this->_dbh = $dbh;
+               $this->_setDefaults();
+               if (is_array($options)) {
+                       $this->_parseOptions($options);
+               }
+       }
+
+       //      }}}
+       //      {{{     _setDefaults()
+
+       /**
+        * Set some default options
+        *
+        * @access private
+        * @return void
+        */
+       private function _setDefaults()
+       {
+               $this->_options['table']       = 'contact';
+               $this->_options['usernamecol'] = 'email';
+               $this->_options['passwordcol'] = 'password';
+               $this->_options['db_fields']   = array('id', 'fname', 'lname');
+               $this->_options['cryptType']   = 'md5';
+               $this->_options['db_where']    = '';
+       }
+
+       //      }}}
+       //      {{{     _parseOptions()
+
+       /**
+        * Parse options passed to the container class
+        *
+        * @param array $array options for class
+        *
+        * @access private
+        * @return void
+        */
+       private function _parseOptions($array)
+       {
+               foreach ($array as $key => $value) {
+                       if (isset($this->_options[$key])) {
+                               $this->_options[$key] = $value;
+                       }
+               }
+       }
+
+       //      }}}
+       //      {{{     fetchData()
+
+    /**
+     * Get the user information from the database
+     *
+     * @param string $username username to authenticate
+     * @param string $password password to authenticate against username
+        *
+     * @return boolean If the user was authenticated or not
+     * @access public
+     * @throws Toolkit_Members_Exception upon error querying DB for user
+     */
+       public function fetchData($username, $password)
+       {
+               if (   is_string($this->_options['db_fields'])
+                       && strstr($this->_options['db_fields'], '*')
+               ) {
+                       $sqlFrom = '*';
+               } else {
+                       $sqlFrom  = $this->_options['usernamecol'];
+                       $sqlFrom .= ', ' . $this->_options['passwordcol'];
+
+                       if (strlen($fields = $this->_getDBFields()) > 0) {
+                               $sqlFrom .= ", $fields";
+                       }
+
+               }
+
+               $pword = ($this->_options['cryptType'] == 'md5') ? 'MD5(:pword)' : ':pword';
+
+               $sql = "
+            SELECT $sqlFrom
+              FROM {$this->_options['table']}
+             WHERE {$this->_options['usernamecol']} = :uname
+               AND {$this->_options['passwordcol']} = $pword";
+
+               try {
+                       $stmt = $this->_dbh->prepare($sql);
+                       $stmt->bindParam(':uname', $username, PDO::PARAM_STR);
+                       $stmt->bindParam(':pword', $password, PDO::PARAM_STR);
+                       $stmt->execute();
+                       $row = $stmt->fetch(PDO::FETCH_ASSOC);
+
+                       if ($row !== false) {
+                               foreach ($row as $key => $value) {
+                                       $this->_auth_obj->setAuthData($key, $value);
+                               }
+                               return true;
+                       }
+
+                       return false;
+               } catch (PDOException $e) {
+                       Toolkit_Logger::logException('DB Error', $e);
+                       throw new Toolkit_Members_Exception(
+                               "Error validating user `$username` - `$password`"
+                       );
+               }
+       }
+
+       //      }}}
+       //      {{{     _getDBFields()
+
+       /**
+        * Get extra db fields to fetch and set in the auth data
+        *
+        * @return array comma separated string of extra db fields for a SQL query
+        * @access private
+        */
+       private function _getDBFields()
+       {
+               if (isset($this->_options['db_fields'])) {
+                       if (is_array($this->_options['db_fields'])) {
+                               return implode(', ', $this->_options['db_fields']);
+                       }
+               }
+       }
+
+       //      }}}
+}
+?>
diff --git a/Toolkit/Members/TripPlanner/Forgot.php b/Toolkit/Members/TripPlanner/Forgot.php
new file mode 100644 (file)
index 0000000..a150b02
--- /dev/null
@@ -0,0 +1,358 @@
+<?php
+//     vim:set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker syntax=php:
+
+/**
+ * Forgot.php
+ *
+ * for contacts that forget their password sends out an email to if they're found.
+ *
+ * PHP versions 4 and 5
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2009 Gaslight Media
+ * @license   Gaslight Media
+ * @version   CVS: $Id: Forgot.php,v 1.4 2010/05/20 15:30:36 matrix Exp $
+ * @link      <>
+ */
+
+/**
+ * Handles rendering and validating the password reminder form
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2009 Gaslight Media
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @link      <>
+ * @see       Toolkit_FormBuilder
+ */
+class Toolkit_Members_TripPlanner_Forgot extends Toolkit_FormBuilder
+{
+       //      {{{     properties
+
+    /**
+     * Table to query when gathering information
+        *
+     * @var    string
+     * @access public
+     */
+       public $tableName = 'contact';
+
+    /**
+     * Custom defined rules to validate against when the form is submitted
+        *
+     * @var    array
+     * @access protected
+     */
+       protected $registeredRules = array();
+
+    /**
+     * Where to perform validation
+        *
+     * @var    string
+     * @access protected
+     */
+       protected $validationType = 'client';
+
+       //      }}}
+       //      {{{     __construct()
+
+    /**
+     * Constructor
+     *
+     * @param PDO     $pdo         PDO object
+     * @param string  $formName    Form's name
+     * @param string  $method      (optional)Form's method defaults to 'POST'
+     * @param string  $action      (optional)Form's action
+     * @param string  $target      (optional)Form's target
+     * @param mixed   $attributes  (optional)Extra attributes for the <form> tag
+     * @param boolean $trackSubmit (optional)Whether to track if the form
+        *                                                         was submitted by adding a special hidden field
+        *
+     * @return void
+     * @access public
+     */
+       public function __construct(
+        PDO $pdo,
+        $formName,
+        $method = 'post',
+        $action = '',
+        $target = '',
+        $attributes = null,
+        $trackSubmit = false
+    ) {
+               parent::__construct(
+            $formName,
+            $method,
+            $action,
+            $target,
+            $attributes,
+            $trackSubmit
+        );
+
+        $this->dbh = $pdo;
+               $this->template = BASE . 'Toolkit/Members/templates/currentTables/';
+       }
+
+       //      }}}
+
+       //      {{{     checkAddressExists()
+
+    /**
+     * Checks to see if the email address exists before allowing an email to go out
+     *
+     * @param string $value submitted email address
+        *
+     * @return boolean If the email address exists or not
+     * @access public
+     */
+       public function checkAddressExists($value)
+       {
+               try {
+                       $sql = "
+                SELECT count(*) AS total
+                  FROM {$this->tableName}
+                 WHERE email = :email
+                   AND verify_password IS NOT NULL
+                   AND verify_password != ''
+                   AND password IS NOT NULL
+                   AND password != ''";
+
+                       $stmt = $this->dbh->prepare($sql);
+                       $stmt->bindParam(':email', $value, PDO::PARAM_STR);
+                       $stmt->execute();
+                       $stmt->bindColumn('total', $exists);
+                       $stmt->fetch();
+
+                       return (bool) $exists;
+               } catch (PDOException $e) {
+                       Toolkit_Common::handleError($e);
+               }
+       }
+
+       //      }}}
+       //      {{{     configureElements()
+
+    /**
+     * Sets up the elements to be configured for use with the form
+     *
+     * @return void
+     * @access protected
+     */
+       protected function configureElements()
+       {
+        $e = array();
+
+               $e[] = array(
+            'type' => 'header',
+            'req' => false,
+            'name' => 'formHdr',
+            'display' => 'Email Reminder'
+        );
+               $e[] = array(
+            'type' => 'text',
+            'req' => true,
+            'name' => 'email',
+            'display' => 'Your Email Address'
+        );
+               $e[] = array(
+            'type' => 'submit',
+            'req' => false,
+            'name' => 'submit',
+            'display' => 'Send'
+        );
+
+               $this->setupElements($e);
+       }
+
+       //      }}}
+       //      {{{     configureFilters()
+
+    /**
+     * Sets up the filters to be used with the form when submitted
+     *
+     * @return void
+     * @access protected
+     */
+       protected function configureFilters()
+       {
+        $f = array();
+
+               $f[] = array(
+            'element' => '__ALL__',
+            'filter' => 'trim'
+        );
+               $this->setupFilters($f);
+       }
+
+       //      }}}
+    //  {{{ configureForm()
+
+    /**
+     * Helper method to setup form
+     *
+     * @return void
+     * @access public
+     */
+    public function configureForm()
+    {
+        $this->configureElements();
+        $this->configureFilters();
+        $this->configureRules();
+    }
+
+    //  }}}
+       //      {{{     configureRules()
+
+    /**
+     * Sets up required rules and extra defined rules for the form
+     *
+     * @return void
+     * @access protected
+     */
+       protected function configureRules()
+       {
+        $r = array();
+
+               $r[] = array(
+            'element' => 'email',
+            'message' => 'ERROR: Invalid Email Format!',
+            'type' => 'email',
+            'format' => null,
+            'validation' => $this->validationType,
+            'reset' => true,
+            'force' => false
+        );
+               $r[] = array(
+            'element' => 'email',
+            'message' => 'ERROR: Cannot locate email address!',
+            'type' => 'callback',
+            'format' => array(&$this,
+            'checkAddressExists'),
+            'validation' => $this->validationType,
+            'reset' => true,
+            'force' => false
+        );
+
+               $this->setupRules($r);
+       }
+
+       //      }}}
+
+       //      {{{     setupRenderers()
+
+    /**
+     * Inject custom renderers into the forms elements for custom display
+     *
+     * @return void
+     * @access protected
+     */
+       protected function setupRenderers()
+       {
+               parent::setupRenderers();
+
+               $renderer =& $this->defaultRenderer();
+               $required = '<!-- BEGIN required --><span class="req">*</span><!-- END required -->';
+               $error    = '<!-- BEGIN error --><div class="form-warning-inside">{error}</div><!-- END error -->';
+
+               $renderer->setElementTemplate('<tr><td colspan="2" align="center">{element}</td></tr>', 'submit');
+       }
+
+       //      }}}
+
+       //      {{{     processData()
+
+    /**
+     * Processes the data submitted by the form
+     *
+        * Gets the login credentials for the matching email address and mails
+        * them to that email address
+     *
+     * @param array $values submitted form values
+        *
+     * @return boolean   Result of mail
+     * @access protected
+     */
+       protected function processData($values)
+       {
+               try {
+                       $sql = "
+                SELECT email, verify_password as password
+                  FROM {$this->tableName}
+                 WHERE email = :email";
+
+                       $stmt = $this->dbh->prepare($sql);
+                       $stmt->bindParam(':email', $values['email'], PDO::PARAM_STR);
+                       $stmt->execute();
+                       $row = $stmt->fetch();
+
+                       $htmlMsg
+                = "Here is your " . SITENAME . " password:<br><br>" .
+                                 "Email: {$values['email']}<br>" .
+                                 "Password: {$row['password']}<br><br>";
+
+                       $msg
+                = "Here is your " . SITENAME . " password:\n\n" .
+                                 "Email: {$values['email']}\n" .
+                                 "Password: {$row['password']}";
+
+                       $crlf     = "\n";
+                       $mimeMail = new Mail_mime($crlf);
+                       $mimeMail->setFrom(SITENAME . ' <' . OWNER_EMAIL . '>');
+                       $mimeMail->setSubject('Your ' . SITENAME . ' Password');
+                       $mimeMail->setHTMLBody($htmlMsg);
+                       $mimeMail->setTXTBody($msg);
+
+                       $mail    =& Mail::factory('mail');
+                       $body    = $mimeMail->get();
+                       $headers = $mimeMail->headers($hdrs);
+
+                       $res = $mail->send($values['email'], $headers, $body);
+
+                       return PEAR::isError($res) ?
+                                       Toolkit_Common::handleError($res) :
+                                       $res;
+               } catch (PDOException $e) {
+                       Toolkit_Common::handlError($e);
+               }
+       }
+
+       //      }}}
+
+       //      {{{     toHtml()
+
+    /**
+     * Returns an HTML version of the form
+     *
+     * @return string HTML version of the form
+     * @access public
+     */
+       public function toHtml()
+       {
+               $this->setupRenderers();
+               if ($this->validate()) {
+                       if ($this->process(array(&$this, 'processData'))) {
+                               $url    = MEDIA_BASE_URL . 'index.php?catid=' . MEMBER_SESSION_PAGE;
+                               $e      =& $this->getElement('email');
+                               $email  = $e->getValue();
+                               $output
+                    = "<p>Your Login Information has been sent to $email</p>" .
+                                         "<p>Continue to <a href=\"$url\">Login</a></p>";
+                       } else {
+                               $output = '<p>Email address not found.</p>';
+                       }
+               } elseif ($this->isSubmitted()) {
+                       $output  = $this->errorMsg;
+                       $output .= parent::toHtml();
+               } else {
+                       $output .= parent::toHtml();
+               }
+
+               return $output;
+       }
+
+       //      }}}
+}
+?>
diff --git a/Toolkit/Members/TripPlanner/Sessions.php b/Toolkit/Members/TripPlanner/Sessions.php
new file mode 100644 (file)
index 0000000..9373402
--- /dev/null
@@ -0,0 +1,291 @@
+<?php
+
+/**
+ * Sessions.php
+ *
+ * PHP version 5
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2009 Gaslight Media
+ * @license   Gaslight Media
+ * @version   CVS: $Id: Sessions.php,v 1.3 2010/05/28 12:54:14 matrix Exp $
+ * @link      <>
+ */
+
+/**
+ * Toolkit_Members_TripPlanner_Sessions
+ *
+ * Trying to place all the code for the storing of the member list to session
+ * into one place
+ * Takes the $_SESSION['wish_list'] used for the trip planner and
+ * add or removes members and their page id to the session
+ * if they're logged in with trip planner then they'll get everything saved to the
+ * database table member_session
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2009 Gaslight Media
+ * @license   Gaslight Media
+ * @version   Release: @package_version@
+ * @link      <>
+ */
+class Toolkit_Members_TripPlanner_Sessions
+{
+    // {{{ Properties
+    /**
+     * Global PDO
+     * @var    object
+     * @access protected
+     */
+    public $dbh;
+
+    /**
+     * Auth object
+     * @var    object
+     * @access protected
+     */
+    protected $auth;
+
+    /**
+     * $_SESSION['wish_list']
+     * @var    array
+     * @access protected
+     */
+    protected $session;
+
+    // }}}
+    // {{{ __construct()
+
+    /**
+     * Set the Auth object ad Global PDO
+     *
+     * @param integer $pdo  Global pdo
+     * @param Auth    $Auth current Auth object used for valid session
+     *
+     * @return void
+     * @access public
+     */
+    function __construct($pdo, Auth $Auth)
+    {
+        $this->dbh     =& $pdo;
+        $this->auth    =& $Auth;
+        $this->session =& $_SESSION['wish_list'];
+    }
+
+    // }}}
+    // {{{ addRecord()
+
+    /**
+     * Add the record into session storing the page it was added on.
+     * page shold always have the catid.
+     * If the user is logged in then store it in database with contact_id
+     *
+     * @param unknown $memberId member_id of record
+     * @param unknown $catId    page catid
+     *
+     * @return void
+     * @access public
+     */
+    function addRecord($memberId, $catId)
+    {
+        if (ctype_digit($memberId) && $memberId) {
+            // add to the session the member record
+            $this->session[$memberId] = array(
+                'catid' => $catId
+            );
+            if ($this->isLoggedIn()) {
+                $cData = $this->auth->getAuthData();
+                try {
+                    $sql = "
+                    INSERT INTO member_session
+                    (contact_id, member_id, catid)
+                    VALUES
+                    (:contact_id, :member_id, :catid)";
+                    $stmt = $this->dbh->prepare($sql);
+                    $stmt->bindParam(":member_id", $memberId, PDO::PARAM_INT);
+                    $stmt->bindParam(":contact_id", $cData['id'], PDO::PARAM_INT);
+                    $stmt->bindParam(":catid", $catId, PDO::PARAM_INT);
+                    $stmt->execute();
+                } catch(PDOException $e) {
+                    Toolkit_Common::handleError($e);
+                }
+            }
+        }
+    }
+
+    // }}}
+    // {{{ dumpList()
+
+    /**
+     * Dump the session for the list and
+     * if they are logged in then remove their list in the
+     * database.
+     *
+     * @return void
+     * @access public
+     */
+    function dumpList()
+    {
+        if ($this->isLoggedIn()) {
+            $cData = $this->auth->getAuthData();
+            $sql = "
+            DELETE
+              FROM member_session
+             WHERE contact_id = :contact_id";
+            $stmt = $this->dbh->prepare($sql);
+            $stmt->bindParam(":contact_id", $cData['id'], PDO::PARAM_INT);
+            $stmt->execute();
+        }
+        unset($this->session);
+        unset($_SESSION['wish_list']);
+    }
+
+    // }}}
+    // {{{ isLoggedIn()
+
+    /**
+     * Check the Auth object function checkAuth to see if the user has a valid
+     * login session.  If so then return true. If not then return false.
+     *
+     * @return boolean true valid login false invalid login
+     * @access public
+     */
+    function isLoggedIn()
+    {
+        if ($this->auth->checkAuth()) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    // }}}
+    // {{{ removeRecord()
+
+    /**
+     * Remove the record from the session.  If the user is logged in then
+     * also remove the data from the database.
+     *
+     * @param unknown $memberId member id for record
+     *
+     * @return void
+     * @access public
+     */
+    function removeRecord($memberId)
+    {
+        unset($this->session[$memberId]);
+        if ($this->isLoggedIn()) {
+            $cData = $this->auth->getAuthData();
+            //die('<p>'.print_r($cData, true).'</p>');
+            try {
+                $sql = "
+                DELETE FROM member_session
+                      WHERE member_id = :member_id
+                        AND contact_id = :contact_id";
+                $stmt = $this->dbh->prepare($sql);
+                $stmt->bindParam(":member_id", $memberId, PDO::PARAM_INT);
+                $stmt->bindParam(":contact_id", $cData['id'], PDO::PARAM_INT);
+                $stmt->execute();
+            } catch(PDOException $e) {
+                Toolkit_Common::handleError($e);
+            }
+        }
+    }
+
+    // }}}
+    // {{{ retrieveList()
+
+    /**
+     * Retreive the list form the database storage and repopulate the accounts data
+     * to the session var $this->session = $_SESSION['wish_list']
+     *
+     * @return void
+     * @access public
+     */
+    function retrieveList()
+    {
+        if ($this->isLoggedIn()) {
+            $cData = $this->auth->getAuthData();
+            try {
+                $sql = "
+                SELECT *
+                  FROM member_session
+                 WHERE contact_id = :contact_id";
+                $stmt = $this->dbh->prepare($sql);
+                $stmt->bindParam(":contact_id", $cData['id'], PDO::PARAM_INT);
+                $stmt->execute();
+                while ($row = $stmt->fetch()) {
+                    // don't dupe the list
+                    if (!$this->session[$row['member_id']]) {
+                        $this->session[$row['member_id']] = array(
+                            'catid' => $row['catid']
+                        );
+                    }
+                }
+            } catch(PDOException $e) {
+                Toolkit_Common::handleError($e);
+            }
+        }
+    }
+
+    // }}}
+    // {{{ saveList()
+
+    /**
+     * Save the list for the user.  This is called on the login page after the
+     * session is repopulated out from database.  No worries about dupes here.
+     * this function checks for existing records then won't reinsert.
+     *
+     * @param int $contactId the id from contact table for lookup
+     *
+     * @return void
+     * @access public
+     */
+    function saveList($contactId = null)
+    {
+        if ($this->isLoggedIn() && is_array($this->session)) {
+            if (isset($contactId) && is_numeric($contactId)) {
+                $cData['id'] = $contactId;
+            } else {
+                $cData = $this->auth->getAuthData();
+            }
+            $sql = "
+            SELECT member_id
+              FROM member_session
+             WHERE contact_id = :contact_id";
+            try {
+                $getDat = $this->dbh->prepare($sql);
+                $getDat->bindParam(":contact_id", $cData['id'], PDO::PARAM_INT);
+                $getDat->execute();
+                while ($row = $getDat->fetch()) {
+                    $mStoreSess[$row['member_id']] = $row['member_id'];
+                }
+            } catch(PDOException $e) {
+                Toolkit_Common::handleError($e);
+            }
+            foreach ($this->session as $memberId => $sData) {
+                if (!$mStoreSess[$memberId]) {
+                    try {
+                        $sql = "
+                        INSERT INTO member_session
+                        (contact_id, member_id, catid)
+                        VALUES
+                        (:contact_id, :member_id, :catid)";
+                        $stmt = $this->dbh->prepare($sql);
+                        $stmt->bindParam(":member_id", $memberId, PDO::PARAM_INT);
+                        $stmt->bindParam(":contact_id", $cData['id'], PDO::PARAM_INT);
+                        $stmt->bindParam(":catid", $sData['catid'], PDO::PARAM_INT);
+                        $stmt->execute();
+                    } catch(PDOException $e) {
+                        Toolkit_Common::handleError($e);
+                    }
+                }
+            }
+        }
+    }
+    // }}}
+}
+?>
diff --git a/Toolkit/Members/TripPlanner/TripAuth.php b/Toolkit/Members/TripPlanner/TripAuth.php
new file mode 100644 (file)
index 0000000..3a35755
--- /dev/null
@@ -0,0 +1,766 @@
+<?php
+//     vim:set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker syntax=php:
+
+/**
+ * Member Authentication
+ *
+ * PHP version 5
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2009 Gaslight Media
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @version   CVS: $Id: TripAuth.php,v 1.6 2010/06/22 11:45:34 jamie Exp $
+ * @link      http://demo.gaslightmedia.com
+ * @see       Toolkit_Members_Auth-LoginForm, Toolkit_Members_Auth-PasswordForm
+ */
+
+require_once 'Auth.php';
+
+/**
+ * Methods for the memberdb authentication system
+ *
+ * Handles Cookie and session generation, id challenges and security for
+ * the memberdb application
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2009 Gaslight Media
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @link      http://demo.gaslightmedia.com
+ * @see       Toolkit_Members_Auth-LoginForm, Toolkit_Members_Auth-PasswordForm
+ */
+class Toolkit_Members_TripPlanner_TripAuth extends Auth
+{
+       //      {{{     properties
+
+    /**
+     * Maximum idle time
+        *
+        * If more seconds pass before a new page request, then the user
+        * will have to re-authenticate back into the application.
+        * 1800 = 30 min
+        * 3600 = 1 hr
+        *
+     * @var    integer
+     * @access protected
+     */
+       protected $idleTime = 1800;
+
+    /**
+     * Authentication options for storage driver
+        *
+        * This is setup here, so it can be overridden in subclasses and merged
+        * into existing authentication options.
+        *
+     * @var    array
+     * @access protected
+     */
+       protected $classOptions = array();
+
+       //      }}}
+       //      {{{     __construct()
+
+    /**
+     * Constructor
+     *
+     * Sets up the storage driver
+     *
+     * @param Toolkit_Members_TripPlanner_AuthContainer $storageDriver storage driver
+     * @param string                                    $loginFunction (optional)Name of the function that
+        *                                                                                                 creates the login form
+     * @param boolean                                   $showLogin     (optional)Should the login form be
+        *                                                                                                 displayed if neccessary?
+        *
+     * @return void
+     * @access public
+     */
+       public function __construct(
+        Toolkit_Members_TripPlanner_AuthContainer $storageDriver,
+        $loginFunction = '',
+        $showLogin = true
+    ) {
+
+               parent::Auth($storageDriver, '', $loginFunction, $showLogin);
+        $this->setSessionName('TripPlanner');
+       }
+
+       //      }}}
+
+       //      {{{     loginForm()
+
+    /**
+     * Function to set up the regular login form
+     *
+     * @param unknown $uname  Last attempted username
+     * @param unknown $status The authorization status
+     * @param unknown &$auth  The authentication object
+        *
+     * @return void
+     * @access public
+     */
+       protected function loginForm($uname = null, $status = null, &$auth = null)
+       {
+               $login = new TripPlannerLoginForm('member_login',
+                                'post',
+                                MEDIA_BASE_URL . 'memberdb/index.php');
+        $login->setDbh(Toolkit_Database::getInstance());
+        $login->configureForm();
+        echo $login->toHtml();
+       }
+
+       //      }}}
+
+       //      {{{     passwordForm()
+
+    /**
+     * Function to set up the forgot password form
+     *
+     * @return void
+     * @access public
+     */
+       protected function passwordForm()
+       {
+               $pword = new TripPlannerPasswordForm('member_password');
+        $pword->setDbh(Toolkit_Database::getInstance());
+        $pword->configureForm();
+               echo $pword->toHtml();
+       }
+
+       //      }}}
+
+       //      {{{     setIdle()
+
+    /**
+     * Set the maximum idle time
+     *
+     * @param integer $time time in seconds
+     * @param boolean $add  (optional)add time to current maximum idle time or not
+        *
+     * @return void
+     * @access public
+     */
+       public function setIdle($time = null, $add = false)
+       {
+               $time = is_null($time) ? $this->idleTime : $time;
+               parent::setIdle($time, $add);
+       }
+
+       //      }}}
+}
+
+/**
+ * Handles rendering and validating the member login form
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @link      http://demo.gaslightmedia.com
+ * @see       Toolkit_FormBuilder
+ */
+class TripPlannerLoginForm extends Toolkit_FormBuilder
+{
+       //      {{{     properties
+
+    /**
+     * Custom rules to check for when validating the form
+        *
+     * @var    array
+     * @access protected
+     */
+       protected $registeredRules = array();
+
+    /**
+     * Where to perform validation
+        *
+     * @var    string
+     * @access protected
+     */
+       protected $validationType = 'client';
+
+       //      }}}
+       //      {{{     __construct()
+
+    /**
+     * Constructor
+     *
+     * @param string  $formName    Form's name
+     * @param string  $method      (optional)Form's method defaults to 'POST'
+     * @param string  $action      (optional)Form's action
+     * @param string  $target      (optional)Form's target
+     * @param mixed   $attributes  (optional)Extra attributes for the <form> tag
+     * @param boolean $trackSubmit (optional)Whether to track if the form
+        *                                                         was submitted by adding a special hidden field
+        *
+     * @return void
+     * @access public
+     */
+       public function __construct(
+        $formName,
+        $method = 'post',
+        $action = '',
+        $target = '',
+        $attributes = null,
+        $trackSubmit = false
+    ) {
+               parent::__construct(
+            $formName,
+            $method,
+            $action,
+            $target,
+            $attributes,
+            $trackSubmit
+        );
+
+               $this->template = dirname(__FILE__) . '/templates/currentTables/';
+       }
+
+       //      }}}
+
+       //      {{{     configureElements()
+
+    /**
+     * Sets up the elements to be configured for use with the form
+     *
+     * @return void
+     * @access protected
+     */
+       protected function configureElements()
+       {
+        $e = array();
+
+               $e[] = array(
+            'type' => 'header',
+            'req' => false,
+            'name' => 'formHdr',
+            'display' => Toolkit_Members_Admin_Controller::$listingType['singular'] . ' Login'
+        );
+               $e[] = array(
+            'type' => 'text',
+            'req' => true,
+            'name' => 'username',
+            'display' => 'Username'
+        );
+               $e[] = array(
+            'type' => 'password',
+            'req' => true,
+            'name' => 'password',
+            'display' => 'Password'
+        );
+               $e[] = array(
+            'type' => 'submit',
+            'req' => false,
+            'name' => 'doLogin',
+            'display' => 'Login now',
+            'opts' => array('id' => 'doLogin')
+        );
+               $e[] = array(
+            'type' => 'link',
+            'req' => false,
+            'name' => 'forgot',
+            'display' => 'Forgot your password? Click',
+            'opts' => MEDIA_BASE_URL . 'index.php?catid='.MEMBERS_CATEGORY.'&forgot=',
+            'att' => 'here'
+        );
+
+               $this->setupElements($e);
+       }
+
+       //      }}}
+       //      {{{     configureFilters()
+
+    /**
+     * Sets up the filters to be used with the form when submitted
+     *
+     * @return void
+     * @access protected
+     */
+       protected function configureFilters()
+       {
+        $f = array();
+
+               $f[] = array(
+            'element' => '__ALL__',
+            'filter' => 'trim'
+        );
+               $this->setupFilters($f);
+       }
+
+       //      }}}
+    //  {{{ configureForm()
+
+    /**
+     * Helper method to setup form
+     *
+     * @return void
+     * @access public
+     */
+    public function configureForm()
+    {
+        $this->configureElements();
+        $this->configureFilters();
+        $this->configureRules();
+    }
+
+    //  }}}
+       //      {{{     configureRules()
+
+    /**
+     * Sets up required rules and extra defined rules for the form
+     *
+     * @return void
+     * @access protected
+     */
+       protected function configureRules()
+       {
+               $this->setupRules($r);
+       }
+
+       //      }}}
+
+    //  {{{ setDbh()
+
+    /**
+     * set the pdo to use for db calls
+     *
+     * @param PDO $pdo PHP Data Object to use
+     *
+     * @return void
+     * @access public
+     */
+    public function setDbh(PDO $pdo)
+    {
+        $this->dbh = $pdo;
+    }
+
+    //  }}}
+       //      {{{     setupRenderers()
+
+    /**
+     * Inject custom renderers into the forms elements for custom display
+     *
+     * @return void
+     * @access protected
+     */
+       protected function setupRenderers()
+       {
+               parent::setupRenderers();
+
+               $renderer =& $this->defaultRenderer();
+               $required = '<!-- BEGIN required --><span class="req">*</span><!-- END required -->';
+               $error    = '<!-- BEGIN error --><div class="form-warning-inside">{error}</div><!-- END error -->';
+
+               $renderer->setElementTemplate('<tr><td colspan="2" class="member-login-forgot">{label} {element}</td></tr>', 'forgot');
+               $renderer->setElementTemplate('<tr><td colspan="2" align="center">{element}</td></tr>', 'doLogin');
+       }
+
+       //      }}}
+
+       //      {{{     toHtml()
+
+    /**
+     * Returns an HTML version of the form
+     *
+     * @return string HTML version of the form
+     * @access public
+     */
+       public function toHtml()
+       {
+               $this->setupRenderers();
+               if ($this->validate()) {
+                       header('Location: ' . MEDIA_BASE_URL . 'memberdb/index.php');
+               } elseif ($this->isSubmitted()) {
+                       $output  = $this->errorMsg;
+                       $output .= parent::toHtml();
+               } else {
+                       if ($_GET['status']) {
+                               switch ($_GET['status']) {
+                               case -1 :
+                                       $error = 'Your session has exceeded the maximum idle time';
+                                       break;
+
+                               case -2 :
+                                       $error = 'Your session has expired.';
+                                       break;
+
+                               case -3 :
+                                       $error = 'Invalid username or password.';
+                                       break;
+
+                               case -4 :
+                                       //      This is primarily used for Development.
+                                       //      Users should never be presented with this error.
+                                       $error = 'Invalid Container';
+                                       break;
+
+                               case -5 :
+                                       //      This is only thrown if the advanced security system
+                                       //      has detected a breach into the system.
+                                       $error = 'The system has encountered an error. Reference code: -5';
+                                       break;
+                               }
+                               $output = "<div id=\"form-warning-top\">$error</div>";
+                       }
+                       $output .= parent::toHtml();
+               }
+
+               return $output;
+       }
+
+       //      }}}
+}
+
+/**
+ * Handles rendering and validating the member password form
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @link      http://demo.gaslightmedia.com
+ * @see       Toolkit_FormBuilder
+ */
+class TripPlannerPasswordForm extends Toolkit_FormBuilder
+{
+       //      {{{     properties
+
+    /**
+     * Table to query when gathering information
+        *
+     * @var    string
+     * @access public
+     */
+       public $tableName = 'member';
+
+    /**
+     * Custom defined rules to validate against when the form is submitted
+        *
+     * @var    array
+     * @access protected
+     */
+       protected $registeredRules = array();
+
+    /**
+     * Where to perform validation
+        *
+     * @var    string
+     * @access protected
+     */
+       protected $validationType = 'client';
+
+       //      }}}
+       //      {{{     __construct()
+
+    /**
+     * Constructor
+     *
+     * @param string  $formName    Form's name
+     * @param string  $method      (optional)Form's method defaults to 'POST'
+     * @param string  $action      (optional)Form's action
+     * @param string  $target      (optional)Form's target
+     * @param mixed   $attributes  (optional)Extra attributes for the <form> tag
+     * @param boolean $trackSubmit (optional)Whether to track if the form
+        *                                                         was submitted by adding a special hidden field
+        *
+     * @return void
+     * @access public
+     */
+       public function __construct(
+        $formName,
+        $method = 'post',
+        $action = '',
+        $target = '',
+        $attributes = null,
+        $trackSubmit = false
+    ) {
+               parent::__construct(
+            $formName,
+            $method,
+            $action,
+            $target,
+            $attributes,
+            $trackSubmit
+        );
+
+               $this->template = dirname(__FILE__) . '/templates/currentTables/';
+       }
+
+       //      }}}
+
+       //      {{{     checkAddressExists()
+
+    /**
+     * Checks to see if the email address exists before allowing an email to go out
+     *
+     * @param string $value submitted email address
+        *
+     * @return boolean If the email address exists or not
+     * @access public
+     */
+       public function checkAddressExists($value)
+       {
+               try {
+                       $sql = "
+                SELECT count(*) AS total
+                  FROM {$this->tableName}
+                 WHERE member_contact_email = :mce";
+
+                       $stmt = $this->dbh->prepare($sql);
+                       $stmt->bindParam(':mce', $value, PDO::PARAM_STR);
+                       $stmt->execute();
+                       $stmt->bindColumn('total', $exists);
+                       $stmt->fetch();
+
+                       return (bool) $exists;
+               } catch (PDOException $e) {
+                       Toolkit_Common::handleError($e);
+               }
+       }
+
+       //      }}}
+       //      {{{     configureElements()
+
+    /**
+     * Sets up the elements to be configured for use with the form
+     *
+     * @return void
+     * @access protected
+     */
+       protected function configureElements()
+       {
+        $e = array();
+
+               $e[] = array(
+            'type' => 'header',
+            'req' => false,
+            'name' => 'formHdr',
+            'display' => 'Email Reminder'
+        );
+               $e[] = array(
+            'type' => 'text',
+            'req' => true,
+            'name' => 'email',
+            'display' => 'Your Member Contact Email Address'
+        );
+               $e[] = array(
+            'type' => 'submit',
+            'req' => false,
+            'name' => 'submit',
+            'display' => 'Send'
+        );
+
+               $this->setupElements($e);
+       }
+
+       //      }}}
+       //      {{{     configureFilters()
+
+    /**
+     * Sets up the filters to be used with the form when submitted
+     *
+     * @return void
+     * @access protected
+     */
+       protected function configureFilters()
+       {
+        $f = array();
+
+               $f[] = array(
+            'element' => '__ALL__',
+            'filter' => 'trim'
+        );
+               $this->setupFilters($f);
+       }
+
+       //      }}}
+    //  {{{ configureForm()
+
+    /**
+     * Helper method to setup form
+     *
+     * @return void
+     * @access public
+     */
+    public function configureForm()
+    {
+        $this->configureElements();
+        $this->configureFilters();
+        $this->configureRules();
+    }
+
+    //  }}}
+       //      {{{     configureRules()
+
+    /**
+     * Sets up required rules and extra defined rules for the form
+     *
+     * @return void
+     * @access protected
+     */
+       protected function configureRules()
+       {
+        $r = array();
+
+               $r[] = array(
+            'element' => 'email',
+            'message' => 'ERROR: Invalid Email Format!',
+            'type' => 'email',
+            'format' => null,
+            'validation' => $this->validationType,
+            'reset' => true,
+            'force' => false
+        );
+               $r[] = array(
+            'element' => 'email',
+            'message' => 'ERROR: Cannot locate email address!',
+            'type' => 'callback',
+            'format' => array(&$this,
+            'checkAddressExists'),
+            'validation' => $this->validationType,
+            'reset' => true,
+            'force' => false
+        );
+
+               $this->setupRules($r);
+       }
+
+       //      }}}
+
+    //  {{{ setDbh()
+
+    /**
+     * set the pdo to use for db calls
+     *
+     * @param PDO $pdo PHP Data Object to use
+     *
+     * @return void
+     * @access public
+     */
+    public function setDbh(PDO $pdo)
+    {
+        $this->dbh = $pdo;
+    }
+
+    //  }}}
+       //      {{{     setupRenderers()
+
+    /**
+     * Inject custom renderers into the forms elements for custom display
+     *
+     * @return void
+     * @access protected
+     */
+       protected function setupRenderers()
+       {
+               parent::setupRenderers();
+
+               $renderer =& $this->defaultRenderer();
+               $required = '<!-- BEGIN required --><span class="req">*</span><!-- END required -->';
+               $error    = '<!-- BEGIN error --><div class="form-warning-inside">{error}</div><!-- END error -->';
+
+               $renderer->setElementTemplate('<tr><td colspan="2" align="center">{element}</td></tr>', 'submit');
+       }
+
+       //      }}}
+
+       //      {{{     processData()
+
+    /**
+     * Processes the data submitted by the form
+     *
+        * Gets the login credentials for the matching email address and mails
+        * them to that email address
+     *
+     * @param array $values submitted form values
+        *
+     * @return boolean Result of mail
+     * @access protected
+     */
+       protected function processData($values)
+       {
+               try {
+                       $sql = "
+                SELECT member_login, member_passwd
+                  FROM {$this->tableName}
+                 WHERE member_contact_email = :mce";
+
+                       $stmt = $this->dbh->prepare($sql);
+                       $stmt->bindParam(':mce', $values['email'], PDO::PARAM_STR);
+                       $stmt->execute();
+                       $row = $stmt->fetch();
+
+                       $htmlMsg
+                = "Here is your " . SITENAME . " password:<br><br>" .
+                                 "Login: {$row['member_login']}<br>" .
+                                 "Email: {$values['email']}<br>" .
+                                 "Password: {$row['member_passwd']}<br><br>";
+
+                       $msg
+                = "Here is your " . SITENAME . " password:\n\n" .
+                                 "Login: {$row['member_login']}\n" .
+                                 "Email: {$values['email']}\n" .
+                                 "Password: {$row['member_passwd']}";
+
+                       $crlf     = "\n";
+                       $mimeMail = new Mail_mime($crlf);
+                       $mimeMail->setFrom(SITENAME . ' <' . OWNER_EMAIL . '>');
+                       $mimeMail->setSubject('Your ' . SITENAME . ' Password');
+                       $mimeMail->setHTMLBody($htmlMsg);
+                       $mimeMail->setTXTBody($msg);
+
+                       $mail    =& Mail::factory('mail');
+                       $body    = $mimeMail->get();
+                       $headers = $mimeMail->headers($hdrs);
+
+                       $res = $mail->send($values['email'], $headers, $body);
+
+                       return PEAR::isError($res) ?
+                                       Toolkit_Common::handleError($res) :
+                                       $res;
+               } catch (PDOException $e) {
+                       Toolkit_Common::handlError($e);
+               }
+       }
+
+       //      }}}
+
+       //      {{{     toHtml()
+
+    /**
+     * Returns an HTML version of the form
+     *
+     * @return string HTML version of the form
+     * @access public
+     */
+       public function toHtml()
+       {
+               $this->setupRenderers();
+               if ($this->validate()) {
+                       if ($this->process(array(&$this, 'processData'))) {
+                               $url    = MEDIA_BASE_URL . 'index.php?catid=' . MEMBERS_CATEGORY;
+                               $e      =& $this->getElement('email');
+                               $email  = $e->getValue();
+                               $output
+                    = "<p>Your Login Information has been sent to $email</p>" .
+                                         "<p>Continue to <a href=\"$url\">Member Login</a></p>";
+                       } else {
+                               $output = '<p>Email address not found.</p>';
+                       }
+               } elseif ($this->isSubmitted()) {
+                       $output  = $this->errorMsg;
+                       $output .= parent::toHtml();
+               } else {
+                       $output .= parent::toHtml();
+               }
+
+               return $output;
+       }
+
+       //      }}}
+}
+?>
diff --git a/Toolkit/Members/TripPlanner/googleMapData.php b/Toolkit/Members/TripPlanner/googleMapData.php
new file mode 100644 (file)
index 0000000..dd5a882
--- /dev/null
@@ -0,0 +1,82 @@
+<?php
+/**
+ * googleMapData.php
+ *
+ * for contacts that forget their password sends out an email to if they're found.
+ *
+ * PHP versions 4 and 5
+ *
+ * @category  Toolkit
+ * @package   Members_TripPlanner
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2009 Gaslight Media
+ * @license   Gaslight Media
+ * @version   CVS: $Id: Forgot.php,v 1.4 2010/05/20 15:30:36 matrix Exp $
+ * @link      <>
+ */
+header('Content-type: text/xml');
+require_once '../../../setup.phtml';
+
+HTTP_Session2::useCookies(false);
+HTTP_Session2::start();
+
+if (!is_array($_SESSION['wish_list'])) {
+    return false;
+}
+$xmlEncoder = new Toolkit_Members_Map();
+$is = new Toolkit_Image_Server();
+
+$members = implode(', ', array_keys($_SESSION['wish_list']));
+
+$sql = "
+    SELECT m.*, s.state_abb, c.city_name
+      FROM member m
+      FULL JOIN state s
+     USING (state_id)
+      FULL JOIN city c
+     USING (city_id)
+     WHERE member_id IN ($members)";
+
+$dbh = Toolkit_Database::getInstance();
+try {
+       $membersArray = array();
+       $urlFormat = MEDIA_BASE_URL . 'Toolkit/Members/memberClickThru.php?member_id=%s&href=%s';
+       $moreInfoFormat = MEDIA_BASE_URL . 'member-profile/%s/%s/';
+       foreach ($dbh->query($sql, PDO::FETCH_ASSOC) as $row) {
+               $url = sprintf(
+                       $format,
+                       $row['member_id'],
+                       urlencode(str_replace('http://', '', $row['url']))
+               );
+               $moreInfoUrl = sprintf(
+                       $moreInfoFormat,
+                       MEMBER_SESSION_PAGE,
+                       $row['member_id']
+               );
+//             list($imgWidth, $imgHeight,) = $is->getImageSize(TRIP_PLANNER_MAP_IMG . $row['logo']);
+               $membersArray[] = array(
+                       'member_id' => $row['member_id'],
+                       'member_name' => $row['member_name'],
+                       'url_member_name' => urlencode($row['member_name']),
+                       'lat' => $row['lat'],
+                       'lng' => $row['lon'],
+                       'street' => $row['street'],
+                       'state_abb' => $row['state_abb'],
+                       'city_name' => $row['city_name'],
+                       'zip' => $row['zip'],
+                       'phone' => $row['phone'],
+                       'url' => $url,
+                       'website' => $row['url'],
+                       'logoPath' => TRIP_PLANNER_MAP_IMG,
+                       'logo' => $row['logo'],
+                       'logoWidth' => $imgWidth,
+                       'logoHeight' => $imgHeight,
+                       'moreInfoUrl' => $moreInfoUrl,
+               );
+       }
+} catch (PDOException $e) {
+       Toolkit_Common::handleError($e);
+}
+
+echo $xmlEncoder->getMemberXML($membersArray);
+?>
diff --git a/Toolkit/Members/TripPlanner/helpme.html b/Toolkit/Members/TripPlanner/helpme.html
new file mode 100644 (file)
index 0000000..218289f
--- /dev/null
@@ -0,0 +1,48 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
+"http://www.w3.org/TR/html4/strict.dtd">
+<html>
+  <head>
+       <title>Help Me</title>
+<style type="text/css">
+<!-- 
+body {
+       padding: 10px;
+       font-family: arial, sans-serif;
+       font-size: 0.8em;
+} 
+li {
+       margin-top: 1em;
+}
+-->
+</style> 
+</head>
+  <body>
+<div id="wrapper">
+<p>
+This section lets you create your own online itinerary. As you find items on this website that may be of interest to you, simply click the "Add to 
+Travel Planner" icon. Each item will then get added to your list. Once you have 
+completed adding items to your planner list, you may take the following actions:</p>
+
+<ol>
+       <li>Request Info<br>
+This will send an email directly to each individual business using our "Send Me More Information" 
+form. For the items you have added to your list that do not have an 
+email, we will give you their phone number. </li>
+
+<li>Print This List<br>
+Prints the list to your printer, if you have one.</li>
+
+<li>View Map<br>
+This will display your itinerary on a Map, also allowing you to retrieve driving directions.
+</li>
+
+<li>
+Save your Trip Planner<br>
+Creates an account with this website so you can access your itinerary later.
+</li>
+
+</ol>
+
+</div>
+  </body>
+</html>
diff --git a/Toolkit/Members/TripPlanner/login.php b/Toolkit/Members/TripPlanner/login.php
new file mode 100644 (file)
index 0000000..fc32667
--- /dev/null
@@ -0,0 +1,67 @@
+<?php
+
+/**
+ * login.php
+ *
+ * To login the front end user using the contact table
+ * with email as username and the password field using
+ * a extended class from Auth in the PEAR library.
+ *
+ * PHP versions 4 and 5
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2009 Gaslight Media
+ * @license   Gaslight Media
+ * @version   CVS: $Id: login.php,v 1.6 2010/06/22 11:45:34 jamie Exp $
+ * @link      <>
+ */
+$includeFunctions = false;
+
+/**
+ * Description for require_once
+ */
+require_once '../../../setup.phtml';
+HTTP_Session2::useCookies(false);
+HTTP_Session2::start();
+
+$authContainer = new Toolkit_Members_TripPlanner_AuthContainer(
+       Toolkit_Database::getInstance(),
+       array(
+               'table'                 => 'contact',
+               'usernamecol'   => 'email',
+               'passwordcol'   => 'password',
+               'db_fields'             => array('id', 'fname', 'lname'),
+               'cryptType'             => 'md5',
+       )
+);
+$tripPlannerAuth = new Toolkit_Members_TripPlanner_TripAuth(
+       $authContainer,
+       '',
+       false
+);
+$tripPlannerAuth->setIdle();
+$tripPlannerAuth->start();
+
+if (isset($_GET['logout'])) {
+       $tripPlannerAuth->logout();
+}
+
+if (!$tripPlannerAuth->checkAuth()) {
+       //      Manually adjust the authentication status for empty credentials
+       if (empty($_POST['username']) || empty($_POST['password'])) {
+               $status = -3;
+       }
+       $status = $tripPlannerAuth->getStatus();
+       header('Location: ' . MEDIA_BASE_URL . 'index.php?catid=' . MEMBER_SESSION_PAGE . '&status=' . $status);
+} else {
+    $sessionList = new Toolkit_Members_TripPlanner_Sessions(
+               Toolkit_Database::getInstance(),
+        $tripPlannerAuth
+       );
+    $sessionList->retrieveList();
+    $sessionList->saveList();
+       header('Location: ' . MEDIA_BASE_URL . 'index.php?catid=' . MEMBER_SESSION_PAGE);
+}
+?>
diff --git a/Toolkit/Members/TripPlanner/memberList.php b/Toolkit/Members/TripPlanner/memberList.php
new file mode 100755 (executable)
index 0000000..7419b88
--- /dev/null
@@ -0,0 +1,56 @@
+<?php
+
+/**
+ * wish-list.php
+ *
+ * for adding and removing members/businesses to a session
+ * storing their email addresses and names
+ *
+ * PHP versions 4 and 5
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2009 Gaslight Media
+ * @license   Gaslight Media
+ * @version   CVS: $Id: memberList.php,v 1.2 2010/05/15 16:35:33 jamie Exp $
+ * @link      <>
+ */
+$includeFunctions = false;
+/**
+ * requires setup.phtml
+ */
+require_once '../../../setup.phtml';
+
+HTTP_Session2::useCookies(false);
+HTTP_Session2::start();
+
+$authContainer = new Toolkit_Members_TripPlanner_AuthContainer(
+       Toolkit_Database::getInstance(),
+       array(
+               'table'                 => 'contact',
+               'usernamecol'   => 'email',
+               'passwordcol'   => 'password',
+               'db_fields'             => array('id', 'fname', 'lname'),
+               'cryptType'             => 'md5',
+       )
+);
+
+$tripPlannerAuth = new Toolkit_Members_TripPlanner_TripAuth(
+       $authContainer,
+       '',
+       false
+);
+$tripPlannerAuth->setIdle();
+$tripPlannerAuth->start();
+$sessionList = new Toolkit_Members_TripPlanner_Sessions(
+       Toolkit_Database::getInstance(),
+       $tripPlannerAuth
+);
+if ($_SESSION['wish_list'][$_REQUEST['member_id']]) {
+    $sessionList->removeRecord($_REQUEST['member_id']);
+} else {
+    $sessionList->addRecord($_REQUEST['member_id'], $_REQUEST['catid']);
+}
+echo count($_SESSION['wish_list']);
+?>
diff --git a/Toolkit/Members/TripPlanner/tripPlannerMap.php b/Toolkit/Members/TripPlanner/tripPlannerMap.php
new file mode 100644 (file)
index 0000000..6a7e832
--- /dev/null
@@ -0,0 +1,33 @@
+<?php
+/**
+ * tripPlannerMap.php
+ *
+ * for contacts that forget their password sends out an email to if they're found.
+ *
+ * PHP versions 4 and 5
+ *
+ * @category  Toolkit
+ * @package   Members_TripPlanner
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2009 Gaslight Media
+ * @license   Gaslight Media
+ * @version   CVS: $Id: Forgot.php,v 1.4 2010/05/20 15:30:36 matrix Exp $
+ * @link      <>
+ */
+
+require_once '../../../setup.phtml';
+?>
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
+<html>
+<head>
+<title>The Official Travel Website for Michigan's Upper Peninsula</title>
+<meta http-equiv="content-type" content="text/html;charset=utf-8">
+<meta http-equiv="imagetoolbar" content="no">
+<meta http-equiv="imagetoolbar" content="false">
+<script type="text/javascript" src="<?php echo MEDIA_APP_BASE_URL;?>libjs/jquery/jquery-1.4.2.min.js" type="text/javascript"></script>
+<script type="text/javascript" src="<?php echo MEDIA_BASE_URL;?>trip-planner-map.js" type="text/javascript"></script>
+<script src="http://maps.google.com/maps/api/js?sensor=false" type="text/javascript"></script>
+</head>
+<body>
+       <div id="map-canvas" style="width: 800px; height: 550px"></div>
+</body>
diff --git a/Toolkit/Members/TripPlanner/wish-list.php b/Toolkit/Members/TripPlanner/wish-list.php
new file mode 100755 (executable)
index 0000000..5f648cf
--- /dev/null
@@ -0,0 +1,72 @@
+<?php
+
+/**
+ * wish-list.php
+ *
+ * for adding and removing members/businesses to a session
+ * storing their email addresses and names
+ *
+ * PHP versions 4 and 5
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2009 Gaslight Media
+ * @license   Gaslight Media
+ * @version   CVS: $Id: wish-list.php,v 1.6 2010/06/22 12:00:10 jamie Exp $
+ * @link      <>
+ */
+//die('<pre>'.print_r($_REQUEST, true).'</pre>');
+$includeFunctions = false;
+/**
+ * requires setup.phtml
+ */
+require_once '../../../setup.phtml';
+
+HTTP_Session2::useCookies(false);
+HTTP_Session2::start();
+
+$authContainer = new Toolkit_Members_TripPlanner_AuthContainer(
+       Toolkit_Database::getInstance(),
+       array(
+               'table'                 => 'contact',
+               'usernamecol'   => 'email',
+               'passwordcol'   => 'password',
+               'db_fields'             => array('id', 'fname', 'lname'),
+               'cryptType'             => 'md5',
+       )
+);
+
+$tripPlannerAuth = new Toolkit_Members_TripPlanner_TripAuth(
+       $authContainer,
+       '',
+       false
+);
+$tripPlannerAuth->setIdle();
+$tripPlannerAuth->start();
+$sessionList = new Toolkit_Members_TripPlanner_Sessions(
+       Toolkit_Database::getInstance(),
+       $tripPlannerAuth
+);
+if ($_SESSION['wish_list'][$_REQUEST['member_id']]) {
+    $sessionList->removeRecord($_REQUEST['member_id']);
+    $add = false;
+} else {
+    $sessionList->addRecord($_REQUEST['member_id'], $_REQUEST['catid']);
+    $add = true;
+}
+
+//remove the email from GET array and goback to member detail page
+unset($_REQUEST['member_email']);
+if (is_array($_REQUEST)) {
+       foreach ($_REQUEST as $pname => $pval) {
+               if ($pval) {
+                       $param_part[] = $pname.'='.urlencode($pval);
+               }
+       }
+}
+if (is_array($param_part)) {
+       $params = implode("&", $param_part);
+}
+
+header("location: " . $_SERVER['HTTP_REFERER']);
diff --git a/Toolkit/Members/TripPlannerList.php b/Toolkit/Members/TripPlannerList.php
new file mode 100644 (file)
index 0000000..b1dc056
--- /dev/null
@@ -0,0 +1,485 @@
+<?php
+
+/**
+ * TripPlannerList.php
+ *
+ * PHP version 5
+ *
+ * @category  Toolkit
+ * @package   Members
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2009 Gaslight Media
+ * @license   Gaslight Media
+ * @version   CVS: $Id$
+ * @link      <>
+ */
+
+/**
+ * Short description for class
+ *
+ * Long description (if any) ...
+ *
+ * @category  Toolkit
+ * @package   Members
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2009 Gaslight Media
+ * @license   Gaslight Media
+ * @version   Release: @package_version@
+ * @link      <>
+ */
+class Toolkit_Members_TripPlannerList
+{
+    //  {{{ properties
+
+    /**
+     * Database handler
+     * @var    string
+     * @access protected
+     */
+    public $dbh;
+
+    /**
+     * Description for $plannerListPageId
+     * @var integer
+     * @access protected
+     */
+    protected $plannerListPageId;
+
+    /**
+     * Description for $plannerFormPageId
+     * @var integer
+     * @access protected
+     */
+    protected $plannerFormPageId;
+
+    /**
+     * Description for $pageGateway
+     * @var Toolkit_Toolbox_PageGatewayAbstract
+     * @access protected
+     */
+       protected $pageGateway;
+
+    //  }}}
+    //  {{{  __construct()
+
+    /**
+     * class constructor
+     *
+     * Only setup values need be done here
+     * any work need to be done within methods
+     *
+     * @param Toolkit_Toolbox_PageGatewayAbstract $gateway gateway
+     * @param PDO                                 $pdo     PDO reference
+     *
+     * @return void
+     * @access public
+     */
+    function __construct(
+               Toolkit_Toolbox_PageGatewayAbstract $gateway,
+               PDO $pdo
+       ) {
+               $this->pageGateway = $gateway;
+        $this->dbh = $pdo;
+        // These settings are used to override flexyOptions.
+        $this->flexyConfig = Toolkit_Members::getFlexyOptions();
+        $this->pagerOptions['containerClass'] = 'pages';
+        $this->plannerListPageId = MEMBER_SESSION_PAGE;
+        $this->plannerFormPageId = MEMBER_SESSION_FORM;
+    }
+
+    //  }}}
+
+       //      {{{ getAncestors()
+
+       /**
+        * getAncestors:get the ancestors for this category
+        *
+        * @param integer $catid catid
+        * @param integer $count starting counter
+        *
+        * @return array
+        * @access  public
+        */
+       function getAncestors($catid, $count = 0)
+       {
+        static $ancestors;
+        if (!$ancestors || $count == 0) {
+            $ancestors = array();
+        }
+               if ($catid) {
+                       $page = $this->pageGateway->find($catid);
+                       $ancestors[$count]['id']    = $catid;
+                       $ancestors[$count]['label'] = $page['navigation_name'];
+                       $ancestors[$count]['link'] 
+                = Toolkit_Template_Page::getSeoUrl($this->pageGateway, $catid);
+                       $this->getAncestors($page['parent'], ++$count);
+                       $ancestors[$count]['link'] 
+                = Toolkit_Template_Page::getSeoUrl($this->pageGateway, $res['id']);
+                       $this->getAncestors($res['parent'], ++$count);
+
+                       return array_reverse($ancestors);
+               }
+       }
+
+       //      }}}
+    // {{{ getSessionList()
+
+
+    /**
+     * get list from session
+     *
+     * @return void
+     * @access public
+     */
+    function getSessionList()
+    {
+        // for the session list need to sort by categories then
+        // we'll have to go through the session first and organize to what category they're in
+        // then run query
+        // have to order by the page name we'll need to find there names first then
+        $sql = "
+            SELECT member_name
+              FROM member
+             WHERE member_id = :member_id";
+        $getMemberName = $this->dbh->prepare($sql);
+        if (is_array($_SESSION['wish_list']) && !empty($_SESSION['wish_list'])) {
+            foreach ($_SESSION['wish_list'] as $member_id => $mData) {
+                               $page = $this->pageGateway->find($mData['catid']);
+                try {
+                    $getMemberName->bindParam(":member_id", $member_id, PDO::PARAM_INT);
+                    $getMemberName->execute();
+                    $memberName = $getMemberName->fetchColumn();
+                    $members[$page['navigation_name']][$memberName] = $member_id;
+                } catch(PDOException $e) {
+                    Toolkit_Common::handleError($e);
+                }
+            }
+            // for sorting members by the page they're added from
+            natSort($members);
+            try {
+                $sql = "
+                SELECT m.*, c.city_name AS city, s.state_name AS state,
+                       s.state_abb AS state_abbr,
+                       m.street||', '||c.city_name||', '||s.state_name||', '||m.zip AS address
+                  FROM member m
+                       JOIN state s USING (state_id)
+                       JOIN city c USING (city_id)
+                 WHERE member_id = :member_id";
+                $stmt = $this->dbh->prepare($sql);
+                if (is_array($members)) {
+                    $count = 0;
+                    foreach ($members as $toolbboxPageName => $memberList) {
+                        // for sorting members under each page
+                        natSort($memberList);
+                        foreach ($memberList as $memberName => $member_id) {
+                            $stmt->bindParam(":member_id", $member_id, PDO::PARAM_INT);
+                            $stmt->execute();
+                            // set record data
+                            $catid             = $_SESSION['wish_list'][$member_id]['catid'];
+                            $recordSet[$count] = $stmt->fetch();
+                            // setup the breadCrumbs
+                            $recordSet[$count]['breadCrumbs'] = $this->printAncestors($catid);
+                            // setup delete link
+                            $recordSet[$count]['deleteUrl'] = MEDIA_BASE_URL .
+                                'Toolkit/Members/TripPlanner/' .
+                                'wish-list.php?catid=' . $_REQUEST['catid'] .
+                                '&member_id=' . $member_id;
+                            // setup the url
+                            $recordSet[$count]['urlText'] = $recordSet[$count]['url'];
+                            if ($recordSet[$count]['url']) {
+                                $recordSet[$count]['url']     = MEDIA_BASE_URL .
+                                    'Toolkit/Members/memberClickThru.php' .
+                                    '?member_id=' . $member_id .
+                                    '&href=' . urlencode($recordSet[$count]['url']);
+                            }
+                            ++$count;
+                        }
+                    }
+                    if (is_array($recordSet)) {
+                        $temp = new HTML_Template_Flexy($this->flexyConfig);
+                        $page = new stdClass;
+                        $page->accountUrl  = Toolkit_Template_Page::getSeoUrl($this->pageGateway, $this->plannerFormPageId);
+                                               $page->forgotUrl   = MEDIA_BASE_URL . 'index.php?catid='.$this->plannerListPageId.'&cPage=forgot';
+                        $page->loginUrl    = MEDIA_BASE_URL . 'Toolkit/Members/TripPlanner/login.php';
+                           $page->logoutUrl   = MEDIA_BASE_URL . 'Toolkit/Members/TripPlanner/login.php?logout=1';
+                        $page->saveUrl     = MEDIA_BASE_URL . 'index.php?catid='.$this->plannerListPageId.'&cPage=Save+List';
+                        $page->requestUrl  = MEDIA_BASE_URL . 'index.php?catid='.$this->plannerListPageId.'&cPage=Send+List';
+                        $page->loginStat   = ($_REQUEST['status']) ? 'Invalid username or password.': '';
+                        $page->isLoggedIn  = $this->isLoggedIn();
+                        $page->username    = $page->isLoggedIn;
+                        $page->baseurl     = MEDIA_BASE_URL;
+                        $page->recordSet   = $recordSet;
+                        $page->numberedSet = count($recordSet);
+                        $temp->compile("tripPlannerList.tpl");
+                        $out .= $temp->BufferedOutputObject($page);
+                    }
+                }
+                //echo '<pre>'.print_r($page, true).'</pre>';
+                echo $out;
+            } catch(PDOException $e) {
+                 Toolkit_Common::handleError($e);
+            }
+        } else {
+            echo $this->showEmpty();
+        }
+    }
+
+    // }}}
+
+    // {{{ isLoggedIn()
+
+    /**
+     * check if user is logged in or not
+     *
+     * @access public
+     * @return string
+     */
+    function isLoggedIn()
+    {
+               $authContainer = new Toolkit_Members_TripPlanner_AuthContainer(
+                       Toolkit_Database::getInstance(),
+                       array(
+                               'table'                 => 'contact',
+                               'usernamecol'   => 'email',
+                               'passwordcol'   => 'password',
+                               'db_fields'             => array('id', 'fname', 'lname'),
+                               'cryptType'             => 'md5',
+                       )
+               );
+
+        $tripPlannerAuth = new Toolkit_Members_TripPlanner_TripAuth(
+                       $authContainer,
+                       '',
+                       false
+               );
+        $tripPlannerAuth->setIdle();
+        $tripPlannerAuth->start();
+
+        $test = $tripPlannerAuth->checkAuth();
+        if ($test) {
+            // give back the username
+            return $tripPlannerAuth->getUsername();
+        } else {
+            return false;
+        }
+    }
+
+    // }}}
+
+       //      {{{ printAncestors()
+
+       /**
+        * printAncestors
+        *
+        * @param mixed $catid page id
+        *
+        * @access public
+        * @return string
+        */
+       function printAncestors($catid)
+       {
+               $ancestors = $this->getAncestors($catid, 0);
+               if (is_array($ancestors)) {
+                       for ($i = 0; $i < count($ancestors); ++$i) {
+                $out[] = '<a href="' .
+                    $ancestors[$i]["link"] .
+                    '">' . $ancestors[$i]["label"] . '</a>';
+                       }
+                       if (is_array($out)) {
+                               return implode(" / ", $out);
+                       }
+               }
+       }
+
+       //      }}}
+
+    // {{{ saveList()
+
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @return string Return description (if any) ...
+     * @access public
+     */
+    function saveList()
+    {
+        if ($this->isLoggedIn()) {
+            $contactId = $_SESSION['_authsession']['data']['id'];
+            // if they already have something saved then wipe it out.
+            // (only one list per contact)
+            try {
+                $sql = "
+                                       DELETE FROM member_session
+                                        WHERE contact_id = :contact_id";
+                $del = $this->dbh->prepare($sql);
+                $del->bindParam(":contact_id", $contactId, PDO::PARAM_INT);
+                $del->execute();
+            } catch(PDOException $e) {
+                Toolkit_Common::handleError($e);
+            }
+
+            $sql = "
+                               INSERT INTO member_session (member_id, contact_id, catid)
+                               VALUES (:member_id,:contact_id,:catid)";
+            $stmt = $this->dbh->prepare($sql);
+            if (is_array($_SESSION['wish_list'])) {
+                foreach ($_SESSION['wish_list'] as $member_id => $mData) {
+                    $catid = $mData['catid'];
+                    try {
+                        $stmt->bindParam(
+                                                       ':member_id',
+                                                       $member_id,
+                                                       PDO::PARAM_INT
+                                               );
+                        $stmt->bindParam(
+                                                       ':contact_id',
+                                                       $contactId,
+                                                       PDO::PARAM_INT
+                                               );
+                        $stmt->bindParam(':catid', $catid, PDO::PARAM_INT);
+                        $stmt->execute();
+                    } catch(PDOException $e) {
+                        Toolkit_Common::handleError($e);
+                    }
+                }
+                $html = '<p>You list has been saved.</p>';
+            }
+        }
+        return $html;
+    }
+
+    // }}}
+    // {{{ show()
+
+
+    /**
+     * Calls the appropiate function for listing,saving,sending
+     * or printing the list also will need to handle grabbing list from
+     * database and repopulate the session.
+     *
+     * @return void
+     * @access public
+     */
+    function show()
+    {
+        echo $this->toHTML();
+    }
+
+    // }}}
+    // {{{ showEmpty()
+
+    /**
+     * shows the template tripPlannerNoList.tpl
+     *
+     * @access public
+     * @return string
+     */
+    function showEmpty()
+    {
+        $temp             = new HTML_Template_Flexy($this->flexyConfig);
+        $temp->compile("tripPlannerNoList.tpl");
+        $page             = new stdClass;
+        $page->accountUrl = Toolkit_Template_Page::getSeoUrl(
+                       $this->pageGateway,
+                       $this->plannerFormPageId
+               );
+        $page->forgotUrl  = MEDIA_BASE_URL . 'index.php?catid='.$this->plannerListPageId.'&cPage=forgot';
+        $page->loginUrl   = MEDIA_BASE_URL . 'Toolkit/Members/TripPlanner/login.php';
+        $page->logoutUrl  = MEDIA_BASE_URL . 'Toolkit/Members/TripPlanner/login.php?logout=1';
+        $page->saveUrl    = MEDIA_BASE_URL . 'index.php?catid='.$this->plannerListPageId.'&cPage=Save+List';
+        $page->requestUrl = MEDIA_BASE_URL . 'index.php?catid='.$this->plannerListPageId.'&cPage=Send+List';
+        $page->loginStat  = ($_REQUEST['status']) ? 'Invalid username or password.': '';
+        $page->isLoggedIn = $this->isLoggedIn();
+        $page->username   = $page->isLoggedIn;
+        $page->baseurl    = MEDIA_BASE_URL;
+        return $temp->BufferedOutputObject($page);
+    }
+
+    // }}}
+
+    // {{{ toHTML()
+
+    /**
+     * Description for toHtml
+     *
+     * Long description (if any) ...
+     *
+     * @return string Return description (if any) ...
+     * @access public
+     */
+    function toHTML()
+    {
+               $GLOBALS['bottomScripts'][] = MEDIA_APP_BASE_URL . 'gallery/thickbox-3.1.1.js';
+               $GLOBALS['styleSheets'][]   = MEDIA_APP_BASE_URL . 'gallery/gallery.css';
+               $GLOBALS['styleSheets'][]   = MEDIA_APP_BASE_URL . 'gallery/thickbox.css';
+               $GLOBALS['styleSheets'][]   = MEDIA_BASE_URL . 'Toolkit/Members/css/member.css';
+
+        if (   is_array($_SESSION['wish_list'])
+                       && !empty($_SESSION['wish_list'])
+               ) {
+            switch ($_REQUEST['cPage']) {
+            case "Send List":
+                $pp = new Toolkit_Contacts_SendTripPlanner(
+                                       $this->dbh,
+                    'TravelList',
+                    'post',
+                    MEDIA_BASE_URL .
+                    'index.php?catid='.$this->plannerListPageId
+                               );
+                $pp->configureForm();
+                $html = $pp->toHTML();
+                break;
+
+            case "Save List":
+                $html = $this->saveList();
+                $html .= $this->getSessionList();
+                break;
+
+            case "forgot":
+                $pp = new Toolkit_Members_TripPlanner_Forgot(
+                                       $this->dbh,
+                    'TravelList',
+                    'post',
+                    MEDIA_BASE_URL .
+                    'index.php?catid='.$this->plannerListPageId.'&cPage=forgot'
+                               );
+                $pp->configureForm();
+                $html = $pp->toHTML();
+                break;
+
+            case "Login":
+                break;
+
+            case "Show List":
+            default:
+                $html = $this->getSessionList();
+                break;
+            }
+        } else {
+            switch ($_REQUEST['cPage']) {
+            case "forgot":
+                $pp = new Toolkit_Members_TripPlanner_Forgot(
+                                       $this->dbh,
+                    'TravelList',
+                    'post',
+                    MEDIA_BASE_URL .
+                    'index.php?catid='.$this->plannerListPageId.'&cPage=forgot'
+                               );
+                $pp->configureForm();
+                $html = $pp->toHTML();
+                break;
+
+            default:
+                $html = $this->showEmpty();
+                break;
+            }
+        }
+
+        return $html;
+    }
+
+    // }}}
+}
diff --git a/Toolkit/Members/UserSearchForm.php b/Toolkit/Members/UserSearchForm.php
new file mode 100644 (file)
index 0000000..d2eb86c
--- /dev/null
@@ -0,0 +1,947 @@
+<?php
+//     vim:set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker syntax=php:
+
+/**
+ * Member Search Form Class
+ *
+ * PHP version 5
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @version   CVS: $Id: UserSearchForm.php,v 1.27 2010/07/14 23:31:14 jamie Exp $
+ * @link      http://demo.gaslightmedia.com
+ */
+
+/**
+ * Member Search Form
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @link      http://demo.gaslightmedia.com
+ */
+class Toolkit_Members_UserSearchForm
+    extends Toolkit_FormBuilder
+{
+       //      {{{     properties
+
+
+    /**
+     * Description for protected
+     * @var    array
+     * @access protected
+     */
+       protected $memberCatsAssignedToPage = array();
+
+    /**
+     * Description for $memberRegiansAssignedToPage
+     * @var array
+     * @access protected
+     */
+       protected $memberRegiansAssignedToPage = array();
+
+       /**
+        * The Table name used to store the data of the member record in the database.
+        *
+        * @var string
+        * @access public
+        */
+       public $tableName = 'member';
+
+       /**
+        * Array of data that holds the meta data info on the table
+        *
+        * Contains information on the type of fields in the database.
+        * That way when we run our automated SQL queries with our PDO
+        * we can properly bind data to our SQL queries.  This will
+        * allow for one more layer of protection against any sql
+        * injection attempts.
+        *
+        * @var string
+        * @access public
+        */
+       public $tableMetaData;
+
+       /**
+        * Flexy options used in the renderer
+        *
+        * @var array
+        * @access protected
+        */
+       protected $flexyOptions;
+
+       /**
+        * The name of the template used to render the member info form
+        *
+        * @var string
+        * @access protected
+        */
+       protected $formTemplate = 'memberSearchForm.tpl';
+
+       /**
+        * Form access from inside the template
+        *
+        * @var object
+        * @access protected
+        */
+       protected $view;
+
+       /**
+        * registered rules for the form
+        *
+        * @var array
+        * @access protected
+        */
+       protected $registeredRules = array();
+
+    /**
+     * catid to use in the form
+     *
+     * @var    integer
+     * @access private
+     */
+    private $_catid;
+
+       //      }}}
+
+       //      {{{ configureConstants()
+
+       /**
+        * Sets the constants for the form
+        *
+        * The member category select list must always default
+        * to the -- Choose Category -- option
+        *
+        * @return void
+        * @access public
+        */
+       public function configureConstants()
+       {
+               $c = array(
+                       'catid'   => $this->_catid,
+            'page_id' => $_REQUEST['page_id'],
+            'search'  => 1
+        );
+
+               $this->setupConstants($c);
+       }
+
+       //      }}}
+       //      {{{ configureDefaults()
+
+       /**
+        * Sets the defaults for the an existing member
+        *
+        * Populates data for the main member form.  Also grabs
+        * data to populate the modules on the form if needed.
+        *
+        * @return array $defaults Returns the array of defaults
+        *                                                 so children who call this function
+        *                                                 can obtain a copy of these values.
+        * @access public
+        */
+       public function configureDefaults()
+       {
+               $d = array(
+            'catid'   => $_GET['catid'],
+            'page_id' => $_GET['page_id']
+        );
+
+               $this->setupDefaults($d);
+
+               return $d;
+       }
+
+       //      }}}
+       //      {{{ configureElements()
+
+       /**
+        * Setup the elements to use on the form.
+        *
+     * @param PDO              $dbh Database handler
+     * @param Config_Container $c   Configuration object
+     *
+        * @return void
+        * @access public
+        */
+       public function configureElements(PDO $dbh, Config_Container $c)
+       {
+        $e = array();
+
+        //  get reference to [listing type] section of config file
+        $singularType = $c->getItem('section', 'listing type')
+                       ->getItem('directive', 'singular')
+                       ->getContent();
+        $pluralType = $c->getItem('section', 'listing type')
+                       ->getItem('directive', 'plural')
+                       ->getContent();
+        $hasRegions = $c->getItem('section', 'conf')
+                       ->getItem('directive', 'regions')
+                       ->getContent();
+
+               $memberCategories    = $this->getMemberCats($dbh);
+               if (!isset($_GET['category_id'])) {
+                       $_GET['category_id'] = null;
+               }
+        $memberSubCategories
+            = $this->getMemberSubCats(
+                               $dbh,
+                               $memberCategories,
+                               $_GET['category_id']
+                       );
+               $memberAmenities = $this->getMemberAmenities($dbh);
+
+        $regions = ($hasRegions) ? $this->getAvailableRegions($dbh) : array();
+        $this->createAvailableMemberTypesFromRegions($dbh, $regions);
+
+               //      All Grouped Elements are created here.
+               foreach ($memberAmenities as $k => $v) {
+                       $amenities[] = array(
+                'type' => 'checkbox',
+                'req' => false,
+                'name' => $v,
+                'display' => $k
+            );
+               }
+
+               //      All Elements are created here.  This includes group element definitions.
+               $e[] = array(
+            'type' => 'header',
+            'req' => false,
+            'name' => 'SearchForm_hdr'
+        );
+               $e[] = array(
+            'type' => 'hidden',
+            'req' => false,
+            'name' => 'catid'
+        );
+        if ($_REQUEST['page_id']) {
+            $e[] = array(
+                'type' => 'hidden',
+                'req'  => false,
+                'name' => 'page_id'
+            );
+        }
+               $e[] = array(
+            'type' => 'hidden',
+            'req' => false,
+            'name' => 'search'
+        );
+               $e[] = array(
+            'type' => 'text',
+            'req' => false,
+            'name' => 'member_name',
+            'display' => "Name",
+            'opts' => array('class' => 'text')
+        );
+        if (isset($regions) && !empty($regions) && count($regions) > 1) {
+            $e[] = array(
+                'type' => 'select',
+                'req' => false,
+                'name' => 'region_id',
+                'display' => 'Region',
+                'opts' => array('' => '-- Select --') + $regions,
+            );
+        } else if (isset($regions) && count($regions) == 1) {
+//            $e[] = array(
+//                'type' => 'hidden',
+//                'req' => false,
+//                'name' => 'region_id',
+//                'display' => implode('', array_keys($regions))
+//            );
+        }
+        if (    isset($memberCategories)
+            && is_array($memberCategories)
+            && !empty($memberCategories)
+        ) {
+            $e[] = array(
+                'type' => 'select',
+                'req' => false,
+                'name' => 'category_id',
+                'display' => "Category",
+                'opts' => array('' => '-- Select --') + $memberCategories
+            );
+        }
+        if (    isset($memberSubCategories)
+            && is_array($memberSubCategories)
+            && !empty($memberSubCategories)
+        ) {
+            $e[] = array(
+                'type' => 'select',
+                'req' => false,
+                'name' => 'sub_category_id',
+                'display' => "Subcategory",
+                'opts' => array('' => '-- Select --') + $memberSubCategories
+            );
+        }
+               //      Only show do if we have amenities to show.
+               if (!empty($memberAmenities)) {
+                       $e[] = array(
+                'type' => 'group',
+                'req' => false,
+                'name' => 'amenities',
+                'group' => $amenities,
+                'seperator' => '',
+                'appendName' => true
+            );
+                       $e[] = array(
+                'type' => 'checkbox',
+                'req' => false,
+                'name' => 'search_all_amenity',
+                'opts' => 'Only show members who offer all selected amenities'
+            );
+               }
+
+               $this->setupElements($e);
+       }
+
+       //      }}}
+       //      {{{ configureFilters()
+
+       /**
+        * Setup the filters to apply to the elements before we are
+        * handed the values submitted
+        *
+     * @return void
+        * @access public
+        */
+       public function configureFilters()
+       {
+        $f = array();
+
+               $f[] = array(
+            'element' => '__ALL__',
+            'filter' => 'trim'
+        );
+
+               $this->setupFilters($f);
+       }
+
+       //      }}}
+       //      {{{ configureForm()
+
+       /**
+        * Helper method to configure the entire form
+        *
+     * @param PDO              $dbh Database handler
+     * @param Config_Container $c   Configuration object
+     *
+     * @return void
+        * @access public
+        */
+       public function configureForm(PDO $dbh, Config_Container $c)
+       {
+               $this->configureElements($dbh, $c);
+               $this->configureRules();
+               $this->configureFilters();
+               $this->configureDefaults();
+               $this->configureConstants();
+       }
+
+       //      }}}
+       //      {{{ configureRules()
+
+       /**
+        * Sets up all the rules to be used when the form is validated.
+        *
+     * @return void
+        * @access public
+        */
+       public function configureRules()
+       {
+               //      No Rules
+               $r = array();
+               $this->setupRules($r);
+       }
+
+       //      }}}
+    //  {{{ createAvailableMemberTypesFromRegions()
+
+    /**
+     * Get available categories from members assigned to regions
+     *
+     * @param PDO   $dbh     Database handler
+     * @param array $regions Available regions to get members from
+     *
+     * @return void
+     * @access protected
+     */
+    protected function createAvailableMemberTypesFromRegions(
+               PDO $dbh,
+               array $regions
+       ) {
+        try {
+            $regionCats   = array();
+            $availRegions = array();
+            $availCats    = array();
+            $mainCats     = array();
+            if (!empty($this->memberCatsAssignedToPage)) {
+                foreach ($this->memberCatsAssignedToPage as $i) {
+                    $cats = Toolkit_Common::getHierarchicalTreeStructure(
+                        $dbh,
+                        'category',
+                        'category_id',
+                        'parent_id',
+                        'pos',
+                        $i,
+                        0,
+                        false
+                    );
+                    if (is_array($cats) && !empty($cats)) {
+                        $availCats = array_merge($availCats, array_keys($cats));
+                    }
+                }
+            }
+            if ($availCats && !empty($availCats)) {
+                $availCats = implode(', ', $availCats);
+            }
+
+            if (!empty($this->memberRegionsAssignedToPage)) {
+                $availRegions = implode(', ', $availRegions);
+            }
+            $pNameSql = "
+            SELECT name
+              FROM category
+             WHERE parent_id = :cid";
+            $getParentName = $dbh->prepare($pNameSql);
+            $sql = "SELECT distinct c.category_id, c.parent_id,
+                                          ( SELECT name FROM category WHERE category_id = c.parent_id) AS parent,
+                                          c.name AS category
+                                         FROM category c
+                     WHERE c.category_id IN (
+                            SELECT distinct c.category_id
+                              FROM category c, member_category mc, member m
+                             WHERE
+                              c.category_id = mc.category_id
+                               AND mc.member_id = m.member_id
+                               AND m.active";
+            if ($regions) {
+                $sql .= " AND (region = :rid OR region IS NULL) ";
+            }
+
+            $sql .= ")
+                       AND c.parent_id > 0 ";
+            if ($availRegions) {
+                $sql .= " AND region in ($availRegions) ";
+            }
+            if ($availCats) {
+                $sql .= " AND c.category_id in ($availCats) ";
+            }
+            $sql .=  " ORDER BY c.name";
+            $stmt = $dbh->prepare($sql);
+            if ($regions) {
+                foreach ($regions as $i => $j) {
+                    if (is_numeric($i)) {
+                        $stmt->bindParam(':rid', $i, PDO::PARAM_INT);
+                        $stmt->execute();
+                        $row = $stmt->fetchAll(PDO::FETCH_ASSOC);
+                        foreach ($row as $x) {
+                            $regionCats[$i][$x['parent_id']][$x['category_id']] = $x['category'];
+                            $mainCats[$x['parent_id']] = $x['parent'];
+                        }
+                    }
+                }
+            } else {
+                $stmt->execute();
+                $row = $stmt->fetchAll(PDO::FETCH_ASSOC);
+                foreach ($row as $x) {
+                    $regionCats[$i][$x['parent_id']][$x['category_id']] = $x['category'];
+                    $mainCats[$x['parent_id']] = $x['parent'];
+                }
+            }
+
+                       asort($mainCats);
+            $this->regionCategories = $regionCats;
+            $this->mainCats = $mainCats;
+        } catch (PDOException $e) {
+            return Toolkit_Common::handleError($e);
+        }
+    }
+
+    //  }}}
+
+       //      {{{     getAvailableRegions()
+
+    /**
+     * Returns all available regions that derive from members assigned to
+     * this page
+     *
+        * @param PDO $dbh Database handler
+        *
+     * @return array regions available to this page
+     * @access protected
+     */
+       protected function getAvailableRegions(PDO $dbh)
+       {
+        // Make sure we have all sub-cats nos also
+
+
+        try {
+            $sql = "SELECT DISTINCT c.category_id
+                      FROM category c, member_category mc, member m
+                     WHERE c.category_id = mc.category_id
+                       AND m.member_id = mc.member_id
+                       AND m.active ";
+            if (is_array($this->memberCatsAssignedToPage)
+                && !empty($this->memberCatsAssignedToPage)) {
+                $mcats = implode(',', $this->memberCatsAssignedToPage);
+                $sql  .= " AND c.parent_id IN ($mcats)";
+            }
+            $mcstmt = $this->dbh->prepare($sql);
+            $mcstmt->execute();
+        } catch (PDOException $e) {
+                       return Toolkit_Common::handleError($e);
+               }
+        while ($row = $mcstmt->fetch()) {
+                   $this->memberCatsAssignedToPage[] = $row['category_id'];
+               }
+
+           $regions = array();
+        $memCats = array();
+               try {
+
+                   $sql = "
+                SELECT *
+                  FROM region
+                 WHERE region_id in (
+                        SELECT distinct(region)
+                          FROM member
+                          WHERE active = 't'
+                          AND region is not null";
+            if (!empty($this->memberCatsAssignedToPage)) {
+                $sql .= " AND member_id in (SELECT distinct(member_id)
+                    FROM member_category
+                    WHERE category_id in (".implode(',', $this->memberCatsAssignedToPage).")
+                )";
+            }
+
+            if (!empty($this->memberRegionsAssignedToPage)) {
+                $sql .= " AND region_id in (".implode(',', $this->memberRegionsAssignedToPage).") ";
+            }
+
+            $sql .= " )
+                 ORDER BY region_name";
+
+            $stmt = $dbh->prepare($sql);
+            $stmt->execute();
+            while ($row = $stmt->fetch()) {
+                $regions[$row['region_id']] = $row['region_name'];
+            }
+
+            asort($regions);
+
+                       return $regions;
+               } catch (PDOException $e) {
+                       return Toolkit_Common::handleError($e);
+               }
+       }
+
+       //      }}}
+       //      {{{     getMemberAmenities()
+
+    /**
+     * Get all the amenities for the member
+     *
+        * @param PDO $dbh Database handler
+        *
+     * @return array member amenities
+     * @access protected
+     */
+       protected function getMemberAmenities(PDO $dbh)
+       {
+               try {
+                       $amenities = array();
+            $sql = "
+            SELECT accommodations
+              FROM category
+             WHERE category_id = :cid";
+            $isAccommodationCategory = $dbh->prepare($sql);
+                       $sql = "
+            SELECT a.*
+              FROM amenity a
+           NATURAL JOIN member_amenity ma
+           NATURAL JOIN member_category mc
+           NATURAL JOIN member m
+             WHERE m.new_member <> CAST(1 AS BOOLEAN)
+               AND m.active = CAST(1 AS BOOLEAN)
+               AND a.display_form = CAST(1 AS BOOLEAN)
+               AND (mc.category_id = :cid
+                OR mc.category_id in (
+                    SELECT category_id
+                      FROM category
+                     WHERE parent_id      = :cid))
+             ORDER BY amenity_name";
+
+                       $stmt = $dbh->prepare($sql);
+                       foreach ($this->memberCatsAssignedToPage as $id) {
+                $isAccommodationCategory->bindParam(':cid', $id, PDO::PARAM_INT);
+                $isAccommodationCategory->execute();
+                if (!$isAccommodationCategory->fetchColumn()) {
+                    continue;
+                }
+                               $stmt->bindParam(':cid', $id, PDO::PARAM_INT);
+                               $stmt->execute();
+                               while ($row = $stmt->fetch()) {
+                                       $amenities[$row['amenity_name']] = $row['amenity_id'];
+                               }
+                       }
+                       return $amenities;
+               } catch (PDOException $e) {
+                       return Toolkit_Common::handleError($e);
+               }
+       }
+
+       //      }}}
+       //      {{{     getMemberCats()
+
+    /**
+     * Get all the main categories available from the categories that
+     * are assigned to the page.
+     *
+     * If a main category is assigned to a page, then add to list.
+     *
+     * If a sub category is assgined to page, then add parent (main) category
+     * to the list.
+     *
+        * @param PDO $dbh Database handler
+        *
+     * @return array Main categories available for page
+     * @access protected
+     */
+       protected function getMemberCats(PDO $dbh)
+       {
+           try {
+
+                       $sql = "
+                SELECT *
+                  FROM category
+                 WHERE category_id = :cid";
+
+                       $stmt = $dbh->prepare($sql);
+            $categories = array();
+                       foreach ($this->memberCatsAssignedToPage as $id) {
+                               $stmt->bindParam(':cid', $id, PDO::PARAM_INT);
+                               $stmt->execute();
+                if ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                    if ($row['parent_id'] == '0') {
+                                       $categories[$row['category_id']] = $row['name'];
+                    } else {
+                        $stmt->bindParam(':cid', $row['parent_id'], PDO::PARAM_INT);
+                        $stmt->execute();
+                        if ($parRow = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                            $categories[$parRow['category_id']] = $parRow['name'];
+                                               }
+                    }
+                }
+            }
+            asort($categories);
+
+                       return $categories;
+
+               } catch (PDOException $e) {
+                       return Toolkit_Common::handleError($e);
+               }
+       }
+
+       //      }}}
+       //      {{{     getMemberSubCats()
+
+    /**
+     * Get all the sub categories available from the main categories that
+     * are available to the page.
+     *
+     * @param PDO     $dbh         Database handler
+     * @param array   $categories  Main level categories to get sub levels for
+     * @param integer $parentIdKey (optional) Main level that was searched on
+     *
+     * @return array     Sub categories available for page
+     * @access protected
+     */
+       protected function getMemberSubCats(
+               PDO $dbh,
+               array $categories,
+               $parentIdKey = null
+       ) {
+        if (empty($categories)) {
+            return array();
+        }
+
+               try {
+            //  Get the category information
+                       $sql = "
+                SELECT *
+                  FROM category
+                 WHERE category_id = :cid";
+
+                       $catStmt = $dbh->prepare($sql);
+
+            //  Get the sub-category information
+            $sql = "
+                SELECT c.*
+                  FROM category c, member_category mc, member m
+                 WHERE c.parent_id = :parent_id
+                                  AND c.category_id = mc.category_id
+                   AND mc.member_id = m.member_id
+                   AND m.active
+                 ";
+            $subCatStmt = $dbh->prepare($sql);
+            $subCategories = array();
+
+            while (list($id,) = each($categories)) {
+                $catStmt->bindParam(':cid', $id, PDO::PARAM_INT);
+                $catStmt->execute();
+                $category = $catStmt->fetch();
+                try {
+                    $subCatStmt->bindParam(
+                        ':parent_id',
+                        $category['category_id'],
+                        PDO::PARAM_INT
+                    );
+                    $subCatStmt->execute();
+                    if ($subCatData = $subCatStmt->fetchAll()) {
+                        foreach ($subCatData as $key => $val) {
+                            $catStmt->bindParam(
+                                ':cid',
+                                $val['category_id'],
+                                PDO::PARAM_INT
+                            );
+                            $catStmt->execute();
+                            $subCat = $catStmt->fetch();
+                            if (in_array($subCat['category_id'], $this->memberCatsAssignedToPage)) {
+                                //  Subcat assgined to page, only assign
+                                //  child of parent cat that is assigned
+                                //  to page.
+                                $subCategories[$subCat['parent_id']][$subCat['category_id']] = $subCat['name'];
+                            } elseif (in_array($subCat['parent_id'], $this->memberCatsAssignedToPage)) {
+                                //  Parent category assigned to page, add all
+                                //  children found
+                                $subCategories[$subCat['parent_id']][$subCat['category_id']] = $subCat['name'];
+                            }
+                        }
+                    }
+                } catch (PDOException $e) {
+                    return Toolkit_Common::handleError($e);
+                }
+            }
+
+            if (   is_numeric($parentIdKey)
+                && array_key_exists($parentIdKey, $subCategories)
+            ) {
+                           return $subCategories[$parentIdKey];
+            } else {
+                $subCategories
+                    = Toolkit_Common::arrayFlatten($subCategories, 2);
+                asort($subCategories);
+                           return $subCategories;
+            }
+               } catch (PDOException $e) {
+                       return Toolkit_Common::handleError($e);
+               }
+       }
+
+       //      }}}
+
+       //      {{{ toHtml()
+
+       /**
+        * Renders the form
+        *
+        * sets the page the form should be redirected to instead of coming back
+        * around to itself.
+        *
+        * @param array                               $flexyOpts   Flexy template engine options
+     * @param Toolkit_Toolbox_PageGatewayAbstract $pageGateway Page gateway
+        *
+        * @return string The rendered form
+        * @access public
+        */
+       public function toHtml(
+               array $flexyOpts,
+               Toolkit_Toolbox_PageGatewayAbstract $pageGateway
+       ) {
+               $GLOBALS['bottomScripts'][]
+                       = MEDIA_BASE_URL . 'Toolkit/Members/libjs/business-search.js';
+
+               $GLOBALS['styleSheets'][] = MEDIA_BASE_URL . 'Toolkit/Members/css/member.css';
+
+               //      We need to validate (and freeze if needed)
+               //      before we render the form. That way the
+               //      template knows about any errors on the form.
+               $this->validated = $this->validate();
+
+               $page = $pageGateway->find($this->_catid);
+
+               //      ProcessData handles settingup the lat/lon coordinates if they were not entered
+               //      into the form.  these values ar calculated and then inserted into the forms
+               //      element values.  So we need to process the data first and then render the form.
+               $this->setupRenderers($flexyOpts);
+
+               return $this->template->bufferedOutputObject($this->view);
+       }
+
+       //      }}}
+
+       //      {{{     setCatId()
+
+    /**
+     * set the catid to use in the form
+     *
+     * @param integer $catid page catid
+     *
+     * @return PEAR    Error return a PEAR error if an invalid catid is passed in
+     * @access public
+     */
+       public function setCatId($catid)
+       {
+        if (ctype_digit((string)$catid)) {
+            $this->_catid = $catid;
+        } else {
+            return PEAR::raiseError('Invalid catid');
+        }
+       }
+
+       //      }}}
+       //      {{{     setPageMemberCategories()
+
+    /**
+     * Relate toolbox categories with main member categories
+     *
+        * @param Toolkit_Toolbox_GatewayAbstract $gateway Page gateway to get categories from
+        *
+     * @return array  toolbox -> category relations
+     * @access public
+     */
+       public function setPageMemberCategories(
+               Toolkit_Toolbox_GatewayAbstract $gateway
+       ) {
+               $page                           = $gateway->find($this->_catid);
+
+               $this->memberCatsAssignedToPage = $page['member_categories'];
+
+        // If there's no member category filter selected, use all with members
+        if (count($this->memberCatsAssignedToPage) == 0) {
+            $sql = "SELECT DISTINCT c.category_id
+                      FROM category c
+                     WHERE (
+                            c.category_id IN (
+                                            SELECT distinct c.category_id
+                                              FROM category c, member_category mc, member m
+                                             WHERE c.parent_id = 0
+                                               AND c.category_id = mc.category_id
+                                               AND mc.member_id = m.member_id
+                                               AND m.active
+                                            )
+                            )
+                        OR (
+                            c.category_id IN (
+                                            SELECT distinct sc.parent_id
+                                              FROM category sc, member_category smc, member sm
+                                             WHERE sc.parent_id > 0
+                                               AND sc.parent_id != sc.category_id
+                                               AND sc.category_id = smc.category_id
+                                               AND smc.member_id = sm.member_id
+                                               AND sm.active
+                                            )
+                            )
+                    ;";
+                       $mcstmt = $this->dbh->prepare($sql);
+                       $mcstmt->execute();
+               while ($row = $mcstmt->fetch()) {
+                           $this->memberCatsAssignedToPage[] = $row['category_id'];
+                       }
+                       $page['member_categories'] = $this->memberCatsAssignedToPage;
+
+           // Otherwise add any subgategories for the selected cats with active members
+        } else {
+
+            $sql = "SELECT DISTINCT c.category_id
+                      FROM category c, member_category mc, member m
+                     WHERE c.category_id = mc.category_id
+                       AND m.member_id = mc.member_id
+                       AND m.active";
+            if (is_array($this->memberCatsAssignedToPage)
+                && !empty($this->memberCatsAssignedToPage)) {
+                $mcats = implode(',', $this->memberCatsAssignedToPage);
+                $sql .= " AND c.parent_id IN ($mcats)";
+            }
+                       $mcstmt = $this->dbh->prepare($sql);
+                       $mcstmt->execute();
+               while ($row = $mcstmt->fetch()) {
+                           $this->memberCatsAssignedToPage[] = $row['category_id'];
+                       }
+                       $page['member_categories'] = $this->memberCatsAssignedToPage;
+        }
+
+               return $page['member_categories'];
+       }
+
+       //      }}}
+       //      {{{     setPageMemberRegions()
+
+    /**
+     * Relate toolbox member cities
+     *
+        * @param Toolkit_Toolbox_GatewayAbstract $gateway Page gateway to get categories from
+        *
+     * @return array  toolbox -> category relations
+     * @access public
+     */
+       public function setPageMemberRegions(
+               Toolkit_Toolbox_GatewayAbstract $gateway
+       ) {
+           $page                           = $gateway->find($this->_catid);
+               $this->memberRegionsAssignedToPage = $page['member_regions'];
+
+               // If no regions specified then use all regions
+        if (count($this->memberRegionsAssignedToPage) == 0) {
+            $sql .= "
+                       SELECT DISTINCT region
+                         FROM member
+                        WHERE active
+                          AND region is not null;";
+            $stmt = $this->dbh->prepare($sql);
+            $stmt->execute();
+            while ($row = $stmt->fetch()) {
+                $this->memberRegionsAssignedToPage[] = $row['region'];
+            }
+            $page['member_regions'] = $this->memberRegionsAssignedToPage;
+        }
+
+               return $page['member_regions'];
+       }
+
+       //      }}}
+       //      {{{ setupRenderers()
+
+    /**
+     * Sets up the Flexy template
+     *
+        * @param array $flexyOpts Flexy template engine options
+        *
+     * @return void
+     * @access protected
+     */
+       protected function setupRenderers(array $flexyOpts)
+       {
+               $renderer = new HTML_QuickForm_Renderer_Object(true);
+
+               $this->accept($renderer);
+
+               $this->template = new HTML_Template_Flexy($flexyOpts);
+
+               //      Make the view a copy of the $this object
+               //      That way we have access to call functions in
+               //      this class from within the template.
+               $this->view = $this;
+               $this->view->form = $renderer->toObject();
+               $this->template->compile($this->formTemplate);
+       }
+
+       //      }}}
+}
+?>
diff --git a/Toolkit/Members/addressHelp.html b/Toolkit/Members/addressHelp.html
new file mode 100644 (file)
index 0000000..69de4d5
--- /dev/null
@@ -0,0 +1,10 @@
+<h2>My business is not in the correct spot in Google Maps.<br>What can I do?</h2>
+
+<p>Google provides the tools needed for placing your business correctly on their maps.
+Visit this page: <a target="_blank" href="http://www.google.com/lbc">http://www.google.com/lbc</a></p>
+<p>In addition to your correct location in their maps, you can also add other information pertaining 
+to your business such as opening hours, services, contact information etc.</p>
+
+<h2>What is the Latitude/Longitude fields on this page.</h2>
+<p>You can use this option to set your lat/long information for the Google Maps on this site only.</p>
+<p>Once your location is displaying right from the Google Local Business Center, you can remove numbers in these fields.</p>
diff --git a/Toolkit/Members/assets/.keepme b/Toolkit/Members/assets/.keepme
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/Toolkit/Members/assets/btn_close.gif b/Toolkit/Members/assets/btn_close.gif
new file mode 100755 (executable)
index 0000000..a7e3193
Binary files /dev/null and b/Toolkit/Members/assets/btn_close.gif differ
diff --git a/Toolkit/Members/assets/btn_planneractions.gif b/Toolkit/Members/assets/btn_planneractions.gif
new file mode 100755 (executable)
index 0000000..a1125b4
Binary files /dev/null and b/Toolkit/Members/assets/btn_planneractions.gif differ
diff --git a/Toolkit/Members/assets/btn_submit.gif b/Toolkit/Members/assets/btn_submit.gif
new file mode 100755 (executable)
index 0000000..13eb0c1
Binary files /dev/null and b/Toolkit/Members/assets/btn_submit.gif differ
diff --git a/Toolkit/Members/assets/saveplanner.gif b/Toolkit/Members/assets/saveplanner.gif
new file mode 100755 (executable)
index 0000000..e950408
Binary files /dev/null and b/Toolkit/Members/assets/saveplanner.gif differ
diff --git a/Toolkit/Members/config.ini b/Toolkit/Members/config.ini
new file mode 100644 (file)
index 0000000..e535ae9
--- /dev/null
@@ -0,0 +1,110 @@
+; Member Database configuration file
+[conf]
+salt = 8qq5i6k519WHqh
+; Turn on/off amenities for the application
+amenities = On
+; Allow regions in the application.
+regions = On
+; Only allow cities in member records that are added by an admin user.
+; Turning on will provide admin with an extra form that will allow them
+; to add/edit/remove cities.
+; Turning off will allow any city name to be entered when editing member
+; records.
+controlledCities = On
+counties = Off
+newMemberRequests = On
+; Determines if member changes must be first approved by admin
+; before they will be written to their actual record.
+strictPending = On
+; Allow duplicate member names in the database
+duplicateMembers = Off
+; Show the mailing address in the members profile page
+showMailingAddress = Off
+; Date start years
+dateStartYear = 2005
+; Default state for member listings
+defaultState = 23
+; uses Gaslight Medias online travel reservation system
+glmReservations = Off
+; to use map numbers for search results
+searchResultNumbered = On
+; turn on to have the member icon click to go up to the member location on map
+searchMapIconActive = On
+; member packages limit if set to Off then no limit
+memberPackageLimit = Off
+; non members On or Off
+nonMembers = Off
+
+
+[add your business]
+approvalNeeded = On
+
+; uses auth.net for processing
+[authorize net]
+useAuthNet = Off
+authNetLoginId = ""
+authNetTranKey = ""
+
+[photos]
+; Maximum number of photos a member is allowed to upload per record
+maxPhotos = 100
+; Maximum length allowed in text fields for photo captions
+maxCaptionLength = 60
+; This is the template when editing / adding photos to a members gallery
+; Not to be confused w/ the templates for the add / edit photo forms.
+editGalleryTemplate = "editPhotoGallery.tpl"
+
+; How the app should be displayed (Business, Members, Users, etc...)
+; [List Businesses] [Add Business Listing] [Pending Business Listing Update]
+; [List Members] [Add Member Listing] [Pending Member Listing Update]
+[listing type]
+singular = "Member"
+plural = "Members"
+
+; How the regions should be displayed (Regions, Counties, Areas, etc...)
+; [Regions] [List Regions] [Add Regions]
+; [Counties] [List Counties] [Add Counties]
+[region type]
+singular = "Region"
+plural = "Region"
+
+[tables]
+pendingTable = "member_updates"
+photosTable = "member_photos"
+
+[packages]
+packageListLayout = ""
+
+; Settings when logged into the members only area
+[members only]
+; Show the coupons subnav link
+coupons = On
+; Show the events subnav link
+events = On
+; show the amenities tab in the record navigation tabs
+amenities = On
+; show the packages tab in the record navigation tabs
+packages = On
+; show the photos tab in the record navigation tabs
+photos = On
+; show the files tab in the record navigation tabs
+files = On
+; show the contacts tab in the record navigation tabs
+contacts = On
+
+; Settings for the admin > members area
+[admin]
+; show the amenities tab in the record navigation tabs
+amenities = On
+; show the packages tab in the record navigation tabs
+packages = On
+; show the photos tab in the record navigation tabs
+photos = On
+; show the files tab in the record navigation tabs
+files = On
+; show the contacts tab in the record navigation tabs
+contacts = On
+; show billing module
+billing = Off
+; the rest of the billing module config setting are in
+; Toolkit/Members/Billing/config.ini
diff --git a/Toolkit/Members/css/member-admin.css b/Toolkit/Members/css/member-admin.css
new file mode 100755 (executable)
index 0000000..47f65d8
--- /dev/null
@@ -0,0 +1,966 @@
+body#memberdb {
+  margin: 0;
+  padding: 0;
+  text-align: center;
+  font-family: arial, sans-serif;
+  font-size: 12px;
+       background-color: #ddd;
+       }
+a {outline: 0}
+
+#memberdb #cal {
+       display: inline;
+       margin-left: 5px;
+}
+#memberdb #wrapper {
+       width: 760px;
+       margin: 0 auto;
+       background:#fff url("../../../images/shadowr.gif") top right repeat-y;
+       text-align: left;
+       margin-top: 15px;
+       height: 1%;
+       overflow: hidden;
+       padding-bottom: 20px;
+       }
+#memberdb #bottom {
+  width: 760px;
+  margin: 0 auto;
+       background:#fff url("../../../images/shadowb.gif") top right no-repeat;
+       height:7px;
+       font-size: 1px;
+       }
+#memberdb #copyright {
+       width: 760px;
+       font-size: 0.9em;
+       margin: 10px auto;
+       }
+
+#memberdb #top {
+       background: url("../../../images/top2.jpg") no-repeat;
+       height: 150px;
+       margin-right: 6px;
+       position: relative;
+       }
+#memberdb #top h1 {
+       position: absolute;
+       top: 10px;
+       left: 20px;
+       color: #000;
+       color: #CC6C06;
+       font-size: 26px;
+       margin: 0;
+       display: none;}
+#memberdb #top img {
+       position: absolute;
+       top: 30px;
+       left: 20px;
+       border: 0px solid #244b8d;
+       background: white;
+       padding: 5px 5px;
+       }
+h2 {font-size: 20px;}
+/*
+fieldset {float: left; width:600px;}
+fieldset form {width:600px;}
+.fieldset-form {margin:0;padding:0;float:left;width:600px;}
+input {font-size: 1.1em;}
+*/
+/* Navigation */
+#navcontainer {
+       margin-right: 6px;
+       height: 1%;
+       overflow: hidden;
+       }
+#navcontainer ul {
+  padding: 0;
+  margin: 0;
+  background-color: #004C64;
+  color: White;
+  float: left;
+  width: 100%;
+}
+#navcontainer ul li { display: inline; }
+#navcontainer ul li a {
+  padding: 0.6em 1.5em;
+  background-color: #004C64;
+  color: White;
+  text-decoration: none;
+  float: left;
+  border-right: 1px solid #fff;
+       font-weight: bold;
+       border-bottom: 1px solid #fff;
+       }
+#navcontainer ul li a:hover {
+  background-color: #2C788F;
+  color: #fff;
+
+}
+#navcontainer ul li a.active  {background-color: #2C788F; color: white;}
+
+/*sub*/
+#navcontainer div + div ul {
+       background-color: #2C788F;
+}
+#navcontainer div + div ul li a {
+       padding: 0.5em 1em;
+       background-color: #2C788F;
+       border-right: 1px solid #ccc;
+       font-weight: normal;
+       border-bottom: none;
+}
+#navcontainer div + div ul li a:hover {
+       color: #000;
+}
+#navcontainer div + div ul li a.active  {
+       background-color: #fff;
+       color: black;
+       font-weight: bold;
+}
+#navcontainer div + div ul li a.hasPending {
+       background: url("../../../images/flag_green.png") center right no-repeat;
+       padding-right: 21px;
+}
+#navcontainer div + div ul li a.active.hasPending {
+       background: white url("../../../images/flag_green.png") center right no-repeat;
+}
+/*
+#subnavcontainer {margin-right: 6px;}
+#subnavcontainer ul {
+  padding: 0;
+  margin: 0;
+  background-color: #2C788F;
+  color: white;
+  float: left;
+  width: 100%;
+}
+#subnavcontainer ul li { display: inline; }
+#subnavcontainer ul li a {
+  padding: 0.5em 1em;
+  background-color: #2C788F;
+  color: white;
+  text-decoration: none;
+  float: left;
+  border-right: 1px solid #ccc;
+       font-weight: normal;
+       }
+#subnavcontainer ul li a:hover {
+  background-color: #2C788F;
+  color: #000;
+}
+#subnavcontainer ul li a.active  {background-color: #fff; color: black; font-weight: bold;}
+*/
+ /* Content area */
+#content {
+       margin: 20px 10px;
+       clear: left;
+       }
+/* BUTTONS what the hell? */
+
+.buttons a, .buttons button {
+    display:block;
+    float:left;
+    margin:0 7px 0 0;
+    background-color:#f5f5f5;
+    border:1px solid #dedede;
+    border-top:1px solid #eee;
+    border-left:1px solid #eee;
+
+    font-family:"Lucida Grande", Tahoma, Arial, Verdana, sans-serif;
+    font-size:100%;
+    line-height:130%;
+    text-decoration:none;
+    font-weight:bold;
+    color:#565656;
+    cursor:pointer;
+    padding:5px 10px 6px 7px; /* Links */
+}
+.buttons button{
+    width:auto;
+    overflow:visible;
+    padding:4px 10px 3px 7px; /* IE6 */
+}
+.buttons button[type]{
+    padding:5px 10px 5px 7px; /* Firefox */
+    line-height:17px; /* Safari */
+}
+.buttons button img, .buttons a img{
+    margin:0 3px -3px 0 !important;
+    padding:0;
+    border:none;
+    width:16px;
+    height:16px;
+}
+/* STANDARD */
+
+.buttons button:hover, .buttons a:hover{
+    background-color:#dff4ff;
+    border:1px solid #c2e1ef;
+    color:#336699;
+}
+.buttons a:active{
+    background-color:#6299c5;
+    border:1px solid #6299c5;
+    color:#fff;
+}
+
+/* POSITIVE */
+
+button.positive, .buttons a.positive{
+    color:#529214;
+}
+.buttons a.positive:hover, button.positive:hover{
+    background-color:#E6EFC2;
+    border:1px solid #C6D880;
+    color:#529214;
+}
+.buttons a.positive:active{
+    background-color:#529214;
+    border:1px solid #529214;
+    color:#fff;
+}
+
+/* NEGATIVE */
+
+.buttons a.negative, button.negative{
+    color:#d12f19;
+}
+.buttons a.negative:hover, button.negative:hover{
+    background:#fbe3e4;
+    border:1px solid #fbc2c4;
+    color:#d12f19;
+}
+.buttons a.negative:active{
+    background-color:#d12f19;
+    border:1px solid #d12f19;
+    color:#fff;
+}
+/* TOOLBOX NAV */
+ul#toolbox {list-style-position:inside;list-style-type:circle;}
+ul#toolbox li {list-style-type:circle}
+ul#toolbox li.toolboxArrow {list-style-type:none;padding-left:0;margin-left:-7px;}
+* html ul#toolbox li.toolboxArrow {margin-left:-20px;} /*style for IE*/
+.member-admin-table {clear:left;}
+.page-links {float:left;height:40px;width:100%;text-align:center;}
+.highlight1 tr,.highlight1 td { margin:1px 0; border:#8fae74 solid 1px; background-color:#8fae74;color:#000; }
+.highlight2 tr,.highlight2 td { margin:1px 0; border:#c2d8ae solid 1px; background-color:#c2d8ae;color:#000; }
+.highlight3 tr,.highlight3 td { margin:1px 0; border:#8fae74 solid 1px; background-color:#8fae74;color:#000; }
+.highlight4 tr,.highlight4 td { margin:1px 0; border:#c2d8ae solid 1px; background-color:#c2d8ae;color:#000; }
+.highlight5 tr,.highlight5 td { margin:1px 0; border:#8fae74 solid 1px; background-color:#8fae74;color:#000; }
+.highlight6 tr,.highlight6 td { margin:1px 0; border:#c2d8ae solid 1px; background-color:#c2d8ae;color:#000; }
+/*     -----------------       */
+/*       NEW MEMBER DB         */
+/*     -----------------       */
+
+/*     DATAGRID PAGER  */
+.paging {
+       text-align: center;
+       margin: 1.0em 0;
+       padding: 8px 0;
+       width: 100%;
+}
+.paging b,
+.paging a {
+       margin-right: 15px;
+}
+.paging b {
+       color: red;
+       padding: 5px 7px;
+       border: 1px solid #585F47;
+}
+.paging a:link,
+.paging a:visited {
+       border: solid 1px #DDDDDD;
+       color: #585F47;
+       padding: 5px 7px;
+       text-decoration: none;
+}
+.paging a:hover {
+       border: 1px solid #585F47;
+}
+
+/*     DATAGRID SORTER  */
+#gridSorter {
+       margin: 10px auto;
+       padding: 10px;
+       text-align: center;
+       border: 1px solid #96A379;
+}
+#gridSorter table,
+#gridSorter td {
+       border: none;
+}
+#gridSorter .fieldcell {
+       width: auto;
+}
+#advanced-search {
+       text-align: right;
+       cursor: pointer;
+       color: blue;
+}
+.req {
+       color: red;
+}
+#form-warning-top {
+       color: black;
+       font-size: 110%;
+       font-weight: bold;
+       margin: 10px;
+       padding: 7px;
+       border: 1px solid red;
+       background-color: #FFCCCC;
+}
+#form-success-top {
+       color: black;
+       font-size: 110%;
+       font-weight: bold;
+       margin: 10px;
+       padding: 7px;
+       border: 1px solid green;
+       background-color: #CCFFCC;
+}
+
+img {
+       border: 0;
+       display: block;
+       }
+/*   ---------------   */
+/*     NAVIGATION      */
+/*   ---------------   */
+#nav-detail {
+       margin-top: 12px;
+       height: 1%;
+       overflow: hidden;
+       clear: left;
+       }
+#nav-detail ul {
+  padding: 0px 0;
+       padding-bottom: 3px;
+  margin: 0;
+       margin-top: 10px;
+  border-bottom: 1px solid #666;
+  font-weight: bold;
+}
+#nav-detail ul li {
+  list-style: none;
+  margin: 0;
+  display: inline;
+}
+#nav-detail ul li a {
+  padding: 3px 0.5em;
+  margin-left: 3px;
+  border: 1px solid #666;
+  border-bottom: none;
+  background: #E6EFD1;
+  text-decoration: none;
+}
+#nav-detail ul li a:link { color: #666; }
+#nav-detail ul li a:visited { color: #666; }
+#nav-detail ul li a:hover {
+  color: #666;
+  background: #ccc;
+       background: #FFFBDF;
+  border-color: #666;
+}
+#nav-detail ul li a.current {
+  background: white;
+  border-bottom: 1px solid white;
+       color: #000;
+}
+
+/* Member Box */
+#member-info   {
+       margin: 0;
+       border: 1px solid #666;
+       border-top: 0;
+       padding: 6px;
+       position: relative;
+       height: 1%;
+       overflow: hidden;
+       clear: left;
+       }
+#member-info h1 {
+       font-size: 16px;
+       }
+#mRow1 {
+       margin: 10px;
+       }
+#memberdb #mRow1 {
+       float: left;
+       position: relative;
+       width: 400px;
+       }
+#mRow1 table {width: 100%;}
+
+#mRow2 {
+       margin: 10px;
+       }
+#memberdb #mRow2 {
+       float: right;
+       position: relative;
+       width: 285px;
+       }
+#mRow2 table {width: 100%;}
+
+#mRow1 .text {
+       width: 180px;
+       }
+#mRow2 .text {
+       width: 150px;
+       }
+.form {
+       clear: left;
+       display: block;
+       position: relative;
+       margin: 2em 0 1em 0;
+       padding: 0;
+       border: 0;
+       }
+#memberdb .form {
+       margin-top: 0;
+       }
+/* narrower column */
+.formNarrow {
+       clear: right;
+       margin: 0 0 1em 0;
+       }
+.form legend {
+       font-size: 1.2em;
+       font-weight: bold;
+       margin: 0;
+       padding: 0 0 0.3em 0;
+       color: #000;
+       }
+.form table,
+.form td {
+       border-collapse: collapse;
+       border: 1px solid #fff;
+       padding: 0;
+       background: #D6DFC3;
+       }
+.form td {
+       padding: 4px 6px;
+       }
+.form tr.fieldPending td {
+       background: #FFFF99;
+       border: 1px solid orange;
+       }
+.form label {
+       display: block;
+       }
+.labelcell {
+       text-align: right;
+       /* width: 120px; */
+       }
+.formNarrow .labelcell {
+       /* width: 110px; */
+       }
+.fieldcell {
+       text-align: left;
+       /* width: 220px; */
+       }
+.fieldcell img {float: left;}
+.formNarrow .fieldcell {
+       /* width: 150px; */
+       }
+
+/* Multiple rows in one cell    */
+.fieldcell i {
+       display: block;
+       font-style: normal;
+       padding: 5px;
+       text-align: left;
+       background-color: #eee;
+       margin-bottom: 1px;
+       }
+.fieldcell i img {
+ margin-right: 6px; }
+/* Category Dropdown */
+.fieldcell i optgroup {
+       font-style: normal;
+       font-size: 11px;
+       }
+.fieldcell i optgroup option {
+       padding: 0;
+       padding-left: 4px;
+       }
+
+
+/* Small graphics */
+.remove {
+       float: right;
+       clear: right;}
+.add {
+       display: block;
+       /* float: right; */
+       display: block;
+       font-style: normal;
+       padding: 5px;
+       text-align: left;
+       background-color: #eee;
+       margin-bottom: 1px;
+       }
+.add img {float: left; margin-right: 10px;}
+.fieldcell .info {
+       float: right;
+       }
+
+.priceFrom,
+.priceTo {
+       width: 4em;
+       }
+.submit {display: block;}
+
+/* Submit */
+.submitArea {
+       background: #D6DFC3;
+       text-align: center;
+       padding: 10px;
+       clear: both;
+       }
+.submitArea input {
+       margin: 0 auto;
+       display: block;
+       }
+
+
+/*   ---------------   */
+/*        PHOTOS       */
+/*   ---------------   */
+
+.movable {
+       background-image: url("../../../assets/buttons/arrow_out.png");
+       background-repeat: no-repeat;
+       background-position: 8px 8px;
+       }
+.photoItem {
+       margin-top: 1em;
+       padding: 28px 18px 18px 28px;
+       border: 1px solid #ccc;
+       height: 1%;
+       overflow: hidden;
+       position: relative;
+       background-color: #eee;
+       }
+.photoItem form {
+       height: 1%;
+       overflow: hidden;
+       border: 0px solid red;
+       }
+.photoItem .thumb {
+       float: left;
+       position: relative;
+       margin-right: 20px;
+       cursor: move;
+       }
+.photoItem i {
+       font-style: normal;
+       font-weight: bold;
+       display: block;
+       }
+.photoItem input {margin-top: 0.5em;}
+.photoItem input.text {
+       width: 400px;
+       }
+.photoItem .photoDelete {
+/*     display: block;
+       margin-top: 1em;
+       border: 1px solid #ccc;
+       background: #FFEFEF;
+       width: 130px;
+       padding: 3px;
+       color: #000;
+*/
+       position: absolute;
+       bottom: 20px;
+       right: 20px;
+
+       }
+.photoDelete:hover {background: #EFD1D1;}
+.photoItem .photoDelete img {
+       float: left;
+       margin-right: 6px;
+       }
+.photoOptions {}
+#pos-info {
+       font-size: 110%;
+       font-weight: bolder;
+       padding: 5px;
+       margin: 5px 0;
+       text-align: center;
+}
+
+
+/*   ---------------   */
+/*      CONTACTS          */
+/*   ---------------   */
+.contact {
+       margin-top: 1em;
+       padding: 20px;
+       border: 1px solid #96A379;
+       overflow: hidden;
+       position: relative;
+       background: #D6DFC3;
+}
+
+.contact input.text {
+       width: 200px;
+       display: block;
+}
+/* SPECIFIC TO Contacts RESULT HERE */
+.contactList {
+       border: 1px solid #96A379;
+       padding: 10px 10px;
+       margin: 5px 0;
+       background-color: #D6DFC3;
+       height: 1%;
+       overflow: hidden;
+       position: relative;
+}
+.contactListOn {
+       background-color: #E6EFD1;
+       cursor: hand;
+       cursor: pointer;
+}
+.contactList h3 {
+       font-size: 15px;
+       color: #333;
+       margin: 0 0 0 24px;
+       float: left;
+       width: 175px;
+}
+.contactListOn h3 {
+       color: #000;
+}
+.contactList .title {
+       position: absolute;
+       bottom: 10px;
+       right: 300px;
+       width: 170px;
+}
+.contactList a.remove {
+       position: absolute;
+       left: 10px;
+}
+.contactList .phone {
+       position: absolute;
+       bottom: 10px;
+       right: 240px;
+}
+.contactList .mail{
+       position: absolute;
+       bottom: 6px;
+       right: 8px;
+}
+.contactList a.email {
+       position: absolute;
+       bottom: 10px;
+       right: 40px;
+}
+.contactList a {
+       color: #96A379;
+   text-decoration: none;
+}
+.contactList a.email {
+       text-decoration: underline;
+}
+.contactList a:link {color: #585F47;}
+.contactList a:visited {color: #585F47;}
+.contactList a:hover {color: #585F47;}
+.contactList a:active {color: #585F47;}
+
+/*   ---------------   */
+/*      PACKAGES       */
+/*   ---------------   */
+
+.packageItem {
+       margin-top: 1em;
+       padding: 20px;
+       border: 1px solid #96A379;
+       height: 1%;
+       overflow: hidden;
+       position: relative;
+       background: #D6DFC3;
+       }
+
+.packageItem .thumb {
+       position: relative;
+       margin-bottom: 1em;
+       }
+.packageText {
+       float: left;
+       width: 400px;
+       padding-bottom: 1em;
+}
+.packageItem i {
+       font-style: normal;
+       font-weight: bold;
+       display: block;
+       padding-top: 1em;
+
+       }
+.packageItem input {margin-top: 0.5em;}
+.packageItem input.text {
+       width: 200px;
+       display: block;
+       }
+.packageItem textarea {
+       width: 300px;
+       height: 150px;
+       }
+.packageItem .packageDelete {
+       display: block;
+       margin-top: 1em;
+       border: 1px solid #ccc;
+       background: #FFEFEF;
+       width: 140px;
+       padding: 3px;
+       position: absolute;
+       bottom: 20px;
+       right: 20px;
+       color: #000;
+       }
+.packageDelete:hover {background: #EFD1D1;}
+.packageItem .packageDelete img {
+       float: left;
+       margin-right: 6px;
+       }
+.packageOptions {float: left;}
+
+
+
+/*   ---------------   */
+/*      AMENITIES      */
+/*   ---------------   */
+.amenityList {
+       list-style-type: none;
+       float: left;
+       position: relative;
+       zoom: 1;
+       width: 200px;
+       margin-right: 20px;
+       }
+#memberdb .amenityList {
+       margin-right: 50px;
+       }
+.amenityList input {
+       /* No Luck, see http://meyerweb.com/eric/thoughts/2007/05/15/formal-weirdness/ */
+}
+.amenityList li {
+       vertical-align: middle;
+       height: 1%;
+       overflow: hidden;
+       font-size: 1.1em;
+       }
+.amenityList label {
+  padding: 4px;
+  display: block;
+       background: #D6DFC3;
+       margin-bottom: 1px;
+}
+.amenityList label.amenityOn {
+       background-color: #E6EFD1;
+       cursor: hand;
+       cursor: pointer;
+       }
+.amenityList li.fieldPending label {
+       background: #FFFF99;
+       border: 1px solid orange;
+       }
+.amenityList li.fieldPending label.amenityOn {
+       background: #FFFFb2;
+       cursor: hand;
+       cursor: pointer;
+       }
+
+/* SPECIFIC TO SEARCH RESULT HERE */
+.searchResult {
+       border: 1px solid #96A379;
+       padding: 10px 20px;
+       margin: 5px 0;
+       background-color: #D6DFC3;
+       height: 1%;
+       overflow: hidden;
+       position: relative;
+}
+.searchResultOn {
+       background: url("../../../assets/searchResultOn.gif") no-repeat 98% center #E6EFD1;
+       cursor: hand;
+       cursor: pointer;
+}
+.searchResult a.remove {
+       position: absolute;
+       left: 10px;
+}
+.searchResult h3 {
+       font-size: 15px;
+       color: #333;
+       margin: 0 0 0 20px;
+       float: left;
+       width: 280px;
+       }
+.searchResultOn h3 {
+       color: #000;
+}
+.searchResult .phone {
+       position: absolute;
+       bottom: 10px;
+       right: 300px;
+}
+.searchResult a.email {
+       position: absolute;
+       bottom: 10px;
+       right: 90px;
+}
+.searchResult a {
+       color: #96A379;
+   text-decoration: none;
+}
+.searchResult a.email {
+       text-decoration: underline;
+}
+.searchResult a:link {color: #585F47;}
+.searchResult a:visited {color: #585F47;}
+.searchResult a:hover {color: #585F47;}
+.searchResult a:active {color: #585F47;}
+
+.pending {
+       background-color: #FFFF99;
+       border: 1px solid orange;
+}
+.pendingMsg {
+       padding: 5px;
+       text-align: center;
+}
+.pendingPhotoCaption, .pendingLogo,
+.pendingFileName {
+       text-align: center;
+}
+.pendingFileName a {
+       text-decoration: none;
+       margin-bottom: 5px;
+       display: block;
+}
+/*
+#pendingUpdates {
+       width: 100%;
+}
+#pendingUpdates .labelcell {
+       width: 100px;
+}
+#pendingUpdates .fieldcell {
+       width: 100%;
+}
+#pendingUpdates .fieldcell div {
+       float: left;
+       color: green;
+}
+#pendingUpdates .fieldcell div + div {
+       color: red;
+}
+label.pendingUpdate {
+       display: block;
+}
+*/
+#subnavcontainer a.hasPending {
+       background: url("../../../images/flag_green.png") center right no-repeat;
+       padding-right: 21px;
+}
+.fieldcell .form_calendar {
+       display: inline;
+       float: none;
+       vertical-align: bottom;
+}
+
+/* Pending Member Stuff */
+
+.pendingUpdates {
+       width: 100%;
+}
+.pendingPhotoCaption img,
+.pendingUpdates img  {
+       float: left;
+}
+.updates .field {
+       float: left;
+       clear: left;
+       margin-top: 3px;
+       position: relative;
+}
+.authorization {
+        float: right;
+       position: relative;
+       width: 100px;
+       padding: 10px;
+       background: #EAEFE0;
+       border: 1px solid #C3CFA8;
+}
+.authorization label {
+       margin: 5px;
+       font-weight: bold;
+       font-size: 13px;
+}
+
+/* search result pagination */
+.pages {
+       padding: 1em 0;
+       clear: left;
+}
+.pages a, .pages b {
+       color: #003366;
+       display: block;
+       float: left;
+       padding: 0.2em 0.5em;
+       margin-right: 0.1em;
+       border: 1px solid #fff;
+       background: #fff;
+}
+.pages b, .business-first-letter a.curr {
+       border: 1px solid #2E6AB1;
+       font-weight: bold;
+       background: #2E6AB1;
+       color: #fff;
+}
+.pages a {
+       border: 1px solid #9AAFE5;
+       text-decoration: none;
+}
+.pages a:hover, .business-first-letter a:hover {
+       border-color: #2E6AB1;
+}
+.business-first-letter {
+       margin: 1em 0;
+}
+.business-first-letter > div {
+       margin-bottom: 1em;
+}
+.business-first-letter a {
+       margin-right: 0.1em;
+       color: #003366;
+       padding: 0.1em 0.4em;
+       border: 1px solid #9AAFE5;
+       text-decoration: none;
+}
+.level-0 {
+        font-weight: bold;
+        padding-left: 0;
+        background-color: #ccc;
+}
+.level-1 {
+        padding-left: 20px;
+        background-color: #ddd;
+        }
+.level-2 {padding-left: 40px;}
+.level-3 {padding-left: 60px;}
+.level-4 {padding-left: 80px;}
+.level-5 {padding-left: 100px;}
+.level-6 {padding-left: 120px;}
+
+#advanced-record-search img.ui-datepicker-trigger {
+       display: inline;
+       float: none;
+}
+.treeOperators {margin: 1em 0;}
+.treeOperators button {
+       margin-right: 10px;
+}
+.tree li a {background-image: url("../../../images/note_edit.png");}
diff --git a/Toolkit/Members/css/member.css b/Toolkit/Members/css/member.css
new file mode 100755 (executable)
index 0000000..267929a
--- /dev/null
@@ -0,0 +1,700 @@
+/* Member Search and display */
+#category-search {
+    clear: both;
+    padding: 0;
+    margin-top: 22px;
+}
+#category-search li {
+    list-style-image:none;
+}
+#category-search fieldset {
+background-color: #ffffff;
+  background-image: -moz-linear-gradient(top, #ffffff, #E9F0F7); /* FF3.6 */
+  background-image: -webkit-gradient(linear,left top,left bottom,color-stop(0, #ffffff),color-stop(1, #E9F0F7)); /* Saf4+, Chrome */
+            filter:  progid:DXImageTransform.Microsoft.gradient(startColorStr='#ffffff', EndColorStr='#E9F0F7'); /* IE6,IE7 */
+        -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorStr='#ffffff', EndColorStr='#E9F0F7')"; /* IE8 */
+  background-position: 0 -1px;
+  background-repeat: repeat-x;
+  border: 1px solid #95B1CD;
+    -moz-border-radius: 5px;
+    -webkit-border-radius: 5px;
+    border-radius: 5px;
+    clear: both;
+  margin-bottom: 1.5em;
+  padding: 0;
+}
+#category-search legend {
+    color: #000;
+    font-weight: bold;
+    margin-left: 1em;
+}
+#category-search fieldset ol {
+    list-style-image: none;
+    list-style-position: outside;
+    list-style-type: none;
+    padding: 1em 1em 0;
+}
+#category-search fieldset li {
+  clear:    left;
+  /* float: left; */
+  padding-bottom:   0.5em;
+  width:    100%;
+}
+#category-search fieldset.submit {
+  border-style: none;
+  left: 120px;
+  position: relative;
+  width: 100px;
+}
+#category-search label {
+    width: 120px;
+    float: left;
+    display: block;
+}
+#category-search #amenitySearchAll label {
+    display: inline;
+    float: none;
+    width: auto;
+}
+#category-search-result {
+  clear: both;
+  margin: 10px;
+}
+#amenities label {
+    text-align: left !important;
+    width: 190px !important;
+    margin-left: 30px;
+    float: left;
+    }
+
+.reservation-button {
+    float: right;
+    margin: 15px 7px 0 7px;
+    }
+
+/* Member List */
+.search-result-item {
+    padding: 15px 20px;
+  background-color: #eee;
+  height: 1%;
+  margin-bottom: 5px;
+  overflow: hidden;
+  position: relative;
+    background-color: #ffffff;
+    background-image: -moz-linear-gradient(top, #ffffff, #E9F0F7); /* FF3.6 */
+    background-image: -webkit-gradient(linear,left top,left bottom,color-stop(0, #ffffff),color-stop(1, #E9F0F7)); /* Saf4+, Chrome */
+    filter:  progid:DXImageTransform.Microsoft.gradient(startColorStr='#ffffff', EndColorStr='#E9F0F7'); /* IE6,IE7 */
+    -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorStr='#ffffff', EndColorStr='#E9F0F7')"; /* IE8 */
+    border: 1px solid #95B1CD;
+    -moz-border-radius: 5px;
+    -webkit-border-radius: 5px;
+    border-radius: 5px;
+}
+.search-result-item-on {
+  border: 1px solid #726646 !important;
+}
+.memberSR {
+    float: right;
+    }
+.list-add-link img, .list-view-link img {
+    margin-bottom: 10px;
+}
+.search-result-img {
+    clear: right;
+    }
+.search-result-item h2 {
+  padding: 0;
+    margin: 0 0 0.5em 0;
+}
+.search-result-item h2 a:link {color: #00456C;}
+.search-result-item h2 a:visited {color: #00456C;}
+.search-result-item h2 a:hover {color: #3D8D3A;}
+.search-result-item h2 a:active {color: #00456C;}
+
+.search-result-location {
+    float: left;
+}
+.search-result-distance {
+  clear: left;
+  float: left;
+  font-style: italic;
+  position: relative;
+}
+.search-result-more2 {
+  border: 0 none;
+  position: absolute;
+  right: 10px;
+  top: 60px;
+}
+div.search-result-more-div {
+    clear: left;
+    margin-top: 3px;
+}
+div.search-result-more-div a {
+    display: block;
+    width: 150px;
+    padding: 3px;
+    font-weight: bold;
+    border: 1px solid #077582;
+    text-decoration: none;
+    text-align: center;
+    margin-top: 3px;
+    }
+div.search-result-more-div a:link {
+    background-image:url(assets/listing-bg1.gif);
+    color: #077582;
+    }
+div.search-result-more-div a:visited {
+    background-image:url(assets/listing-bg1.gif);
+    color: #077582;
+    }
+div.search-result-more-div a:hover {
+    background-image:url(assets/listing-bg2.gif);
+    color: #077582;
+    }
+div.search-result-more-div a:active {
+    background-image:url(assets/listing-bg1.gif);
+    color: #077582;
+    }
+.member-coupon, .member-valuecard {
+    float: left;
+    clear: left;
+    position: relative;
+    margin-top: 10px;
+    margin-left: 76px;
+    display: inline;
+    }
+#member-detail {
+    clear: left;
+    padding-top: 10px;
+    }
+#member-img-1 { padding-right:10px; }
+#business-list-contacts {clear: left;}
+#business-list-contacts h2 {margin-bottom: 0;}
+#member-detail .vcard {
+    float: left;
+    width:  200px;
+    }
+#member-detail .member-button {
+    float: left;
+    clear: right;
+    position: relative;
+    margin: 15px 5px 15px 0;
+    }
+.business-contact {display: list-item; margin-left: 20px;}
+.vcard h1 {
+    font-size: 18px;
+    }
+#image-box {
+    float: right;
+    width: 270px;
+    margin-bottom: 10px;
+    margin-left: 10px;
+    }
+#photo-caption {
+    text-align: center;
+    margin-top: 3px;
+    }
+#google-div {
+    margin: 1em 0;
+    clear: left;
+    width: 230px;
+    background: #CBDEF2;
+    border: 1px solid #28578F;
+    padding: 6px 10px;
+    position: relative;
+    }
+#google-div h3 {margin: 0; font-size: 12px !important; margin-bottom: 3px}
+#google-div input {font-size: 12px;}
+#ccards {
+    height: 1%;
+    overflow: hidden;
+    float: right;
+    border: 1px solid #ccc;
+    padding: 5px;
+    width: 150px;
+    clear: right;
+    margin-top: 12px;
+    margin-left: 12px;
+    }
+#ccards img {float: left; margin: 0 10px 10px 0;}
+#ccards h2 {font-size: 12px; margin: 0;}
+#member-img-l {
+    display:block;
+}
+.member-img-s {
+    cursor: pointer;
+    cursor: hand;
+  float: left;
+  margin: 10px 10px 0 0;
+  position: relative;
+    display: inline;
+}
+.business-list-amenities {
+    font-weight: bold;
+    line-height: 15px;
+}
+.business-list-amenities ul {
+  margin:0;
+  padding:10px;
+    margin-left: 10px;}
+.business-list-amenities li {
+    }
+.business-list-amenities ul {
+    margin: 0;
+    padding: 10px;
+    list-style-type: disc;
+  }
+.business-list-amenities li {
+    border: 0;
+    background: none;
+    list-style-type: disc;
+  }
+.member-travel-item {
+    margin-top: 1em;
+    clear: both;
+    }
+.member-travel-item img {
+    float: right;
+    clear: right;
+    padding-bottom: 10px;
+    }
+.member-travel-item h2 {
+    clear: both;
+    }
+#search-more {
+  height: 1%;
+  margin: 10px 10px 10px 10px;
+  overflow: hidden;
+  padding: 10px;
+}
+#search-more fieldset {
+  background-color: #AFCDEF;
+    background-position: 0 -1px;
+  background-repeat: repeat-x;
+  border: 1px solid #244B8D;
+  clear: both;
+  margin: 0 10px 0 0;
+  padding: 5px 0;
+}
+#search-more legend {
+  color: #000;
+  font-weight: bold;
+  margin-left: 1em;
+}
+#search-more form {
+    margin: 0;
+}
+fieldset.submit {
+    border-style: none;
+}
+#search-more select {margin-top: 3px;}
+#search-more label {
+  display: block;
+  float: left;
+  height: 1%;
+  margin: 5px;
+  text-align: right;
+  width: 100px;
+  clear: left;
+}
+/*
+3 Specialty Sections.
+Restaurant, Hotels and Golf Courses
+*/
+
+#member-files {
+    margin-top: 1em;
+    height: 1%;
+    overflow: hidden;
+}
+/* Header Sizes */
+.member-golf-result h2,
+.member-hotel-result h2,
+.member-restaurant-item h2,
+#member-files h2 {
+    font-size: 15px;
+    }
+/* Golf result list */
+.member-golf-result {
+    clear: both;
+    height: 1%;
+    overflow: hidden;
+    margin-top: 1em;
+    }
+/* walking Course? */
+.member-golf-walking {
+    margin-left: 20px;  }
+/* Par stats table */
+.member-golf-stats {
+    float: left;
+    clear: left;
+    margin-top: 2px;
+    margin-left: 2px;
+    margin-bottom: 6px;
+    margin-right: 20px
+    }
+/* Fees Table */
+.member-golf-fees { float: left;    }
+.member-golf-result table, .member-golf-result td, .member-golf-result th {
+    border: 1px solid #BDD8BC;
+    border-collapse: collapse;
+    background: #eee;
+    font-size: 11px;
+    }
+.member-golf-result td, .member-golf-result th {
+    padding: 3px 6px;
+    }
+/* Accommodations Result List */
+.search-result-item-info .member-hotel-result {
+    height: 1%;
+    overflow: hidden;
+    position: absolute;
+    top: 14px;
+    right: 12px;
+    height: 50px;
+    display: none;
+    }
+#member-detail .member-hotel-result {
+    position: relative;
+    float: right;
+    margin: 0 0 20px 20px;
+    background: #ddd;
+    border: 2px solid #ccc;
+    padding: 10px;
+    }
+#member-detail .member-hotel-result img {
+    margin-bottom: 2px;
+    }
+#member-detail p {margin-top: 0;}
+/* Restaurant Information */
+.member-restaurant-item {
+    clear: left;
+    margin-top: 1em;
+    margin-left: 0;
+    margin-bottom: 6px;
+    margin-right: 20px;
+    }
+.member-restaurant-item table, .member-restaurant-item td, .member-restaurant-item th {
+    border: 1px solid #BDD8BC;
+    border-collapse: collapse;
+    background: #eee;
+    font-size: 12px;
+    }
+.member-restaurant-item td, .member-restaurant-item th {
+    padding: 4px 8px;
+    }
+
+/* Controls the transfer box when updating a member image
+ * on the member-detail page
+ */
+.ui-effects-transfer {border: 2px solid black;}
+/* Right Column Member Deatil */
+#mColumn {
+    clear: right;
+    float: right;
+    position: relative;
+    width: 146px;
+    border: 1px solid #ccc;
+    border-bottom: 0;
+    margin-left: 20px;
+    }
+#mColumn h2 {
+    font-size: 12px;
+    border-bottom: 1px solid #ccc;
+    padding: 8px 8px 4px 8px;
+    margin: 0;
+    background: #eee;
+    }
+#mColumn ul, #mColumn li {
+    margin: 0;
+    padding: 0;
+    list-style-type: none;
+    font-size: 12px;
+     }
+#mColumn li {
+    border-bottom: 1px solid #ccc;
+    background-position: 5px 5px;
+    background-repeat: no-repeat;
+    }
+#mColumn ul ul {
+    margin: 0;
+    padding: 10px;
+    list-style-type: disc;
+    list-style-position: inside;
+  }
+#mColumn ul ul li {
+    border: 0;
+    background: none;
+    list-style-type: disc;
+  }
+#mColumn p {
+    margin: 0;
+    padding: 4px 8px 4px 28px;
+  }
+#mColumn a {
+    margin: 0;
+    display: block;
+    padding: 4px 8px 4px 28px;
+    }
+#mColumn ul ul a {
+    display: inline;
+    padding: 0;
+    }
+#mColumn a:hover {
+        text-decoration: none;
+    }
+/* Icons */
+.mFacebook  { background-image: url(http://app.gaslightmedia.com/assets/deadsimple/facebook.gif); }
+.mPinterest { background-image: url(http://app.gaslightmedia.com/assets/deadsimple/pinterest.png); }
+.mMyspace   { background-image: url(http://app.gaslightmedia.com/assets/deadsimple/myspace.gif); }
+.mTwitter   { background-image: url(http://app.gaslightmedia.com/assets/deadsimple/twitter.gif); }
+.mLinkedIn  { background-image: url(http://app.gaslightmedia.com/assets/deadsimple/linkedin.gif); }
+.mBlog      { background-image: url(http://app.gaslightmedia.com/assets/deadsimple/blogger.gif); }
+.mInstagram { background-image: url(http://app.gaslightmedia.com/assets/deadsimple/instagram.gif); }
+.mYouTube   { background-image: url(http://app.gaslightmedia.com/assets/deadsimple/youtube2.gif); }
+.mGooglePlus { background-image: url(http://app.gaslightmedia.com/assets/deadsimple/googleplus.gif); }
+.mReservations  { background-image: url(http://app.gaslightmedia.com/assets/icons/book_open.png); }
+/* Photo Gallery on member detail pages */
+#member-detail #photo-gallery {
+    clear: both;
+    margin: 0;
+    border-top: 1px dotted #aaa;
+    padding-top: 1em;
+}
+div.pBox {
+    -moz-box-sizing: border-box;
+    background: none repeat scroll 0 0 #F5F5F5;
+    border: 1px solid #CCCCCC;
+    clear: both;
+    display: block;
+    float: left;
+    margin: 10px 0 0;
+    overflow: hidden;
+    padding: 20px;
+    width: 100%;
+}
+/* Trip Planner Stuff */
+.tpBox {
+    border-right: 1px solid #3D1006;
+    border-bottom: 1px solid #3D1006;
+    border-left: 1px solid #CBCBAA;
+    border-top: 1px solid #CBCBAA;
+    background: #eee;
+    }
+#accountBox {
+    height: 1%;
+    overflow: hidden;
+    }
+#accountBox h2 {
+    margin: 0 0 6px 0;
+    padding: 0;
+    }
+#loggedInAs {
+    float: left;
+    width: 65%;
+    padding: 6px;
+    }
+#plannerLogin, #plannerAcc {
+    width: 31%;
+    float: left;
+    padding: 6px;
+    }
+.tpBoxNoList #plannerAcc, .tpBoxNoList #plannerLogin  {
+    width: 44%;
+    padding: 16px;
+    }
+#plannerAcc {
+    border-right: 1px dashed #ccc;
+    border-left: 1px dashed #ccc;
+    }
+.tpBoxNoList #plannerAcc {
+    border: 0;
+    }
+.tpBoxNoList #plannerLogin {
+    border-right: 1px dashed #ccc;
+    }
+#plannerUser, #plannerP, #plannerSubmit {
+    margin: 3px 0;
+    }
+.tpBoxNoList #plannerUser, .tpBoxNoList #plannerP, .tpBoxNoList #plannerSubmit {
+text-align: right;  }
+
+#accountBox #plannerLogin input {
+    width: 140px;
+    display: block;
+    font-size: 14px;
+    }
+#accountBox.tpBoxNoList #plannerLogin input {
+    width: 140px;
+    display: inline;
+    font-size: 14px;
+    }
+#plannerForgot {
+    font-size: 11px;
+    margin: 3px 0;
+    }
+#accountBox #plannerNew {
+    display: block;
+    padding: 6px 6px 6px 26px;
+    background: url(http://app.gaslightmedia.com/assets/icons/add.png) no-repeat 5px center #fff;
+    margin-left: 0;
+    border: 1px solid #ccc;
+    font-size: 16px;
+    }
+#accountBox #plannerNew:hover {
+    background-color: green;
+    color: white;
+    }
+/* Items */
+#plannerList {
+    padding: 6px;
+    margin: 0;
+    clear: both;
+    }
+/* page break */
+.page-break { display:none; }
+
+.plannerItem {
+    border: 1px solid #ddd;
+    border-left: 20px solid #eee;
+    background: #fff;
+    padding: 1px 0 6px 0;
+    position: relative;
+    clear: both;
+    margin-top: 12px;
+    height: 1%;
+    overflow: hidden;
+}
+.plannerRemove {
+    text-indent: -5000px;
+    background: url(http://app.gaslightmedia.com/assets/icons/delete.png) no-repeat;
+    width: 20px;
+    height: 20px;
+    display: block;
+    position: absolute;
+    top: 6px;
+    right: 6px;
+    }
+#tpBreadcrumbs {
+    font-size: 12px;
+    padding: 6px 0 3px 10px;
+    margin: 0;
+}
+#tpBreadcrumbs a {
+    font-weight: normal;
+}
+.plannerItem h2 {
+    font-size: 18px;
+    padding: 3px 0 0 10px;
+    margin: 0;
+    }
+.plannerAddress {
+    width: 48%;
+    float: left;
+    padding-top: 0.5em;
+        }
+.plannerItem p {
+    margin: 0 10px;
+    }
+.plannerInfo {
+    float: right;
+    width: 48%;
+    margin-top: 0px;
+    padding-top: 0.5em;
+    }
+p.plannerTime {
+    background: #CBCBAA;
+    background: transparent;
+    padding: 3px 6px;
+    padding: 0;
+    margin: 0;
+    position: absolute;
+    bottom: 6px;
+    left: 10px;
+    font-style: italic;
+    }
+ul#plannerNav {
+    list-style-type: none;
+    margin: 0;
+    padding: 10px;
+    width: 28%;
+    float: left;
+    }
+ul#plannerNav li {
+    list-style-type: none;
+    margin: 0;
+    padding: 0;
+}
+ul#plannerNav li a {
+    display: block;
+    padding: 5px 0 5px 26px;
+    background-position: 4px center;
+    background-repeat: no-repeat;
+    margin-bottom: 3px;
+    border: 1px solid #eee;
+    }
+ul#plannerNav li#pN1 a {
+    background-image: url(http://app.gaslightmedia.com/assets/icons/email_edit.png);
+}
+ul#plannerNav li#pN2 a {
+    background-image: url(http://app.gaslightmedia.com/assets/icons/printer.png);
+}
+ul#plannerNav li#pN3 a {
+    background-image: url(http://app.gaslightmedia.com/assets/icons/map.png);
+    }
+ul#plannerNav li#pN4 a {
+    background-image: url(http://app.gaslightmedia.com/assets/icons/help.png);
+    }
+ul#plannerNav a:hover {
+    background-color: white !important;
+    border: 1px solid #ccc;
+    }
+
+.pages {
+    padding: 1em 0;
+    clear: left;
+}
+.pages a, .pages b {
+    color: #003366;
+    display: block;
+    float: left;
+    padding: 0.2em 0.5em;
+    margin-right: 0.1em;
+    border: 1px solid #fff;
+    background: #fff;
+}
+.pages b, .business-first-letter a.curr {
+    border: 1px solid #2E6AB1;
+    font-weight: bold;
+    background: #2E6AB1;
+    color: #fff;
+}
+.pages a {
+    border: 1px solid #9AAFE5;
+    text-decoration: none;
+}
+.pages a:hover, .business-first-letter a:hover {
+    border-color: #2E6AB1;
+}
+.business-first-letter {
+    margin: 1em 0;
+}
+.business-first-letter > div {
+    margin-bottom: 1em;
+}
+.business-first-letter a {
+    margin-right: 0.1em;
+    color: #003366;
+    padding: 0.1em 0.4em;
+    border: 1px solid #9AAFE5;
+    text-decoration: none;
+}
+/* member db category search result, numbered icons */
+.search-result-map-img {
+    float: left;
+    margin-right: 7px;
+    }
+div.thumb {
+    margin: 2px 3px;
+    }
diff --git a/Toolkit/Members/export-images-is0.php b/Toolkit/Members/export-images-is0.php
new file mode 100755 (executable)
index 0000000..892db05
--- /dev/null
@@ -0,0 +1,81 @@
+<?php
+/**
+ * Export images from local directories into the Image Server
+ *
+ * PHP version 5
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @author    Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @version   CVS: $Id: export-images-is0.php,v 1.4 2009/12/29 14:17:49 jamie Exp $
+ * @link      http://demo.gaslightmedia.com
+ * @see       References to other sections (if any)...
+ */
+
+/**
+ * setup file
+ */
+require_once '../../setup.phtml';
+
+/**
+ * Image Server API
+ */
+require_once BASE.'Toolkit/Image/Server.php';
+
+$db = new PDO('pgsql:'.CONN_STR);
+
+/**
+ * Description for define
+ */
+define('OLDORG', 'http://demo.gaslightmedia.com/images/member_original/');
+$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
+$sql = "
+  SELECT member_id, logo
+    FROM member
+   WHERE logo <> ''
+     AND logo not like 'is%'
+   ORDER BY member_id";
+try {
+    $stmt = $db->query($sql);
+    $data = $stmt->fetchAll(PDO::FETCH_ASSOC);
+    echo '<pre>';
+    print_r($data);
+    echo '</pre>';
+} catch(PDOException $e) {
+    die($e->getMessage());
+}
+$db->beginTransaction();
+$IServer = new Toolkit_Image_Server();
+if (is_array($data)) {
+    $prep2 = "
+    UPDATE member
+       SET logo = :image
+     WHERE member_id = :id";
+    $stmt3 = $db->prepare($prep2);
+
+    foreach ($data as &$row) {
+        $row['logo'] = trim($row['logo']);
+            var_dump(OLDORG.$row['logo']);
+        if (!preg_match("/^is/", $row['logo'])) {
+            $image_URL = OLDORG.urlencode($row['logo']);
+            $image     = $IServer->imageUpload($image_URL);
+            if ($image) {
+                try {
+                    $stmt3->bindParam(":image", $image, PDO::PARAM_STR);
+                    $stmt3->bindParam(":id", $row['member_id'], PDO::PARAM_INT);
+                    $stmt3->execute();
+                } catch(PDOException $e) {
+                    die($e->getMessage());
+                }
+            }
+            echo '<br>Image Name Returned: ';
+            var_dump($image);
+        }
+    }
+}
+//$db->commit();
+$db->rollBack();
+?>
diff --git a/Toolkit/Members/libjs/advancedsearch.js b/Toolkit/Members/libjs/advancedsearch.js
new file mode 100644 (file)
index 0000000..f2916ea
--- /dev/null
@@ -0,0 +1,71 @@
+var AdvancedSearch =
+{
+       init: function()
+       {
+               //      select groups that need a calendar
+               var $elements = new Array(
+                       'create_date_beg',
+                       'create_date_end',
+                       'join_date_beg',
+                       'join_date_end',
+                       'last_modified_date_beg',
+                       'last_modified_date_end'
+               );
+
+               for (i in $elements) {
+                       //      Used to create unique ids for the hidden elements
+                       var $myDate = new Date();
+                       var $selectElem = $("select[name='"+$elements[i]+"[Y]']");
+                       var $newId = 'datePicker_' + $myDate.valueOf();
+                       $('<input type="hidden" disabled="disabled" id="'+$newId+'">').insertAfter($selectElem);
+
+                       var $minYear = $selectElem.find('option:nth-child(2)');
+                       var $maxYear = $selectElem.find('option:last-child');
+                       $('#' + $newId).datepicker({
+                               beforeShow: AdvancedSearch.readSelected,
+                               onSelect: AdvancedSearch.updateSelected,
+                               minDate: new Date($minYear.val(), 1 - 1, 1),
+                               maxDate: new Date($maxYear.val(), 12 - 1, 31),
+                               showOn: 'both',
+                               buttonImageOnly: true,
+                               buttonImage: '//app.gaslightmedia.com/assets/icons/calendar.png'
+                       });
+               }
+       },
+
+       readSelected: function(input, inst)
+       {
+               var $selects = $('#' + inst.id).prevUntil('img');
+               var $tgtDate = $('#' + inst.id);
+               $tgtDate.val(
+                       $selects.get(2).value + '/' +
+                       $selects.get(1).value + '/' +
+                       $selects.get(0).value
+               );
+       },
+
+       updateSelected: function(date, inst)
+       {
+               var $selects = $('#' + inst.id).prevUntil('img');
+
+               //      select month values are 1, 2, 3  not 01, 02, 03
+               //      If we're on a single digit, just grab that single
+               //      digit substring value
+               var $month = (date.substring(0, 1) == '0')
+                       ? date.substring(1, 2)
+                       : date.substring(0, 2);
+
+               //      select day values are 1, 2, 3  not 01, 02, 03
+               //      If we're on a single digit, just grab that single
+               //      digit substring value
+               var $day = (date.substring(3, 4) == '0')
+                       ? date.substring(4, 5)
+                       : date.substring(3, 5);
+
+               $selects.get(2).value = $month;
+               $selects.get(1).value = $day;
+               $selects.get(0).value = date.substring(6, 10);
+       }
+};
+
+$(document).ready(AdvancedSearch.init);
diff --git a/Toolkit/Members/libjs/authorizeChanges.js b/Toolkit/Members/libjs/authorizeChanges.js
new file mode 100644 (file)
index 0000000..3abf74f
--- /dev/null
@@ -0,0 +1,19 @@
+/**
+ * setup CKEditors for any textarea field that might exists on the page.
+ */
+var AuthorizeChanges =
+{
+       init: function()
+       {
+        //  Only try to replace the textarea if the 
+        //  CKEditor is compatible w/ the browser.
+        if (CKEDITOR.env.isCompatible) {
+            CKEDITOR.replaceAll(function(textarea, config)
+                {
+                    config.toolbar = 'LimitedToolset';
+                });
+        }
+       }
+}
+
+$(document).ready(AuthorizeChanges.init);
diff --git a/Toolkit/Members/libjs/business-detail.js b/Toolkit/Members/libjs/business-detail.js
new file mode 100755 (executable)
index 0000000..e73da72
--- /dev/null
@@ -0,0 +1,83 @@
+var GDirs =
+{
+       dBox: null,
+       originalText: null,
+       addyBox: null,
+
+       init: function()
+       {
+               $('#member-detail a.external').each(function() {
+                       $(this).click(function() {
+                               var $newWindow = window.open($(this).attr('href'));
+                               $newWindow.focus();
+                               return false;
+                       });
+               });
+
+               GDirs.addyBox = $('#toaddress');
+               GDirs.originalText = GDirs.addyBox.val();
+               GDirs.dBox = $('#google-div');
+               GDirs.dBox.before('<a href="#" id="google-map-link" class="member-button" title="Get Driving Directions"> Directions </a>');
+               GDirs.dBox.hide();
+               $('#google-map-link').bind('click', GDirs.toggleBox);
+               $('#GDirs').bind('submit', GDirs.submitForm);
+               GDirs.addyBox.bind('focus', GDirs.clearForm);
+               GDirs.addyBox.bind('blur', GDirs.fillForm);
+        $('.list-add-link').click(function(){
+            var member_id = $(this).attr('rel');
+            $.ajax({
+                type: "POST",
+                url: glm_base_url + "trip-planner-list/" + member_id + "/" + glm_catid + "/",
+                beforeSend: function() {$('#loading-planner').show()},
+                complete: function() {$('#loading-planner').hide()},
+                success: function(ret){
+                    GDirs.toggleTravelLink(ret);
+                }
+            });
+            return false;
+        });
+
+       },
+
+    toggleTravelLink: function(event) {
+        GDirs.updateList(event);
+        var html = '<a href="' + glm_base_url + 'my-west-michigan-' + glm_trip_planner_id + '/" flexy:if="plink" class="list-view-link">';
+        html += '<img alt="View Your Travel List" title="View Your Travel List" src="';
+        html += glm_base_url + 'assets/viewTravelPlanner.gif" style="float: right; margin: 10px;clear: right;">';
+        html += '</a>';
+        $('.list-add-link').replaceWith(html);
+    },
+
+       toggleBox: function(event)
+       {
+               event.preventDefault();
+               GDirs.dBox.slideToggle('normal');
+       },
+
+       submitForm: function(event)
+       {
+               if (GDirs.addyBox.val() == GDirs.originalText || GDirs.addyBox.val() == '') {
+                       event.preventDefault();
+                       alert('Please fill in your Address');
+               }
+       },
+
+       clearForm: function(event)
+       {
+               if (GDirs.addyBox.val() == GDirs.originalText) {
+                       GDirs.addyBox.val('');
+               }
+       },
+
+       fillForm: function(event)
+       {
+               if (GDirs.addyBox.val() == '') {
+                       GDirs.addyBox.val(GDirs.originalText);
+               }
+       },
+    updateList: function (listCount){
+        $(".trip-list-count").html(listCount);
+    }
+};
+
+$(document).ready(GDirs.init);
diff --git a/Toolkit/Members/libjs/business-search.js b/Toolkit/Members/libjs/business-search.js
new file mode 100644 (file)
index 0000000..69aa72e
--- /dev/null
@@ -0,0 +1,273 @@
+var Search =
+{
+    //  {{{ properties
+
+    catSelected: false,
+    subCatSelected: false,
+
+    //  }}}
+    //  {{{ init()
+
+       init: function()
+       {
+        $(".t-list-link").bind("click", function(){
+            var href = $(this).attr('href');
+            $(this).attr('href', href + '&pageOffset=' + window.pageYOffset);
+        });
+
+               $('.search-result-item')
+            .mouseover(Search.highlightMember)
+            .mouseout(Search.lowlightMember)
+            .click(Search.goToMember);
+
+               //      Stop the propagation so the goToMember function won't
+               //      be called when these links are clicked.
+               $('.search-result-email, .search-result-website, .search-result-directions')
+                       .click(function(event) {
+                               event.stopPropagation();
+                       });
+
+               $("select[name='category_id']").change(Search.populateMemberSubType);
+               $("select[name='region_id']").change(Search.repopulateCategories);
+
+        Search.setGET();
+               Search.repopulateCategories();
+               Search.populateMemberSubType();
+       },
+
+    //  }}}
+    //  {{{ setGET()
+
+    setGET: function()
+    {
+        var get=(""+location.search).substring(1).split("&");
+        window.location.GET = new Array();
+        for (var i in get) {
+            var temp = get[i].split("=");
+            window.location.GET[temp[0]] = temp.splice(1, temp.length-1).join("=");
+        }
+    },
+
+    //  }}}
+    //  {{{ highlightMember()
+
+       highlightMember: function(event)
+       {
+               if ($(this).find("a[title='More Info']").is('a')) {
+                       $(this).addClass('search-result-item-on');
+               }
+       },
+
+    //  }}}
+    //  {{{ lowlightMember()
+
+       lowlightMember: function(event)
+       {
+               if ($(this).find("a[title='More Info']").is('a')) {
+                       $(this).removeClass('search-result-item-on');
+               }
+       },
+
+    //  }}}
+    //  {{{ goToMember()
+
+       goToMember: function(event)
+       {
+               var link = $(this).find("a.moreInfo");
+               if (link.is('a')) {
+                       document.location.href = link.attr('href');
+               }
+       },
+
+    //  }}}
+    //  {{{ repopulateCategories()
+
+    /**
+     * repopulate the sub category box based on the the value
+     * selected in the region box
+     *
+     * if no region was selected, then get all main categories available
+     *
+     * if a region was selected, then get all main categories available in that
+     * region.
+     */
+    repopulateCategories: function(event)
+    {
+       // Get currently selected "Region"
+        if ($("[name='region_id']").is("select")) {
+            var region = $("select[name='region_id'] option:selected").val();
+        } else {
+            var region = $("input[name='region_id']").val();
+        }
+
+        // Get currently selected "Member Type" (category)
+               var catList = $("select[name='category_id']");
+
+               // Clear category list
+               $("select[name='category_id'], select[name='sub_category_id']")
+            .empty()
+            .append('<option value="">-- Select --</option>');
+
+               var usedValues = [];
+               var options = [];
+               var cats = [];
+
+       // If we have the RegionCats data (json)
+               if (window.RegionCats != undefined) {
+            var RegionCats = window.RegionCats;
+                       
+                       // If no region is selected
+                       if (region == undefined || region == '') {
+                // For each region
+                               $.each(RegionCats, function(id, obj) {
+                                       // If that region has categories in RegionCats
+                                       if (RegionCats[id]) {
+                                               // Add each of those categories
+                                               $.each(RegionCats[id], function(i, j) {
+                                                       if (!usedValues[i]) {
+                                                               usedValues[i] = true;
+                                                               cats[mainCats[i]] = i;
+                                                               options.push(mainCats[i]);
+                                                       }
+                                               });
+                                       }
+                               });
+                               
+                       // Otherwise do this for the selected region
+                       } else {
+                               // For each category in this region 
+                               $.each(RegionCats[region], function(id, obj) {
+                                       // Add the category
+                                       cats[mainCats[id]] = id;
+                                       options.push(mainCats[id]);
+                               });
+                       }
+                       
+        } // if RegionCats
+               
+               options.sort();
+               if (options.length == 0) {
+                       catList.append('<option value="">-- Select --</option>');
+               }
+               $.each(options, function(idx, itm) {
+                       catList.append('<option value="'+cats[itm]+'">'+itm+'</option>');
+               });
+
+        //  If only one option is available, default to have it selected.
+               var catListOptions = catList.children('option');
+        if (catListOptions.length == 2) {
+            $("select[name='category_id'] option:last")
+                               .attr('selected', 'selected');
+            //  need to repopulate the sub cats, b/c we already know what
+            //  the selected cat is.
+            Search.populateMemberSubType(false);
+        }
+
+        //  Figure out wich category we need to set as the selected value
+        var category_id = window.location.GET['category_id'];
+        if (!isNaN(parseInt(category_id))) {
+            if (!Search.catSelected) {
+                Search.catSelected = true;
+                $("select[name='category_id'] option[value='"+category_id+"']").attr('selected', 'selected');
+            }
+        }
+    },
+
+    //  }}}
+    //  {{{ populateMemberSubType()
+
+    /**
+     * repopulate the sub category box based on the the value
+     * selected in the main category box
+     *
+     * if no region was selected, then get all sub categories available
+     * under the main category selected
+     *
+     * if a region was selected, then get all sub categories available in that
+     * region under the main category selected.
+     */
+    populateMemberSubType: function(event)
+    {
+        //var region = $("select[name='region_id'] option:selected").val();
+        if ($("[name='region_id']").is("select")) {
+            var region = $("select[name='region_id'] option:selected").val();
+        } else {
+            var region = $("input[name='region_id']").val();
+        }
+        var cat  = $("select[name='category_id'] option:selected").val();
+               var subList = $("select[name='sub_category_id']");
+
+               // Clear sub-category list
+        subList.empty()
+               .append('<option value="">-- Select --</option>');
+
+        var usedValues = [];
+               var options = [];
+               var subCats = [];
+               
+               // If we have RegionCats data (json)
+       if (window.RegionCats != undefined) {
+
+               // If no region is selected
+               if (region == undefined || region == '') {
+                       // for each region
+                       $.each(RegionCats, function(id, obj) {
+                               // If that region has categories in RegionCats
+                               if (RegionCats[id]) {
+                                       // If this category has any sub-cats
+                                       if(RegionCats[id][cat]) {
+                                               // Add each of those categories
+                                                       $.each(RegionCats[id][cat], function(i, j) {
+                                                               if (!usedValues[i]) {
+                                                                       usedValues[i] = true;
+                                                                       subCats[j] = i;
+                                                                       options.push(j);
+                                                               } 
+                                                       });
+                                       }
+                               }
+                               });
+               // Otherwise do this for the selected region
+               } else {
+                               // For each sub-category in selected category in region
+                               $.each(RegionCats[region][cat], function(i, j) {
+                                       if (!usedValues[i]) {
+                                               usedValues[i] = true;
+                                               subCats[j] = i;
+                                               options.push(j);
+                                       }
+                               });
+               }
+               
+       }
+    
+    
+               options.sort();
+               if (options.length == 0) {
+                       subList.append('<option value="">-- Select --</option>');
+               }
+               $.each(options, function(idx, itm) {
+                       subList.append('<option value="'+subCats[itm]+'">'+itm+'</option>');
+               });
+
+        //  If only one option is available, default to have it selected.
+               var subListOptions = subList.children('option');
+        if (subListOptions.length == 2) {
+            $("select[name='sub_category_id'] option:last-child")
+                .attr('selected', 'selected');
+        }
+
+        //  Figure out which sub category we need to set as the selected value
+        var sub_category_id = window.location.GET['sub_category_id'];
+        if (parseInt(sub_category_id) != 'NaN') {
+            if (!Search.subCatSelected && event != false) {
+                Search.subCatSelected = true;
+                $("select[name='sub_category_id'] option[value='"+sub_category_id+"']").attr('selected', 'selected');
+            }
+        }
+    }
+
+    //  }}}
+};
+
+$(document).ready(Search.init);
diff --git a/Toolkit/Members/libjs/edit-amenity.js b/Toolkit/Members/libjs/edit-amenity.js
new file mode 100644 (file)
index 0000000..92b1e7c
--- /dev/null
@@ -0,0 +1,22 @@
+/**
+ * When we are adding an amenity we want to focus on the first text field
+ * of the form.
+ */
+var MemberAmenity =
+{
+       init: function()
+       {
+               //      Find a member id in the search params, this will let us know we
+               //      are editing a member and then we don't want to focus on the
+               //      the first field.
+               var idRegExp = /.*(\?|&)id=\d+.*$/g;
+               if (!idRegExp.test(location.search)) {
+            $('form :input:text:visible:enabled:first').focus();
+               }
+        $("input[name='delete']").click(function(){
+            return confirm('This will delete the amenity and cannot be undone!\nAre you sure?');
+        });
+       }
+}
+
+$(document).ready(MemberAmenity.init);
diff --git a/Toolkit/Members/libjs/edit-category.js b/Toolkit/Members/libjs/edit-category.js
new file mode 100644 (file)
index 0000000..c88cc5a
--- /dev/null
@@ -0,0 +1,22 @@
+/**
+ * When we are adding a category we want to focus on the first text field
+ * of the form.
+ */
+var MemberCategory =
+{
+       init: function()
+       {
+               //      Find a member id in the search params, this will let us know we
+               //      are editing a member and then we don't want to focus on the
+               //      the first field.
+               var idRegExp = /.*(\?|&)id=\d+.*$/g;
+               if (!idRegExp.test(location.search)) {
+            $('form :input:text:visible:enabled:first').focus();
+               }
+        $("input[name='delete']").click(function(){
+            return confirm('This will delete the category and cannot be undone!\nAre you sure?');
+        });
+       }
+}
+
+$(document).ready(MemberCategory.init);
diff --git a/Toolkit/Members/libjs/edit-city.js b/Toolkit/Members/libjs/edit-city.js
new file mode 100644 (file)
index 0000000..5a6b731
--- /dev/null
@@ -0,0 +1,33 @@
+/**
+ * When adding a city, set the focus to the first text field in the form
+ * after the page loads.
+ */
+var MemberCity =
+{
+       init: function()
+       {
+               //      Find a city id in the search params, this will let us know we
+               //      are editing a member and then we don't want to focus on the
+               //      the first field.
+               var idRegExp = /.*(\?|&)id=\d+.*$/g;
+               if (!idRegExp.test(location.search)) {
+                       $(":text:visible:enabled:first").focus();
+               }
+
+               if ($('#description').is('textarea')) {
+            //  Only try to replace the textarea if the
+            //  CKEditor is compatible w/ the browser.
+            if (CKEDITOR.env.isCompatible) {
+                CKEDITOR.replace('description',
+                    {
+                        toolbar : 'LimitedToolset'
+                    });
+            }
+               }
+        $("input[name='delete']").click(function(){
+            return confirm('This will delete the city and cannot be undone!\nAre you sure?');
+        });
+       }
+}
+
+$(document).ready(MemberCity.init);
diff --git a/Toolkit/Members/libjs/edit-html-email.js b/Toolkit/Members/libjs/edit-html-email.js
new file mode 100644 (file)
index 0000000..a5e784f
--- /dev/null
@@ -0,0 +1,36 @@
+/**
+ * When adding a html newsletter, set the focus to the first text field in the form
+ * after the page loads.
+ */
+var HtmlEmail =
+{
+       init: function()
+       {
+               //      Find a newsletter id in the search params, this will let us know we
+               //      are editing a newsletter and then we don't want to focus on the
+               //      the first field.
+               var idRegExp = /.*(\?|&)id=\d+.*$/g;
+               if (!idRegExp.test(location.search)) {
+                       $(":text:visible:enabled:first").focus();
+               }
+
+               if ($('#response').is('textarea')) {
+            //  Only try to replace the textarea if the 
+            //  CKEditor is compatible w/ the browser.
+            if (CKEDITOR.env.isCompatible) {
+                CKEDITOR.replace('response',
+                    {
+                        toolbar : 'Default',
+                        width : 600,
+                        height : 500,
+                        filebrowserImageBrowseUrl : '../Toolkit/CKImages/browser.php?folder=1',
+                        filebrowserImageUploadUrl : '../Toolkit/CKImages/controller.php?command=Upload',
+                                               filebrowserImageWindowWidth : '760',
+                                               filebrowserImageWindowHeight : '500'
+                    });
+            }
+               }
+       }
+}
+
+$(document).ready(HtmlEmail.init);
diff --git a/Toolkit/Members/libjs/edit-member-amenities.js b/Toolkit/Members/libjs/edit-member-amenities.js
new file mode 100644 (file)
index 0000000..f696ac5
--- /dev/null
@@ -0,0 +1,21 @@
+var MemberAmenity =
+{
+       init: function()
+       {
+               $('label').each(function() {
+            $(this).hover(MemberAmenity.highlight, MemberAmenity.lowlight);
+               });
+       },
+
+       highlight: function(event)
+       {
+               $(this).addClass('amenityOn');
+       },
+
+       lowlight: function(event)
+       {
+               $(this).removeClass('amenityOn');
+       }
+}
+
+$(document).ready(MemberAmenity.init);
diff --git a/Toolkit/Members/libjs/edit-member-contacts.js b/Toolkit/Members/libjs/edit-member-contacts.js
new file mode 100644 (file)
index 0000000..8e6399b
--- /dev/null
@@ -0,0 +1,39 @@
+var MemberContacts=
+{
+       init: function()
+       {
+               $('.remove').click(MemberContacts.remove);
+               
+               $('.contactList').each(function() {
+            $(this).hover(MemberContacts.mouseOn, MemberContacts.mouseOff);
+            $(this).click(MemberContacts.goToContact);
+               });
+       },
+
+       mouseOn: function(event)
+       {
+               $(this).addClass('contactListOn');
+       },
+
+       mouseOff: function(event)
+       {
+               $(this).removeClass('contactListOn');
+       },
+
+       goToContact: function(event)
+       {
+               document.location.href = $(this).find('h3 a').attr('href');
+               return false;
+       },
+
+       remove: function(event)
+       {
+               if (!confirm('Are you sure you wish to remove this contact?\nThis is not reversible!')) {
+            event.preventDefault();
+               }
+        //  Don't propagate so we won't go into the member record
+        event.stopImmediatePropagation();
+       }
+}
+
+$(document).ready(MemberContacts.init);
diff --git a/Toolkit/Members/libjs/edit-member-files.js b/Toolkit/Members/libjs/edit-member-files.js
new file mode 100644 (file)
index 0000000..87086e5
--- /dev/null
@@ -0,0 +1,18 @@
+var MemberFiles =
+{
+       init: function()
+       {
+               $('.fileDelete').each(function() {
+            $(this).click(MemberFiles.remove);
+               });
+       },
+
+       remove: function(event)
+       {
+               if (!confirm('Are you sure you wish to remove this file?\nThis is not reversible!')) {
+            event.preventDefault();
+               }
+       }
+}
+
+$(document).ready(MemberFiles.init);
diff --git a/Toolkit/Members/libjs/edit-member-packages.js b/Toolkit/Members/libjs/edit-member-packages.js
new file mode 100755 (executable)
index 0000000..79ac5bd
--- /dev/null
@@ -0,0 +1,20 @@
+var MemberPackages =
+{
+    init: function()
+    {
+        if (CKEDITOR.env.isCompatible) {
+            $("textarea.ckeditor").each(function() {
+                CKEDITOR.replace(
+                    $(this).attr('id'),
+                    {
+                        toolbar : 'LimitedToolset',
+                        width : 300,
+                        height : 200
+                    }
+                );
+            });
+        }
+    }
+}
+
+$(document).ready(MemberPackages.init);
diff --git a/Toolkit/Members/libjs/edit-member-photos.js b/Toolkit/Members/libjs/edit-member-photos.js
new file mode 100755 (executable)
index 0000000..ae73b11
--- /dev/null
@@ -0,0 +1,58 @@
+var MemberPhotos =
+{
+       init: function()
+       {
+               $('.photoDelete').click(MemberPhotos.remove);
+
+               //      Make just the photos uploaded have an additional class
+               //      on them, so we can make them movable later.
+               $(".photoItem:not(.photoUploadForm)").addClass('movable');
+
+               //      Update the text on the update button.
+               //      Since they have javascript on, the only update
+               //      they will actually do on the form is the caption.
+               //      They won't be updating the position through the select list.
+               $(":submit[name='update']").val('Update Caption Text');
+
+               //      Remove all the positioning select elemnts
+               //      so they can't override any new position adjustments
+               //      done through drag and drop.
+               $('.position').remove();
+
+               //      Add a message for users to tell them how to 
+               //      reposition thier images.
+               $('#photoList').before('<div id="pos-info">Click and drag images to reorder positions. Image positions are saved when the fields flash yellow</div>');
+
+               $('div.container').sortable({
+                       placeholder: 'ui-state-highlight',
+                       handle: 'img.thumb',
+                       opacity: 0.6,
+                       revert: true,
+                       scroll: true,
+                       tolerance: 'pointer',
+                       zIndex: 5,
+                       axis: 'y',
+                       cursor: 'n-resize',
+                       update: MemberPhotos.updatePos
+               });
+               $('img.thumb').disableSelection();
+       },
+
+       updatePos: function(event, ui)
+       {
+               var url = '../member-save-photos/?' + $(this).sortable('serialize');
+               //      Update the photo positions through AJAX.
+               $.get(url, function(data) {
+                       $('.movable').effect('highlight', {}, 700);
+               });
+       },
+
+       remove: function(event)
+       {
+               if (!confirm('Are you sure you wish to remove this photo?\nThis is not reversible!')) {
+                       event.preventDefault();
+               }
+       }
+};
+
+$(document).ready(MemberPhotos.init);
diff --git a/Toolkit/Members/libjs/edit-member.js b/Toolkit/Members/libjs/edit-member.js
new file mode 100755 (executable)
index 0000000..2a0ff43
--- /dev/null
@@ -0,0 +1,111 @@
+var Member =
+{
+    categorySelectList: null,
+
+    init: function()
+    {
+        //    Find a member id in the search params, this will let us know we
+        //    are editing a member and then we don't want to focus on the
+        //    the first field.
+        var idRegExp = /.*(\?|&)id=\d+.*$/g;
+        if (!idRegExp.test(location.search)) {
+            $('form :input:text:visible:enabled:first').focus();
+        }
+
+//        var meals = ['breakfast', 'brunch', 'lunch', 'dinner'];
+//        jQuery.each(meals, function(i, j) {
+//            $('#edit_member input[name='+j+']:checkbox').click(Member.toggleMealView);
+//            $('#edit_member input[name='+j+']:checkbox:not(:checked)').parents('tr').next().toggle().next().toggle();
+//        });
+
+        $('.tooltip').cluetip({
+            //  character to split title text from body text
+            splitTitle: '|',
+            //  show arrow on cluetip
+            arrows: true,
+            //  hide cluetip on mouseout
+            mouseOutClose: true,
+            //  track mouse movements
+            tracking: true,
+            //  keep z index above all other elements on the page
+            cluezIndex: 10000,
+            //  effect to open cluetip with
+            fx: {open: 'fadeIn'}
+        });
+
+        if (Member.categorySelectList = $('#categories')) {
+            Member.categorySelectList.after('<input type="button" id="addCat" value="Add">');
+            $('#addCat').click(Member.addCategory);
+        }
+
+        if ($('#description').is('textarea')) {
+            //  Only try to replace the textarea if the
+            //  CKEditor is compatible w/ the browser.
+            if (CKEDITOR.env.isCompatible) {
+                CKEDITOR.replace('description',
+                    {
+                        toolbar : 'LimitedToolset'
+                    });
+            }
+        }
+        $("#edit_member").submit(function(){
+            var member_cats = Member.requireMemberCategory();
+            //alert(member_cats);
+            if (member_cats == 0) {
+                alert('You must supply at least one Member Category!');
+                return false;
+            } else {
+                return true;
+            }
+        });
+    },
+
+    addCategory: function(event)
+    {
+        //    Don't allow insertion of the -- Choose Category -- option.
+        if (Member.categorySelectList.get(0).selectedIndex == 0) {
+            return;
+        }
+        var catid = Member.categorySelectList.get(0).value;
+        $('#edit_member').append('<input type="hidden" id="'+catid +'catid" value="'+catid+'" name="member_cats[]">');
+
+        var index = Member.categorySelectList.get(0).selectedIndex;
+        var category = Member.categorySelectList.get(0).options[index].innerHTML;
+        var d = new Date();
+        var id = d.getTime()
+        var i = '<i><label class="remove" for="' + id + '"><input type="checkbox" ' +
+                'value="' + catid + '" id="' + id + '" name="removeCat[]">Remove</label>' +
+                category + '</i>';
+        $('#addCategory').after(i);
+        Member.categorySelectList.get(0).selectedIndex = 0;
+    },
+
+    removeCategory: function(event)
+    {
+        event.stop();
+        $('catid' + this.readAttribute('rel')).remove();
+        this.up().remove();
+    },
+
+    toggleMealView: function(event)
+    {
+        $(this).parents('tr').next().toggle().next().toggle();
+    },
+
+    requireMemberCategory: function(event)
+    {
+        var count = 0;
+        $('input[name="member_cats[]"][type="hidden"]').each(function(){
+            var val = $(this).val();
+            var val2 = $('input:checkbox[name="removeCat[]"]:checked[id="'+val+'"]').val();
+            if (val2 != undefined) {
+                //alert('removed!!!' + val2);
+            } else {
+                ++count;
+            }
+        });
+        return count;
+    }
+};
+
+$(document).ready(Member.init);
diff --git a/Toolkit/Members/libjs/edit-region.js b/Toolkit/Members/libjs/edit-region.js
new file mode 100644 (file)
index 0000000..7ffa54a
--- /dev/null
@@ -0,0 +1,22 @@
+/**
+ * When we are adding an region we want to focus on the first text field
+ * of the form.
+ */
+var MemberRegion =
+{
+       init: function()
+       {
+               //      Find a member id in the search params, this will let us know we
+               //      are editing a member and then we don't want to focus on the
+               //      the first field.
+               var idRegExp = /.*(\?|&)id=\d+.*$/g;
+               if (!idRegExp.test(location.search)) {
+            $('form :input:text:visible:enabled:first').focus();
+               }
+        $("input[name='delete']").click(function(){
+            return confirm('This will delete the region and cannot be undone!\nAre you sure?');
+        });
+       }
+}
+
+$(document).ready(MemberRegion.init);
diff --git a/Toolkit/Members/libjs/google-map.js b/Toolkit/Members/libjs/google-map.js
new file mode 100644 (file)
index 0000000..f6299e6
--- /dev/null
@@ -0,0 +1,139 @@
+var Map = {
+    _map: null,
+    _latLngBounds: null,
+    _infoWindow: null,
+    arrMarkers: [],
+
+    init: function()
+    {
+        var canvas = document.getElementById('map-canvas');
+        var myOptions = {
+            zoom: 13,
+            mapTypeControl: true,
+            mapTypeControlOptions: {style: google.maps.MapTypeControlStyle.DROPDOWN_MENU},
+            navigationControl: true,
+            navigationControlOptions: {style: google.maps.NavigationControlStyle.SMALL},
+            mapTypeId: google.maps.MapTypeId.ROADMAP
+        }
+
+        Map._map = new google.maps.Map(canvas, myOptions);
+        Map._latLngBounds = new google.maps.LatLngBounds();
+        Map._infoWindow = new google.maps.InfoWindow;
+
+        $.get("member-db-google-map/"+location.search, Map._loadData, 'xml');
+    },
+
+    _loadData: function(data)
+    {
+        var markers = data.documentElement.getElementsByTagName("marker");
+        for (i = 0; i < markers.length; i++) {
+            var name = markers[i].getAttribute('member_name');
+            var street = markers[i].getAttribute('street');
+            var city = markers[i].getAttribute('city_name');
+            var state = markers[i].getAttribute('state_abb');
+            var zip = markers[i].getAttribute('zip');
+            var lat = markers[i].getAttribute('lat');
+            var lng = markers[i].getAttribute('lng');
+            var phone = markers[i].getAttribute('phone');
+            var clickThru = markers[i].getAttribute('url');
+            var website = markers[i].getAttribute('website');
+            var logoPath = markers[i].getAttribute('logoPath');
+            var logo = markers[i].getAttribute('logo');
+            var logoWidth = markers[i].getAttribute('logoWidth');
+            var logoHeight = markers[i].getAttribute('logoHeight');
+            var hasTripPlanner = markers[i].getAttribute('hasTripPlanner');
+            var addToPlannerUrl = markers[i].getAttribute('addToPlannerUrl');
+            var viewPlannerUrl = markers[i].getAttribute('viewPlannerUrl');
+            var plannerText = markers[i].getAttribute('plannerText');
+            var moreInfoUrl = markers[i].getAttribute('moreInfoUrl');
+            var iconUrl = markers[i].getAttribute('iconUrl');
+            var zIndex = markers[i].getAttribute('zIndex');
+
+            var point = new google.maps.LatLng(
+                parseFloat(lat),
+                parseFloat(lng)
+            );
+            Map._latLngBounds.extend(point);
+
+            var html = '<table><tbody><tr>';
+            html += '<td><b>' + name + '</b><br>' +
+                        street + '<br>' + city + ', ' + state + ' ' + zip;
+
+            if (phone != '') {
+                html += '<br>' + phone;
+            }
+            if (website != '') {
+                html += '<br>' + '<a target="_blank" href="'+clickThru+'">'+website+'</a>';
+            }
+
+            html += '</td>';
+            html += '</tr></tbody></table>';
+
+            html += '<span class="infoWindow moreInfo"><a href="'+moreInfoUrl+'">More Info</a></span>';
+            html += '<span class="infoWindow divider"> - </span>';
+            if (addToPlannerUrl != '' && hasTripPlanner) {
+                html += '<span class="infoWindow addToPlanner">' +
+                            '<a href="'+addToPlannerUrl+'">'+plannerText+'</a>' +
+                            '<a style="display: none;" href="'+viewPlannerUrl+'">View Planner</a>' +
+                        '</span>';
+                html += '<span class="infoWindow divider"> - </span>';
+            }
+
+            html += '<span class="infoWindow directions"><a target="_blank" href="http://maps.google.com/maps?daddr='+name+'@'+lat+','+lng+'">Get Directions</a></span>';
+            if (iconUrl && typeof(glm_catid) != 'undefined') {
+                var marker = new google.maps.Marker({
+                    title: name,
+                    map: Map._map,
+                    position: point,
+                    icon: iconUrl,
+                    zIndex: -zIndex
+                });
+            } else {
+                var marker = new google.maps.Marker({
+                    title: name,
+                    map: Map._map,
+                    position: point
+                });
+            }
+            Map.arrMarkers.push(marker);
+            Map._bindInfoWindow(marker, Map._map, Map._infoWindow, html);
+        }
+
+        Map._map.setCenter(
+            Map._latLngBounds.getCenter()
+        );
+        Map._map.fitBounds(Map._latLngBounds);
+
+        if (typeof glm_searchMapIconActive !== "undefined" && glm_searchMapIconActive) {
+            $(".map-link").click(function (){
+                var linkId = $(this).attr("rel");
+                google.maps.event.trigger(Map.arrMarkers[linkId], "click");
+                myAnchor = '#map-canvas';
+                window.location = String(window.location).replace(/\#.*$/, "") + myAnchor;
+                Map._map.setZoom(14);
+            });
+        }
+    },
+
+    _bindInfoWindow: function(marker, map, infoWindow, html)
+    {
+        google.maps.event.addListener(marker, 'click', function() {
+            infoWindow.setContent(html);
+            infoWindow.open(map, marker);
+
+            $('.addToPlanner a:first').click(function(event) {
+                if ($(this).text() == 'Add To Planner') {
+                    event.preventDefault();
+
+                    $.get($(this).attr('href'), function(data, textstatus) {
+                        $(".trip-list-count").html(data);
+                        $('.addToPlanner a').toggle();
+                    });
+                    return false;
+                }
+            });
+        });
+    }
+};
+
+$(document).ready(Map.init);
diff --git a/Toolkit/Members/libjs/list-categories.js b/Toolkit/Members/libjs/list-categories.js
new file mode 100644 (file)
index 0000000..97e3bb8
--- /dev/null
@@ -0,0 +1,50 @@
+var Tree =
+{
+       init: function()
+       {
+               Tree.insertGlobalFunctions();
+
+               tree1 = new tree_component();
+               tree1.init($("#categoryTree"), {
+                       cookies : {
+                               prefix : "tree1",
+                               opts : { path : '/' }
+                       },
+                       ui : {
+                               animation : 500
+                       }
+               });
+
+               $('#categoryTree a').bind('click', Tree.editNode);
+       },
+
+       insertGlobalFunctions: function()
+       {
+               $('#categoryTree').before('<div class="treeOperators">' +
+                       '<button class="expandAll">Expand All</button>' +
+                       '<button class="collapseAll">Collapse All</button>' +
+                       '</div>');
+
+               $('.expandAll').bind('click', Tree.expandAll);
+               $('.collapseAll').bind('click', Tree.collapseAll);
+       },
+
+       expandAll: function(event)
+       {
+               $('li.closed').toggleClass('open');
+               $('li.closed').toggleClass('closed');
+       },
+
+       collapseAll: function (event)
+       {
+               $('li.open').toggleClass('closed');
+               $('li.open').toggleClass('open');
+       },
+
+       editNode: function(event)
+       {
+               location.href = $(this).attr('href');
+       }
+};
+
+$(document).ready(Tree.init);
diff --git a/Toolkit/Members/libjs/member-list.js b/Toolkit/Members/libjs/member-list.js
new file mode 100644 (file)
index 0000000..01dd2f9
--- /dev/null
@@ -0,0 +1,63 @@
+var MemberList =
+{
+    baseUrl: null,
+
+    init: function()
+    {
+        $('.searchResult').each(function() {
+            $(this).hover(MemberList.mouseOn, MemberList.mouseOff);
+            $(this).click(MemberList.goToMember);
+        });
+
+        $('.remove').each(function() {
+            $(this).click(MemberList.remove);
+        });
+
+        if (   $('#advanced-record-search').is('form')
+            && $('.searchResult').length > 0
+        ) {
+            $('#advanced-record-search')
+                .hide()
+                .after('<div id="advanced-search-refine">Refine your search</div>');
+
+            $('#advanced-search-refine').click(MemberList.toggleAdvancedSearch);
+        }
+    },
+
+    toggleAdvancedSearch: function(event)
+    {
+        if ($(this).text() == 'Refine your search') {
+            $(this).text('Click here to hide');
+        } else {
+            $(this).text('Refine your search');
+        }
+        $('#advanced-record-search').slideToggle('slow');
+    },
+
+    mouseOn: function(event)
+    {
+        $(this).addClass('searchResultOn');
+    },
+
+    mouseOff: function(event)
+    {
+        $(this).removeClass('searchResultOn');
+    },
+
+    goToMember: function(event)
+    {
+        document.location.href = $(this).find('h3 a').attr('href');
+        return false;
+    },
+
+    remove: function(event)
+    {
+        if (!confirm('Are you sure you wish to remove this business?\nThis is not reversible!')) {
+            event.preventDefault();
+        }
+        //  Don't propagate so we won't go into the member record
+        event.stopImmediatePropagation();
+    }
+};
+
+$(document).ready(MemberList.init);
diff --git a/Toolkit/Members/libjs/member-pending-list.js b/Toolkit/Members/libjs/member-pending-list.js
new file mode 100644 (file)
index 0000000..204260a
--- /dev/null
@@ -0,0 +1,28 @@
+var MemberPendingList =
+{
+    init: function()
+    {
+               $('.searchResult').each(function() {
+                       $(this).hover(MemberPendingList.mouseOn, MemberPendingList.mouseOff);
+                       $(this).click(MemberPendingList.goToMember);
+               });
+    },
+
+       mouseOn: function(event)
+       {
+               $(this).addClass('searchResultOn');
+       },
+
+       mouseOff: function(event)
+       {
+               $(this).removeClass('searchResultOn');
+       },
+
+       goToMember: function(event)
+       {
+               document.location.href = $(this).find('h3 a').attr('href');
+               return false;
+       }
+}
+
+$(document).ready(MemberPendingList.init);
diff --git a/Toolkit/Members/libjs/new-member-sign-up.js b/Toolkit/Members/libjs/new-member-sign-up.js
new file mode 100755 (executable)
index 0000000..f8d4a96
--- /dev/null
@@ -0,0 +1,10 @@
+var NewMember= 
+{
+       init: function()
+       {
+        $('#charcount').html('<span>1000</span>');
+        $('textarea').textlimit('#charcount span', 1000);
+       }
+};
+
+$(document).ready(NewMember.init);
diff --git a/Toolkit/Members/libjs/travel-list.js b/Toolkit/Members/libjs/travel-list.js
new file mode 100644 (file)
index 0000000..98e50d8
--- /dev/null
@@ -0,0 +1,40 @@
+var TravelList =
+{
+    //  {{{ init()
+    
+       init: function()
+       {
+         $("#glmLoad").bind("ajaxSend", function(){
+            $(this).show();
+         }).bind("ajaxComplete", function(){
+             $(this).hide();
+         });
+        $(".list-add-link").click(function(){
+            var member_id = $(this).attr('rel');
+            TravelList.addToList(member_id);
+            return false;
+        });
+       },
+
+    //  }}}
+    // {{{ addToList()
+    addToList: function (member_id){
+        $.ajax({
+            type: "GET",
+            url: glm_base_url + "trip-planner-list/" + member_id + "/" + glm_catid + "/",
+            success: function(msg) {
+                var viewLink = '<a href="' + glm_base_url + 'index.php?catid=' + glm_trip_planner_id + '" class="list-view-link" rel="'+member_id+'">';
+                viewLink += '<img title="View" src="' + glm_base_url + 'assets/viewTravelPlanner.gif" />';
+                viewLink += '</a>';
+                $("#add-"+member_id).replaceWith(viewLink);
+                TravelList.updateList(msg);
+            }
+        });
+    },
+    updateList: function (listCount){
+        $(".trip-list-count").html(listCount);
+    }
+    // }}}
+};
+
+$(document).ready(TravelList.init);
diff --git a/Toolkit/Members/libjs/trip-planner-map.js b/Toolkit/Members/libjs/trip-planner-map.js
new file mode 100644 (file)
index 0000000..8ad68c3
--- /dev/null
@@ -0,0 +1,105 @@
+var TPM = {
+       _map: null,
+       _latLngBounds: null,
+       _infoWindow: null,
+
+       init: function()
+       {
+               var canvas = document.getElementById('map-canvas');
+               var myOptions = {
+                       zoom: 10,
+                       center: new google.maps.LatLng(45.374893, -84.958404),
+                       mapTypeId: google.maps.MapTypeId.ROADMAP
+               }
+
+               TPM._map = new google.maps.Map(canvas, myOptions);
+               TPM._latLngBounds = new google.maps.LatLngBounds();
+               TPM._infoWindow = new google.maps.InfoWindow;
+
+               $.get('../../../trip-planner-members/', TPM._loadData, 'xml');
+       },
+
+       _loadData: function(data)
+       {
+               var markers = data.documentElement.getElementsByTagName('marker');
+               for (i = 0; i < markers.length; i++) {
+                       var name = markers[i].getAttribute('member_name');
+                       var url_name = markers[i].getAttribute('url_member_name');
+                       var street = markers[i].getAttribute('street');
+                       var city = markers[i].getAttribute('city_name');
+                       var state = markers[i].getAttribute('state_abb');
+                       var zip = markers[i].getAttribute('zip');
+                       var lat = markers[i].getAttribute('lat');
+                       var lng = markers[i].getAttribute('lng');
+                       var phone = markers[i].getAttribute('phone');
+                       var clickThru = markers[i].getAttribute('url');
+                       var website = markers[i].getAttribute('website');
+                       var logoPath = markers[i].getAttribute('logoPath');
+                       var logo = markers[i].getAttribute('logo');
+                       var logoWidth = markers[i].getAttribute('logoWidth');
+                       var logoHeight = markers[i].getAttribute('logoHeight');
+                       var moreInfoUrl = markers[i].getAttribute('moreInfoUrl');
+
+                       var point = new google.maps.LatLng(
+                               parseFloat(lat),
+                               parseFloat(lng)
+                       );
+                       TPM._latLngBounds.extend(point);
+
+                       var html = '<table><tbody><tr>';
+                       html += '<td><b>' + name + '</b><br>' +
+                                               street + '<br>' + city + ', ' + state + ' ' + zip;
+
+                       if (phone != '') {
+                               html += '<br>' + phone;
+                       }
+                       if (website != '') {
+                               html += '<br>' + '<a target="_blank" href="'+clickThru+'">'+website+'</a>';
+                       }
+
+                       html += '</td>';
+
+/*
+Commented out b/c the image server will take to long
+to generate the image dimensions for many members.
+
+this causes the page load to slow down to much
+if you uncomment this be sure to uncomment the command
+to get the image dimensions in /member-db-google-map/
+                       if (logo != '') {
+                               var img = '<img src="'+logoPath+logo+'" width="'+logoWidth+'" height="'+logoHeight+'">';
+                               html += '<td>'+img+'</td>';
+                       }
+*/
+                       html += '</tr></tbody></table>';
+                       
+//                     html += '<span class="infoWindow moreInfo"><a target="_parent" href="'+moreInfoUrl+'">More Info</a></span>';
+//                     html += '<span class="infoWindow divider"> - </span>';
+                       
+                       html += '<a target="_blank" href="http://maps.google.com/maps?daddr='+url_name+'@'+lat+','+lng+'">Get Directions</a>';
+                       var marker = new google.maps.Marker({
+                               map: TPM._map,
+                               position: point,
+                               title: name
+                       });
+
+                       TPM._bindInfoWindow(marker, TPM._map, TPM._infoWindow, html);
+               }
+
+               TPM._map.setCenter(
+                       TPM._latLngBounds.getCenter()
+               );
+
+               TPM._map.fitBounds(TPM._latLngBounds);
+       },
+
+       _bindInfoWindow: function(marker, map, infoWindow, html)
+       {
+               google.maps.event.addListener(marker, 'click', function() {
+                       infoWindow.setContent(html);
+                       infoWindow.open(map, marker);
+               });
+       }
+};
+
+$(document).ready(TPM.init);
diff --git a/Toolkit/Members/memberClickThru.php b/Toolkit/Members/memberClickThru.php
new file mode 100644 (file)
index 0000000..dfabd8d
--- /dev/null
@@ -0,0 +1,30 @@
+<?php
+
+/**
+ * tracks clicks for url links from members
+ *
+ * PHP versions 4 and 5
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2009 Gaslight Media
+ * @license   Gaslight Media
+ * @version   CVS: $Id: memberClickThru.php,v 1.5 2010/05/15 16:36:12 jamie Exp $
+ * @link      <>
+ */
+
+$includeFunctions = false;
+/**
+ * Description for require_once
+ */
+require_once '../../setup.phtml';
+$memberId = filter_input(INPUT_GET, 'member_id', FILTER_VALIDATE_INT);
+if ($memberId) {
+    $exposure = new Toolkit_Members_Exposure($memberId, 'click');
+       $exposure->runUpdate();
+}
+if ($_GET['href']) {
+    $href = str_replace("http://", "", $_GET['href']);
+    header("Location: http://" . $href);
+}
\ No newline at end of file
diff --git a/Toolkit/Members/memberDBGoogleAreaMap.php b/Toolkit/Members/memberDBGoogleAreaMap.php
new file mode 100644 (file)
index 0000000..b352ff4
--- /dev/null
@@ -0,0 +1,256 @@
+<?php
+/**
+ * memberDBGoogleAreaMap.phgp
+ * 
+ * PHP Version 5.2
+ * 
+ * @category  Toolkit
+ * @package   Members
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @version   SVN: $id$
+ * @link      <>
+ */
+header("Content-type: text/xml");
+require_once '../../setup.phtml';
+
+HTTP_Session2::useCookies(false);
+HTTP_Session2::start();
+
+$dbh = Toolkit_Database::getInstance();
+
+$conf = new Config;
+$root =& $conf->parseConfig(
+       BASE . 'Toolkit/Members/config.ini',
+       'IniFile'
+);
+$radius =& $root
+    ->getItem('section', 'map')
+    ->getItem('directive', 'radius')
+    ->getContent();
+$memberCategories =& $root
+    ->getItem('section', 'map')
+    ->getItem('directive', 'category')
+    ->getContent();
+$latBounds =& $root
+    ->getItem('section', 'map')
+    ->getItem('directive', 'latBounds')
+    ->getContent();
+$lonBounds =& $root
+    ->getItem('section', 'map')
+    ->getItem('directive', 'lonBounds')
+    ->getContent();
+// setup category/subcategory array
+$sql = "
+SELECT category_id
+  FROM category
+ WHERE category_id IN (
+       SELECT category_id
+         FROM member_category
+        WHERE member_id = :member_id)
+   AND category_id IN (".implode(',', $memberCategories).")
+ ORDER BY parent_id,name";
+$getMemberCats = $dbh->prepare($sql);
+$catSubCatArray = array();
+$hexColorArray = array(
+    14 => 'E52222', // Adventure Sports
+    13 => 'FFFF7A', // Arts & Culture
+    6 => '258E25', // Culinary Travel
+    7 => '2C2CE0', // Family Fun
+    8 => '932493', // Girlfriend Getaways
+    9 => 'ED8115', // History
+    9 => '8E3B3B', // Mancations
+    10 => 'ffffff', // NightLife
+    11 => 'eeeeee', // Outdoor Recreation
+    12 => 'cccccc', // Pet Friendly
+    1 => 'dddddd', // Places to Stay
+    40 => 'bbbbbb' // Travel Services
+);
+$sql = "
+SELECT category_id,name
+  FROM category
+ ORDER BY category_id";
+$catStmt = $dbh->query($sql);
+while ($catRow = $catStmt->fetch()) {
+    $sql = "
+    SELECT category_id,name
+      FROM category
+     WHERE parent_id = :parent";
+    $subCatStmt = $dbh->prepare($sql);
+    $subCatStmt->bindParam(':parent', $catRow['category_id']);
+    $subCatStmt->execute();
+    while ($subCatRow = $subCatStmt->fetch()) {
+        $catSubCatArray[$subCatRow['category_id']] = $catRow['category_id'];
+    }
+}
+$is = new Toolkit_Image_Server();
+
+$params = array();
+$xmlEncoder  = new Toolkit_Members_MapArea();
+//$searchList  = new Toolkit_Members_SearchList($dbh, null, null, null, false);
+if (is_numeric($_GET['city_id'])) {
+    try {
+        $sql = "
+            SELECT lat, lon
+              FROM city
+             WHERE city_id = :cid";
+        $stmt = $dbh->prepare($sql);
+        $stmt->bindParam(':cid', $_GET['city_id'], PDO::PARAM_INT);
+        $stmt->execute();
+        $row = $stmt->fetch(PDO::FETCH_ASSOC);
+    } catch (PDOException $e) {
+        return Toolkit_Common::handleError($e);
+    }
+    list($lat, $lng) = array_values($row);
+    $distanceQuery = null;
+    try {
+        $foo = "
+            (pow(sin(((m.lat * pi()/180.0) - ($lat * pi()/180.0)) / 2.0),
+            2) + cos(($lat * pi() / 180.0)) * cos((m.lat * pi() / 180.0)) *
+            pow(sin(((m.lon * pi() / 180.0) - ($lng * pi() / 180.0)) / 2.0), 2))";
+
+        $distanceQuery = "ceil(3956 * 2 * atan2(sqrt($foo), sqrt(1 - ($foo))))";
+    } catch (PDOException $e) {
+        return Toolkit_Common::handleError($e);
+    }
+    $latDist = ($radius * .87) / 60;
+    $lngFactor = cos($lat * (2 * pi() / 360));
+    $lngDist = (($radius / $lngFactor) * .87) / 60;
+    $latMin = round($lat - $latDist, 6);
+    $latMax = round($lat + $latDist, 6);
+    $lngMin = round($lng - $lngDist, 6);
+    $lngMax = round($lng + $lngDist, 6);
+    $params[] = "$distanceQuery < {$radius}";
+    $distanceQuery .= ' AS distance,';
+} else {
+    $distanceQuery = null;
+}
+if (is_numeric($_GET['region'])) {
+    $params[] = "m.city_id IN (
+        SELECT city_id
+          FROM city
+         WHERE region_id = {$_GET['region']})";
+    $params[] = "m.region = {$_GET['region']}";
+}
+// we need to determine if the page has a search form on it
+// and if the search form has been submitted
+$sql = "
+SELECT m.*,$distanceQuery c.city_name, s.state_name AS state,
+       s.state_abb AS state_abbr, ma.reservation_id,
+       ma.num_rooms, ma.year_round
+  FROM member m NATURAL FULL JOIN member_accommodations ma
+       FULL JOIN state s USING (state_id) FULL JOIN city c USING (city_id)";
+
+$params[] = 'new_member != true';
+$params[] = 'active = true';
+if (!empty($params)) {
+    $sql .= "WHERE " . implode(" AND ", $params);
+}
+if (   empty($_GET['category'])
+    && !$_GET['city_id']
+    && !$_GET['region']) {
+    return false;
+} else if ($_GET['category']) {
+    $sqlCats = array();
+    $sql .= " AND m.member_id IN
+            (SELECT member_id
+               FROM member_category
+              WHERE category_id IN
+                (".implode(",",$_GET['category']).")
+                   )";
+
+}
+// add restriction for lat lon
+$sql .= " AND m.lat BETWEEN {$latBounds} AND m.lon BETWEEN {$lonBounds}";
+// need to add sort
+$sql .= " ORDER BY m.member_name ASC";
+try {
+       $members = array();
+    $url = MEDIA_BASE_URL . "trip-planner-list/%d/%d/";
+       $urlFormat
+               = MEDIA_BASE_URL . 'Toolkit/Members/memberClickThru.php?member_id=%s&href=%s';
+       $addToPlannerFormat
+               = $url;
+       $viewPlannerFormat
+               = MEDIA_BASE_URL . 'index.php?catid=%s';
+       $moreInfoFormat
+               = MEDIA_BASE_URL . 'member-profile/%s/%s/';
+    $count = 1;
+    $totalCatCounter = array();
+       foreach ($dbh->query($sql, PDO::FETCH_ASSOC) as $row) {
+               if (!is_null($row['lat']) && !is_null($row['lon'])) {
+                       $websiteUrl = sprintf(
+                               $urlFormat,
+                               $row['member_id'],
+                               urlencode(str_replace('http://', '', $row['url']))
+                       );
+                       $viewPlannerUrl = sprintf(
+                               $viewPlannerFormat,
+                               MEMBER_SESSION_PAGE
+                       );
+                       if ($_SESSION['wish_list'][$row['member_id']]) {
+                               $plannerText = 'View Planner';
+                $addToPlannerUrl = sprintf(
+                                       $viewPlannerFormat,
+                                       MEMBER_SESSION_PAGE
+                               );
+                       } else {
+                               $plannerText = 'Add To Planner';
+                               $addToPlannerUrl = sprintf(
+                    $addToPlannerFormat,
+                    $row['member_id'],
+                    MEMBER_MAP_PAGE
+                               );
+                       }
+                       $moreInfoUrl = sprintf(
+                               $moreInfoFormat,
+                               $_GET['catid'],
+                               $row['member_id']
+                       );
+            $searchResultNumbered =& $root
+                ->getItem('section', 'conf')
+                ->getItem('directive', 'searchResultNumbered')
+                ->getContent();
+
+            $totalCatCounter[] = $totalCats;
+            $iconUrl = MEDIA_BASE_URL . "areamap/62BBE8.png";
+            $moreInfoUrl
+                = (true)
+                ? sprintf(
+                        $moreInfoFormat,
+                        MEMBERS_GOOGLE_MAP_PAGE,
+                        $row['member_id']
+                    )
+                : '';
+                       $members[] = array(
+                               'member_id'       => $row['member_id'],
+                               'member_name'     => $row['member_name'],
+                               'lat'             => $row['lat'],
+                               'lng'             => $row['lon'],
+                               'street'          => $row['street'],
+                               'state_abb'       => $row['state_abbr'],
+                               'city_name'       => $row['city_name'],
+                               'zip'             => $row['zip'],
+                               'phone'           => $row['phone'],
+                               'url'             => $websiteUrl,
+                               'website'         => $row['url'],
+                               'logoPath'        => MEMBER_GOOGLE_MAP,
+                               'logo'            => $row['logo'],
+                               'logoWidth'       => $imgWidth,
+                               'logoHeight'      => $imgHeight,
+                               'hasTripPlanner'  => MEMBER_SESSION_LIST,
+                               'addToPlannerUrl' => $addToPlannerUrl,
+                               'viewPlannerUrl'  => $viewPlannerUrl,
+                               'plannerText'     => $plannerText,
+                               'moreInfoUrl'     => $moreInfoUrl,
+                'iconUrl'         => $iconUrl,
+                'zIndex'         => $count,
+                       );
+               }
+        ++$count;
+       }
+} catch (PDOException $e) {
+       Toolkit_Common::handleError($e);
+}
+echo $xmlEncoder->getMemberXML($members);
diff --git a/Toolkit/Members/memberDBGoogleMap.php b/Toolkit/Members/memberDBGoogleMap.php
new file mode 100644 (file)
index 0000000..dab973f
--- /dev/null
@@ -0,0 +1,148 @@
+<?php
+/**
+ * memberDBGoogleMap.php
+ *
+ * PHP version 5
+ *
+ * @category  Toolkit
+ * @package   Members
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2009 Gaslight Media
+ * @license   Gaslight Media
+ * @version   CVS: $Id$
+ * @link      <>
+ */
+
+header("Content-type: text/xml");
+require_once '../../setup.phtml';
+
+HTTP_Session2::useCookies(false);
+HTTP_Session2::start();
+
+$dbh = Toolkit_Database::getInstance();
+
+if (   defined("SHORT_URLS")
+    && SHORT_URLS
+    && isset($_GET['glmPage'])
+    && $_GET['glmPage']
+) {
+    $shortURL = new Toolkit_ShortURL(
+        Toolkit_Database::getInstance()
+    );
+    if (!$catid = $shortURL->getShortUrlId($_GET['glmPage'])) {
+        $catid = HOME_ID;
+    }
+    $_GET['catid'] = $catid;
+}
+$pageId = filter_var($_REQUEST['page_id'], FILTER_VALIDATE_INT);
+if ($pageId) {
+    $_GET['catid'] = $pageId;
+}
+$is = new Toolkit_Image_Server();
+$conf = new Config;
+$root =& $conf->parseConfig(
+    BASE . 'Toolkit/Members/config.ini',
+    'IniFile'
+);
+
+$xmlEncoder  = new Toolkit_Members_Map();
+$searchQuery = new Toolkit_Members_SearchQueryGenerator(true, $root);
+
+$sql = $searchQuery->getQuery($dbh);
+
+// need to add sort
+$sql .= " ORDER BY m.member_name ASC";
+
+try {
+    $members = array();
+    $urlFormat
+        = MEDIA_BASE_URL . 'Toolkit/Members/memberClickThru.php?member_id=%s&href=%s';
+    $addToPlannerFormat
+        = MEDIA_BASE_URL . 'trip-planner-wish-list/%s/%s/';
+    $viewPlannerFormat
+        = MEDIA_BASE_URL . 'index.php?catid=%s';
+    $moreInfoFormat
+        = MEDIA_BASE_URL . 'member-profile/%s/%s/';
+    $count = 1;
+    foreach ($dbh->query($sql, PDO::FETCH_ASSOC) as $row) {
+        if (!is_null($row['lat']) && !is_null($row['lon'])) {
+            $websiteUrl = sprintf(
+                $urlFormat,
+                $row['member_id'],
+                urlencode(str_replace('http://', '', $row['url']))
+            );
+            $viewPlannerUrl = sprintf(
+                $viewPlannerFormat,
+                MEMBER_SESSION_PAGE
+            );
+            if ($_SESSION['wish_list'][$row['member_id']]) {
+                $plannerText = 'View Planner';
+                $addToPlannerUrl = sprintf(
+                    $viewPlannerFormat,
+                    MEMBER_SESSION_PAGE
+                );
+            } else {
+                $plannerText = 'Add To Planner';
+                $addToPlannerUrl = sprintf(
+                    $addToPlannerFormat,
+                    $_GET['catid'],
+                    $row['member_id']
+                );
+            }
+            $memberName = htmlentities(strip_tags($row['member_name']));
+            $memberName = str_replace(' ', '-', $memberName);
+            $pattern = '/[\/#&?\'"]|amp;/';
+            $name = preg_replace(
+                $pattern,
+                '',
+                strip_tags(strtolower(trim($memberName)))
+            );
+            $moreInfoUrl
+                = MEDIA_BASE_URL
+                . "memberProfiles/"
+                . htmlspecialchars($name) . "-{$row['member_id']}.html";
+            if ($pageId = filter_var($_REQUEST['page_id'], FILTER_VALIDATE_INT)) {
+                $moreInfoUrl = MEDIA_BASE_URL
+                    . "members-only-area/?catid={$pageId}&member_id={$row['member_id']}"
+                    . "&page_id={$pageId}";
+            }
+            $searchResultNumbered =& $root
+                ->getItem('section', 'conf')
+                ->getItem('directive', 'searchResultNumbered')
+                ->getContent();
+            $iconUrl = ($searchResultNumbered == true)
+                ? MEDIA_BASE_URL
+                . "map/5680FC/{$count}.png"
+                : '';
+            $members[] = array(
+                'member_id'       => $row['member_id'],
+                'member_name'     => $row['member_name'],
+                'lat'             => $row['lat'],
+                'lng'             => $row['lon'],
+                'street'          => $row['street'],
+                'state_abb'       => $row['state_abbr'],
+                'city_name'       => $row['city_name'],
+                'zip'             => $row['zip'],
+                'phone'           => $row['phone'],
+                'url'             => $websiteUrl,
+                'website'         => $row['url'],
+                'logoPath'        => MEMBER_GOOGLE_MAP,
+                'logo'            => $row['logo'],
+                'logoWidth'       => $imgWidth,
+                'logoHeight'      => $imgHeight,
+                'hasTripPlanner'  => MEMBER_SESSION_LIST,
+                'addToPlannerUrl' => $addToPlannerUrl,
+                'viewPlannerUrl'  => $viewPlannerUrl,
+                'plannerText'     => $plannerText,
+                'moreInfoUrl'     => $moreInfoUrl,
+                'iconUrl'         => $iconUrl,
+                'zIndex'         => $count,
+            );
+        }
+        ++$count;
+    }
+} catch (PDOException $e) {
+    Toolkit_Common::handleError($e);
+}
+
+echo $xmlEncoder->getMemberXML($members);
diff --git a/Toolkit/Members/memberFileDownload.php b/Toolkit/Members/memberFileDownload.php
new file mode 100644 (file)
index 0000000..aa5046c
--- /dev/null
@@ -0,0 +1,33 @@
+<?php
+/**
+ * Handles Sending files downloads to the browser
+ *
+ * PHP versions 4 and 5
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @version   CVS: $Id: memberFileDownload.php,v 1.4 2010/05/15 16:36:01 jamie Exp $
+ * @link      http://demo.gaslightmedia.com
+ */
+$includeFunctions = false;
+
+/**
+ * Description for require_once
+ */
+require_once '../../setup.phtml';
+
+$download = new Toolkit_Members_FileDownload(
+    Toolkit_Database::getInstance()
+);
+$file = $download->getFile($_GET['mid'], $_GET['fid']);
+
+if (!PEAR::isError($file)) {
+    $download->sendFileToBrowser($file);
+} else {
+    header($_SERVER['SERVER_PROTOCOL'] . ' 404 Not Found');
+    return;
+}
+
diff --git a/Toolkit/Members/memberdb.css b/Toolkit/Members/memberdb.css
new file mode 100644 (file)
index 0000000..3ef3869
--- /dev/null
@@ -0,0 +1,410 @@
+/* 
+
+h1 {
+       font-size: 16px;
+       }       
+#content {
+       margin: 0px;
+}
+#subnavcontainer, #navcontainer,
+#top {
+       margin-right: 0px;
+}
+#wrapper {
+       width: 700px;
+       margin: 0 auto;
+       text-align: left;
+       background: white;
+       padding: 20px;
+       height: 1%;
+       overflow: hidden;
+       }
+Above is only for template , DISREGARD :) */   
+#advanced-search {
+       text-align: right;
+       cursor: pointer;
+       color: blue;
+}
+.req {
+       color: red;
+}
+#form-warning-top {
+       color: black;
+       font-size: 110%;
+       font-weight: bold;
+       margin: 10px;
+       padding: 7px;
+       border: 1px solid red;
+       background-color: #FFCCCC;
+}
+#form-success-top {
+       color: black;
+       font-size: 110%;
+       font-weight: bold;
+       margin: 10px;
+       padding: 7px;
+       border: 1px solid green;
+       background-color: #CCFFCC;
+}
+       
+img {
+       border: 0;
+       display: block;
+       }       
+/*   ---------------   */      
+/*     NAVIGATION      */
+/*   ---------------   */      
+#nav-detail {
+       margin-top: 12px;
+       height: 1%;
+       overflow: hidden;
+       clear: left;
+       }
+#nav-detail ul {
+  padding: 0px 0;
+       padding-bottom: 3px;
+  margin: 0;
+       margin-top: 10px;
+  border-bottom: 1px solid #666;
+  font-weight: bold;
+}
+#nav-detail ul li {
+  list-style: none;
+  margin: 0;
+  display: inline;
+}
+#nav-detail ul li a {
+  padding: 3px 0.5em;
+  margin-left: 3px;
+  border: 1px solid #666;
+  border-bottom: none;
+  background: #E6EFD1;
+  text-decoration: none;
+}
+#nav-detail ul li a:link { color: #666; }
+#nav-detail ul li a:visited { color: #666; }
+#nav-detail ul li a:hover {
+  color: #666;
+  background: #ccc;
+       background: #FFFBDF;
+  border-color: #666;
+}
+#nav-detail ul li a.current {
+  background: white;
+  border-bottom: 1px solid white;
+       color: #000;
+}
+
+/* Member Box */
+#member-info   {
+       margin: 0;
+       border: 1px solid #666;
+       border-top: 0;
+       padding: 10px;
+       position: relative;
+       height: 1%;
+       overflow: hidden;
+       clear: left;
+       }
+       
+#mRow1 {
+       float: left;
+       position: relative;
+       margin: 0 0 0 10px;
+       display: inline;
+       width: 380px;
+       }
+#mRow2 {
+       float: right;
+       position: relative;
+       }
+#mRow1 .text {
+       width: 180px;
+       }       
+#mRow2 .text {
+       width: 150px;
+       }               
+.form {
+       clear: left;
+       display: block;
+       position: relative;
+       margin: 0 0 1em 0;
+       padding: 0;
+       border: 0;
+       }
+/* narrower column */ 
+.formNarrow {
+       clear: right;
+       margin: 0 0 1em 0;
+       }       
+.form legend {
+       font-size: 1.2em;
+       font-weight: bold;
+       margin: 0;
+       padding: 0 0 0.4em 0;
+       color: #000;
+       }
+.form table,
+.form td {
+       border-collapse: collapse;
+       border: 1px solid #fff;
+       padding: 0;
+       background: #D6DFC3;
+       }       
+.form td {
+       padding: 4px 6px;
+       }
+.form label {
+       display: block;
+       }
+.labelcell {
+       text-align: right;
+       width: 120px;
+       }
+.formNarrow .labelcell {
+       width: 110px;
+       }       
+.fieldcell {
+       text-align: left;
+       width: 220px;
+       }
+.formNarrow .fieldcell {
+       width: 150px;
+       }
+       
+/* Multiple rows in one cell    */
+.fieldcell i {
+       display: block;
+       font-style: normal;
+       padding: 5px;
+       text-align: left;
+       background-color: #eee;
+       margin-bottom: 1px;
+       }
+.fieldcell i img {
+       margin-right: 6px;}
+       
+/* Small graphics */
+.remove {
+       float: right;
+       clear: right;}
+.add {
+       display: block;
+       /* float: right; */
+       display: block;
+       font-style: normal;
+       padding: 5px;
+       text-align: left;
+       background-color: #eee;
+       margin-bottom: 1px;
+       }
+.add img {float: left; margin-right: 10px;}    
+.info {
+       float: right;
+       }
+
+.priceFrom,
+.priceTo {
+       width: 4em;
+       }       
+.submit {display: block;}      
+
+/* Submit */
+.submitArea {
+       background: #D6DFC3;
+       text-align: center;
+       padding: 10px;
+       clear: both;
+       }
+.submitArea input {
+       margin: 0 auto;
+       display: block;
+       }
+
+       
+/*   ---------------   */      
+/*        PHOTOS       */
+/*   ---------------   */      
+.photoItem {
+       margin-top: 1em;
+       padding: 20px;
+       border: 1px solid #ccc;
+       height: 1%;
+       overflow: hidden;
+       position: relative;
+       background: #D6DFC3;
+       }
+.photoItem .thumb {
+       float: left;
+       position: relative;
+       margin-right: 20px;
+       }
+.photoItem i {
+       font-style: normal;
+       font-weight: bold;
+       display: block;
+       }
+.photoItem input {margin-top: 0.5em;}
+.photoItem input.text {
+       width: 400px;
+       display: block;
+       }       
+.photoItem .photoDelete {
+/*     display: block;
+       margin-top: 1em;
+       border: 1px solid #ccc;
+       background: #FFEFEF;
+       width: 130px;
+       padding: 3px;
+       color: #000;
+*/
+       position: absolute;
+       bottom: 20px;
+       right: 20px;    
+
+       }
+.photoDelete:hover {background: #EFD1D1;}      
+.photoItem .photoDelete img {
+       float: left;
+       margin-right: 6px;
+       }
+.photoOptions {float: left;}
+
+
+/*   ---------------   */      
+/*      PACKAGES       */
+/*   ---------------   */      
+
+.packageItem {
+       margin-top: 1em;
+       padding: 20px;
+       border: 1px solid #96A379;
+       height: 1%;
+       overflow: hidden;
+       position: relative;
+       background: #D6DFC3;
+       }
+
+.packageItem .thumb {
+       position: relative;
+       margin-bottom: 1em;
+       }
+.packageText {
+       float: left;
+       width: 350px;
+       padding-bottom: 1em;
+       }       
+.packageItem i {
+       font-style: normal;
+       font-weight: bold;
+       display: block;
+       padding-top: 1em;
+       
+       }
+.packageItem input {margin-top: 0.5em;}
+.packageItem input.text {
+       width: 200px;
+       display: block;
+       }
+.packageItem textarea {
+       width: 300px;
+       height: 150px;
+       }       
+.packageItem .packageDelete {
+       display: block;
+       margin-top: 1em;
+       border: 1px solid #ccc;
+       background: #FFEFEF;
+       width: 140px;
+       padding: 3px;
+       position: absolute;
+       bottom: 20px;
+       right: 20px;    
+       color: #000;
+       }
+.packageDelete:hover {background: #EFD1D1;}    
+.packageItem .packageDelete img {
+       float: left;
+       margin-right: 6px;
+       }
+.packageOptions {float: left;}
+
+
+
+/*   ---------------   */      
+/*      AMENITIES      */
+/*   ---------------   */      
+.amenityList {
+       list-style-type: none;
+       float: left;
+       position: relative;
+       margin-right: 50px;
+       zoom: 1;
+       width: 200px;
+       }
+.amenityList input {
+       /* No Luck, see http://meyerweb.com/eric/thoughts/2007/05/15/formal-weirdness/ */
+}
+.amenityList li {
+       vertical-align: middle;
+       height: 1%;
+       overflow: hidden;
+       font-size: 1.2em;
+       }
+.amenityList label {
+  padding: 4px;
+  display: block;
+       background: #D6DFC3;
+       margin-bottom: 1px;
+} 
+.amenityList label.amenityOn {
+       background-color: #E6EFD1;
+       cursor: hand;
+       cursor: pointer;
+       }       
+
+/* SPECIFIC TO SEARCH RESULT HERE */
+.searchResult {
+       border: 1px solid #96A379;
+       padding: 10px 20px;
+       margin: 5px 0;
+       background-color: #D6DFC3;
+       height: 1%;
+       overflow: hidden;
+       position: relative;
+}
+.searchResultOn {
+       background: url(../../assets/searchResultOn.gif) no-repeat 95% center #E6EFD1;
+       cursor: hand;
+       cursor: pointer;
+}
+.searchResult h3 {
+       font-size: 15px;
+       color: #333;
+       margin: 0;
+       float: left;
+}
+.searchResultOn h3 {
+       color: #000;
+}
+.searchResult .phone {
+       position: absolute;
+       bottom: 10px;
+       right: 300px;
+}
+.searchResult a.email {
+       position: absolute;
+       bottom: 10px;
+       right: 150px;
+}
+.searchResult a {
+       color: #96A379;
+   text-decoration: none;
+}
+.searchResult a.email {
+       text-decoration: underline;
+}
+.searchResult a:link {color: #585F47;}
+.searchResult a:visited {color: #585F47;}
+.searchResult a:hover {color: #585F47;}
+.searchResult a:active {color: #585F47;}
diff --git a/Toolkit/Members/sortPhotos.php b/Toolkit/Members/sortPhotos.php
new file mode 100644 (file)
index 0000000..72852c7
--- /dev/null
@@ -0,0 +1,55 @@
+<?php
+/**
+ * File Doc Comment
+ *
+ * PHP version 5
+ *
+ * @category MembersDB
+ * @package  Toolkit_Members
+ * @author   Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @license     http://www.gaslightmedia.com Gaslightmedia
+ * @version  CVS: $Id: sortPhotos.php,v 1.5 2009/12/29 14:17:49 jamie Exp $
+ * @link        http://demo.gaslightmedia.com
+ */
+
+require_once '../../setup.phtml';
+//     Make sure the users browser doesn't cache the result.
+
+//     Time in the past.
+header('Expires: Wed, 23 Dec 1980 00:30::00 GMT');
+header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
+header('Cache-Control: no-cache, must-revalidate');
+header('Pragma: no-cache');
+
+if (!is_array($_GET['photos'])) {
+       return;
+}
+$newOrder = $_GET['photos'];
+
+$dbh = Toolkit_Database::getInstance();
+try {
+       $dbh->beginTransaction();
+
+       $sql = "
+        UPDATE member_photos
+           SET pos = :pos
+         WHERE id  = :id";
+       $stmt = $dbh->prepare($sql);
+       foreach ($newOrder as $k => &$v) {
+               $pos = $k + 1;
+               $stmt->bindParam(':pos', $pos, PDO::PARAM_INT);
+               $stmt->bindParam(':id', $v, PDO::PARAM_INT);
+               $stmt->execute();
+       }
+       $dbh->commit();
+       echo true;
+} catch (PDOException $e) {
+       $dbh->rollBack();
+       echo 'PDO Exception Caught.  ';
+       echo 'Error with the database:<br>';
+       echo 'Error: ' . $e->getMessage() . '<br>';
+       echo 'File: ' . $e->getFile() . '<br>';
+       echo 'Line: ' . $e->getLine() . '<br>';
+       print_r($dbh->errorInfo());
+}
+?>
diff --git a/Toolkit/Members/templates/addPackage.tpl b/Toolkit/Members/templates/addPackage.tpl
new file mode 100644 (file)
index 0000000..bfece7d
--- /dev/null
@@ -0,0 +1,40 @@
+<div class="packageItem packageUploadForm">
+    {form.javascript:h}
+    {form.outputHeader():h}
+    {form.hidden:h}
+        <div class="packageText">
+            <i>{form.title.label:h}</i>
+            <div flexy:if="form.title.error" class="req">{form.title.error:h}</div>
+            {form.title.html:h}
+            <i>{form.description.label:h}</i>
+            <div flexy:if="form.description.error" class="req">{form.description.error:h}</div>
+            {form.description.html:h}
+            <i>
+                <span class="req" flexy:if="form.sdate.required">*</span>
+                {form.sdate.label:h}
+            </i>
+            <div flexy:if="form.sdate.error" class="req">{form.sdate.error:h}</div>
+            {form.sdate.html:h}
+            <i>
+                <span class="req" flexy:if="form.edate.required">*</span>
+                {form.edate.label:h}
+            </i>
+            <div flexy:if="form.edate.error" class="req">{form.edate.error:h}</div>
+            {form.edate.html:h}
+        </div>
+        <div class="packagePhoto">
+            <div flexy:if="showCurrImg">
+                <label>
+                    {form.remove_img_rmv.html:h}
+                    {form.remove_img_rmv.label:h}
+                </label>
+                <i>{form.curr_image.label:h}</i>
+                {form.curr_image.html:h}
+            </div>
+            <i>{form.image.label:h}</i>
+            <div flexy:if="form.image.error" class="req">{form.image.error:h}</div>
+            {form.image.html:h}
+        </div>
+        <div class="submitArea"> {form.add_rmv.html:h} </div>
+    </form>
+</div>
diff --git a/Toolkit/Members/templates/addPhoto.tpl b/Toolkit/Members/templates/addPhoto.tpl
new file mode 100644 (file)
index 0000000..91f9d3a
--- /dev/null
@@ -0,0 +1,15 @@
+<div class="photoItem photoUploadForm">
+    {form.javascript:h}
+    {form.outputHeader():h}
+        {form.hidden:h}
+        <div class="photoOptions">
+            <i>{form.file.label:h}</i>
+            <div flexy:if="form.file.error" class="req">{form.file.error:h}</div>
+            {form.file.html:h}
+            <i>{form.caption.label:h}</i>
+            <div flexy:if="form.caption.error" class="req">{form.caption.error:h}</div>
+            {form.caption.html:h}
+            {form.submit.html:h}
+        </div>
+    </form>
+</div>
diff --git a/Toolkit/Members/templates/addYourBusinessAdminEmail.tpl b/Toolkit/Members/templates/addYourBusinessAdminEmail.tpl
new file mode 100755 (executable)
index 0000000..9996f68
--- /dev/null
@@ -0,0 +1,53 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+    <meta http-equiv="content-type" content="text/html;charset=utf-8">
+    <title>New Member Notification - {member_name:h}</title>
+</head>
+<body>
+<center>
+<table cellspacing="0" cellpadding="0" bgcolor="#ffffff" border="0">
+        <tr>
+            <td>
+                <table cellspacing="1" cellpadding="15" border="0" bgcolor="#cccccc" width="450">
+                    <tr bgcolor="#cccccc">
+                        <td bgcolor="#ffffff">
+                            <font size="4" face="arial, sans-serif">
+                                <b>You have a new member</b>
+                            </font>
+                            <br><br>
+                            <font size="3" face="arial, sans-serif">
+                                <b>{member_name:h}</b> has joined {client_name:h} from your website
+                            </font>
+                            <br><br>
+                            {if:approval_needed}
+                            <font size="2" face="arial, sans-serif">
+                                Your approval is required to complete this process.
+                                <br>
+                                <a href="baseurl/admin/members.php?page=memberRequests&module=listNewMemberRequests" target="_blank">Member administration area</a>
+                            </font>
+                            {end:}
+                        </td>
+                    </tr>
+                </table>
+            </td>
+        </tr>
+        <tr>
+            <td>
+                <table cellspacing="0" cellpadding="15" border="0" width="450">
+                    <tr>
+                        <td bgcolor="#cccccc">
+                            <font size="1" face="arial, sans-serif">
+                                Please do not reply to this email, it will not go anywhere.
+                                <br><br>
+                                To ensure the delivery of these e-mails to your inbox, please add donotreply@gaslightmedia.com to your e-mail Address Book or Safe List.
+                            </font>
+                        </td>
+                    </tr>
+                </table>
+            </td>
+        </tr>
+    </table>
+</center>
+</body>
+</html>
diff --git a/Toolkit/Members/templates/addYourBusinessMemberEmail.tpl b/Toolkit/Members/templates/addYourBusinessMemberEmail.tpl
new file mode 100644 (file)
index 0000000..c4e90c2
--- /dev/null
@@ -0,0 +1,57 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+    <meta http-equiv="content-type" content="text/html;charset=utf-8">
+    <title>Registration - {client_name:h}</title>
+</head>
+<body>
+<center>
+<table cellspacing="0" cellpadding="0" bgcolor="#ffffff" border="0">
+        <tr>
+            <td>
+                <table cellspacing="1" cellpadding="15" border="0" bgcolor="#cccccc" width="450">
+                    <tr bgcolor="#cccccc">
+                        <td bgcolor="#ffffff">
+                            {if:!approval_needed}
+                            <font size="3" face="arial, sans-serif">
+                                <b>Your registration is complete</b>
+                            </font>
+                            <br><br>
+                            <font size="2" face="arial, sans-serif">
+                                Dear {first_name:h},<br>
+                                Thanks for joining the {client_name:h} website.<br>
+                                Below is the username and password you selected.
+                                <br><br>
+                                Username: {username:h}
+                                <br>
+                                Password: {password:h}
+                                <br><br>
+                                Use this to <a href="{base_url:h}index.php?catid={member_category}" target="_blank">log into your members area</a>.
+                                <br><br>
+                                Sincerely,<br>
+                                {client_name:h}
+                            </font>
+                            {else:}
+                            <font size="3" face="arial, sans-serif">
+                                <b>Your registration is pending.</b>
+                            </font>
+                            <br><br>
+                            <font size="2" face="arial, sans-serif">
+                                Dear {first_name:h},<br>
+                                Thanks for joining the {client_name:h} website.
+                                <br><br>
+                                We need to approve your information first. As soon as this is done we will notify you by email.
+                                <br><br>
+                                Sincerely,<br>
+                                {client_name:h}
+                            </font>
+                            {end:}
+                        </td>
+                    </tr>
+                </table>
+            </td>
+        </tr>
+    </table>
+</center>
+</body>
+</html>
diff --git a/Toolkit/Members/templates/admin.tpl b/Toolkit/Members/templates/admin.tpl
new file mode 100644 (file)
index 0000000..e831c18
--- /dev/null
@@ -0,0 +1,29 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
+"http://www.w3.org/TR/html4/strict.dtd">
+<html>
+<head>
+<title>{title:h}</title>
+<meta http-equiv="content-type" content="text/html;charset=utf-8">
+<meta http-equiv="imagetoolbar" content="no">
+<meta http-equiv="imagetoolbar" content="false">
+{styles:h}
+{topScripts:h}
+</head>
+<body id="memberdb">
+    <div id="wrapper">
+        <div id="top"></div>
+        <div id="navcontainer"> {nav:h} </div>
+        <div flexy:if="breadcrumbs" id="breadcrumbs">
+            <a href="baseurl/admin/members.php?rt=Members&ac=listMembers">{listingType:h}</a>
+            &gt;
+            <a href="{searchResults:h}">Search Results</a>
+        </div>
+        <div id="content"> {content:h} </div>
+    </div><!-- /#wrapper -->
+    <div id="bottom"></div>
+    <div id="copyright">
+        Copyright &copy; 2008 Gaslight Media, All Rights Reserved
+    </div>
+    {bottomScripts:h}
+</body>
+</html>
diff --git a/Toolkit/Members/templates/authorizeChanges.tpl b/Toolkit/Members/templates/authorizeChanges.tpl
new file mode 100644 (file)
index 0000000..dc4add4
--- /dev/null
@@ -0,0 +1,46 @@
+<div id="member-info">
+    <p class="reminder">
+        Remember to click on the Submit button on the
+        bottom of the page after doing any changes.
+    </p>
+    {form.javascript:h}
+
+    {form.outputHeader():h}
+    {form.hidden:h}
+
+        <!-- Error or Success Message -->
+        {validated():h}
+        <div>
+            <fieldset flexy:foreach="form.sections,sec" class="form">
+                <legend>{sec.header}</legend>
+                <table class="pendingUpdates">
+                    <tbody>
+                        <tr flexy:foreach="sec.elements,elem">
+                            <td class="labelcell">
+                                {elem.label:h}
+                            </td>
+                            <td class="fieldcell updates">
+                                    <div flexy:foreach="elem.elements,gitem" class="field">{gitem.html:h}</div>
+                                <div class="authorization">
+                                    <label class="pendingUpdate" for="pmuyes{elem.name:h}">
+                                        <input type="radio" id="pmuyes{elem.name:h}"
+                                            value="yes" name="{group(elem.name):h}[{elem.name:h}]">
+                                        Accept
+                                    </label>
+                                    <label class="pendingUpdate" for="pmuno{elem.name:h}">
+                                        <input type="radio" id="pmuno{elem.name:h}"
+                                            value="no" name="{group(elem.name):h}[{elem.name:h}]">
+                                        Reject
+                                    </label>
+                                </div>
+                            </td>
+                        </tr>
+                    </tbody>
+                </table>
+            </fieldset>
+        </div>
+        <div class="submitArea">
+            <input type="submit" class="submit" value="Submit">
+        </div>
+    </form>
+</div>
diff --git a/Toolkit/Members/templates/currentTables/Element.tpl b/Toolkit/Members/templates/currentTables/Element.tpl
new file mode 100644 (file)
index 0000000..595457b
--- /dev/null
@@ -0,0 +1,14 @@
+<tr>
+       <td class="labelcell">
+               <!-- BEGIN required -->
+               <span class="req">*</span>
+               <!-- END required -->
+               <label>{label}</label>
+       </td>
+       <td class="fieldcell">
+               <!-- BEGIN error -->
+               <div class="req"> {error} </div>
+               <!-- END error -->
+               {element}
+       </td>
+</tr>
diff --git a/Toolkit/Members/templates/currentTables/Form.tpl b/Toolkit/Members/templates/currentTables/Form.tpl
new file mode 100644 (file)
index 0000000..f59286a
--- /dev/null
@@ -0,0 +1,7 @@
+<div id="contact">
+       <form{attributes}>
+               <table>
+                       {content}
+               </table>
+       </form>
+</div>
diff --git a/Toolkit/Members/templates/currentTables/Group.tpl b/Toolkit/Members/templates/currentTables/Group.tpl
new file mode 100644 (file)
index 0000000..cdd24cf
--- /dev/null
@@ -0,0 +1,5 @@
+<table class="group">
+       <tbody>
+               {content}
+       </tbody>
+</table>
diff --git a/Toolkit/Members/templates/currentTables/GroupElement.tpl b/Toolkit/Members/templates/currentTables/GroupElement.tpl
new file mode 100644 (file)
index 0000000..1a4ba27
--- /dev/null
@@ -0,0 +1,9 @@
+<tr>
+       <td>
+               {element}
+               <!-- BEGIN required -->
+               <span class="req">*</span>
+               <!-- END required -->
+               {label}
+       </td>
+</tr>
diff --git a/Toolkit/Members/templates/currentTables/Header.tpl b/Toolkit/Members/templates/currentTables/Header.tpl
new file mode 100644 (file)
index 0000000..64ac244
--- /dev/null
@@ -0,0 +1,5 @@
+<tr class="hdr">
+       <td colspan="2">
+               {header}
+       </td>
+</tr>
diff --git a/Toolkit/Members/templates/currentTables/RequiredNote.tpl b/Toolkit/Members/templates/currentTables/RequiredNote.tpl
new file mode 100644 (file)
index 0000000..525ef33
--- /dev/null
@@ -0,0 +1 @@
+<span class="req">*</span> Denotes required field
diff --git a/Toolkit/Members/templates/editAmenities.tpl b/Toolkit/Members/templates/editAmenities.tpl
new file mode 100644 (file)
index 0000000..1fc1515
--- /dev/null
@@ -0,0 +1,41 @@
+<div id="nav-detail"> {nav:h} </div>
+<div id="member-info">
+    {if:pending}
+        <div class="pending pendingMsg">
+            The yellow colored items are pending approval from the website
+            administrator.<br>Upon approval, your changes will be visible on
+            the website.
+        </div>
+    {end:}
+    {form.javascript:h}
+    {form.outputHeader():h}
+    {form.hidden:h}
+
+    <!-- Error or Success Message -->
+    {validated():h}
+    <h1>Edit Amenities</h1>
+    <p>
+        Check off any available amenities and click
+        the submit button on the bottom of the page.
+    </p>
+    {foreach:form.sections,sec}
+        <ul class="amenityList">
+            {foreach:sec.elements,elem}
+                {if:fieldPending(elem)}
+                    <li class="fieldPending">
+                {else:}
+                    <li>
+                {end:}
+                    <label>
+                        {elem.html:h}
+                        {elem.label:h}
+                    </label>
+                </li>
+            {end:}
+        </ul>
+    {end:}
+    <div class="submitArea">
+        <input type="submit" class="submit" value="Submit Changes">
+    </div>
+    </form>
+</div>
diff --git a/Toolkit/Members/templates/editAmenity.tpl b/Toolkit/Members/templates/editAmenity.tpl
new file mode 100644 (file)
index 0000000..7816203
--- /dev/null
@@ -0,0 +1,42 @@
+<div id="member-info">
+    {form.javascript:h}
+    {form.outputHeader():h}
+    {form.hidden:h}
+    <!-- Error or Success Message -->
+    {validated():h}
+    <div id="mRow1">
+        <fieldset flexy:foreach="form.sections,sec" class="form">
+            <legend>{sec.header}</legend>
+            <table>
+                <tr flexy:foreach="sec.elements,elem">
+                    <!-- CheckBoxes go here. -->
+                    {if:elem.isType(#checkbox#)}
+                        <td class="labelcell"> {elem.html:h} </td>
+                        <td class="fieldcell"> {elem.label:h} </td>
+                    {else:}
+                        <!-- All regular elements go here. -->
+                        <td class="labelcell">
+                            <span flexy:if="elem.required" class="req">*</span>
+                            {if:elem.error}<span class="req">{end:}
+                                {elem.label}
+                            {if:elem.error}</span>{end:}
+                        </td>
+                        <td class="fieldcell">
+                            <div flexy:if="elem.error" class="req">
+                                {elem.error}
+                            </div>
+                            {elem.html:h}
+                        </td>
+                    {end:}
+                </tr>
+            </table>
+        </fieldset>
+    </div>
+    <div class="submitArea">
+        <input type="submit" class="submit" value="Save Amenity">
+        {if:isEdit()}
+            <input type="submit" name="delete" class="submit" value="Remove Amenity">
+        {end:}
+    </div>
+    </form>
+</div>
diff --git a/Toolkit/Members/templates/editBilling.tpl b/Toolkit/Members/templates/editBilling.tpl
new file mode 100644 (file)
index 0000000..d9fcd01
--- /dev/null
@@ -0,0 +1,48 @@
+<link rel="stylesheet" type="text/css" href="http://app.gaslightmedia.com/libjs/Jscal/system.css">
+
+<script type="text/javascript" src="baseurl/Toolkit/Members/Billing/js/edit-billing.js?t=1"></script>
+{jsCalScripts:h}
+<div id="nav-detail"> {nav:h} </div>
+<div id="member-info">
+    {form.javascript:h}
+    {form.outputHeader():h}
+    {form.hidden:h}
+    <!-- Error or Success Message -->
+    {validated():h}
+    <div id="mRow1">
+        <fieldset flexy:foreach="form.sections,sec" class="form">
+            <legend>{sec.header}</legend>
+            <table>
+                <tr flexy:foreach="sec.elements,elem">
+                    <!-- All regular elements go here. -->
+                    <td class="labelcell">
+                        <span flexy:if="elem.required" class="req">*</span>
+                        {if:elem.error}<span class="req">{end:}
+                            {elem.label}
+                        {if:elem.error}</span>{end:}
+                    </td>
+                    <td class="fieldcell">
+                        <span flexy:if="elem.error" class="req">
+                            {elem.error}
+                        </span>
+                        {elem.html:h}
+                    </td>
+                </tr>
+            </table>
+        </fieldset>
+    </div>
+
+    <div class="submitArea" style="height:30px;width:400px;">
+        <b style="width:200px;float:left;">
+            <input type="submit" class="submit" value="Submit Changes">
+        </b>
+        {if:hasInfo()}
+        <b style="width:200px;float:left;">
+            <input type="submit" name="delete" class="submit" value="Remove Billing Info"
+                   onClick="return(confirm('This cannot be undone! Are You Sure?'));">
+        </b>
+        {end:}
+    </div>
+    </form>
+    {memberStatements:h}
+</div>
diff --git a/Toolkit/Members/templates/editCategory.tpl b/Toolkit/Members/templates/editCategory.tpl
new file mode 100644 (file)
index 0000000..76a9556
--- /dev/null
@@ -0,0 +1,42 @@
+<div id="member-info">
+    {form.javascript:h}
+    {form.outputHeader():h}
+    {form.hidden:h}
+    <!-- Error or Success Message -->
+    {validated():h}
+    <div id="mRow1">
+        <fieldset flexy:foreach="form.sections,sec" class="form">
+            <legend>{sec.header}</legend>
+            <table>
+                    <tr flexy:foreach="sec.elements,elem">
+                        <!-- CheckBoxes go here. -->
+                        {if:elem.isType(#checkbox#)}
+                            <td class="labelcell"> {elem.html:h} </td>
+                            <td class="fieldcell"> {elem.label:h} </td>
+                        {else:}
+                            <!-- All regular elements go here. -->
+                            <td class="labelcell">
+                                <span flexy:if="elem.required" class="req">*</span>
+                                {if:elem.error}<span class="req">{end:}
+                                    {elem.label}
+                                {if:elem.error}</span>{end:}
+                            </td>
+                            <td class="fieldcell">
+                                <span flexy:if="elem.error" class="req">
+                                    {elem.error}
+                                </span>
+                                {elem.html:h}
+                            </td>
+                        {end:}
+                    </tr>
+            </table>
+        </fieldset>
+    </div>
+    <div class="submitArea">
+        <input type="submit" class="submit" value="Save Category">
+        {if:isEdit()}
+        <input type="submit" name="delete" class="submit" value="Remove Category">
+        {end:}
+    </div>
+    </form>
+</div>
diff --git a/Toolkit/Members/templates/editCity.tpl b/Toolkit/Members/templates/editCity.tpl
new file mode 100644 (file)
index 0000000..a858637
--- /dev/null
@@ -0,0 +1,42 @@
+<div id="member-info">
+    {form.javascript:h}
+    {form.outputHeader():h}
+    {form.hidden:h}
+    <!-- Error or Success Message -->
+    {validated():h}
+    <div id="mRow1">
+        <fieldset flexy:foreach="form.sections,sec" class="form">
+            <legend>{sec.header}</legend>
+            <table>
+                <tr flexy:foreach="sec.elements,elem">
+                    <!-- CheckBoxes go here. -->
+                    {if:elem.isType(#checkbox#)}
+                        <td class="labelcell"> {elem.html:h} </td>
+                        <td class="fieldcell"> {elem.label:h} </td>
+                    {else:}
+                        <!-- All regular elements go here. -->
+                        <td class="labelcell">
+                            <span flexy:if="elem.required" class="req">*</span>
+                            {if:elem.error}<span class="req">{end:}
+                                {elem.label}
+                            {if:elem.error}</span>{end:}
+                        </td>
+                        <td class="fieldcell">
+                            <div flexy:if="elem.error" class="req">
+                                {elem.error}
+                            </div>
+                            {elem.html:h}
+                        </td>
+                    {end:}
+                </tr>
+            </table>
+        </fieldset>
+    </div>
+    <div class="submitArea">
+        <input type="submit" class="submit" value="Save City">
+        {if:isEdit()}
+            <input type="submit" name="delete" class="submit" value="Remove City">
+        {end:}
+    </div>
+    </form>
+</div>
diff --git a/Toolkit/Members/templates/editContact.html b/Toolkit/Members/templates/editContact.html
new file mode 100644 (file)
index 0000000..ccae41e
--- /dev/null
@@ -0,0 +1,21 @@
+<fieldset class="form">
+       {form.outputHeader():h}
+               {form.hidden:h}
+               <table>
+                       <tbody>
+                               <tr flexy:foreach="form.elements,e">
+                                       <td class="labelcell">
+                                               <span flexy:if="e.required" class="req">*</span>
+                                               {if:e.error}<span class="req">{end:}
+                                                       {e.label:h}
+                                               {if:e.error}</span>{end:}
+                                       </td>
+                                       <td class="fieldcell">
+                                               <div flexy:if="e.error" class="req">{e.error:h}</div>
+                                               {e.html:h}
+                                       </td>
+                               </tr>
+                       </tbody>
+               </table>
+       </form>
+</fieldset>
diff --git a/Toolkit/Members/templates/editContacts.tpl b/Toolkit/Members/templates/editContacts.tpl
new file mode 100644 (file)
index 0000000..86c1d5c
--- /dev/null
@@ -0,0 +1,57 @@
+<div id="nav-detail"> {nav:h} </div>
+<div id="member-info">
+    <h1>Contacts</h1>
+    {form.javascript:h}
+    <!-- Error or Success Message -->
+    {validated():h}
+    {foreach:form.sections,k,sec}
+        {if:isForm(k)}
+            <fieldset class="form">
+                <table>
+                    <tbody>
+                        {form.outputHeader():h}
+                        {form.hidden:h}
+                            <input type="hidden" name="target" value="{k}">
+                            <tr flexy:foreach="sec.elements,elem">
+                                <td class="labelcell">
+                                    <span flexy:if="elem.required" class="req">*</span>
+                                    {if:elem.error}<span class="req">{end:}
+                                        {elem.label:h}
+                                    {if:elem.error}</span>{end:}
+                                </td>
+                                <td class="fieldcell">
+                                    <span flexy:if="elem.error" class="req">
+                                        {elem.error}
+                                    </span>
+                                    {elem.html:h}
+                                </td>
+                            </tr>
+                        </form>
+                    </tbody>
+                </table>
+            </fieldset>
+        {else:}
+            <div class="contactList">
+                <a class="remove" href="{getDelUrl(sec):h}">
+                    <img width="16" height="16" title="Delete Contact"
+                         src="<?php echo MEDIA_APP_BASE_URL;?>assets/icons/user_delete.png" alt="remove">
+                </a>
+                {foreach:sec.elements,elem}
+                    <h3 flexy:if="isName(elem.name)">
+                        <a href="{getEditUrl(sec):h}">{elem.html:h}</a>
+                    </h3>
+                    <div flexy:if="isTitle(elem.name)" class="title">
+                        {elem.html:h}
+                    </div>
+                    <div flexy:if="isPhone(elem.name)" class="phone">
+                        {elem.html:h}
+                    </div>
+                    <a flexy:if="isEmail(elem.name)" class="email" href="mailto:{elem.html:h}">
+                        {elem.html:h}
+                    </a>
+                {end:}
+                {getMailIcon(sec.elements[5]):h}
+            </div>
+        {end:}<!-- isForm(k) -->
+    {end:}
+</div><!-- /#member-info -->
diff --git a/Toolkit/Members/templates/editCouponEmail.tpl b/Toolkit/Members/templates/editCouponEmail.tpl
new file mode 100755 (executable)
index 0000000..18ffa8b
--- /dev/null
@@ -0,0 +1,54 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+    <meta http-equiv="content-type" content="text/html;charset=utf-8">
+    <title>Member Coupon Notification for - {member_name:h}</title>
+</head>
+<body>
+<center>
+<table cellspacing="0" cellpadding="0" bgcolor="#ffffff" border="0">
+        <tr>
+            <td>
+                <table cellspacing="1" cellpadding="15" border="0" bgcolor="#cccccc" width="450">
+                    <tr bgcolor="#cccccc">
+                        <td bgcolor="#ffffff">
+                            <font size="4" face="arial, sans-serif">
+                                <b>{member_name:h}</b>
+                                <br>
+                                {if:edit}
+                                    <b>has edited a coupon</b>
+                                {else:}
+                                    <b>has created a new coupon</b>
+                                {end:}
+                            </font>
+                            <br><br>
+                            <font size="2" face="arial, sans-serif">
+                                Your approval is required to complete this process.
+                                <br>
+                                Please follow this link to review this change
+                                <a href="{baseUrl:h}admin/index.phtml?page=coupons.php%3Fpage=editCoupon%26module=editCoupon%26id={coupon_id:h}" target="_blank">Coupons Admin</a>
+                            </font>
+                        </td>
+                    </tr>
+                </table>
+            </td>
+        </tr>
+        <tr>
+            <td>
+                <table cellspacing="0" cellpadding="15" border="0" width="450">
+                    <tr>
+                        <td bgcolor="#cccccc">
+                            <font size="1" face="arial, sans-serif">
+                                Please do not reply to this email, it will not go anywhere.
+                                <br><br>
+                                To ensure the delivery of these e-mails to your inbox, please add donotreply@gaslightmedia.com to your e-mail Address Book or Safe List.
+                            </font>
+                        </td>
+                    </tr>
+                </table>
+            </td>
+        </tr>
+    </table>
+</center>
+</body>
+</html>
diff --git a/Toolkit/Members/templates/editFile.tpl b/Toolkit/Members/templates/editFile.tpl
new file mode 100644 (file)
index 0000000..a3a8a96
--- /dev/null
@@ -0,0 +1,45 @@
+<div id="nav-detail"> {nav:h} </div>
+<div id="member-info">
+    {if:pending}
+        <div class="pending pendingMsg">
+            The yellow colored items are pending approval from the website
+            administrator.<br>Upon approval, your changes will be visible on
+            the website.
+        </div>
+    {end:}
+    <h1>Files</h1>
+    {form.javascript:h}
+    <!-- Error or Success Message -->
+    {validated():h}
+    <div flexy:foreach="form.sections,k,sec" class="photoItem FileItem {pendingClass(k)}">
+        {form.outputHeader():h}
+        {form.hidden:h}
+            <!--
+                Check to see if the first element is an image.
+                If it is, then we need to output before the photoOptions
+                div
+            -->
+            {if:showFile(sec.elements[0])}
+                {sec.elements[0].html:h}
+            {end:}
+            <div class="photoOptions fileOptions">
+                {foreach:sec.elements,elem}
+                    <!--
+                        since we already output the image, if the current
+                        element is anything but an image, then show it.
+                        Otherwise, skip the image.
+                    -->
+                    {if:!elem.isType(#static#)}
+                        {if:elem.isType(#text#)}
+                            <i> {elem.label} </i>
+                        {end:}
+                        <div flexy:if="elem.error" class="req">
+                            {elem.error:h}
+                        </div>
+                        {elem.html:h}
+                    {end:}
+                {end:}
+            </div><!-- /.photoOptions -->
+        </form>
+    </div><!-- /.photoItem -->
+</div><!-- /#member-info -->
diff --git a/Toolkit/Members/templates/editHtmlEmail.tpl b/Toolkit/Members/templates/editHtmlEmail.tpl
new file mode 100644 (file)
index 0000000..88bad42
--- /dev/null
@@ -0,0 +1,42 @@
+<div id="member-info">
+    {form.javascript:h}
+    {form.outputHeader():h}
+    {form.hidden:h}
+    <!-- Error or Success Message -->
+    {validated():h}
+    <div id="mRow1">
+        <fieldset flexy:foreach="form.sections,sec" class="form">
+            <legend>{sec.header}</legend>
+            <table>
+                <tr flexy:foreach="sec.elements,elem">
+                    <!-- CheckBoxes go here. -->
+                    {if:elem.isType(#checkbox#)}
+                        <td class="labelcell"> {elem.html:h} </td>
+                        <td class="fieldcell"> {elem.label:h} </td>
+                    {else:}
+                        <!-- All regular elements go here. -->
+                        <td class="labelcell">
+                            <span flexy:if="elem.required" class="req">*</span>
+                            {if:elem.error}<span class="req">{end:}
+                                {elem.label}
+                            {if:elem.error}</span>{end:}
+                        </td>
+                        <td class="fieldcell">
+                            <div flexy:if="elem.error" class="req">
+                                {elem.error}
+                            </div>
+                            {elem.html:h}
+                        </td>
+                    {end:}
+                </tr>
+            </table>
+        </fieldset>
+    </div>
+    <div class="submitArea">
+        <input type="submit" class="submit" value="Save Html Email">
+        {if:isEdit()}
+            <input type="submit" name="delete" class="submit" value="Remove Html Email">
+        {end:}
+    </div>
+    </form>
+</div>
diff --git a/Toolkit/Members/templates/editMember.tpl b/Toolkit/Members/templates/editMember.tpl
new file mode 100644 (file)
index 0000000..5f7a47e
--- /dev/null
@@ -0,0 +1,150 @@
+<div id="nav-detail"> {nav:h} </div>
+<div id="member-info">
+    <div flexy:if="pending" class="pending pendingMsg">
+        The yellow colored items are pending approval from the website
+        administrator.<br>Upon approval, your changes will be visible on
+        the website.
+    </div>
+    <p class="reminder">
+        Remember to click on the Submit button on the
+        bottom of the page after doing any changes.
+    </p>
+    {form.javascript:h}
+    {form.outputHeader():h}
+    {form.hidden:h}
+
+    <!-- Error or Success Message -->
+    {validated():h}
+    <div id="mRow1">
+    {foreach:form.sections,sec}
+        {if:newColumn(sec.header)}
+            </div>
+            <div id="mRow2">
+        {end:}
+            {if:inColumn2(sec.header)}
+                <fieldset class="form formNarrow">
+            {else:}
+                <fieldset class="form">
+            {end:}<!-- /inColumn2 -->
+            <legend>
+                {sec.header}
+            </legend>
+            <?php if ($sec->header == 'Street Address') :?>
+            <a class="thickbox addr_help" href="baseurl/Toolkit/Members/addressHelp.html?KeepThis=true&TB_ifram=true&height=400&width=600">Need help with my address</a>
+            <?php endif;?>
+            <table>
+                {foreach:sec.elements,elem}
+                    {if:fieldPending(elem)}
+                        <tr class="fieldPending">
+                    {else:}
+                        <tr>
+                    {end:}
+                        {if:elem.isType(#textarea#)}
+                            <td>
+                                <div flexy:if="elem.error" class="req">
+                                    {elem.error:h}
+                                </div>
+                                {elem.html:h}
+                            </td>
+                        {else:}
+                            <!-- CheckBoxes go here. -->
+                            {if:elem.isType(#checkbox#)}
+                                {if:elem.isName(#active#)}
+                                    <td class="labelcell"> {elem.label:h} </td>
+                                    <td class="fieldcell active_field"> {elem.html:h} </td>
+                                {else:}
+                                    {if:elem.isName(#non_member#)}
+                                        <td class="labelcell"> {elem.label:h} </td>
+                                        <td class="fieldcell active_field"> {elem.html:h} </td>
+                                    {else:}
+                                    <td class="labelcell"> {elem.html:h} </td>
+                                    <td class="fieldcell"> {elem.label:h} </td>
+                                {end:}
+                                {end:}
+                            {else:}
+                                <!-- Grouped Elements go here. -->
+                                {if:elem.isType(#group#)}
+                                    <td class="labelcell">
+                                        {if:elem.error}<span class="req">{end:}
+                                            {elem.label:h}
+                                        {if:elem.error}</span>{end:}
+                                    </td>
+                                    <td class="fieldcell">
+                                        <div flexy:if="elem.error" class="req">
+                                            {elem.error:h}
+                                        </div>
+                                        {foreach:elem.elements,gitem}
+                                            {if:elem.isName(#city#)}
+                                            <small>{gitem.label:h}</small><br>
+                                            {end:}
+                                            {gitem.html:h}
+                                            {if:elem.separator}{elem.separator:h}{end:}
+                                        {end:}
+                                    </td>
+                                {else:}
+                                    {if:elem.isName(#member_cats[]#)}
+                                        <td class="fieldcell">
+                                            <div flexy:if="elem.error" class="req">
+                                                {elem.error:h}
+                                            </div>
+                                            <div id="addCategory">
+                                                {elem.html:h}
+                                            </div>
+                                            {showCategories():h}
+                                        </td>
+                                    {else:}
+                                    <!-- All regular elements go here. -->
+                                    <td class="labelcell">
+                                        <span class="req" flexy:if="elem.required">*</span>
+                                        {if:elem.error}<span class="req">{end:}
+                                            {elem.label}
+                                        {if:elem.error}</span>{end:}
+                                    </td>
+                                    <td class="fieldcell">
+                                        <div flexy:if="elem.error" class="req">
+                                            {elem.error:h}
+                                        </div>
+                                        {if:addInfoElement(elem.name)}
+                                            {if:elem.isName(#street#)}
+                                            <?php
+                                                $head = 'Additional Information';
+                                                $body = 'Enter an actual street address to
+                                                calculate driving directions.
+                                                Do not use a P.O. Box.';
+                                            ?>
+                                            {end:}
+                                            {if:elem.isName(#member_contact_email#)}
+                                            <?php
+                                                $head = 'Additional Information';
+                                                $body = 'Enter the email address for the
+                                                business you would like visitors
+                                                to see.';
+                                            ?>
+                                            {end:}
+                                            <a>
+                                                <span class="tooltip" title="<?php echo $head . '|' . $body;?>">
+                                                <img class="info" width="16" height="16"
+                                                    title="" alt=""
+                                                    src="<?php echo MEDIA_APP_BASE_URL;?>assets/icons/information.png">
+                                                </span>
+                                        {end:}
+                                        {elem.html:h}
+                                        {if:addInfoElement(elem.label)}
+                                            </a>
+                                        {end:}
+                                    </td>
+                                    {end:}
+                                {end:}<!-- /isType(#group#) -->
+                            {end:}<!-- /isType(#checkbox#) -->
+                        {end:}<!-- /isType(#textarea#) -->
+                    </tr>
+                {end:}<!-- /foreach() -->
+            </table>
+        </fieldset>
+    {end:}<!-- /newColumn() -->
+    </div>
+    <div class="submitArea">
+        <input type="submit" class="submit" value="Submit Changes">
+    </div>
+    </form>
+</div>
diff --git a/Toolkit/Members/templates/editPackage.tpl b/Toolkit/Members/templates/editPackage.tpl
new file mode 100644 (file)
index 0000000..8af7a7a
--- /dev/null
@@ -0,0 +1,54 @@
+<div class="packageItem packageUploadForm">
+    {form.javascript:h}
+    {form.outputHeader():h}
+    {form.hidden:h}
+        <div class="packageText">
+            <i>{form.title.label:h}</i>
+            <div flexy:if="form.title.error" class="req">
+                {form.title.error:h}
+            </div>
+            {form.title.html:h}
+            <i>{form.description.label:h}</i>
+            <div flexy:if="form.description.error" class="req">
+                {form.description.error:h}
+            </div>
+            {form.description.html:h}
+            <i>
+                <span class="req" flexy:if="form.sdate.required">*</span>
+                {form.sdate.label:h}
+            </i>
+            <div flexy:if="form.sdate.error" class="req">
+                {form.sdate.error:h}
+            </div>
+            {form.sdate.html:h}
+            <i>
+                <span class="req" flexy:if="form.edate.required">*</span>
+                {form.edate.label:h}
+            </i>
+            <div flexy:if="form.edate.error" class="req">
+                {form.edate.error:h}
+            </div>
+            {form.edate.html:h}
+        </div>
+        <div class="packagePhoto">
+            <div flexy:if="showCurrImg">
+                <label>
+                    {form.remove_img_rmv.html:h}
+                    {form.remove_img_rmv.label:h}
+                </label>
+
+                <i>{form.curr_image.label:h}</i>
+                {form.curr_image.html:h}
+            </div>
+            <i>{form.image.label:h}</i>
+            <div flexy:if="form.image.error" class="req">
+                {form.image.error:h}
+            </div>
+            {form.image.html:h}
+        </div>
+        <div class="submitArea">
+            {form.add_rmv.html:h}
+            {form.remove_rmv.html:h}
+        </div>
+    </form>
+</div>
diff --git a/Toolkit/Members/templates/editPackages.tpl b/Toolkit/Members/templates/editPackages.tpl
new file mode 100644 (file)
index 0000000..f8e2191
--- /dev/null
@@ -0,0 +1,27 @@
+<style type="text/css">
+.ui-state-highlight {
+    border: 3px dashed #666666;
+    height: 1%;
+    margin-top: 1em;
+    overflow: hidden;
+    padding: 28px 18px 18px 28px;
+    position: relative;
+}
+</style>
+<div id="nav-detail"> {nav:h} </div>
+<div id="member-info">
+    {if:hasPendingPackages}
+        <div class="pending pendingMsg">
+            The yellow colored items are pending approval from the website
+            administrator.<br>Upon approval, your changes will be visible on
+            the website.
+        </div>
+    {end:}
+    <h1>Packages</h1>
+    {uploadForm:h}
+    <div id="packageList" class="container">
+    {foreach:editForm,i}
+        {i:h}
+    {end:}
+    </div>
+</div><!-- /#member-info -->
diff --git a/Toolkit/Members/templates/editPhoto.tpl b/Toolkit/Members/templates/editPhoto.tpl
new file mode 100644 (file)
index 0000000..6280d1b
--- /dev/null
@@ -0,0 +1,24 @@
+{if:pending}
+<div id="photos_{photoId}" class="pending photoItem">
+{else:}
+<div id="photos_{photoId}" class="photoItem">
+{end:}
+    {form.javascript:h}
+    {form.outputHeader():h}
+        {form.hidden:h}
+        <div>
+            <img class="thumb" alt="{img_alt:h}" src="{img_src:h}">
+        </div>
+        <div class="photoOptions">
+            <i>{form.caption.label:h}</i>
+            <div flexy:if="form.caption.error" class="req">{form.caption.error:h}</div>
+            {form.caption.html:h}
+            <div class="position">
+                {form.pos.label:h}
+                {form.pos.html:h}
+            </div>
+            {form.update.html:h}
+            {form.delete.html:h}
+        </div>
+    </form>
+</div>
diff --git a/Toolkit/Members/templates/editPhotoGallery.tpl b/Toolkit/Members/templates/editPhotoGallery.tpl
new file mode 100644 (file)
index 0000000..f0f2a6c
--- /dev/null
@@ -0,0 +1,27 @@
+<style type="text/css">
+.ui-state-highlight {
+    border: 3px dashed #666666;
+    height: 1%;
+    margin-top: 1em;
+    overflow: hidden;
+    padding: 28px 18px 18px 28px;
+    position: relative;
+}
+</style>
+<div id="nav-detail"> {nav:h} </div>
+<div id="member-info">
+    {if:hasPendingPhotos}
+        <div class="pending pendingMsg">
+            The yellow colored items are pending approval from the website
+            administrator.<br>Upon approval, your changes will be visible on
+            the website.
+        </div>
+    {end:}
+    <h1>Photos</h1>
+    {uploadForm:h}
+    <div id="photoList" class="container">
+    {foreach:editForms,i}
+        {i:h}
+    {end:}
+    </div>
+</div><!-- /#member-info -->
diff --git a/Toolkit/Members/templates/editRegion.tpl b/Toolkit/Members/templates/editRegion.tpl
new file mode 100644 (file)
index 0000000..a75a4bc
--- /dev/null
@@ -0,0 +1,36 @@
+<div id="member-info">
+    {form.javascript:h}
+    {form.outputHeader():h}
+    {form.hidden:h}
+    <!-- Error or Success Message -->
+    {validated():h}
+    <div id="mRow1">
+        <fieldset flexy:foreach="form.sections,sec" class="form">
+            <legend>{sec.header}</legend>
+            <table>
+                <tr flexy:foreach="sec.elements,elem">
+                    <!-- All regular elements go here. -->
+                    <td class="labelcell">
+                        <span flexy:if="elem.required" class="req">*</span>
+                        {if:elem.error}<span class="req">{end:}
+                            {elem.label}
+                        {if:elem.error}</span>{end:}
+                    </td>
+                    <td class="fieldcell">
+                        <span flexy:if="elem.error" class="req">
+                            {elem.error}
+                        </span>
+                        {elem.html:h}
+                    </td>
+                </tr>
+            </table>
+        </fieldset>
+    </div>
+    <div class="submitArea">
+        <input type="submit" class="submit" value="Save Region">
+        {if:isEdit()}
+            <input type="submit" name="delete" class="submit" value="Remove Region">
+        {end:}
+    </div>
+    </form>
+</div>
diff --git a/Toolkit/Members/templates/emailOwner.tpl b/Toolkit/Members/templates/emailOwner.tpl
new file mode 100644 (file)
index 0000000..367fd53
--- /dev/null
@@ -0,0 +1,51 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+    <meta http-equiv="content-type" content="text/html;charset=utf-8">
+    <title>Member Record update</title>
+</head>
+<body>
+<center>
+<table cellspacing="0" cellpadding="0" bgcolor="#ffffff" border="0">
+        <tr>
+            <td>
+                <table cellspacing="1" cellpadding="15" border="0" bgcolor="#cccccc" width="450">
+                    <tr bgcolor="#cccccc">
+                        <td bgcolor="#ffffff">
+                            <font size="4" face="arial, sans-serif">
+                                <b>You have a pending member record</b>
+                            </font>
+                            <br><br>
+                            <font size="3" face="arial, sans-serif">
+                                <b>{member:h}</b> has updated their member record and the changes are waiting your approval.
+                            </font>
+                            <br><br>
+                            <font size="2" face="arial, sans-serif">
+                                Your approval is required to complete this process.
+                                <br>
+                                <a href="{url:h}admin/members.php?page=memberUpdates&module=listPendingMembers" target="_blank">Member administration area</a>
+                            </font>
+                        </td>
+                    </tr>
+                </table>
+            </td>
+        </tr>
+        <tr>
+            <td>
+                <table cellspacing="0" cellpadding="15" border="0" width="450">
+                    <tr>
+                        <td bgcolor="#cccccc">
+                            <font size="1" face="arial, sans-serif">
+                                Please do not reply to this email, it will not go anywhere.
+                                <br><br>
+                                To ensure the delivery of these e-mails to your inbox, please add {email:h} to your e-mail Address Book or Safe List.
+                            </font>
+                        </td>
+                    </tr>
+                </table>
+            </td>
+        </tr>
+    </table>
+</center>
+</body>
+</html>
diff --git a/Toolkit/Members/templates/exposureDetail.tpl b/Toolkit/Members/templates/exposureDetail.tpl
new file mode 100755 (executable)
index 0000000..738a53a
--- /dev/null
@@ -0,0 +1,35 @@
+{if:numberedSet}
+<style type="text/css">
+#exposure-detail th, #exposure-detail td {
+    padding: 5px 30px;
+    text-align: center;
+    font-size: 1.1em;
+    }
+#exposure-detail td:first-child {
+    text-align: left;
+    }
+#exposure-detail th {
+    background: #eee;
+    }
+#exposure-detail td {
+    background: rgba(252, 123, 21, 0.3);
+    }
+</style>
+<h3>{recordSet[0][member_name]}</h3>
+    <table id="exposure-detail">
+        <tr>
+            <th>Month</th>
+            <th>Listed</th>
+            <th>Details</th>
+            <th>Clicks</th>
+      </tr>
+        <tr flexy:foreach="recordSet,k,row">
+            <td>{row[month]}</td>
+            <td>{row[list]}</td>
+            <td>{row[detail]}</td>
+            <td>{row[click]}</td>
+      </tr>
+    </table>
+{else:}
+    <div class="NoMembers">No reports are available at this time.</div>
+{end:}
diff --git a/Toolkit/Members/templates/exposureList.tpl b/Toolkit/Members/templates/exposureList.tpl
new file mode 100755 (executable)
index 0000000..6ee368e
--- /dev/null
@@ -0,0 +1,43 @@
+<h1>Exposure Reports</h1>
+<div>
+    Exposure Reports - check the statistics being compiled that include:
+    <ul>
+        <li>Listed - number of times your member record was viewed in search result page</li>
+        <li>Details - number of time your member profile page was visited</li>
+        <li>Clicks - number of times your Web site address was clicked on</li>
+    </ul>
+</div>
+<hr>
+{exposureForm:h}
+{if:numberedSet}
+    <div class="results">{getResults(#Found %s Businesses in %s page(s)#):h}</div>
+{getPaging():h}
+<div flexy:if="letters" class="business-first-letter">
+    <div>Narrow result by letter:</div>
+    <a flexy:foreach="letters,k,v" class="{v[class]:h}" href="{v[url]:h}">{k}</a>
+</div>
+<p>Showing records {firstRecord} to {lastRecord}</p>
+    <table id="exposure-list">
+        <tr>
+            <th> Member Name </th>
+            <th> Listed </th>
+            <th> Details </th>
+            <th> Clicks </th>
+      </tr>
+        <tr flexy:foreach="recordSet,k,row">
+            <td>
+                <a href="{row[directUrl]}">{row[member_name]:h}</a>
+            </td>
+            <td class="right"> {row[list]} </td>
+            <td class="right"> {row[detail]} </td>
+            <td class="right"> {row[click]} </td>
+      </tr>
+    </table>
+{getPaging():h}
+<div flexy:if="letters" class="business-first-letter">
+    <div>Narrow result by letter:</div>
+    <a flexy:foreach="letters,k,v" class="{v[class]:h}" href="{v[url]:h}">{k}</a>
+</div>
+{else:}
+    <div class="NoMembers">No Data For Found. Try another search.</div>
+{end:}
diff --git a/Toolkit/Members/templates/htmlEmailBody.tpl b/Toolkit/Members/templates/htmlEmailBody.tpl
new file mode 100644 (file)
index 0000000..2d94c17
--- /dev/null
@@ -0,0 +1,39 @@
+<table cellspacing="0" cellpadding="0" border="0" width="100%">
+    <tr>
+        <td align="center">
+            <table cellspacing="0" cellpadding="0" border="0" width="550" bgcolor="#ffffff">
+                <tr>
+                    <td>
+                        <a href="baseurl/"><img src="baseurl/assets/nHeader.jpg" width="550" height="114" alt="" style="border: 0; display: block;"></a>
+                    </td>
+                </tr>
+                <tr>
+                    <td>
+                        <table cellspacing="15" cellpadding="0" border="0">
+                            <tr>
+                                <td align="left">
+                                    <font face="arial, helvetica, sans-serif" size="2">
+                                        {response:h}
+                                    </font>
+                                </td>
+                            </tr>
+                            <tr>
+                                <td>
+                                    <font face="arial, helvetica, sans-serif" size="1">
+                                        <hr>
+                                        You are receiving this message because you have expressed an interest in
+                                        receiving specials and information from {sitename}. If you do not
+                                        wish to receive future items of this nature, please reply to this e-mail
+                                        with the word "CANCEL" on the subject line. You will then be removed
+                                        from future mailings.<br>
+                                        <a href="mailto:{membersEnewsEmail}?subject=CANCEL">{membersEnewsEmail}</a><br>
+                                    </font>
+                                </td>
+                            </tr>
+                        </table>
+                    </td>
+                </tr>
+            </table>
+        </td>
+    </tr>
+</table>
diff --git a/Toolkit/Members/templates/listAmenities.tpl b/Toolkit/Members/templates/listAmenities.tpl
new file mode 100644 (file)
index 0000000..94606fb
--- /dev/null
@@ -0,0 +1,19 @@
+<table class="member-admin-table">
+    <thead>
+        <tr>
+            <th></th>
+            <th>Amenity Name</th>
+        </tr>
+    </thead>
+    <tr flexy:foreach="amenities,k,v">
+        <td>
+            <div class="buttons">
+                <a href="members.php?rt=Amenities&amp;ac=editAmenity&amp;id={k:h}">
+                    <img alt="edit" src="<?php echo MEDIA_APP_BASE_URL;?>assets/icons/note_edit.png">
+                    Edit
+                </a>
+            </div>
+        </td>
+        <td>{v:h}</td>
+    </tr>
+</table>
diff --git a/Toolkit/Members/templates/listCategories.tpl b/Toolkit/Members/templates/listCategories.tpl
new file mode 100644 (file)
index 0000000..2eb681d
--- /dev/null
@@ -0,0 +1,3 @@
+<div id="categoryTree">
+    {tree:h}
+</div>
diff --git a/Toolkit/Members/templates/listCities.tpl b/Toolkit/Members/templates/listCities.tpl
new file mode 100644 (file)
index 0000000..992e620
--- /dev/null
@@ -0,0 +1,21 @@
+<table class="member-admin-table">
+    <thead>
+        <tr>
+            <th></th>
+            <th>City Name</th>
+        </tr>
+    </thead>
+    <tbody>
+        <tr flexy:foreach="cities,k,v">
+            <td>
+                <div class="buttons">
+                    <a href="members.php?rt=Cities&ac=editCity&id={k:h}">
+                        <img src="<?php echo MEDIA_APP_BASE_URL;?>assets/icons/note_edit.png">
+                        Edit
+                    </a>
+                </div>
+            </td>
+            <td>{v:h}</td>
+        </tr>
+    </tbody>
+</table>
diff --git a/Toolkit/Members/templates/listContacts.html b/Toolkit/Members/templates/listContacts.html
new file mode 100644 (file)
index 0000000..a117e5d
--- /dev/null
@@ -0,0 +1,18 @@
+{if:numberedSet}
+       <div flexy:foreach="recordSet,r" class="contactList">
+               <a class="remove" href="{baseUrl:h}admin/members.php?rt=Members&ac=editMember&tab=contacts&id={member}&cid={r[id]}&d=t">
+                       <img title="Delete Contact" width="16" height="16" alt="delete contact" src="<?php echo MEDIA_APP_BASE_URL;?>assets/icons/user_delete.png">
+               </a>
+               <h3>
+                       <a href="{baseUrl:h}admin/members.php?rt=Members&ac=editMember&tab=contacts&id={member}&cid={r[id]}">{r[fname]:h} {r[lname]:h}</a>
+               </h3>
+               <div flexy:if="r[title]" class="title">{r[title]:h}</div>
+               <a flexy:if="r[email]" class="email" href="mailto:{r[email]:h}">{r[email]:h}</a>
+               <div flexy:if="r[phone]" class="phone">{r[phone]:h}</div>
+               {if:r[send_mail]}
+                       <img title="Receives newsletters" width="16" height="16" class="mail" alt="can mail" src="<?php echo MEDIA_APP_BASE_URL;?>assets/icons/email_add.png">
+               {else:}
+                       <img title="Does not receive newsletters" width="16" height="16" class="mail" alt="no mail" src="<?php echo MEDIA_APP_BASE_URL;?>assets/icons/email_delete.png">
+               {end:}
+       </div>
+{end:}
diff --git a/Toolkit/Members/templates/listHtmlEmails.tpl b/Toolkit/Members/templates/listHtmlEmails.tpl
new file mode 100644 (file)
index 0000000..0a94dd2
--- /dev/null
@@ -0,0 +1,36 @@
+{if:newsletters}
+<table class="member-admin-table" style="width:700px;">
+    <thead>
+        <tr>
+            <th></th>
+            <th></th>
+            <th>Subject</th>
+            <th>Last Updated</th>
+        </tr>
+    </thead>
+    <tbody>
+        <tr flexy:foreach="newsletters,v">
+            <td style="width:50px;">
+                <div class="buttons">
+                    <a href="members.php?rt=Newsletter&ac=editHtmlEmail&id={v[id]:h}">
+                        <img src="<?php echo MEDIA_APP_BASE_URL;?>assets/icons/page_edit.png">
+                        Edit
+                    </a>
+                </div>
+            </td>
+            <td style="width:50px;">
+                <div class="buttons">
+                    <a href="members.php?rt=Newsletter&ac=editHtmlEmail&preview=1&news_id={v[id]:h}">
+                        <img src="<?php echo MEDIA_APP_BASE_URL;?>assets/icons/photo.png">
+                        Preview
+                    </a>
+                </div>
+            </td>
+            <td>{v[subject]:h}</td>
+            <td>{v[last_update]:h}</td>
+        </tr>
+    </tbody>
+</table>
+{else:}
+<h3>No {title:h} found!</h3>
+{end:}
diff --git a/Toolkit/Members/templates/listMembers.tpl b/Toolkit/Members/templates/listMembers.tpl
new file mode 100644 (file)
index 0000000..d478b5a
--- /dev/null
@@ -0,0 +1,34 @@
+{if:numberedSet}
+    {showLetters():h}
+
+    <div flexy:if="letters" class="business-first-letter">
+        <div>Narrow result by letter:</div>
+        <a flexy:foreach="letters,k,v" class="{v[class]:h}" href="{v[url]:h}">{k}</a>
+    </div>
+
+    <div class="results">{getResults(#Found %s records in %s page(s)#):h}</div>
+    {getPaging():h}
+    <p>Showing records {firstRecord} to {lastRecord}</p>
+    <div class="searchResult" flexy:foreach="recordSet,k,row">
+        <div id="floating">
+            <div>
+                <div>
+                    <a class="remove" href="{row[del_url]:h}">
+                        <img width="16" height="16" title="Delete record"
+                            src="<?php echo MEDIA_APP_BASE_URL;?>assets/icons/user_delete.png" alt="remove">
+                    </a>
+                </div>
+            </div>
+        </div>
+        <h3>
+            <a href="{row[member_id]:h}">{row[member_name]:h}</a>
+        </h3>
+        <div flexy:if="row[phone]" class="phone">P: {row[phone]:h}</div>
+        <a flexy:if="row[member_contact_email]" class="email" href="mailto:{row[member_contact_email]:h}">
+            {row[member_contact_email]:h}
+        </a>
+    </div>
+    {getPaging():h}
+{else:}
+    <div class="NoMembers">No Records Found!</div>
+{end:}
diff --git a/Toolkit/Members/templates/listNewMembers.tpl b/Toolkit/Members/templates/listNewMembers.tpl
new file mode 100644 (file)
index 0000000..0844b72
--- /dev/null
@@ -0,0 +1,20 @@
+{if:numberedSet}
+    {showLetters():h}
+    <div class="results">
+        {getResults(#Found %s new requests listed in %s page(s)#):h}
+    </div>
+    {getPaging():h}
+    <p>Showing records {firstRecord} to {lastRecord}</p>
+    <div class="searchResult" flexy:foreach="recordSet,k,row">
+        <h3>
+            <a href="{row[member_id]:h}">{row[member_name]:h}</a>
+        </h3>
+        <div flexy:if="row[phone]" class="phone">P: {row[phone]:h}</div>
+        <a flexy:if="row[member_contact_email]" class="email" href="mailto:{row[member_contact_email]:h}">
+            {row[member_contact_email]:h}
+        </a>
+    </div>
+    {getPaging():h}
+{else:}
+    <div class="NoMembers">No New Members Found!</div>
+{end:}
diff --git a/Toolkit/Members/templates/listPendingMembers.tpl b/Toolkit/Members/templates/listPendingMembers.tpl
new file mode 100644 (file)
index 0000000..36b3048
--- /dev/null
@@ -0,0 +1,17 @@
+{if:numberedSet}
+    {showLetters():h}
+    <div class="results">
+        {getResults(#Found %s pending records in %s page(s)#):h}
+    </div>
+    {getPaging():h}
+    <p>Showing records {firstRecord} to {lastRecord}</p>
+    <div class="searchResult" flexy:foreach="recordSet,k,row">
+        <h3>
+            <a href="{row[member_id]:h}">{row[member_name]:h}</a>
+        </h3>
+        <a flexy:if="row[member_contact_email]" class="email"
+            href="mailto:{row[member_contact_email]:h}">{row[member_contact_email]:h}</a>
+    </div>
+{else:}
+    <div class="NoMembers">No Approvals Needed!</div>
+{end:}
diff --git a/Toolkit/Members/templates/listRegions.tpl b/Toolkit/Members/templates/listRegions.tpl
new file mode 100644 (file)
index 0000000..245a267
--- /dev/null
@@ -0,0 +1,21 @@
+<table class="member-admin-table">
+    <thead>
+        <tr>
+            <th></th>
+            <th>Region Name</th>
+        </tr>
+    </thead>
+    <tbody>
+        <tr flexy:foreach="regions,k,v">
+            <td>
+                <div class="buttons">
+                    <a href="members.php?rt=Regions&ac=editRegion&id={k:h}">
+                        <img src="<?php echo MEDIA_APP_BASE_URL;?>assets/icons/note_edit.png">
+                        Edit
+                    </a>
+                </div>
+            </td>
+            <td>{v:h}</td>
+        </tr>
+    </tbody>
+</table>
diff --git a/Toolkit/Members/templates/memberContactsList.tpl b/Toolkit/Members/templates/memberContactsList.tpl
new file mode 100644 (file)
index 0000000..61eff25
--- /dev/null
@@ -0,0 +1,46 @@
+<div id="contact">
+{memberContactSearchForm:h}
+</div>
+{memberSendForm:h}
+{if:numberedSet}
+    <div class="results">{getResults(#Found %s Contacts in %s pages#):h}</div>
+    <div class="paging">
+    {getPaging():h}
+    </div>
+    <p>Showing records {firstRecord} to {lastRecord}</p>
+    <table id="admin-list-table">
+        <tr>
+            <th>
+                &nbsp;
+            </th>
+            <th>
+                Member Name
+            </th>
+            <th>
+                First Name
+            </th>
+            <th>
+                Last Name
+            </th>
+      </tr>
+        <tr flexy:foreach="recordSet,k,row">
+            <td>
+            <a href="{row[directUrl]}">[Edit]</a>
+            </td>
+            <td>
+            {row[member_name]:h}
+            </td>
+            <td>
+            {row[fname]}
+            </td>
+            <td>
+            {row[lname]}
+            </td>
+      </tr>
+    </table>
+    <div class="paging">
+    {getPaging():h}
+    </div>
+{else:}
+    <div class="NoMembers">No Members Found!</div>
+{end:}
diff --git a/Toolkit/Members/templates/memberDetail.tpl b/Toolkit/Members/templates/memberDetail.tpl
new file mode 100644 (file)
index 0000000..e13e130
--- /dev/null
@@ -0,0 +1,232 @@
+<flexy:toJavascript
+flexy:prefix="glm_"
+base_url="base_url"
+trip_planner_id="trip_planner_id"
+catid="catid"
+>
+</flexy:toJavascript>
+<div id="member-detail">
+    <div id="image-box" style="height: 1%; overflow: hidden;">
+        <div flexy:if="logo" id="logo-spot">
+            <img id="member-img-1"
+                alt="Member Logo Image"
+                src="{image_path:h}{logo:h}">
+        </div>
+    </div>
+
+    {memberSince:h}
+
+    <div class="vcard">
+        <h1 class="fn org">{member_name:h}</h1>
+        <div class="adr">
+            <div flexy:if="hasMailingAddy" class="type">Street Address</div>
+            <div class="street-address">{street:h}</div>
+            <div>
+                <span class="locality">{city:h}</span>,
+                <span class="region" title="{state_name:h}">
+                    {state_abbr:h}
+                </span>
+                <span class="postal-code">{zip:h}</span>
+            </div>
+        </div><!-- /.adr -->
+        <div flexy:if="hasMailingAddy" class="adr">
+            <div class="type">Mailing Address</div>
+            <div class="street-address">{mailing_address:h}</div>
+            <div>
+                <span class="locality">{mailing_city:h}</span>,
+                <span class="region" title="{mailing_state_name:h}">
+                    {mailing_state_abbr:h}
+                </span>
+                <span class="postal-code">{mailing_zip:h}</span>
+            </div>
+        </div><!-- /.adr -->
+        <div flexy:if="phone" class="tel">
+            <span class="type">Phone</span>:
+            <span class="value">{phone:h}</span>
+        </div>
+        <div flexy:if="toll_free" class="tel">
+            <span class="type">Toll Free</span>:
+            <span class="value">{toll_free:h}</span>
+        </div>
+        <div flexy:if="fax" class="tel">
+            <span class="type">Fax</span>:
+            <span class="value">{fax:h}</span>
+        </div>
+        <a flexy:if="email" class="email member-button" title="Email" href="mailto:{email:h}">Email</a>
+        <a flexy:if="url" title="Website" href="{url:h}" class="external member-button member-website" rel="{member_id}">Website</a>
+        <div id="google-div">
+            <div class="customDialog">
+                <form id="GDirs" action="http://maps.google.com/maps" method="get" target="_blank">
+                    <h3>Get Driving Directions:</h3>
+                    <div class="hiddenElements">
+                        <input id="toaddress" type="text" name="saddr" size="40" value="Your Address City, State Zip">
+                        <input type="hidden" name="daddr" value="{daddr:h}">
+                        <input type="submit" value="Get Directions">
+                    </div>
+                </form>
+            </div><!--/.customDialog -->
+        </div><!-- /#google-div -->
+    </div><!-- /.vcard -->
+
+    <div flexy:if="show_attributes" id="mColumn">
+        <h2 flexy:if="has_accommodations">Accommodations Info</h2>
+        <ul flexy:if="has_accommodations" id="mColumnAcc">
+            <li>
+                <ul>
+                    <li flexy:if="year_round"> Open Year Round </li>
+                    <li flexy:if="lists_number_of_rooms">
+{num_rooms:h} Rooms
+                    </li>
+{if:does_online_reservations}
+                    <li flexy:if="reservation_id">
+                        <a href="{base_url:h}index.php?catid=50&amp;set_property={reservation_id:h}">Reservations</a>
+                    </li>
+                    <li flexy:if="reservation_url">
+                        <a class="external" href="{reservation_url:h}">Reservations</a>
+                    </li>
+{end:}
+                </ul>
+            </li>
+        </ul>
+        <h2 flexy:if="has_social_media">Social Media Links</h2>
+        <ul flexy:if="has_social_media" id="mColumnSoc">
+            <li flexy:if="facebook" class="mFacebook">
+                <a class="external" href="{facebook:h}">Facebook</a>
+            </li>
+            <li flexy:if="twitter" class="mTwitter">
+                <a class="external" href="{twitter:h}">Twitter</a>
+            </li>
+            <li flexy:if="pinterest" class="mPinterest">
+                <a class="external" href="{pinterest:h}">Pinterest</a>
+            </li>
+            <li flexy:if="myspace" class="mInstagram">
+                <a class="external" href="{instagram:h}">Instagram</a>
+            </li>
+            <li flexy:if="myspace" class="mGooglePlus">
+                <a class="external" href="{google_plus:h}">Google+</a>
+            </li>
+            <li flexy:if="linkedin" class="mLinkedIn">
+                <a class="external" href="{linkedin:h}">LinkedIn</a>
+            </li>
+            <li flexy:if="youtube" class="mYouTube">
+                <a class="external" href="{youtube:h}">YouTube</a>
+            </li>
+            <li flexy:if="blog" class="mBlog">
+                <a class="external" href="{blog:h}">Blog</a>
+            </li>
+            <li flexy:if="myspace" class="mMyspace">
+                <a class="external" href="{myspace:h}">MySpace</a>
+            </li>
+        </ul>
+        <h2 flexy:if="ccards">Payment Types Accepted</h2>
+        <ul flexy:if="ccards">
+            <li>
+                <ul>
+                    <li flexy:foreach="ccards,v">{v:h}</li>
+                </ul>
+            </li>
+        </ul>
+    </div><!-- /#mColumn -->
+
+{if:addToLink}
+    <a id="add-{member_id}" class="list-add-link" rel="{member_id}" flexy:if="plink" href="{plink:h}">
+        <img alt="Add to Your Travel List" class="list-add-link" rel="{member_id}" title="Add to Your Travel List" src="{base_url:h}assets/addto.gif" style="float: right; margin: 10px;clear: right;">
+        <img id="loading-planner" style="display: none;" src="{glm_base_url:h}gallery/loadingAnimation.gif">
+    </a>
+{else:}
+    <a href="{plink:h}" flexy:if="plink" class="list-view-link">
+        <img alt="View Your Travel List" title="View Your Travel List" src="{base_url:h}assets/viewTravelPlanner.gif" style="float: right; margin: 10px;clear: right;">
+    </a>
+{end:}
+
+    <div style="clear: left; text-align: justify;">{description:h}</div>
+
+    <div flexy:if="amenities" class="business-list-amenities">
+        <ul>
+            <li flexy:foreach="amenities,v">{v:h}</li>
+        </ul>
+    </div>
+
+    <div flexy:if="files" id="member-files">
+        <h2>Files Available for Download</h2>
+        <a flexy:foreach="files,k,v" class="file-download pdf external" href="{v[href]:h}">{v[name]:h}</a>
+    </div>
+
+    <div flexy:if="packages" class="member-travel-item" id="packageSection">
+        <h2>Packages</h2>
+        {foreach:packages,v}
+        <div id="member_package_{v[id]:h}">
+            <h3 flexy:if="v[title]">{v[title]:h}</h3>
+            {if:v[image]}
+                <img alt="{v[image]:h}" src="{v[image]:h}">
+            {end:}
+
+            {if:v[description]}
+                {v[description]:h}
+            {end:}
+        </div>
+        {end:}
+    </div>
+
+    <div flexy:if="photos" id="photo-gallery">
+        <h2>Photo Gallery</h2>
+        <div flexy:foreach="photos,v" class="thumb {v[class]:h}">
+            <a class="thickbox" title="{v[alt]:h}" rel="gallery-photos" href="{v[id]:h}">
+                <img alt="{v[alt]:h}" src="{v[src]:h}">
+            </a>
+        </div>
+    </div>
+
+    <div flexy:if="golf_info" class="member-golf-result">
+        <h2>Course Information</h2>
+        <table class="member-golf-stats">
+            <tbody>
+                <tr>
+                    <th flexy:if="par">Par</th>
+                    <th flexy:if="yardage">Yardage</th>
+                    <th flexy:if="course_rating">Course Rating</th>
+                    <th flexy:if="slope_rating">Slope Rating</th>
+                    <th flexy:if="holes9">9 Holes</th>
+                    <th flexy:if="holes18">18 Holes</th>
+                </tr>
+                <tr>
+                    <td flexy:if="par">{par:h}</td>
+                    <td flexy:if="yardage">{yardage:h}</td>
+                    <td flexy:if="course_rating">{course_rating:h}</td>
+                    <td flexy:if="slope_rating">{slope_rating:h}</td>
+                    <td flexy:if="holes9">{holes9:h}</td>
+                    <td flexy:if="holes18">{holes18:h}</td>
+                </tr>
+            </tbody>
+        </table>
+        <div class="member-golf-walking">
+            <strong>Walking Course:</strong>
+            {walking_course:h}
+        </div>
+        <div class="member-golf-teetime" flexy:if="res_url">
+            <a href="{res_url:h}">Make a Tee Time</a>
+        </div>
+    </div>
+
+    <div flexy:if="restaurant_info" class="member-restaurant-item">
+        <h2>Restaurant Information</h2>
+        <table>
+            <tbody>
+                <tr>
+                    <th>Breakfast</th>
+                    <th>Brunch</th>
+                    <th>Lunch</th>
+                    <th>Dinner</th>
+                    <th>Alcohol</th>
+                </tr>
+                <tr>
+                    <td>{breakfast:h}</td>
+                    <td>{brunch:h}</td>
+                    <td>{lunch:h}</td>
+                    <td>{dinner:h}</td>
+                    <td>{alcohol:h}</td>
+                </tr>
+            </tbody>
+        </table>
+    </div>
+</div>
diff --git a/Toolkit/Members/templates/memberNewsletter.tpl b/Toolkit/Members/templates/memberNewsletter.tpl
new file mode 100755 (executable)
index 0000000..d226ed4
--- /dev/null
@@ -0,0 +1,46 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+    <meta http-equiv="content-type" 
+    content="text/html;charset=utf-8">
+</head>
+<body>
+<table cellspacing="0" cellpadding="0" border="0" width="100%">
+    <tr>
+        <td align="center">
+      <table cellspacing="0" cellpadding="0" 
+      border="0" width="550" bgcolor="#ffffff">
+          <tr>
+              <td>
+<a href="baseurl"><img src="baseurl/assets/nHeader.jpg" 
+      width="550" height="114" alt="" 
+      style="border: 0; display: block;"></a>
+      </td>
+       </tr>
+       <tr>
+        <td>
+<table cellspacing="15" cellpadding="0" border="0">
+                <tr>
+                    <td>
+<font face="arial, helvetica, sans-serif" size="2">
+                        {response}
+                        </font>
+                    </td>
+                </tr>
+                <tr>
+                 <td>
+<font face="arial, helvetica, sans-serif" size="1">
+                 <hr>
+                     {bailout}
+                     </font>
+                 </td>
+             </tr>
+            </table>
+        </td>
+    </tr>
+            </table>
+        </td>
+    </tr>
+</table>
+</body>
+</html>
diff --git a/Toolkit/Members/templates/memberSearchForm.tpl b/Toolkit/Members/templates/memberSearchForm.tpl
new file mode 100644 (file)
index 0000000..81fb0b7
--- /dev/null
@@ -0,0 +1,48 @@
+<div id="category-search" class="category-search-accommodations">
+    {form.javascript:h}
+    <flexy:toJSON RegionCats="regionCategories" />
+    <flexy:toJSON mainCats="mainCats" />
+    {form.outputHeader():h}
+    {form.hidden:h}
+        <fieldset>
+            <legend>Search</legend>
+            <ol>
+                {foreach:form.sections,sec}
+                    {foreach:sec.elements,elem}
+                        {if:elem.isType(#group#)}
+                            {if:elem.isName(#amenities#)}
+                                <li id="amenities">
+                                {foreach:elem.elements,gitem}
+                                    <label class="amenity-label">
+                                        {gitem.html:h}{gitem.label:h}
+                                    </label>
+                                {end:}
+                                </li>
+                            {end:}
+                        {else:}
+                            {if:elem.isName(#search_all_amenity#)}
+                                <li id="amenitySearchAll">
+                                    {elem.html:h}{elem.label:h}
+                                </li>
+                            {else:}
+                                <li>
+                                    <label for="{elem.name:h}">{elem.label:h}</label>
+                                    {elem.html:h}
+                                </li>
+                            {end:}
+                        {end:}
+                    {end:}
+                {end:}
+                <li flexy:if="businessAmenities" id="amenities">
+                    <label flexy:foreach="businessAmenities,k,v" class="amenity-label">
+                        <input type="checkbox" value="{v:h}" name="amenities[]">
+                        {k:h}
+                    </label>
+                </li>
+                <li>
+                    <input class="submit" type="submit" value="Search">
+                </li>
+            </ol>
+        </fieldset>
+    </form>
+</div>
diff --git a/Toolkit/Members/templates/membersList.tpl b/Toolkit/Members/templates/membersList.tpl
new file mode 100644 (file)
index 0000000..1e49eaa
--- /dev/null
@@ -0,0 +1,97 @@
+<div flexy:if="member_search_form">{member_search_form:h}</div>
+    <flexy:toJavascript
+    flexy:prefix="glm_"
+    base_url="base_url"
+    trip_planner_id="trip_planner_id"
+    catid="catid"
+    searchMapIconActive="searchMapIconActive"
+    >
+</flexy:toJavascript>
+<div flexy:if="letters" class="business-first-letter opensearchserver.ignore">
+    <div>Narrow result by letter:</div>
+    <a flexy:foreach="letters,k,v" class="{v[class]:h}" href="{v[url]:h}">{k}</a>
+</div>
+
+{if:numberedSet}
+<div class="results">{getResults(#Found %s Businesses in %s page(s)#):h}</div>
+<div class="opensearchserver.ignore">{getPaging():h}</div>
+<p>Showing records {firstRecord} to {lastRecord}</p>
+
+<div flexy:foreach="recordSet,k,row" class="search-result-item vcard">
+
+    <div class="memberSR">
+
+{if:row[addToLink]}
+        <a id="add-{row[member_id]}" class="list-add-link" rel="{row[member_id]}" flexy:if="row[plink]" title="Add to Your Travel List" href="{row[plink]:h}">
+            <img alt="Add to Your Travel List" src="baseurl/assets/addto.gif">
+        </a>
+{else:}
+        <a class="list-view-link" flexy:if="row[plink]" title="View Your Travel List" href="{row[plink]:h}">
+            <img alt="View Your Travel List" src="baseurl/assets/viewTravelPlanner.gif">
+        </a>
+{end:}
+{if:row[logo]}
+        <img class="search-result-img" alt="{row[logo]}" src="{row[logo]}">
+{end:}
+    </div><!-- /.memberSR -->
+        <h2 class="fn org"><a title="More Info" href="{row[url]:h}">{row[member_name]:h}</a></h2>
+        <div class="search-result-item-info">
+    {if:row[map_icon]}
+        <div class="map-link" rel="{row[map_icon][id]}">
+            <img class="search-result-map-img" src="{row[map_icon][icon]:h}">
+        </div>
+    {end:}
+            <div class="search-result-location adr">
+                <div class="street-address">{row[street]:h}</div>
+                <span class="locality">{row[city]:h}</span>,
+                <span class="region" title="{row[state]:h}">{row[state_abbr]:h}</span>
+                <span class="postal-code">{row[zip]:h}</span>
+                <div flexy:if="row[phone]" class="search-result-phones">
+                    <div class="tel">Phone: {row[phone]:h}</div>
+                </div>
+                <div style="height: 1%; overflow: hidden; margin-top: 0.5em;">
+                    <div flexy:if="row[member_contact_email]" class="search-result-email" style="float: left; margin-right: 15px;">
+                        <a title="Email" href="mailto:{row[member_contact_email]:h}">Email</a>
+                    </div>
+                    <div flexy:if="row[website]" class="search-result-website" style="float: left; margin-right: 15px;">
+                        <a target="_blank" title="Website" href="{row[website]:h}" rel="{row[member_id]}" class="member-website">Website</a>
+                    </div>
+                    <div flexy:if="row[address]" class="search-result-directions" style="float: left; margin-right: 15px;">
+                        <a target="_blank" title="Driving Directions" href="{row[address]:h}">Driving Directions</a>
+                    </div>
+                    <div flexy:if="row[has_member_packages]" class="search-result-packages" style="float: left; margin-right: 15px;">
+                        <a title="Member Packages" href="{row[url]:h}#packageSection">Special Offers</a>
+                    </div>
+                </div>
+            </div>
+
+
+
+
+            <div flexy:if="row[has_hotel_info]" class="member-hotel-result">
+                <a flexy:if="row[reservation_id]"
+                    href="{base_url:h}index.php?catid=50&set_property={row[reservation_id]:h}">
+                    <img alt="Online Reservations"
+                        src="baseurl/assets/buttons/reservations.gif">
+                </a>
+                {if:row[num_rooms]}
+                    Number of Rooms: {row[num_rooms]:h}
+                {end:}
+                <br>
+                Open Year Round: {row[year_round]:h}
+            </div>
+        <a
+            flexy:if="row[res_url]"
+            id="ext_detail_teetime_btn"
+            href="{row[res_url]:h}">Make a Tee Time</a>
+        </div>
+    </div>
+
+{getPaging():h}
+<div flexy:if="letters" class="business-first-letter">
+    <div>Narrow result by letter:</div>
+    <a flexy:foreach="letters,k,v" class="{v[class]:h}" href="{v[url]:h}">{k}</a>
+</div>
+{else:}
+<div class="NoMembers">No Members matched your search criteria!</div>
+{end:}
diff --git a/Toolkit/Members/templates/newMemberApproval.tpl b/Toolkit/Members/templates/newMemberApproval.tpl
new file mode 100755 (executable)
index 0000000..727dc25
--- /dev/null
@@ -0,0 +1,40 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+    <meta http-equiv="content-type" content="text/html;charset=utf-8">
+    <title>Registration Complete - {client_name:h}</title>
+</head>
+<body>
+<center>
+<table cellspacing="0" cellpadding="0" bgcolor="#ffffff" border="0">
+        <tr>
+            <td>
+                <table cellspacing="1" cellpadding="15" border="0" bgcolor="#cccccc" width="450">
+                    <tr bgcolor="#cccccc">
+                        <td bgcolor="#ffffff">
+                            <font size="3" face="arial, sans-serif">
+                        <b>Your registration is complete</b>
+                </font>
+                            <br><br>
+                <font size="2" face="arial, sans-serif">
+                                Dear {first_name:h},<br>
+                                Your registration is approved. Below is the username and password you selected.
+                                <br><br>
+                                Username: {member_login:h}
+                                <br>
+                                Password: {member_passwd:h}
+                                <br><br>
+                                Use this to <a href="{base_url:h}index.php?catid={member_category}" target="_blank">log into your members area</a>.
+                                <br><br>
+                                Sincerely,<br>
+                                {client_name:h}
+                            </font>
+                        </td>
+                    </tr>
+                </table>
+            </td>
+        </tr>
+    </table>
+</center>
+</body>
+</html>
diff --git a/Toolkit/Members/templates/newMemberDenial.tpl b/Toolkit/Members/templates/newMemberDenial.tpl
new file mode 100755 (executable)
index 0000000..2a9f19b
--- /dev/null
@@ -0,0 +1,36 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+    <meta http-equiv="content-type" content="text/html;charset=utf-8">
+    <title>Registration not completed - {client_name:h}</title>
+</head>
+<body>
+<center>
+<table cellspacing="0" cellpadding="0" bgcolor="#ffffff" border="0">
+        <tr>
+            <td>
+                <table cellspacing="1" cellpadding="15" border="0" bgcolor="#cccccc" width="450">
+                    <tr bgcolor="#cccccc">
+                        <td bgcolor="#ffffff">
+                            <font size="3" face="arial, sans-serif">
+                        <b>We were unable to complete your registration</b>
+                </font>
+                            <br><br>
+                <font size="2" face="arial, sans-serif">
+                                Dear {first_name:h},<br>
+                                Unfortunately, we were not able to complete your registration.
+                                <br><br>
+                                If you think there has been an error, please contact us.
+                                <br><br>
+                                Sincerely,<br>
+                                {client_name:h}
+                            </font>
+                        </td>
+                    </tr>
+                </table>
+            </td>
+        </tr>
+    </table>
+</center>
+</body>
+</html>
diff --git a/Toolkit/Members/templates/packageList.tpl b/Toolkit/Members/templates/packageList.tpl
new file mode 100644 (file)
index 0000000..119eb18
--- /dev/null
@@ -0,0 +1,43 @@
+
+{if:numberedSet}
+
+<div flexy:foreach="recordSet,k,row" class="search-result-item vcard pItem">
+    <h2><a href="{row[url]:h}">{row[member_name]:h}</a></h2>
+    <div class="memberSR">
+    {if:row[logo]}
+        <img class="search-result-img" alt="{row[logo]}" src="{row[logo]}">
+    {end:}
+    </div>
+    <div class="search-result-location adr">
+        <div class="street-address">{row[street]:h}</div>
+        <span class="locality">{row[city]:h}</span>,
+        <span class="region" title="{row[state]:h}">{row[state_abbr]:h}</span>
+        <span class="postal-code">{row[zip]:h}</span>
+        <div flexy:if="row[phone]" class="search-result-phones">
+            <div class="tel">Phone: {row[phone]:h}</div>
+        </div>
+        <div style="height: 1%; overflow: hidden; margin: 0.5em 0 1em 0;">
+            <div flexy:if="row[member_contact_email]" class="search-result-email" style="float: left; margin-right: 15px;">
+                <a title="Email" href="mailto:{row[member_contact_email]:h}">Email</a>
+            </div>
+            <div flexy:if="row[website]" class="search-result-website" style="float: left; margin-right: 15px;">
+                <a target="_blank" title="Website" href="{row[website]:h}">Website</a>
+            </div>
+            <div flexy:if="row[address]" class="search-result-directions" style="float: left; margin-right: 15px;">
+                <a target="_blank" title="Driving Directions" href="{row[address]:h}">Driving Directions</a>
+            </div>
+        </div>
+    </div>
+
+    <div class="pBox" flexy:foreach="row[packages],item">
+        <h2><a href="{row[url]:h}#member_package_{item[id]}">{item[title]:h}</a></h2>
+        <div>
+        {if:item[image]}
+        <img style="float:right;" alt="" src="{item[image]:h}" class="pImg">
+        {end:}
+        {item[description]:h}
+        </div>
+    </div>
+</div>
+
+{end:}
diff --git a/Toolkit/Members/templates/previewHtmlEmail.tpl b/Toolkit/Members/templates/previewHtmlEmail.tpl
new file mode 100644 (file)
index 0000000..070b059
--- /dev/null
@@ -0,0 +1,47 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+    <meta http-equiv="content-type" content="text/html;charset=utf-8">
+</head>
+<body>
+<table cellspacing="0" cellpadding="0" border="0" width="100%">
+    <tr>
+        <td align="center">
+            <table cellspacing="0" cellpadding="0" border="0" width="550" bgcolor="#ffffff">
+                <tr>
+                    <td>
+                        <a href="baseurl/"><img src="baseurl/assets/nHeader.jpg" width="550" height="114" alt="" style="border: 0; display: block;"></a>
+                    </td>
+                </tr>
+                <tr>
+                    <td>
+                        <table cellspacing="15" cellpadding="0" border="0">
+                            <tr>
+                                <td>
+                                    <font face="arial, helvetica, sans-serif" size="2">
+                                        {response:h}
+                                    </font>
+                                </td>
+                            </tr>
+                            <tr>
+                                <td>
+                                    <font face="arial, helvetica, sans-serif" size="1">
+                                        <hr>
+                                        You are receiving this message because you have expressed an interest in
+                                        receiving specials and information from {sitename}. If you do not
+                                        wish to receive future items of this nature, please reply to this e-mail
+                                        with the word "CANCEL" on the subject line. You will then be removed
+                                        from future mailings.<br>
+                                        <a href="mailto:{membersEnewsEmail}?subject=CANCEL">{membersEnewsEmail}</a><br>
+                                    </font>
+                                </td>
+                            </tr>
+                        </table>
+                    </td>
+                </tr>
+            </table>
+        </td>
+    </tr>
+</table>
+</body>
+</html>
diff --git a/Toolkit/Members/templates/previewNewsletterWrapper.html b/Toolkit/Members/templates/previewNewsletterWrapper.html
new file mode 100644 (file)
index 0000000..9f3d9e8
--- /dev/null
@@ -0,0 +1,2 @@
+<iframe height="600px" width="730px" src="{src:h}">
+</iframe>
diff --git a/Toolkit/Members/templates/settings.html b/Toolkit/Members/templates/settings.html
new file mode 100644 (file)
index 0000000..d9e1245
--- /dev/null
@@ -0,0 +1 @@
+{nav:h}
diff --git a/Toolkit/Members/templates/tripPlannerList.tpl b/Toolkit/Members/templates/tripPlannerList.tpl
new file mode 100644 (file)
index 0000000..e3e7c55
--- /dev/null
@@ -0,0 +1,57 @@
+{if:numberedSet}
+<div id="accountBox" class="tpBox">
+    <p flexy:if="isLoggedIn" id="loggedInAs">
+        Logged in as {username}
+        <a href="{logoutUrl:h}" style="display: block; margin-top: 6px;">Log Out</a>
+    </p>
+    <form id="plannerLogin" action="{loginUrl}" method="post" flexy:if="!isLoggedIn">
+        <h2>Sign In</h2>
+        <p id="plannerUser">Email: <input name="username" type="text"></p>
+        <p id="plannerP">Password: <input name="password" type="password"></p>
+        <p id="plannerSubmit"><input type="submit" class="submit" value="Log In"></p>
+      <p flexy:if="loginStat">{loginStat}</p>
+        <p id="plannerForgot"><a href="{forgotUrl:h}">Forgot your Password?</a></p>
+    </form>
+    <div id="plannerAcc" flexy:if="!isLoggedIn">
+        <h2>If you do not have an account</h2>
+        <a href="{accountUrl:h}" id="plannerNew">Create an account</a>
+        <p>By creating an account, you'll be able to save your list for later.</p>
+    </div>
+<ul id="plannerNav">
+    <li id="pN1"><a href="{requestUrl:h}">Get more info</a></li>
+    <li id="pN2"><a href="#" onclick="window.print()">Print this list</a></li>
+    <li id="pN3"><a class="thickbox" href="baseurl/Toolkit/Members/TripPlanner/tripPlannerMap.php?TB_iframe=true&height=550&width=800">View Map</a></li>
+    <li id="pN4"><a class="thickbox" href="baseurl/Toolkit/Members/TripPlanner/helpme.html?TB_iframe=true">Help</a></li>
+</ul></div>
+
+
+
+<div id="plannerList" style="padding: 6px 0;">
+
+<?php $i = 0;?>
+{foreach:recordSet,k,row}
+<div class="plannerItem">
+    <div id="tpBreadcrumbs">{row[breadCrumbs]:h}</div>
+    <h2>{row[member_name]:h}</h2>
+  <div class="plannerAddress">
+      <p>{row[street]:h}</p>
+    <p>{row[city]:h}, {row[state_abbr]:h} {row[zip]:h}</p>
+    <p flexy:if="row[time_allocated]" class="plannerTime">Time you should plan to spend here: {row[time_allocated]}</p>
+    </div><!-- /.plannerAddress -->
+  <div class="plannerInfo">
+      <p flexy:if="row[phone]">Phone: {row[phone]:h}</p>
+    <p flexy:if="row[member_contact_email]">Email: <a href="mailto:{row[member_contact_email]:h}">{row[member_contact_email]:h}</a></p>
+    <p flexy:if="row[url]"><a href="{row[url]:h}">Website</a></p>
+        <a href="{row[deleteUrl]:h}" class="plannerRemove" title="Remove from list">Remove</a>
+  </div><!-- /.planenrInfo -->
+</div><!-- /.plannerItem -->
+<?php
+if (++$i == 8) {
+$i = 0;
+echo '<div class="page-break"></div>';
+}
+?>
+{end:}
+
+</div><!-- /#plannerList -->
+{end:}
diff --git a/Toolkit/Members/templates/tripPlannerNoList.tpl b/Toolkit/Members/templates/tripPlannerNoList.tpl
new file mode 100644 (file)
index 0000000..16a25d3
--- /dev/null
@@ -0,0 +1,46 @@
+<p>
+This section lets you create your own online itinerary. As you find items on this website that may be of interest to you, simply click the "Add to
+ Travel Planner" icon. Each item will then get added to your list. Once you have
+ completed adding items to your planner list, you may take the following actions:</p>
+
+<ol>
+    <li><strong>Request Info</strong><br>
+This will send an email directly to each individual business using our "Send Me More Information"
+ form. For the items you have added to your list that do not have an
+ email, we will give you their phone number. </li>
+
+<li><strong>Print This List</strong><br>
+Prints the list to your printer, if you have one.</li>
+
+<li><strong>View Map</strong><br>
+This will display your itinerary on a Map, also allowing you to retrieve driving directions.
+</li>
+
+<li>
+<strong>Save your Trip Planner</strong><br>
+Creates an account with this website so you can access your itinerary later.
+</li>
+
+</ol>
+
+<div id="accountBox" class="tpBox tpBoxNoList">
+
+    <p flexy:if="isLoggedIn" id="loggedInAs">
+        Logged in as {username}
+        <a href="{logoutUrl:h}" style="display: block; margin-top: 6px;">Log Out</a>
+    </p>
+    <form id="plannerLogin" action="{loginUrl}" method="post" flexy:if="!isLoggedIn">
+        <h2>Sign In</h2>
+        <p id="plannerUser">Email: <input name="username" type="text"></p>
+        <p id="plannerP">Password: <input name="password" type="password"></p>
+        <p id="plannerSubmit"><input type="submit" class="submit" value="Log In"></p>
+      <p flexy:if="loginStat">{loginStat}</p>
+        <p id="plannerForgot"><a href="{forgotUrl:h}">Forgot your Password?</a></p>
+    </form>
+
+    <div id="plannerAcc" flexy:if="!isLoggedIn">
+        <h2>If you do not have an account</h2>
+        <a href="{accountUrl:h}" id="plannerNew">Create an account</a>
+        <p>By creating an account, you'll be able to save your list for later.</p>
+    </div>
+</div>
diff --git a/Toolkit/Membersonly.php b/Toolkit/Membersonly.php
new file mode 100644 (file)
index 0000000..676dd3c
--- /dev/null
@@ -0,0 +1,407 @@
+<?php
+//    vim:set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker syntax=php:
+
+/**
+ * File Doc Comment
+ *
+ * PHP version 5
+ *
+ * @category MembersDB
+ * @package  Toolkit_Members
+ * @author   Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @license     http://www.gaslightmedia.com Gaslightmedia
+ * @version  CVS: $Id: Membersonly.php,v 1.39 2010/08/15 19:32:01 jamie Exp $
+ * @link     http://demo.gaslightmedia.com
+ */
+
+define('MEMBERS_ONLY_AREA_ON', true);
+
+/**
+ * Base class for the memberdb
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2008 Gaslight Media
+ * @license      http://www.gaslightmedia.com Gaslightmedia
+ * @link      http://demo.gaslightmedia.com
+ */
+class Toolkit_Membersonly
+{
+    //    {{{    properties
+
+    /**
+     * Strict pending for member updates
+     */
+    const STRICT_PENDING = true;
+
+    /**
+     * Table that holds the member update requests
+     */
+    const PENDING_TABLE = 'member_updates';
+
+    //    }}}
+
+    //    {{{    __construct()
+
+    /**
+     * Constructor
+     *
+     * @access    public
+     */
+    public function __construct()
+    {
+    }
+
+    //    }}}
+
+    public static function checkMemberExists(PDO $dbh, $memberId)
+    {
+        try {
+            $sql = "
+            SELECT count(member_id)
+              FROM member
+             WHERE member_id = :member_id";
+            $stmt = $dbh->prepare($sql);
+            $stmt->bindParam(
+                ':member_id',
+                $memberId,
+                PDO::PARAM_INT
+            );
+            $stmt->execute();
+            return $stmt->fetchColumn();
+        } catch(PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+    }
+
+    //    {{{ toHtml()
+
+    /**
+     * Determine which form to show to the user
+     *
+     * When editing a member the (a)ction in the URL controls
+     * which form is displayed to the user. Member Id's from
+     * the database should be passed along as the ID in the $_GET array.
+     *
+     * @param PDO     $dbh Database handler
+     * @param integer $mid member id to use for controller
+     *
+     * @return string HTML page of members only control forms
+     * @access public
+     */
+    public function toHtml(PDO $dbh, $mid)
+    {
+        if (!ctype_digit((string)$mid)) {
+            throw new InvalidArgumentException(
+                "\$mid must be an integer - `$mid` given"
+            );
+        }
+
+        $GLOBALS['styleSheets'][] = MEDIA_BASE_URL . 'Toolkit/Members/css/member-admin.css';
+
+        //  application configuration
+        $conf = new Config;
+        $root = $conf->parseConfig(
+            BASE . 'Toolkit/Members/config.ini', 'IniFile'
+        );
+
+        $nav = new Toolkit_Members_RecordNavigation($root);
+        $nav->setupUserNavStructure();
+
+        $tplOpts =  Toolkit_Members::getFlexyOptions();
+        $tEngine = new HTML_Template_Flexy($tplOpts);
+
+        switch ($_GET['tab']) {
+        case 'contacts' :
+            $GLOBALS['bottomScripts'][]
+                = MEDIA_BASE_URL . 'Toolkit/Members/libjs/edit-member-contacts.js';
+
+            $mc = new Toolkit_Members_EditMemberOnlyContacts(
+                $dbh,
+                'edit_contacts',
+                'post',
+                null,
+                null,
+                null,
+                true
+            );
+            if (   isset($_GET['d'])
+                && $_GET['d'] == 't'
+                && $contactId = filter_input(INPUT_GET, 'cid', FILTER_VALIDATE_INT)
+            ) {
+                $mc->removeContact($contactId, $_GET['id']);
+            }
+            $mc->setConfig($root);
+            $mc->configureForm();
+            $out = $mc->toHtml();
+            break;
+
+        case 'files' :
+            $GLOBALS['bottomScripts'][]
+                = MEDIA_BASE_URL . 'Toolkit/Members/libjs/edit-member-files.js';
+            $mf = new Toolkit_Members_EditMemberOnlyFile($dbh, 'edit_files');
+            $mf->setConfig($root);
+            $mf->configureForm();
+            $out = $mf->toHtml();
+            break;
+
+        case 'amenities' :
+            $GLOBALS['bottomScripts'][]
+                = MEDIA_BASE_URL . 'Toolkit/Members/libjs/edit-member-amenities.js';
+
+            $ma = new Toolkit_Members_EditMemberOnlyAmenities(
+                $dbh,
+                'edit_amenities',
+                'post',
+                null,
+                null,
+                null,
+                true
+            );
+            $ma->setConfig($root);
+            $ma->configureForm();
+            $out = $ma->toHtml();
+            break;
+
+        case 'packages' :
+            $GLOBALS['bottomScripts'][]
+                = CKEDITOR_JS . '';
+            $GLOBALS['bottomScripts'][]
+                = MEDIA_BASE_URL . 'Toolkit/Members/libjs/edit-member-packages.js';
+
+            $mp = new Toolkit_Members_EditPackages($dbh, $tEngine);
+            $mp->setupPage($root);
+            $out = $mp->getPage($nav);
+            break;
+
+        case 'photos' :
+            $GLOBALS['bottomScripts'][]
+                = MEDIA_APP_BASE_URL . 'libjs/jqueryui/1.8.13/js/jquery-ui-1.8.13.custom.min.js';
+            $GLOBALS['bottomScripts'][]
+                = MEDIA_BASE_URL . 'Toolkit/Members/libjs/edit-member-photos.js';
+
+            $notifyOwner = filter_var($_REQUEST['notify'], FILTER_VALIDATE_BOOLEAN);
+            //  Object to use when rendering the page template
+            $page = new stdClass();
+            //  Page navigation
+            $page->nav = $nav->getPageNav();
+
+            //  Linked List of member photos
+            $linkedList = new Toolkit_Members_Photos(null, $mid);
+            $linkedList->createMemberList(
+                $dbh,
+                $root
+            );
+            //  Member Object to get calculate attributes about a member
+            $member = new Toolkit_Members_Member(
+                $dbh,
+                $root
+            );
+            //  Server side caching
+            $cache = new Cache_Lite(Toolkit_Members::getCacheOptions());
+            //  Image server for processing uploaded images
+            $is = new Toolkit_Image_Server();
+
+            if ($member->canAddPhotos($linkedList, $root)) {
+                $aForm = new Toolkit_Members_AddPhoto(
+                    $dbh,
+                    'new_member_photo',
+                    'post',
+                    MEDIA_BASE_URL . "members-only-area/?rt=EditProfile&tab=photos",
+                    '',
+                    null,
+                    true
+                );
+                $aForm->configureForm($root);
+                $page->uploadForm = $aForm->toHtml(
+                    $tEngine,
+                    $cache,
+                    $root,
+                    $is,
+                    $linkedList
+                );
+            }
+
+            if ($member->hasUploadedPhotos($linkedList)) {
+                $editForms = array();
+                $linkedList->rewind();
+
+                foreach ($linkedList as $i) {
+                    $id = $i->getId();
+                    $eForm = new Toolkit_Members_EditPhoto(
+                        $dbh,
+                        $linkedList,
+                        "edit_member_photo_$id",
+                        'post',
+                        null,
+                        null,
+                        array('id' => $id),
+                        true
+                    );
+                    $eForm->configureForm($root);
+                    $page->editForms[$id] = $eForm->toHtml(
+                        $tEngine,
+                        $cache,
+                        $is,
+                        $root
+                    );
+                }
+            }
+
+            if ($notifyOwner) {
+                $mail =& Mail::factory('mail');
+                $this->updateNotificationEmail(
+                    $root,
+                    $tEngine,
+                    $mail
+                );
+            }
+            //  get reference to [photos] section of config file
+            $config = $root->getItem('section', 'photos');
+            $template = $config->getItem('directive', 'editGalleryTemplate');
+
+            $tEngine->compile($template->getContent());
+            $out = $tEngine->bufferedOutputObject($page);
+            break;
+
+        case 'info' :
+        default :
+            $GLOBALS['bottomScripts'][]
+                = CKEDITOR_JS . '';
+            $GLOBALS['bottomScripts'][]
+                = MEDIA_BASE_URL . 'Toolkit/Members/libjs/edit-member.js';
+            $GLOBALS['bottomScripts'][]
+                = MEDIA_APP_BASE_URL . 'gallery/thickbox-3.1.1.js';
+            $GLOBALS['styleSheets'][]
+                = MEDIA_APP_BASE_URL . 'gallery/thickbox.css';
+
+            $mr = new Toolkit_Members_EditMemberOnlyInfo(
+                $dbh,
+                'edit_member',
+                'post',
+                null,
+                null,
+                null,
+                true
+            );
+
+            $mr->setStates(
+                Toolkit_Common::getStates($dbh)
+            );
+            $mr->setCities(
+                Toolkit_Common::getCities($dbh)
+            );
+            $mr->setConfig($root);
+            $mr->configureForm();
+            $mr->useCluetip();
+            $out = $mr->toHtml();
+            break;
+        }
+
+        return $out;
+    }
+
+    //    }}}
+
+    //    {{{    show()
+
+    /**
+     * Call the toHtml function and echo out the results
+     *
+     * @param PDO     $dbh Database handler
+     * @param integer $mid member id to use for controller
+     *
+     * @return void
+     * @access public
+     */
+    public function show(PDO $dbh, $mid)
+    {
+        echo $this->toHtml($dbh, $mid);
+    }
+
+    //    }}}
+
+    //    {{{    updateNotificationEmail()
+
+    /**
+     * Emails the owner and anyone else who wants to be advised of updates
+     *
+     * A false value in the MEMBER_RECORD_UPDATES_ADVISOR will cause no email
+     * to be sent. all secondary advisees listed in the constructor are carbon
+     * copied in the email.
+     *
+     * Emails are sent out in both HTML and TXT forms.
+     *
+     * @param Config_Container    $c       Configuration object
+     * @param HTML_Template_Flexy $tEngine Flexy Templating Engine
+     * @param Mail                $mail    Mail object
+     *
+     * @return boolean result of mailing
+     * @access protected
+     * @since  Method available since Release 1.5
+     */
+    static public function updateNotificationEmail(
+        Config_Container $c,
+        HTML_Template_Flexy $tEngine,
+        Mail $mail
+    ) {
+        if (MEMBER_RECORD_UPDATES_ADVISOR === false) {
+            return;
+        } else {
+            try {
+                $sql = "
+                    SELECT member_name
+                      FROM member
+                     WHERE member_id = :member_id";
+                $dbh = Toolkit_Database::getInstance();
+                $stmt = $dbh->prepare($sql);
+                $stmt->bindParam(':member_id', $_GET['id'], PDO::PARAM_INT);
+                $stmt->execute();
+                $row = $stmt->fetch(PDO::FETCH_ASSOC);
+                $memberName = $row['member_name'];
+            } catch (PDOException $e) {
+                return Toolkit_Common::handleError($e);
+            }
+            $page     = new StdClass;
+
+            $page->member   = $memberName;
+            $page->url      = MEDIA_BASE_URL;
+            $page->email    = DO_NOT_REPLY_EMAIL;
+            $page->siteName = SITENAME;
+            $page->link     = '<a target="_blank"  href="'.MEDIA_BASE_URL.'pending-member/'.$_GET['id'].'/">link</a>';
+
+            $tEngine->compile('emailOwner.tpl');
+            //    Merge the compiled template with the $page object.
+            $htmlMsg = $tEngine->bufferedOutputObject($page);
+
+            $msg = "
+                <h3>$memberName</h3>
+                <p>
+                    Has updated their business record and is now in a pending
+                    state. To approve / reject their changes you can either log
+                    into your {$page->siteName} admin area or follow this
+                    {$page->link}
+                </p>";
+            $crlf     = "\n";
+            $mimeMail = new Mail_mime($crlf);
+            $from = preg_replace("%[^A-Za-z ]%", "", SITENAME) . ' <' . DO_NOT_REPLY_EMAIL . '>';
+            $mimeMail->setFrom($from);
+            $mimeMail->setSubject('Member Record Update');
+            $mimeMail->setHTMLBody($htmlMsg);
+            $mimeMail->setTXTBody($msg);
+
+            $body    = $mimeMail->get();
+            $headers = $mimeMail->headers($hdrs);
+
+            $res = $mail->send(MEMBER_RECORD_UPDATES_ADVISOR, $headers, $body);
+            if (PEAR::isError($res)) {
+                return Toolkit_Common::handleError($res);
+            } else {
+                return $res;
+            }
+        }
+    }
+
+    //    }}}
+}
diff --git a/Toolkit/NavigationAbstract.php b/Toolkit/NavigationAbstract.php
new file mode 100644 (file)
index 0000000..524243a
--- /dev/null
@@ -0,0 +1,89 @@
+<?php
+/**
+ * NavigationAbstract.php
+ *
+ * PHP version 5.2
+ *
+ * @category  Toolkit
+ * @package   None
+ * @author    Chuck Scott <cscott@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @version   SVN: $id$
+ * @link      <>
+ */
+
+/**
+ * Toolkit_NavigationAbstract
+ *
+ * Description of NavigationAbstract
+ *
+ * @category  Toolkit
+ * @package   None
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @release   Release: $id$
+ * @link      <>
+ */
+abstract class Toolkit_NavigationAbstract
+{
+    //  {{{ properties
+
+    /**
+     * Default nav index
+     * @param integer
+     * @access protected
+     */
+    protected $currIndex;
+
+    /**
+     * page menu
+     * @param HTML_Menu
+     * @access protected
+     */
+    protected $menu;
+
+    /**
+     * menu rendering engine
+     * @param HTML_Menu_DirectRenderer
+     * @access protected
+     */
+    protected $rEngine;
+
+    //  }}}
+    //  {{{ renderPageNav()
+
+    /**
+     * Render the page navigation that is defined in the navStructure
+     *
+     * @param array  $navStructure Page navigation structure
+     * @param string $type         Type of menu to render
+     *
+     * @return string  Page navigation structure
+     * @access public
+     */
+    public function renderPageNav(array $navStructure, $type)
+    {
+        $this->menu->setMenu($navStructure);
+        $this->menu->setMenuType($type);
+
+        $this->setCurrentIndex();
+        $this->setNavTemplates();
+
+        $this->menu->render($this->rEngine);
+        return $this->rEngine->toHtml();
+    }
+
+    //  }}}
+    //  {{{ setNavTemplates()
+    /**
+     * set nav templates
+     *
+     * @return void
+     */
+    abstract protected function setNavTemplates();
+
+    //  }}}
+}
+?>
diff --git a/Toolkit/NavigationFactoryAbstract.php b/Toolkit/NavigationFactoryAbstract.php
new file mode 100644 (file)
index 0000000..9288e31
--- /dev/null
@@ -0,0 +1,48 @@
+<?php
+/**
+ * NavigationFactoryAbstract.php
+ *
+ * PHP version 5
+ *
+ * @category  Toolkit
+ * @package   Toolkit_NavigationFactoryAbstract
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @release   Release: $id$
+ * @link      <>
+ */
+
+/**
+ * Toolkit_BaseControllerAbstract
+ *
+ * Generate a sitemap for the site
+ * need to remove members only pages and other pages you
+ * don't want viewed by public
+ *
+ * @category  Toolkit
+ * @package   Toolkit_NavigationFactoryAbstract
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @release   Release: $id$
+ * @link      <>
+ */
+
+abstract class Toolkit_NavigationFactoryAbstract
+{
+    /**
+     * create side nav
+     *
+     * @return void
+     */
+    abstract public function createSideNav();
+
+    /**
+     * create side nav
+     *
+     * @return void
+     */
+    abstract public function createMainNav();
+}
+?>
diff --git a/Toolkit/PHPImageEditor/classes/phpimageeditor.php b/Toolkit/PHPImageEditor/classes/phpimageeditor.php
new file mode 100644 (file)
index 0000000..1abe15b
--- /dev/null
@@ -0,0 +1,843 @@
+<?php
+    /**
+    * phpimageeditor.php
+    * 
+    * PHP version 5.2
+    * 
+    * @category  Toolkit
+    * @package   PHPImageEditor/Classes
+    * @author    Jamie Kahgee <steve@gaslightmedia.com>
+    * @copyright 2012 Gaslight Media
+    * @license   Gaslight Media
+    * @version   SVN: $id$
+    * @link      <>
+    */
+
+
+    /*
+    Copyright 2008, 2009, 2010 Patrik Hultgren
+
+    YOUR PROJECT MUST ALSO BE OPEN SOURCE IN ORDER TO USE PHP IMAGE EDITOR.
+    OR ELSE YOU NEED TO BUY THE COMMERCIAL VERSION AT:
+    http://www.shareit.com/product.html?productid=300296445&backlink=http%3A%2F%2Fwww.phpimageeditor.se%2F
+
+    This file is part of PHP Image Editor.
+
+    PHP Image Editor is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    PHP Image Editor is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with PHP Image Editor.  If not, see <http://www.gnu.org/licenses/>.
+    */
+
+
+       //Requires
+       //PHP: 4.3
+       //GD: 2.0.28
+
+       /*PHP FUNCTIONS REQUIRES THESE VERSIONS
+       getimagesize PHP 4
+       image_type_to_mime_type PHP 4.3
+       imagecopyresized PHP 4
+       file_exists PHP 4
+       copy PHP 4
+       imagecreatetruecolor PHP 4.0.6, GD 2.0.1
+       imagecopyresized PHP 4
+       imagecopy PHP 4
+       imagecreatefromjpeg PHP 4, GD 1.8
+       imagecreatefromgif PHP 4, GD 2.0.28
+       imagecreatefrompng PHP 4
+       imagefilter PHP 5, GD
+       imagejpeg PHP 4, GD-1.8
+       imagepng PHP 4
+       imagegif PHP 4
+       imagerotate PHP 4.3 GD
+       imagesx PHP 4
+       imagesy PHP 4
+       */
+
+    
+    /**
+    * Description of PHPImageEditor
+    * 
+    * @category  Toolkit
+    * @package   PHPImageEditor/Classes
+    * @author    Jamie Kahgee <steve@gaslightmedia.com>
+    * @copyright 2012 Gaslight Media
+    * @license   Gaslight Media
+    * @release   Release: $id$
+    * @link      <>
+    */
+class PHPImageEditor
+{
+               public $srcEdit = "";
+               public $srcOriginal = "";
+               public $srcPng = "";
+               public $srcWorkWith = "";
+               public $resourceWorkWith = false;
+               public $mimeType = "";
+               public $actionSaveAndClose = "save";
+               public $actionRotateLeft = "rotate:90";
+               public $actionRotateRight = "rotate:-90";
+               public $actionGrayscale = "grayscale";
+               public $actionContrast = "contrast";
+               public $actionBrightness = "brightness";
+               public $actionUndo = "undo";
+               public $actionUpdate = "update";
+               public $actionRotateIsSelected = false;
+               public $actionRotate = "";
+               public $actionSeparatorSign = "#";
+               public $fieldNameKeepProportions = "keepproportions";
+               public $errorMessages = array();
+               public $formName = "phpimageeditor";
+        public $inputImageName = '';
+               public $inputWidth = -1;
+               public $inputHeight = -1;
+               public $inputCropLeft = 0;
+               public $inputCropRight = 0;
+               public $inputCropTop = 0;
+               public $inputCropBottom = 0;
+               public $inputKeepProportions = true;
+               public $inputCropKeepProportions = false;
+               public $inputCropKeepProportionsRatio = 1;
+               public $inputPanel = MENU_RESIZE;
+               public $inputLanguage = DEFUALT_LANGUAGE;
+               public $inputContrast = 0;
+               public $inputBrightness = 0;
+               public $inputContrastLast = 0;
+               public $inputBrightnessLast = 0;
+               public $inputGrayscale = false;
+               public  $httpImageKey = "imagesrc";
+               public  $texts = array();
+               public $actions = "";
+               public $isPostBack = false;
+               public $isAjaxPost = false;
+               public $finalWidth = -1;
+               public $finalHeight = -1;
+               public $widthKeepProportions = -1;
+               public $heightKeepProportions = -1;
+               public $userId = "";
+
+               public $contrastMax = 100;
+               public $brightnessMax = 255;
+
+
+               function PHPImageEditor()
+               {
+                       $this->LoadLanguage();
+
+                       if (version_compare(phpversion(), PHP_VERSION_MINIMUM, "<"))
+                       {
+                               $this->errorMessages[] = phpversion()." ".$this->texts["OLD PHP VERSION"]." ".PHP_VERSION_MINIMUM;
+                               return;
+                       }
+
+                       $this->isPostBack = isset($_POST["actiontype"]);
+
+                       $srcEdit = "";
+
+                       if ($_GET[$this->httpImageKey] != NULL)
+                       {
+                               $srcEdit = $_GET[$this->httpImageKey];
+                       }
+
+                       if ($srcEdit == "")
+                       {
+                               $this->errorMessages[] = $this->texts["NO PROVIDED IMAGE"];
+                               return;
+                       }
+
+                       $this->srcEdit = urldecode($srcEdit);
+            $this->inputImageName = urlencode($_GET['image_name']);
+
+                       if (isset($_POST["userid"]))
+                               $this->userId = $_POST["userid"];
+                       else
+                               $this->userId = "_".time()."_".str_replace(".", "_", $_SERVER['REMOTE_ADDR']);
+
+                       $this->SetSrcOriginal();
+                       $this->SetSrcPng();
+                       $this->SetSrcWorkWith();
+
+                       if (!file_exists($this->srcEdit))
+                       {
+                               $this->errorMessages[] = $this->texts["IMAGE DOES NOT EXIST"];
+                               return;
+                       }
+
+                       $info = getimagesize($this->srcEdit);
+
+                       if (!$info)
+                       {
+                               $this->errorMessages[] = $this->texts["INVALID IMAGE TYPE"];
+                               return;
+                       }
+
+                       $this->mimeType = image_type_to_mime_type($info[2]);
+
+                       if ($this->mimeType == image_type_to_mime_type(IMAGETYPE_JPEG) || $this->mimeType == image_type_to_mime_type(IMAGETYPE_GIF) || $this->mimeType == image_type_to_mime_type(IMAGETYPE_PNG))
+                       {
+                               if (!$this->isPostBack)
+                                       $this->SaveOriginal();
+
+                               $this->resourceWorkWith = $this->CreateImage($this->srcOriginal);
+                               $this->SavePng();
+                               copy($this->srcPng, $this->srcWorkWith);
+
+                               $this->resourceWorkWith = $this->CreateImage($this->srcPng);
+                       }
+                       else
+                       {
+                               $this->errorMessages[] = $this->texts["INVALID IMAGE TYPE"];
+                               return;
+                       }
+
+                       $this->finalWidth = $this->GetWidth();
+                       $this->finalHeight = $this->GetHeight();
+                       $this->widthKeepProportions = $this->GetWidth();
+                       $this->heightKeepProportions = $this->GetHeight();
+
+                       $this->GrantAllAccess($this->srcOriginal);
+                       $this->GrantAllAccess($this->srcPng);
+                       $this->GrantAllAccess($this->srcWorkWith);
+
+                       if ($this->isPostBack)
+                       {
+                               $this->actionRotateIsSelected = ($_POST["rotate"] != "-1");
+                               $this->actionRotate = $_POST["rotate"];
+                               $this->actions = $_POST["actions"];
+                               $this->isAjaxPost = ($_POST["isajaxpost"] == "true");
+
+//                $this->inputImageName = $_POST['image_name'];
+                               $this->inputWidth = (int)$_POST["width"];
+                               $this->inputHeight = (int)$_POST["height"];
+                               $this->inputCropLeft = (int)$_POST["cropleft"];
+                               $this->inputCropRight = (int)$_POST["cropright"];
+                               $this->inputCropTop = (int)$_POST["croptop"];
+                               $this->inputCropBottom = (int)$_POST["cropbottom"];
+                               $this->inputPanel = (int)$_POST["panel"];
+                               $this->inputLanguage = $_POST["language"];
+                               $this->inputKeepProportions = ($_POST["keepproportionsval"] == "1");
+                               $this->inputCropKeepProportions = ($_POST["cropkeepproportionsval"] == "1");
+                               $this->inputCropKeepProportionsRatio = (float)$_POST["cropkeepproportionsratio"];
+                               $this->inputGrayscale = ($_POST["grayscaleval"] == "1");
+                               $this->inputBrightness = (int)$_POST["brightness"];
+                               $this->inputContrast = (int)$_POST["contrast"];
+                               $this->inputBrightnessLast = (int)$_POST["brightnesslast"];
+                               $this->inputContrastLast = (int)$_POST["contrastlast"];
+
+                               $this->Action($_POST["actiontype"]);
+                       }
+               }
+
+               function LoadLanguage()
+               {
+                       $language = "";
+
+                       if (isset($_POST["language"]))
+                       {
+                               $this->inputLanguage = $_POST["language"];
+                               $language = $this->inputLanguage;
+                       }
+                       else if (isset($_GET["language"]))
+                       {
+                               $this->inputLanguage = $_GET["language"];
+                               $language = $this->inputLanguage;
+                       }
+                       else
+                               $language = DEFUALT_LANGUAGE;
+
+                       $tryLanguage = "language/".$language."/".$language.".com_admin.ini";
+                       if (file_exists($tryLanguage))
+                               $this->texts = PIE_GetTexts("language/".$language."/".$language.".com_admin.ini");
+                       else
+                               $this->texts = PIE_GetTexts("language/".DEFUALT_LANGUAGE."/".DEFUALT_LANGUAGE.".com_admin.ini");
+               }
+
+               function SetSrcOriginal()
+               {
+                       $arr = explode("/", $this->srcEdit);
+                       $this->srcOriginal = IMAGE_ORIGINAL_PATH.$this->AddUserIdToImageSrc($arr[count($arr)-1]);
+               }
+
+               function SetSrcWorkWith()
+               {
+                       $arr = explode("/", $this->srcEdit);
+                       $srcWorkWith = IMAGE_WORK_WITH_PATH.$this->AddUserIdToImageSrc($arr[count($arr)-1]);
+                       $srcWorkWith = substr($srcWorkWith, 0, strripos($srcWorkWith, ".")).".png";
+                       $this->srcWorkWith = $srcWorkWith;
+               }
+
+               function SetSrcPng()
+               {
+                       $arr = explode("/", $this->srcEdit);
+                       $srcPng = IMAGE_PNG_PATH.$this->AddUserIdToImageSrc($arr[count($arr)-1]);
+                       $srcPng = substr($srcPng, 0, strripos($srcPng, ".")).".png";
+                       $this->srcPng = $srcPng;
+               }
+
+               function SaveOriginal()
+               {
+                       copy($this->srcEdit, $this->srcOriginal);
+
+                       //Resize to fit in max width/height.
+                       $imageTmp = $this->CreateImage($this->srcOriginal);
+                       $finalWidth = $this->GetWidthFromImage($imageTmp);
+                       $finalHeight = $this->GetHeightFromImage($imageTmp);
+
+                       $doSave = false;
+
+                       if ($finalWidth > IMAGE_MAX_WIDTH)
+                       {
+                               $widthProp = IMAGE_MAX_WIDTH/$finalWidth;
+                               $finalWidth = IMAGE_MAX_WIDTH;
+                               $finalHeight = round($finalHeight*$widthProp);
+                               $doSave = true;
+                       }
+
+                       if ($finalHeight > IMAGE_MAX_HEIGHT)
+                       {
+                               $heightProp = IMAGE_MAX_HEIGHT/$finalHeight;
+                               $finalHeight = IMAGE_MAX_HEIGHT;
+                               $finalWidth = round($finalWidth*$heightProp);
+                               $doSave = true;
+                       }
+
+                       if ($doSave)
+                       {
+                               $imageTmp = $this->ActionResize($finalWidth, $finalHeight, $imageTmp);
+                               $this->SaveImage($imageTmp, $this->srcOriginal);
+                       }
+               }
+
+               function SavePng()
+               {
+                       $this->SaveImage($this->resourceWorkWith, $this->srcPng, image_type_to_mime_type(IMAGETYPE_PNG));
+               }
+
+               function ErrorHasOccurred()
+               {
+                       return (count($this->errorMessages) > 0);
+               }
+
+               function GetWidthFinal()
+               {
+                       return $this->finalWidth;
+               }
+
+               function GetHeightFinal()
+               {
+                       return $this->finalHeight;
+               }
+
+               function GetWidth()
+               {
+                       return $this->GetWidthFromImage($this->resourceWorkWith);
+               }
+
+               function GetWidthLast()
+               {
+                       if ($this->isPostBack)
+                               return (int)$_POST["widthlast"];
+
+                       return $this->GetWidth();
+               }
+
+               function GetHeight()
+               {
+                       return $this->GetHeightFromImage($this->resourceWorkWith);
+               }
+
+               function GetHeightLast()
+               {
+                       if ($this->isPostBack)
+                               return (int)$_POST["heightlast"];
+
+                       return $this->GetWidth();
+               }
+
+        function GetImageNameFinal()
+        {
+            if ($this->isPostBack)
+                               return $_POST["image_name"];
+
+            return $this->inputImageName;
+        }
+
+        function getImageName()
+        {
+                       return $this->inputImageName;
+        }
+
+               function GetWidthFromImage($image)
+               {
+                       return imagesx($image);
+               }
+
+               function GetHeightFromImage($image)
+               {
+                       return imagesy($image);
+               }
+
+               function Action($actionType)
+               {
+                       $doSave = false;
+
+                       if ($actionType == $this->actionUndo) {
+                               $this->ActionUndo();
+                               $doSave = true;
+                       }
+
+                       if (   $actionType == $this->actionUpdate
+                || $actionType == $this->actionSaveAndClose) {
+                               if (   $this->inputWidth != $this->GetWidthLast()
+                    || $this->inputHeight != $this->GetHeightLast()) {
+                                       $this->actions .= $this->GetActionSeparator()."resize:".$this->inputWidth.",".$this->inputHeight;
+                }
+
+                               if (   $this->inputCropLeft != 0 || $this->inputCropRight != 0
+                    || $this->inputCropTop != 0 || $this->inputCropBottom != 0) {
+                                       $this->actions
+                        .= $this->GetActionSeparator()
+                        ."crop:".$this->inputCropLeft.","
+                        .$this->inputCropRight.","
+                        .$this->inputCropTop.","
+                        .$this->inputCropBottom;
+                }
+//                var_dump($this->inputImageName);
+//                var_dump($this->getImageNameFinal());
+//                var_dump($_POST);
+//                var_dump($_GET);
+//                exit;
+                if (urldecode($this->inputImageName) != $this->GetImageNameFinal()) {
+                    $this->actions .= $this->GetActionSeparator ()."imageName:".$this->GetImageNameFinal();
+                }
+                               $doSave = true;
+                       }
+
+                       if ($actionType == $this->actionUpdate && $this->inputGrayscale) {
+                               if (strpos($this->actions, $this->actionGrayscale) === false) {
+                                       $this->actions .= $this->GetActionSeparator().$this->actionGrayscale.":0";
+                                       $doSave = true;
+                               }
+                       } else if ($actionType == $this->actionUpdate && !$this->inputGrayscale) {
+                               if (!(strpos($this->actions, $this->actionGrayscale) === false)) {
+                                       $this->actions = str_replace($this->actionGrayscale.":0".$this->GetActionSeparator(), "", $this->actions);
+                                       $this->actions = str_replace($this->GetActionSeparator().$this->actionGrayscale.":0", "", $this->actions);
+                                       $this->actions = str_replace($this->actionGrayscale.":0", "", $this->actions);
+                                       $doSave = true;
+                               }
+                       }
+
+                       if ($this->inputContrast != $this->inputContrastLast) {
+                               $this->actions .= $this->GetActionSeparator().$this->actionContrast.":".$this->inputContrast;
+                               $doSave = true;
+                       }
+
+                       if ($this->inputBrightness != $this->inputBrightnessLast) {
+                               $this->actions .= $this->GetActionSeparator().$this->actionBrightness.":".$this->inputBrightness;
+                               $doSave = true;
+                       }
+
+                       if ($this->actionRotateIsSelected) {
+                               if ($this->actionRotate == $this->actionRotateLeft) {
+                                       $this->actions .= $this->GetActionSeparator().$this->actionRotateLeft;
+                                       $doSave = true;
+                               } else if ($this->actionRotate == $this->actionRotateRight) {
+                                       $this->actions .= $this->GetActionSeparator().$this->actionRotateRight;
+                                       $doSave = true;
+                               }
+                       }
+
+                       $finalContrast = 0;
+                       $finalBrightness = 0;
+                       $finalContrastFound = false;
+                       $finalBrightnessFound = false;
+                       $finalGrayscale = false;
+
+                       if ($doSave && $this->actions != "") {
+                               $allActions = explode($this->actionSeparatorSign, $this->actions);
+
+                               $finalRotate = 0;
+                               $finalCropLeft = 0;
+                               $finalCropRight = 0;
+                               $finalCropTop = 0;
+                               $finalCropBottom = 0;
+
+                               $doSwitch = false;
+
+                               foreach ($allActions as $loopAction) {
+                                       $actionDetail = explode(":", $loopAction);
+                                       $actionValues = explode(",", $actionDetail[1]);
+
+                                       if ($actionDetail[0] == "resize") {
+                                               $this->finalWidth = (int)$actionValues[0];
+                                               $this->finalHeight = (int)$actionValues[1];
+                                       } else if ($actionDetail[0] == "crop") {
+                                               $actionValueLeft = (int)$actionValues[0];
+                                               $actionValueRight = (int)$actionValues[1];
+                                               $actionValueTop = (int)$actionValues[2];
+                                               $actionValueBottom = (int)$actionValues[3];
+
+                                               $widthProp = 1;
+                                               $heightProp = 1;
+
+                                               if ($doSwitch) {
+                                                       $widthProp = (($this->GetHeight()-($finalCropTop + $finalCropBottom)) / $this->finalWidth);
+                                                       $heightProp = (($this->GetWidth()-($finalCropLeft + $finalCropRight)) / $this->finalHeight);
+                                               } else {
+                                                       $widthProp = (($this->GetWidth()-($finalCropLeft + $finalCropRight)) / $this->finalWidth);
+                                                       $heightProp = (($this->GetHeight()-($finalCropTop + $finalCropBottom)) / $this->finalHeight);
+                                               }
+
+                                               $cropLeft = $actionValueLeft * $widthProp;
+                                               $cropRight = $actionValueRight * $widthProp;
+                                               $cropTop = $actionValueTop * $heightProp;
+                                               $cropBottom = $actionValueBottom * $heightProp;
+
+                                               $cropValues = array();
+                                               $cropValues[] = $cropRight;
+                                               $cropValues[] = $cropBottom;
+                                               $cropValues[] = $cropLeft;
+                                               $cropValues[] = $cropTop;
+
+                                               if ($finalRotate != 0)
+                                                       $cropValues = $this->RotateArray(($finalRotate/-90), $cropValues);
+
+                                               $finalCropRight += $cropValues[0];
+                                               $finalCropBottom += $cropValues[1];
+                                               $finalCropLeft += $cropValues[2];
+                                               $finalCropTop += $cropValues[3];
+
+                                               $this->finalWidth -= ($actionValueLeft + $actionValueRight);
+                                               $this->finalHeight -= ($actionValueTop + $actionValueBottom);
+                                       } else if ($actionDetail[0] == $this->actionGrayscale && $this->inputGrayscale) {
+                                               $finalGrayscale = true;
+                                       } else if ($actionDetail[0] == "contrast") {
+                                               $finalContrastFound = true;
+                                               $finalContrast = $actionValues[0];
+                                       } else if ($actionDetail[0] == "brightness") {
+                                               $finalBrightnessFound = true;
+                                               $finalBrightness = $actionValues[0];
+                                       } else if ($actionDetail[0] == "rotate") {
+                                               $finalRotate += (int)$actionValues[0];
+                                               $finalWidthTmp = $this->finalWidth;
+                                               $this->finalWidth = $this->finalHeight;
+                                               $this->finalHeight = $finalWidthTmp;
+                                       }
+
+                                       if ($finalRotate == -360 || $finalRotate == 360) {
+                                               $finalRotate = 0;
+                    }
+
+                                       $doSwitch = ($finalRotate != 0 && ($finalRotate == 90 || $finalRotate == 270 || $finalRotate == -90 || $finalRotate == -270));
+                               }
+
+                               //1. All effects.
+                               if ($finalGrayscale) {
+                                       $this->ActionGrayscale();
+                }
+
+                               if ($finalBrightnessFound) {
+                                       $this->ActionBrightness($finalBrightness);
+                }
+
+                               if ($finalContrastFound) {
+                                       $this->ActionContrast($finalContrast*-1);
+                }
+
+                               //2. Do cropping.
+                               $finalCropLeft = round($finalCropLeft);
+                               $finalCropRight = round($finalCropRight);
+                               $finalCropTop = round($finalCropTop);
+                               $finalCropBottom = round($finalCropBottom);
+                               if ($finalCropLeft != 0 || $finalCropRight != 0 || $finalCropTop != 0 || $finalCropBottom != 0) {
+                                       $this->ActionCrop($finalCropLeft, $finalCropRight, $finalCropTop, $finalCropBottom);
+                }
+
+                               //3. Rotate
+                               if ($finalRotate != 0) {
+                                       $this->ActionRotate($finalRotate);
+                }
+
+                               //Calculate keep proportions values.
+                               if (round($this->finalWidth/$this->finalHeight,1) == round($this->GetWidth()/$this->GetHeight(),1)) {
+                                       //It seems to have the same proportions as the original. Use the original proportions value.
+                                       $this->widthKeepProportions = $this->GetWidth();
+                                       $this->heightKeepProportions = $this->GetHeight();
+                               } else {
+                                       //The proportions has been changed. Use the new width and height instead.
+                                       $this->widthKeepProportions = $this->finalWidth;
+                                       $this->heightKeepProportions = $this->finalHeight;
+                               }
+
+                               //4. Resize
+                               if ($this->finalWidth > 0 && $this->finalHeight > 0)
+                                       $this->resourceWorkWith = $this->ActionResize($this->finalWidth, $this->finalHeight, $this->resourceWorkWith);
+
+                               $this->SaveImage($this->resourceWorkWith, $this->srcWorkWith, image_type_to_mime_type(IMAGETYPE_PNG));
+                       }
+
+                       $this->inputBrightness = $finalBrightness;
+                       $this->inputContrast = $finalContrast;
+                       $this->inputGrayscale = $finalGrayscale;
+
+                       if ($actionType == $this->actionSaveAndClose) {
+                               $this->SaveImage($this->resourceWorkWith, $this->srcEdit, $this->mimeType);
+
+                               /*** GLM stuff ***/
+                               require_once '../../setup.phtml';
+                               $CKUpdater = new Toolkit_CKImages_ImageUpdater(Toolkit_Database::getInstance());
+                               $CKUpdater->load(basename($_GET['imagesrc']));
+                               $CKUpdater->update(new Toolkit_FileServer_ImageAdapter());
+                               /*****************/
+
+                               unlink($this->srcOriginal);
+                               unlink($this->srcPng);
+                               unlink($this->srcWorkWith);
+                               $this->DeleteOldImages(IMAGE_ORIGINAL_PATH);
+                               $this->DeleteOldImages(IMAGE_PNG_PATH);
+                               $this->DeleteOldImages(IMAGE_WORK_WITH_PATH);
+                               $reloadParentBrowser = RELOAD_PARENT_BROWSER_ON_SAVE ? 'window.opener.location.reload();' : '';
+                               PIE_Echo('<script language="javascript" type="text/javascript">'.$reloadParentBrowser.'window.open(\'\',\'_parent\',\'\');window.close();</script>');
+                       }
+               }
+
+               function ActionResize($width, $height, $image)
+               {
+                       $newImage = @imagecreatetruecolor($width, $height);
+                       imagecopyresampled($newImage, $image, 0, 0, 0, 0, $width, $height, $this->GetWidthFromImage($image), $this->GetHeightFromImage($image));
+                       return $newImage;
+               }
+
+               function ActionCrop($cropLeft, $cropRight, $cropTop, $cropBottom)
+               {
+                       $cropWidth = $this->GetWidth() - $cropLeft - $cropRight;
+                       $cropHeight = $this->GetHeight() - $cropTop - $cropBottom;
+
+                       $newImageCropped = @imagecreatetruecolor($cropWidth, $cropHeight);
+                       imagecopy($newImageCropped, $this->resourceWorkWith, 0, 0, $cropLeft, $cropTop, $cropWidth, $cropHeight);
+
+                       $this->resourceWorkWith = $newImageCropped;
+               }
+
+               function ActionUndo()
+               {
+                       $separatorPos = strrpos($this->actions, $this->actionSeparatorSign);
+                       if (!($separatorPos === false))
+                       {
+                               $this->actions = substr($this->actions, 0, $separatorPos);
+                       }
+                       else
+                       {
+                               $this->actions = "";
+                       }
+               }
+
+               function CreateImage($srcEdit)
+               {
+                       $info = getimagesize($srcEdit);
+
+                       if (!$info)
+                               return NULL;
+
+                       $mimeType = image_type_to_mime_type($info[2]);
+
+                       if ($mimeType == image_type_to_mime_type(IMAGETYPE_JPEG))
+                       {
+                               return imagecreatefromjpeg($srcEdit);
+                       }
+                       else if ($mimeType == image_type_to_mime_type(IMAGETYPE_GIF))
+                       {
+                               return imagecreatefromgif($srcEdit);
+                       }
+                       else if ($mimeType == image_type_to_mime_type(IMAGETYPE_PNG))
+                       {
+                               return imagecreatefrompng($srcEdit);
+                       }
+
+                       return NULL;
+               }
+
+               function ActionRotate($Degrees)
+               {
+                       $this->resourceWorkWith = imagerotate($this->resourceWorkWith, $Degrees, 0);
+               }
+
+               function ActionGrayscale()
+               {
+                       imagefilter($this->resourceWorkWith, IMG_FILTER_GRAYSCALE);
+               }
+
+               function ActionContrast($contrast)
+               {
+                       //-100 = max contrast, 0 = no change, +100 = min contrast
+                       imagefilter($this->resourceWorkWith, IMG_FILTER_CONTRAST, $contrast);
+               }
+
+               function ActionBrightness($light)
+               {
+                       //-255 = min brightness, 0 = no change, +255 = max brightness
+                       imagefilter($this->resourceWorkWith, IMG_FILTER_BRIGHTNESS, $light);
+               }
+
+               function GetErrorMessages()
+               {
+                       if (count($this->errorMessages))
+                       {
+                               PIE_Echo('<div class="error">');
+                               PIE_Echo('<ul>');
+
+                               foreach ($this->errorMessages as $errorMessage)
+                                       PIE_Echo ('<li>'.$errorMessage.'</li>');
+
+                               PIE_Echo("</ul>");
+                               PIE_Echo('</div>');
+                       }
+               }
+
+               function GetActions()
+               {
+                       PIE_Echo($this->actions);
+               }
+
+               function GetActionSeparator()
+               {
+                       if ($this->actions != "")
+                               return $this->actionSeparatorSign;
+
+                       return "";
+               }
+
+               function SaveImage($image, $toSrc, $mimeType = -1)
+               {
+                       if ($mimeType == -1)
+                               $mimeType = $this->mimeType;
+
+                       if ($mimeType == image_type_to_mime_type(IMAGETYPE_JPEG))
+                       {
+                               imagejpeg($image, $toSrc);
+                       }
+                       else if ($mimeType == image_type_to_mime_type(IMAGETYPE_GIF))
+                       {
+                               imagegif($image, $toSrc);
+                       }
+                       else if ($mimeType == image_type_to_mime_type(IMAGETYPE_PNG))
+                       {
+                               imagepng($image, $toSrc);
+                       }
+               }
+
+               function CleanUp()
+               {
+                       if ($this->resourceWorkWith)
+                               imagedestroy($this->resourceWorkWith);
+               }
+
+               function RotateArray($numberOfSteps, $arr)
+               {
+                       $finalArray = array();
+
+                       //-3 to 3
+                       $finalArray[] = $arr[$this->NumberOfStepsCalculator($numberOfSteps + 0)];
+                       $finalArray[] = $arr[$this->NumberOfStepsCalculator($numberOfSteps + 1)];
+                       $finalArray[] = $arr[$this->NumberOfStepsCalculator($numberOfSteps + 2)];
+                       $finalArray[] = $arr[$this->NumberOfStepsCalculator($numberOfSteps + 3)];
+
+                       return $finalArray;
+               }
+
+               function NumberOfStepsCalculator($sum)
+               {
+                       $maxIndex = 3;
+                       if ($sum > $maxIndex)
+                               return ($sum-$maxIndex)-1;
+                       else if ($sum < 0)
+                       {
+                               return ($sum+$maxIndex)+1;
+                       }
+
+                       return $sum;
+               }
+
+               function AddUserIdToImageSrc($imageSrc)
+               {
+                       return str_replace(".", $this->userId.".", $imageSrc);
+               }
+
+               function IsPHP5OrHigher()
+               {
+                       return version_compare(phpversion(), "5", ">=");
+               }
+
+               function GetFormAction()
+               {
+                       return "index.php?"
+                . $this->httpImageKey . "=" . $this->srcEdit
+                . '&image_name=' . $this->inputImageName;
+               }
+
+               function GetWidthKeepProportions()
+               {
+                       return $this->widthKeepProportions;
+               }
+
+               function GetHeightKeepProportions()
+               {
+                       return $this->heightKeepProportions;
+               }
+
+               function DeleteOldImages($srcdir)
+               {
+                       if($curdir = opendir($srcdir))
+                       {
+                               while($file = readdir($curdir))
+                               {
+                                       if($file != '.' && $file != '..')
+                                       {
+                                               //$srcfile = $srcdir . '\\' . $file;
+                                               //$srcfile = $srcdir.'/'.$file;
+                                               $srcfile = $srcdir.$file;
+                                               $srcfileLower = strtolower($srcfile);
+
+                                               if (stripos($srcfile, ".svn") === false)
+                                               {
+                                                       if(is_file($srcfile))
+                                                       {
+                                                               $doDelete = true;
+
+                                                               if (substr_count($srcfile, "sample.jpg") > 0 || substr_count($srcfile, "sample.png") > 0  || (substr_count($srcfileLower, ".jpg") == 0 && substr_count($srcfileLower, ".gif") == 0 && substr_count($srcfileLower, ".png") == 0))
+                                                                       $doDelete = false;
+
+                                                               if ($doDelete)
+                                                               {
+
+                                                                       $deleteTime = mktime(0, 0, 0, date("m"), date("d")-2, date("Y"));
+                                                                       if (fileatime($srcfile) < $deleteTime)
+                                                                       {
+                                                                               //Image is old and will be deleted. Or else the server space will be filled up with not needed images.
+                                                                               //echo "<h2>DELETE $srcfile".date("F d Y H:i:s.", fileatime($srcfile))."</h2>";
+                                                                               unlink($srcfile);
+                                                                       }
+                                                               }
+                                                       }
+                                               }
+                                       }
+                               }
+                               closedir($curdir);
+                       }
+               }
+
+               function GrantAllAccess($fileName)
+               {
+                       chmod($fileName, 0777);
+               }
+       }
+?>
diff --git a/Toolkit/PHPImageEditor/config.php b/Toolkit/PHPImageEditor/config.php
new file mode 100644 (file)
index 0000000..2714cb2
--- /dev/null
@@ -0,0 +1,33 @@
+<?php
+
+
+    /*
+    Copyright 2008, 2009, 2010 Patrik Hultgren
+
+    YOUR PROJECT MUST ALSO BE OPEN SOURCE IN ORDER TO USE PHP IMAGE EDITOR.
+    OR ELSE YOU NEED TO BUY THE COMMERCIAL VERSION AT:
+    http://www.shareit.com/product.html?productid=300296445&backlink=http%3A%2F%2Fwww.phpimageeditor.se%2F
+
+    This file is part of PHP Image Editor.
+
+    PHP Image Editor is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    PHP Image Editor is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with PHP Image Editor.  If not, see <http://www.gnu.org/licenses/>.
+    */
+
+
+    define("IMAGE_MAX_WIDTH", 700);
+       define("IMAGE_MAX_HEIGHT", 500);
+       define("DEFUALT_LANGUAGE", "en-GB");
+       define("RELOAD_PARENT_BROWSER_ON_SAVE", true);
+       define("AJAX_POST_TIMEOUT_MS", 20000);
+?>
diff --git a/Toolkit/PHPImageEditor/css/jquery.jcrop.css b/Toolkit/PHPImageEditor/css/jquery.jcrop.css
new file mode 100644 (file)
index 0000000..da9bffb
--- /dev/null
@@ -0,0 +1,45 @@
+/* Fixes issue here http://code.google.com/p/jcrop/issues/detail?id=1 */
+.jcrop-holder
+{
+       text-align: left;
+}
+
+.jcrop-vline, .jcrop-hline
+{
+       font-size: 0;
+       position: absolute;
+       background: white url('../images/jcrop.gif') top left repeat;
+       /*
+       opacity: .5;
+       *filter:alpha(opacity=50);
+       */
+}
+.jcrop-vline { height: 100%; width: 1px !important; }
+.jcrop-hline { width: 100%; height: 1px !important; }
+.jcrop-handle {
+       font-size: 1px;
+       width: 7px !important;
+       height: 7px !important;
+       border: 1px #eee solid;
+       background-color: #333;
+       *width: 9px;
+       *height: 9px;
+}
+
+.jcrop-tracker {
+       *background-color: gray;
+       width: 100%; height: 100%;
+}
+
+.custom .jcrop-vline,
+.custom .jcrop-hline
+{
+       background: yellow;
+}
+.custom .jcrop-handle
+{
+       border-color: black;
+       background-color: #C7BB00;
+       -moz-border-radius: 3px;
+       -webkit-border-radius: 3px;
+}
\ No newline at end of file
diff --git a/Toolkit/PHPImageEditor/css/style.css b/Toolkit/PHPImageEditor/css/style.css
new file mode 100644 (file)
index 0000000..94976f4
--- /dev/null
@@ -0,0 +1,228 @@
+body
+{
+       margin: 0;
+       padding: 0;
+       font-family: Arial;
+       font-size: 80%;
+}
+
+form
+{
+       margin: 0;
+       padding: 0;
+       background-color: #ffffff;
+}
+
+label
+{
+       display: block;
+}
+
+label.checkbox
+{
+       display: inline;
+}
+
+div.field
+{
+       clear: both;
+       padding: 17px;
+}
+
+div.col-1,
+div.col-2
+{
+       float: left;
+}
+
+div.col-1
+{
+       margin-right: 10px;
+}
+
+input.checkbox
+{
+}
+
+input.input-number
+{
+       width: 70px;
+       margin-bottom: 10px;
+       border: 1px solid #cbcbcb;
+       background-color: #ffffff;
+}
+
+.main-actions
+{
+       background-color: #eaeaea;
+       border-top: 1px solid white;
+       border-bottom: 1px solid #d6d6d6;
+       padding: 0 10px 0 10px;
+       padding: 12px 10px 12px 10px;
+}
+
+#editimage
+{
+       margin: 0 10px 10px 10px;
+       position: relative;
+       top: 0px;
+       left: 0px;
+}
+
+div.error,
+div.info
+{
+       background-color: #ffc1c1;
+       padding: 5px;
+       margin: 0 10px 10px 10px;
+}
+
+div.error ul,
+div.info ul
+{
+       list-style-type: square;
+}
+
+div.error li,
+div.info li
+{
+       font-weight: bold;
+       margin-bottom: 5px;
+}
+
+div.croparea
+{
+       background-color: #ffffff;
+       filter:alpha(opacity=80);
+       -moz-opacity:.80;
+       opacity:.80;
+       overflow: hidden;               
+}
+
+div#actionContainer
+{
+       background-color: #e1f3ff;
+       background-image: url('../images/tab_body.png');
+       background-repeat: repeat-x;
+}
+
+div#menu
+{
+       height: 30px;
+       overflow: hidden;
+       font-weight: bold;
+}
+
+.selected
+{
+       float: left;
+       color: #000000;
+       border-right: 1px solid #ffffff;
+       line-height: 30px;
+       padding: 0 20px 0 20px;
+       cursor: pointer;
+       height: 30px;
+       background-image: url('../images/tab_selected.png');
+       background-repeat: repeat-x;
+}
+
+.not-selected
+{
+       float: left;
+       color: #ffffff;
+       border-right: 1px solid #ffffff;
+       border-bottom: 1px solid #ffffff;
+       line-height: 30px;
+       padding: 0 20px 0 20px;
+       cursor: pointer;
+       height: 29px;
+       background-image: url('../images/tab_not_selected.png');
+       background-repeat: repeat-x;
+}
+
+.panel
+{
+       filter:alpha(opacity=0);
+       -moz-opacity:0;
+       opacity:0;
+       display: none;                                                 
+}
+
+.tabs
+{
+       margin: 10px;
+}
+
+.widthAndHeight
+{
+       height: 23px; 
+       overflow: hidden;
+}
+
+#image
+{
+       position: absolute;
+       top: 0;
+       left: 0;
+}
+
+#loading
+{
+       border-top: 1px solid white;
+       background-color: #c7e7fc;
+       padding: 15px;
+       font-weight: bold;
+}
+
+#loading_bar
+{
+       margin-top: 3px;
+       background-color: #3d7ab0;
+       height: 3px;
+}
+
+#imageResizerKeepProportions,
+#imageResizerNoProportions
+{
+       background-color: #d6d6d6;
+       position: absolute; 
+       left: 0px; 
+       top: 0px;
+       filter:alpha(opacity=0);
+       -moz-opacity:.0;
+       opacity:.0;     
+}
+
+.help
+{
+       background-color: #fffcd1;
+       line-height: 1.5em;
+       padding: 10px;
+       border: 1px solid #d8d8d8;
+}
+
+.help-header
+{
+       font-weight: bold;
+       text-transform: uppercase;
+}
+
+.help-content
+{
+}
+
+#crophelp
+{
+}
+
+.crop-settings
+{
+       background-color: #cde3f2;
+}
+
+.crop-top
+{
+       border-bottom: 1px solid #a8c6da;
+       padding-bottom: 8px;
+       margin-bottom: 8px;
+}
\ No newline at end of file
diff --git a/Toolkit/PHPImageEditor/css/ui.resizable.css b/Toolkit/PHPImageEditor/css/ui.resizable.css
new file mode 100644 (file)
index 0000000..44efeb2
--- /dev/null
@@ -0,0 +1,13 @@
+/* Resizable
+----------------------------------*/
+.ui-resizable { position: relative;}
+.ui-resizable-handle { position: absolute;font-size: 0.1px;z-index: 99999; display: block;}
+.ui-resizable-disabled .ui-resizable-handle, .ui-resizable-autohide .ui-resizable-handle { display: none; }
+.ui-resizable-n { cursor: n-resize; height: 7px; width: 100%; top: -5px; left: 0px; }
+.ui-resizable-s { cursor: s-resize; height: 7px; width: 100%; bottom: -5px; left: 0px; }
+.ui-resizable-e { cursor: e-resize; width: 7px; right: -5px; top: 0px; height: 100%; }
+.ui-resizable-w { cursor: w-resize; width: 7px; left: -5px; top: 0px; height: 100%; }
+.ui-resizable-se { cursor: se-resize; width: 12px; height: 12px; right: 1px; bottom: 1px; }
+.ui-resizable-sw { cursor: sw-resize; width: 9px; height: 9px; left: -5px; bottom: -5px; }
+.ui-resizable-nw { cursor: nw-resize; width: 9px; height: 9px; left: -5px; top: -5px; }
+.ui-resizable-ne { cursor: ne-resize; width: 9px; height: 9px; right: -5px; top: -5px;}
\ No newline at end of file
diff --git a/Toolkit/PHPImageEditor/css/ui.slider.css b/Toolkit/PHPImageEditor/css/ui.slider.css
new file mode 100644 (file)
index 0000000..5d47af0
--- /dev/null
@@ -0,0 +1,19 @@
+/* Slider
+----------------------------------*/
+.ui-slider { position: relative; text-align: left; top: 15px; }
+.ui-slider .ui-slider-handle { background: url(../images/slider_pointer.gif) 0 0 no-repeat; position: absolute; z-index: 2; width: 11px; height: 17px; cursor: default; background-color: none; border: 0; }
+.ui-slider .ui-slider-range { position: absolute; z-index: 1; font-size: 1%; display: block; border: 0; }
+
+       
+
+.ui-slider-horizontal { background: url(../images/slider_track.gif) 0 0 repeat-x; height: 7px; background-color: none; border: 0; width: 401px;}
+.ui-slider-horizontal .ui-slider-handle { top: -.6em; margin-left: -5px; }
+.ui-slider-horizontal .ui-slider-range { top: 0; height: 100%; }
+.ui-slider-horizontal .ui-slider-range-min { left: 0; }
+.ui-slider-horizontal .ui-slider-range-max { right: 0; }
+
+.ui-slider-vertical { width: .8em; height: 100px; }
+.ui-slider-vertical .ui-slider-handle { left: -.3em; margin-left: 0; margin-bottom: -.6em; }
+.ui-slider-vertical .ui-slider-range { left: 0; width: 100%; }
+.ui-slider-vertical .ui-slider-range-min { bottom: 0; }
+.ui-slider-vertical .ui-slider-range-max { top: 0; }
\ No newline at end of file
diff --git a/Toolkit/PHPImageEditor/images/crop_view.png b/Toolkit/PHPImageEditor/images/crop_view.png
new file mode 100644 (file)
index 0000000..3589e8b
Binary files /dev/null and b/Toolkit/PHPImageEditor/images/crop_view.png differ
diff --git a/Toolkit/PHPImageEditor/images/edit.gif b/Toolkit/PHPImageEditor/images/edit.gif
new file mode 100644 (file)
index 0000000..1a5a0af
Binary files /dev/null and b/Toolkit/PHPImageEditor/images/edit.gif differ
diff --git a/Toolkit/PHPImageEditor/images/empty.gif b/Toolkit/PHPImageEditor/images/empty.gif
new file mode 100644 (file)
index 0000000..35d42e8
Binary files /dev/null and b/Toolkit/PHPImageEditor/images/empty.gif differ
diff --git a/Toolkit/PHPImageEditor/images/jcrop.gif b/Toolkit/PHPImageEditor/images/jcrop.gif
new file mode 100644 (file)
index 0000000..72ea7cc
Binary files /dev/null and b/Toolkit/PHPImageEditor/images/jcrop.gif differ
diff --git a/Toolkit/PHPImageEditor/images/slider_pointer.gif b/Toolkit/PHPImageEditor/images/slider_pointer.gif
new file mode 100644 (file)
index 0000000..8e8fdcd
Binary files /dev/null and b/Toolkit/PHPImageEditor/images/slider_pointer.gif differ
diff --git a/Toolkit/PHPImageEditor/images/slider_track.gif b/Toolkit/PHPImageEditor/images/slider_track.gif
new file mode 100644 (file)
index 0000000..e1dc8e0
Binary files /dev/null and b/Toolkit/PHPImageEditor/images/slider_track.gif differ
diff --git a/Toolkit/PHPImageEditor/images/tab_body.png b/Toolkit/PHPImageEditor/images/tab_body.png
new file mode 100644 (file)
index 0000000..6e164fe
Binary files /dev/null and b/Toolkit/PHPImageEditor/images/tab_body.png differ
diff --git a/Toolkit/PHPImageEditor/images/tab_not_selected.png b/Toolkit/PHPImageEditor/images/tab_not_selected.png
new file mode 100644 (file)
index 0000000..3051203
Binary files /dev/null and b/Toolkit/PHPImageEditor/images/tab_not_selected.png differ
diff --git a/Toolkit/PHPImageEditor/images/tab_selected.png b/Toolkit/PHPImageEditor/images/tab_selected.png
new file mode 100644 (file)
index 0000000..d4fd93c
Binary files /dev/null and b/Toolkit/PHPImageEditor/images/tab_selected.png differ
diff --git a/Toolkit/PHPImageEditor/includes/constants.php b/Toolkit/PHPImageEditor/includes/constants.php
new file mode 100644 (file)
index 0000000..0a7a255
--- /dev/null
@@ -0,0 +1,49 @@
+<?php
+    
+    /**
+    * constants.php
+    * 
+    * PHP version 5.2
+    * 
+    * @category  Toolkit
+    * @package   PHPImageEditor/includes
+    * @author    Jamie Kahgee <steve@gaslightmedia.com>
+    * @copyright 2012 Gaslight Media
+    * @license   Gaslight Media
+    * @version   SVN: $id$
+    * @link      <>
+    */
+    /*
+    Copyright 2008, 2009, 2010 Patrik Hultgren
+    
+    YOUR PROJECT MUST ALSO BE OPEN SOURCE IN ORDER TO USE PHP IMAGE EDITOR.
+    OR ELSE YOU NEED TO BUY THE COMMERCIAL VERSION AT:
+    http://www.shareit.com/product.html?productid=300296445&backlink=http%3A%2F%2Fwww.phpimageeditor.se%2F
+    
+    This file is part of PHP Image Editor.
+
+    PHP Image Editor is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    PHP Image Editor is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with PHP Image Editor.  If not, see <http://www.gnu.org/licenses/>.
+    */
+
+
+    define("PHP_VERSION_MINIMUM", "5");
+       define("GD_VERSION_MINIMUM", "2.0.28");
+       define("IMAGE_ORIGINAL_PATH", "editimagesoriginal/");
+       define("IMAGE_WORK_WITH_PATH", "editimagesworkwith/");
+       define("IMAGE_PNG_PATH", "editimagespng/");
+       define("MENU_RESIZE", "0");
+       define("MENU_ROTATE", "1");
+       define("MENU_CROP", "2");
+       define("MENU_EFFECTS", "3");
+?>
\ No newline at end of file
diff --git a/Toolkit/PHPImageEditor/includes/functions.php b/Toolkit/PHPImageEditor/includes/functions.php
new file mode 100644 (file)
index 0000000..5fa5bbb
--- /dev/null
@@ -0,0 +1,50 @@
+<?php
+    
+       
+    /*
+    Copyright 2008, 2009, 2010 Patrik Hultgren
+    
+    YOUR PROJECT MUST ALSO BE OPEN SOURCE IN ORDER TO USE PHP IMAGE EDITOR.
+    OR ELSE YOU NEED TO BUY THE COMMERCIAL VERSION AT:
+    http://www.shareit.com/product.html?productid=300296445&backlink=http%3A%2F%2Fwww.phpimageeditor.se%2F
+    
+    This file is part of PHP Image Editor.
+
+    PHP Image Editor is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    PHP Image Editor is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with PHP Image Editor.  If not, see <http://www.gnu.org/licenses/>.
+    */
+
+
+    function PIE_GetTexts($filePath)
+       {
+               $texts = array();
+               $lines = file($filePath);
+               
+               foreach($lines as $line_num => $line)
+               {
+                       if (substr_count($line, "#") == 0)
+                       {
+                               $keyAndText = explode("=", trim($line));
+                               $texts[$keyAndText[0]] = $keyAndText[1];
+                       }
+               }
+               
+               return $texts;
+       }
+       
+       function PIE_Echo($text)
+       {
+               echo $text;
+               //echo utf8_encode($text);
+       }       
+?>
\ No newline at end of file
diff --git a/Toolkit/PHPImageEditor/index.php b/Toolkit/PHPImageEditor/index.php
new file mode 100644 (file)
index 0000000..99b3f7f
--- /dev/null
@@ -0,0 +1,250 @@
+<?php  
+    
+       
+    /*
+    Copyright 2008, 2009, 2010 Patrik Hultgren
+    
+    YOUR PROJECT MUST ALSO BE OPEN SOURCE IN ORDER TO USE PHP IMAGE EDITOR.
+    OR ELSE YOU NEED TO BUY THE COMMERCIAL VERSION AT:
+    http://www.shareit.com/product.html?productid=300296445&backlink=http%3A%2F%2Fwww.phpimageeditor.se%2F
+    
+    This file is part of PHP Image Editor.
+
+    PHP Image Editor is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    PHP Image Editor is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with PHP Image Editor.  If not, see <http://www.gnu.org/licenses/>.
+    */
+
+    
+    
+       header("Cache-Control: no-store"); 
+       header('content-type: text/html; charset: utf-8');
+       include 'includes/constants.php';
+       include 'config.php';
+       include 'includes/functions.php';
+       include 'classes/phpimageeditor.php';
+       global $objPHPImageEditor;
+       $objPHPImageEditor = new PHPImageEditor();
+?>
+<?php if (!$objPHPImageEditor->isAjaxPost) { ?>
+<!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>PHP Image Editor</title>
+           <script type="text/javascript" src="javascript/jquery-1.3.2.min.js"></script>
+           <script type="text/javascript" src="javascript/jquery.jcrop.js"></script>
+        <script type="text/javascript" src="javascript/jquery.numeric.js"></script>
+           <script type="text/javascript" src="javascript/ui.core.js"></script>
+           <script type="text/javascript" src="javascript/ui.slider.js"></script>
+           <script type="text/javascript" src="javascript/ui.resizable.js"></script>
+           <script type="text/javascript" src="javascript/effects.core.js"></script>
+        <script type="text/javascript" src="javascript/phpimageeditor.js"></script>
+           
+           <link rel="stylesheet" type="text/css" href="css/style.css"/>
+           <link rel="stylesheet" type="text/css" href="css/ui.resizable.css"/>
+           <link rel="stylesheet" type="text/css" href="css/ui.slider.css"/>
+           <link rel="stylesheet" type="text/css" href="css/jquery.jcrop.css"/>
+           
+               <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+               
+        <script type="text/javascript">
+               var ImageMaxWidth = <?php PIE_Echo(IMAGE_MAX_WIDTH); ?>;
+               var ImageMaxHeight = <?php PIE_Echo(IMAGE_MAX_HEIGHT); ?>;
+               var ImageWidth = <?php PIE_Echo($objPHPImageEditor->GetWidthFinal()); ?>;
+               var ImageHeight = <?php PIE_Echo($objPHPImageEditor->GetHeightFinal()); ?>;
+               var TextIsRequired = "<?php PIE_Echo($objPHPImageEditor->texts["IS REQUIRED"]); ?>";
+               var TextMustBeNumeric = "<?php PIE_Echo($objPHPImageEditor->texts["MUST BE NUMERIC"]); ?>";
+               var TextWidth = "<?php PIE_Echo($objPHPImageEditor->texts["WIDTH"]); ?>";
+               var TextHeight = "<?php PIE_Echo($objPHPImageEditor->texts["HEIGHT"]); ?>";
+               var TextNotNegative = "<?php PIE_Echo($objPHPImageEditor->texts["NOT NEGATIVE"]); ?>";
+               var TextNotInRange = "<?php PIE_Echo($objPHPImageEditor->texts["NOT IN RANGE"]); ?>";
+               var TextCantBeLargerThen = "<?php PIE_Echo($objPHPImageEditor->texts["CANT BE LARGER THEN"]); ?>";
+               var TextAnUnexpectedError = "<?php PIE_Echo($objPHPImageEditor->texts["AN UNEXPECTED ERROR"]); ?>";
+               var Brightness = <?php PIE_Echo($objPHPImageEditor->inputBrightness); ?>;
+               var Contrast = <?php PIE_Echo($objPHPImageEditor->inputContrast); ?>;
+               var BrightnessMax = <?php PIE_Echo($objPHPImageEditor->brightnessMax); ?>;
+               var ContrastMax = <?php PIE_Echo($objPHPImageEditor->contrastMax); ?>;
+            var FormAction = "<?php PIE_Echo($objPHPImageEditor->GetFormAction()); ?>";
+            var FormId = "<?php PIE_Echo($objPHPImageEditor->formName); ?>";
+            var ActionUpdate = "<?php PIE_Echo($objPHPImageEditor->actionUpdate); ?>";
+            var ActionUndo = "<?php PIE_Echo($objPHPImageEditor->actionUndo); ?>";
+            var ActionSaveAndClose = "<?php PIE_Echo($objPHPImageEditor->actionSaveAndClose); ?>";
+            var ActionRotateLeft = "<?php PIE_Echo($objPHPImageEditor->actionRotateLeft); ?>";
+            var ActionRotateRight = "<?php PIE_Echo($objPHPImageEditor->actionRotateRight); ?>";
+            var ActionSaveAndClose = "<?php PIE_Echo($objPHPImageEditor->actionSaveAndClose); ?>";
+            var MenuResize = "<?php PIE_Echo(MENU_RESIZE); ?>";
+            var MenuRotate = "<?php PIE_Echo(MENU_ROTATE); ?>";
+            var MenuCrop = "<?php PIE_Echo(MENU_CROP); ?>";
+            var MenuEffects = "<?php PIE_Echo(MENU_EFFECTS); ?>";
+            var AjaxPostTimeoutMs = <?php PIE_Echo(AJAX_POST_TIMEOUT_MS); ?>; 
+        </script>
+       </head>
+       <body>
+               <div id="phpImageEditor">
+<?php } ?>
+
+                       <form id="<?php PIE_Echo($objPHPImageEditor->formName); ?>" name="<?php PIE_Echo($objPHPImageEditor->formName); ?>" method="post" action="<?php PIE_Echo($objPHPImageEditor->GetFormAction()); ?>">
+                               <?php if (!$objPHPImageEditor->ErrorHasOccurred()) { ?>
+                                        
+                                       <div class="tabs">
+                                       
+                                               <div id="menu">
+                                                       <div class="<?php PIE_Echo($objPHPImageEditor->inputPanel == MENU_RESIZE ? 'selected' : 'not-selected'); ?>" id="menuitem_<?php PIE_Echo(MENU_RESIZE); ?>">
+                                                               <?php PIE_Echo($objPHPImageEditor->texts["RESIZE IMAGE"]); ?>
+                                                       </div>
+                                                       <!--
+                                                       <div class="<?php PIE_Echo($objPHPImageEditor->inputPanel == MENU_ROTATE ? 'selected' : 'not-selected'); ?>" id="menuitem_<?php PIE_Echo(MENU_ROTATE); ?>">
+                                                               <?php PIE_Echo($objPHPImageEditor->texts["ROTATE IMAGE"]); ?>
+                                                       </div>
+                                                       -->
+                                                       <div class="<?php PIE_Echo($objPHPImageEditor->inputPanel == MENU_CROP ? 'selected' : 'not-selected'); ?>" id="menuitem_<?php PIE_Echo(MENU_CROP); ?>">
+                                                               <?php PIE_Echo($objPHPImageEditor->texts["CROP IMAGE"]); ?>
+                                                       </div>
+                                                       <!--
+                                                       <?php if ($objPHPImageEditor->IsPHP5OrHigher()) { ?>
+                                                               <div class="<?php PIE_Echo($objPHPImageEditor->inputPanel == MENU_EFFECTS ? 'selected' : 'not-selected'); ?>" id="menuitem_<?php PIE_Echo(MENU_EFFECTS); ?>">
+                                                                       <?php PIE_Echo($objPHPImageEditor->texts["EFFECTS"]); ?>
+                                                               </div>
+                                                       <?php } ?>
+                                                       -->
+                                               </div>
+                                                       
+                                               <div id="actionContainer">
+                       
+                                                       <div id="panel_<?php PIE_Echo(MENU_RESIZE); ?>" class="panel">
+                                                               <table cellpadding="0" cellspacing="0" border="0">
+                                                                       <tr>
+                                                                               <td>
+                                                                                       <div class="field widthAndHeight">
+                                                                                               <div class="col-1">
+                                                                                                       <label for="width"><?php PIE_Echo($objPHPImageEditor->texts["WIDTH"]); ?></label>
+                                                                                                       <input class="input-number" type="text" name="width" id="width" value="<?php PIE_Echo($objPHPImageEditor->GetWidthFinal()); ?>"/>
+                                                                                                       <input type="hidden" name="widthoriginal" id="widthoriginal" value="<?php PIE_Echo($objPHPImageEditor->GetWidth()); ?>"/>
+                                                                                               </div>
+                                                                                               <div class="col-2">
+                                                                                                       <label for="height"><?php PIE_Echo($objPHPImageEditor->texts["HEIGHT"]); ?></label>
+                                                                                                       <input class="input-number" type="text" name="height" id="height" value="<?php PIE_Echo($objPHPImageEditor->GetHeightFinal()); ?>"/>
+                                                                                                       <input type="hidden" name="heightoriginal" id="heightoriginal" value="<?php PIE_Echo($objPHPImageEditor->GetHeight()); ?>"/>
+                                                                                               </div>
+                                                                                       </div>
+                                                                                       <div class="field">
+                                                                                               <input class="checkbox" type="checkbox" name="<?php PIE_Echo($objPHPImageEditor->fieldNameKeepProportions); ?>" id="<?php PIE_Echo($objPHPImageEditor->fieldNameKeepProportions); ?>" <?php PIE_Echo($objPHPImageEditor->inputKeepProportions ? 'checked="checked"' : ''); ?>/>
+                                                                                               <input type="hidden" name="keepproportionsval" id="keepproportionsval" value="<?php PIE_Echo($objPHPImageEditor->inputKeepProportions ? '1' : '0'); ?>"/>
+                                                                                               <label for="<?php PIE_Echo($objPHPImageEditor->fieldNameKeepProportions); ?>" class="checkbox"><?php PIE_Echo($objPHPImageEditor->texts["KEEP PROPORTIONS"]); ?></label>
+                                                                                       </div>
+                                                                               </td>
+                                                                               <td>
+                                                                                       <div class="help" id="resizehelp">
+                                                                                               <div class="help-header" id="resizehelpheader"><?php PIE_Echo($objPHPImageEditor->texts["INSTRUCTIONS"]); ?></div>
+                                                                                               <div class="help-content" id="resizehelpcontent"><?php PIE_Echo($objPHPImageEditor->texts["RESIZE HELP"]); ?></div>
+                                                                                       </div>
+                                                                               </td>
+                                                                       </tr>
+                                                               </table>
+                                                       </div>
+               
+                                                       <div id="panel_<?php PIE_Echo(MENU_ROTATE); ?>" class="panel">
+                                                               <div class="field">
+                                                                       <input id="btnRotateLeft" type="button" value="<?php PIE_Echo($objPHPImageEditor->texts["LEFT 90 DEGREES"]); ?>"/>
+                                                                       <input id="btnRotateRight" type="button" value="<?php PIE_Echo($objPHPImageEditor->texts["RIGHT 90 DEGREES"]); ?>"/>
+                                                                       <input type="hidden" name="rotate" id="rotate" value="-1"/>
+                                                               </div>
+                                                       </div>
+               
+                                                       <div id="panel_<?php PIE_Echo(MENU_CROP); ?>" class="panel">
+                                                               <div class="field">
+                                                                       <input class="input-number" type="hidden" name="croptop" id="croptop" value="0"/>
+                                                                       <input class="input-number" type="hidden" name="cropleft" id="cropleft" value="0"/>
+                                                                       <input class="input-number" type="hidden" name="cropright" id="cropright" value="0"/>
+                                                                       <input class="input-number" type="hidden" name="cropbottom" id="cropbottom" value="0"/>
+                                                                       <div class="help" id="crophelp">
+                                                                               <div class="help-header" id="crophelpheader"><?php PIE_Echo($objPHPImageEditor->texts["INSTRUCTIONS"]); ?></div>
+                                                                               <div class="help-content" id="crophelpcontent"><?php PIE_Echo($objPHPImageEditor->texts["CROP HELP"]); ?></div>
+                                                                       </div>
+                                                               </div>
+                                                               <div class="field crop-settings">
+                                                                       <div class="crop-top">
+                                                                               <?php PIE_Echo($objPHPImageEditor->texts["CROP WIDTH"]); ?>: <span id="cropwidth">0</span>
+                                                                               <?php PIE_Echo($objPHPImageEditor->texts["CROP HEIGHT"]); ?>: <span id="cropheight">0</span>
+                                                                       </div>
+                                                                       <input id="cropkeepproportions" class="checkbox" type="checkbox" name="cropkeepproportions" <?php PIE_Echo($objPHPImageEditor->inputCropKeepProportions ? 'checked="checked"' : ''); ?>/>
+                                                                       <label class="checkbox" for="cropkeepproportions"><?php PIE_Echo($objPHPImageEditor->texts["CROP KEEP PROPORTIONS"]); ?></label>
+                                                                       <input id="cropkeepproportionsval" type="hidden" name="cropkeepproportionsval" value="<?php PIE_Echo($objPHPImageEditor->inputCropKeepProportions ? '1' : '0'); ?>"/>                                                                   
+                                                                       <input id="cropkeepproportionsratio" type="hidden" name="cropkeepproportionsratio" value="<?php PIE_Echo($objPHPImageEditor->inputCropKeepProportionsRatio); ?>"/>                                                                      
+                                                               </div>
+                                                       </div>
+                                                       <div id="panel_<?php PIE_Echo(MENU_EFFECTS); ?>" class="panel" style="display: <?php PIE_Echo($objPHPImageEditor->IsPHP5OrHigher() ? 'block' : 'none'); ?>;">
+                                                               <div class="field">
+                                                                       <label for="brightness"><?php PIE_Echo($objPHPImageEditor->texts["BRIGHTNESS"]); ?></label>
+                                                                       <div id="brightness_slider_track"></div>
+                                                               </div>
+                                                               <input type="hidden" name="brightness" id="brightness" value="<?php PIE_Echo($objPHPImageEditor->inputBrightness); ?>"/>
+                                                               <div class="field">
+                                                                       <label for="contrast"><?php PIE_Echo($objPHPImageEditor->texts["CONTRAST"]); ?></label>
+                                                                       <div id="contrast_slider_track"></div>
+                                                               </div>
+                                                               <input type="hidden" name="contrast" id="contrast" value="<?php PIE_Echo($objPHPImageEditor->inputContrast); ?>"/>
+                                                               <div class="field">
+                                                                       <input class="checkbox" type="checkbox" name="<?php PIE_Echo($objPHPImageEditor->actionGrayscale); ?>" id="<?php PIE_Echo($objPHPImageEditor->actionGrayscale); ?>" <?php PIE_Echo($objPHPImageEditor->inputGrayscale ? 'checked="checked"' : ''); ?>/>
+                                                                       <label for="<?php PIE_Echo($objPHPImageEditor->actionGrayscale); ?>" class="checkbox"><?php PIE_Echo($objPHPImageEditor->texts["GRAYSCALE"]); ?></label>
+                                                                       <input type="hidden" name="grayscaleval" id="grayscaleval" value="<?php PIE_Echo($objPHPImageEditor->inputGrayscale ? '1' : '0'); ?>"/>
+                                                               </div>
+                                                       </div>
+                                                       <div id="loading" style="display: none;"><?php PIE_Echo($objPHPImageEditor->texts["LOADING"]); ?>...<div id="loading_bar" style="width: 0px;"></div></div>
+               
+                                               </div>
+                                               
+                                               <div class="main-actions">
+                                                       <input type="button" id="btnupdate" name="btnupdate" value="<?php PIE_Echo($objPHPImageEditor->texts["UPDATE"]); ?>"/>
+                                                       <input type="button" <?php PIE_Echo($objPHPImageEditor->actions == "" ? 'disabled="disabled"' : ''); ?> id="btnsave" name="btnsave" value="<?php PIE_Echo($objPHPImageEditor->texts["SAVE AND CLOSE"]); ?>"/>
+                                                       <input type="button" <?php PIE_Echo($objPHPImageEditor->actions == "" ? 'disabled="disabled"' : ''); ?> id="btnundo" name="btnundo" value="<?php PIE_Echo($objPHPImageEditor->texts["UNDO"]); ?>"/>
+                                               </div>
+                        <div class="field">
+                            <label><input type="text" id="image_name" name="image_name" value="<?php PIE_Echo(urldecode($objPHPImageEditor->GetImageNameFinal())); ?>" /></label>
+                        </div>
+               
+                                       </div>
+                                       <input type="hidden" name="actiontype" id="actiontype" value="<?php PIE_Echo($objPHPImageEditor->actionUpdate); ?>"/>
+                                       <input type="hidden" name="panel" id="panel" value="<?php PIE_Echo($objPHPImageEditor->inputPanel); ?>"/>
+                                       <input type="hidden" name="language" id="language" value="<?php PIE_Echo($objPHPImageEditor->inputLanguage); ?>"/>
+                                       <input type="hidden" name="actions" id="actions" style="width: 1000px;" value="<?php $objPHPImageEditor->GetActions(); ?>"/>
+                    <input type="hidden" name="imageNameLast" id="imageNameLast" value="<?php PIE_Echo($objPHPImageEditor->GetImageNameFinal()); ?>" />
+                                       <input type="hidden" name="widthlast" id="widthlast" value="<?php PIE_Echo($objPHPImageEditor->GetWidthFinal()); ?>"/>
+                                       <input type="hidden" name="heightlast" id="heightlast" value="<?php PIE_Echo($objPHPImageEditor->GetHeightFinal()); ?>"/>
+                                       <input type="hidden" name="widthlastbeforeresize" id="widthlastbeforeresize" value="<?php PIE_Echo($objPHPImageEditor->GetWidthKeepProportions()); ?>"/>
+                                       <input type="hidden" name="heightlastbeforeresize" id="heightlastbeforeresize" value="<?php PIE_Echo($objPHPImageEditor->GetHeightKeepProportions()); ?>"/>
+                                       <input type="hidden" name="userid" id="userid" value="<?php PIE_Echo($objPHPImageEditor->userId); ?>"/>
+                                       <input type="hidden" name="contrastlast" id="contrastlast" value="<?php PIE_Echo($objPHPImageEditor->inputContrast); ?>"/>
+                                       <input type="hidden" name="brightnesslast" id="brightnesslast" value="<?php PIE_Echo($objPHPImageEditor->inputBrightness); ?>"/>
+                                       <input type="hidden" name="isajaxpost" id="isajaxpost" value="false"/>
+                               <?php } ?>
+                       </form>
+                       <?php $objPHPImageEditor->GetErrorMessages(); ?>
+                       <div id="divJsErrors" class="error" style="display: none;">
+                               <ul id="ulJsErrors" style="display: none;"><li></li></ul>
+                       </div>
+                       <div><img src="images/empty.gif" alt=""/></div>
+                       <?php if (!$objPHPImageEditor->ErrorHasOccurred()) { ?>
+                               <div id="editimage">
+                                       <img id="image" style="position: absolute; left: 0px; top: 0px; width: <?php PIE_Echo($objPHPImageEditor->GetWidthFinal()); ?>px; height: <?php PIE_Echo($objPHPImageEditor->GetHeightFinal()); ?>px;" alt="" src="<?php PIE_Echo($objPHPImageEditor->srcWorkWith); ?>?timestamp=<?php PIE_Echo(time()); ?>"/>
+                                       <div id="imageResizerKeepProportions" style="diplay: <?php PIE_Echo(($objPHPImageEditor->inputKeepProportions && $objPHPImageEditor->inputPanel == MENU_RESIZE) ? 'block' : 'none'); ?>; width: <?php PIE_Echo($objPHPImageEditor->GetWidthFinal()); ?>px; height: <?php PIE_Echo($objPHPImageEditor->GetHeightFinal()); ?>px;"></div>
+                                       <div id="imageResizerNoProportions" style="diplay: <?php PIE_Echo((!$objPHPImageEditor->inputKeepProportions && $objPHPImageEditor->inputPanel == MENU_RESIZE) ? 'block' : 'none'); ?>; width: <?php PIE_Echo($objPHPImageEditor->GetWidthFinal()); ?>px; height: <?php PIE_Echo($objPHPImageEditor->GetHeightFinal()); ?>px;"></div>
+                               </div>  
+                       <?php } ?>
+
+<?php if (!$objPHPImageEditor->isAjaxPost) { ?>
+               </div>
+       </body>
+       </html>
+<?php } ?>
+
+<?php $objPHPImageEditor->CleanUp(); ?>
\ No newline at end of file
diff --git a/Toolkit/PHPImageEditor/javascript/effects.core.js b/Toolkit/PHPImageEditor/javascript/effects.core.js
new file mode 100644 (file)
index 0000000..9c983c1
--- /dev/null
@@ -0,0 +1,543 @@
+/*
+ * jQuery UI Effects 1.7
+ *
+ * Copyright (c) 2009 AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT (MIT-LICENSE.txt)
+ * and GPL (GPL-LICENSE.txt) licenses.
+ *
+ * http://docs.jquery.com/UI/Effects/
+ */
+;jQuery.effects || (function($) {
+
+$.effects = {
+       version: "1.7",
+
+       // Saves a set of properties in a data storage
+       save: function(element, set) {
+               for(var i=0; i < set.length; i++) {
+                       if(set[i] !== null) element.data("ec.storage."+set[i], element[0].style[set[i]]);
+               }
+       },
+
+       // Restores a set of previously saved properties from a data storage
+       restore: function(element, set) {
+               for(var i=0; i < set.length; i++) {
+                       if(set[i] !== null) element.css(set[i], element.data("ec.storage."+set[i]));
+               }
+       },
+
+       setMode: function(el, mode) {
+               if (mode == 'toggle') mode = el.is(':hidden') ? 'show' : 'hide'; // Set for toggle
+               return mode;
+       },
+
+       getBaseline: function(origin, original) { // Translates a [top,left] array into a baseline value
+               // this should be a little more flexible in the future to handle a string & hash
+               var y, x;
+               switch (origin[0]) {
+                       case 'top': y = 0; break;
+                       case 'middle': y = 0.5; break;
+                       case 'bottom': y = 1; break;
+                       default: y = origin[0] / original.height;
+               };
+               switch (origin[1]) {
+                       case 'left': x = 0; break;
+                       case 'center': x = 0.5; break;
+                       case 'right': x = 1; break;
+                       default: x = origin[1] / original.width;
+               };
+               return {x: x, y: y};
+       },
+
+       // Wraps the element around a wrapper that copies position properties
+       createWrapper: function(element) {
+
+               //if the element is already wrapped, return it
+               if (element.parent().is('.ui-effects-wrapper'))
+                       return element.parent();
+
+               //Cache width,height and float properties of the element, and create a wrapper around it
+               var props = { width: element.outerWidth(true), height: element.outerHeight(true), 'float': element.css('float') };
+               element.wrap('<div class="ui-effects-wrapper" style="font-size:100%;background:transparent;border:none;margin:0;padding:0"></div>');
+               var wrapper = element.parent();
+
+               //Transfer the positioning of the element to the wrapper
+               if (element.css('position') == 'static') {
+                       wrapper.css({ position: 'relative' });
+                       element.css({ position: 'relative'} );
+               } else {
+                       var top = element.css('top'); if(isNaN(parseInt(top,10))) top = 'auto';
+                       var left = element.css('left'); if(isNaN(parseInt(left,10))) left = 'auto';
+                       wrapper.css({ position: element.css('position'), top: top, left: left, zIndex: element.css('z-index') }).show();
+                       element.css({position: 'relative', top: 0, left: 0 });
+               }
+
+               wrapper.css(props);
+               return wrapper;
+       },
+
+       removeWrapper: function(element) {
+               if (element.parent().is('.ui-effects-wrapper'))
+                       return element.parent().replaceWith(element);
+               return element;
+       },
+
+       setTransition: function(element, list, factor, value) {
+               value = value || {};
+               $.each(list, function(i, x){
+                       unit = element.cssUnit(x);
+                       if (unit[0] > 0) value[x] = unit[0] * factor + unit[1];
+               });
+               return value;
+       },
+
+       //Base function to animate from one class to another in a seamless transition
+       animateClass: function(value, duration, easing, callback) {
+
+               var cb = (typeof easing == "function" ? easing : (callback ? callback : null));
+               var ea = (typeof easing == "string" ? easing : null);
+
+               return this.each(function() {
+
+                       var offset = {}; var that = $(this); var oldStyleAttr = that.attr("style") || '';
+                       if(typeof oldStyleAttr == 'object') oldStyleAttr = oldStyleAttr["cssText"]; /* Stupidly in IE, style is a object.. */
+                       if(value.toggle) { that.hasClass(value.toggle) ? value.remove = value.toggle : value.add = value.toggle; }
+
+                       //Let's get a style offset
+                       var oldStyle = $.extend({}, (document.defaultView ? document.defaultView.getComputedStyle(this,null) : this.currentStyle));
+                       if(value.add) that.addClass(value.add); if(value.remove) that.removeClass(value.remove);
+                       var newStyle = $.extend({}, (document.defaultView ? document.defaultView.getComputedStyle(this,null) : this.currentStyle));
+                       if(value.add) that.removeClass(value.add); if(value.remove) that.addClass(value.remove);
+
+                       // The main function to form the object for animation
+                       for(var n in newStyle) {
+                               if( typeof newStyle[n] != "function" && newStyle[n] /* No functions and null properties */
+                               && n.indexOf("Moz") == -1 && n.indexOf("length") == -1 /* No mozilla spezific render properties. */
+                               && newStyle[n] != oldStyle[n] /* Only values that have changed are used for the animation */
+                               && (n.match(/color/i) || (!n.match(/color/i) && !isNaN(parseInt(newStyle[n],10)))) /* Only things that can be parsed to integers or colors */
+                               && (oldStyle.position != "static" || (oldStyle.position == "static" && !n.match(/left|top|bottom|right/))) /* No need for positions when dealing with static positions */
+                               ) offset[n] = newStyle[n];
+                       }
+
+                       that.animate(offset, duration, ea, function() { // Animate the newly constructed offset object
+                               // Change style attribute back to original. For stupid IE, we need to clear the damn object.
+                               if(typeof $(this).attr("style") == 'object') { $(this).attr("style")["cssText"] = ""; $(this).attr("style")["cssText"] = oldStyleAttr; } else $(this).attr("style", oldStyleAttr);
+                               if(value.add) $(this).addClass(value.add); if(value.remove) $(this).removeClass(value.remove);
+                               if(cb) cb.apply(this, arguments);
+                       });
+
+               });
+       }
+};
+
+
+function _normalizeArguments(a, m) {
+
+       var o = a[1] && a[1].constructor == Object ? a[1] : {}; if(m) o.mode = m;
+       var speed = a[1] && a[1].constructor != Object ? a[1] : (o.duration ? o.duration : a[2]); //either comes from options.duration or the secon/third argument
+               speed = $.fx.off ? 0 : typeof speed === "number" ? speed : $.fx.speeds[speed] || $.fx.speeds._default;
+       var callback = o.callback || ( $.isFunction(a[1]) && a[1] ) || ( $.isFunction(a[2]) && a[2] ) || ( $.isFunction(a[3]) && a[3] );
+
+       return [a[0], o, speed, callback];
+       
+}
+
+//Extend the methods of jQuery
+$.fn.extend({
+
+       //Save old methods
+       _show: $.fn.show,
+       _hide: $.fn.hide,
+       __toggle: $.fn.toggle,
+       _addClass: $.fn.addClass,
+       _removeClass: $.fn.removeClass,
+       _toggleClass: $.fn.toggleClass,
+
+       // New effect methods
+       effect: function(fx, options, speed, callback) {
+               return $.effects[fx] ? $.effects[fx].call(this, {method: fx, options: options || {}, duration: speed, callback: callback }) : null;
+       },
+
+       show: function() {
+               if(!arguments[0] || (arguments[0].constructor == Number || (/(slow|normal|fast)/).test(arguments[0])))
+                       return this._show.apply(this, arguments);
+               else {
+                       return this.effect.apply(this, _normalizeArguments(arguments, 'show'));
+               }
+       },
+
+       hide: function() {
+               if(!arguments[0] || (arguments[0].constructor == Number || (/(slow|normal|fast)/).test(arguments[0])))
+                       return this._hide.apply(this, arguments);
+               else {
+                       return this.effect.apply(this, _normalizeArguments(arguments, 'hide'));
+               }
+       },
+
+       toggle: function(){
+               if(!arguments[0] || (arguments[0].constructor == Number || (/(slow|normal|fast)/).test(arguments[0])) || (arguments[0].constructor == Function))
+                       return this.__toggle.apply(this, arguments);
+               else {
+                       return this.effect.apply(this, _normalizeArguments(arguments, 'toggle'));
+               }
+       },
+
+       addClass: function(classNames, speed, easing, callback) {
+               return speed ? $.effects.animateClass.apply(this, [{ add: classNames },speed,easing,callback]) : this._addClass(classNames);
+       },
+       removeClass: function(classNames,speed,easing,callback) {
+               return speed ? $.effects.animateClass.apply(this, [{ remove: classNames },speed,easing,callback]) : this._removeClass(classNames);
+       },
+       toggleClass: function(classNames,speed,easing,callback) {
+               return ( (typeof speed !== "boolean") && speed ) ? $.effects.animateClass.apply(this, [{ toggle: classNames },speed,easing,callback]) : this._toggleClass(classNames, speed);
+       },
+       morph: function(remove,add,speed,easing,callback) {
+               return $.effects.animateClass.apply(this, [{ add: add, remove: remove },speed,easing,callback]);
+       },
+       switchClass: function() {
+               return this.morph.apply(this, arguments);
+       },
+
+       // helper functions
+       cssUnit: function(key) {
+               var style = this.css(key), val = [];
+               $.each( ['em','px','%','pt'], function(i, unit){
+                       if(style.indexOf(unit) > 0)
+                               val = [parseFloat(style), unit];
+               });
+               return val;
+       }
+});
+
+/*
+ * jQuery Color Animations
+ * Copyright 2007 John Resig
+ * Released under the MIT and GPL licenses.
+ */
+
+// We override the animation for all of these color styles
+$.each(['backgroundColor', 'borderBottomColor', 'borderLeftColor', 'borderRightColor', 'borderTopColor', 'color', 'outlineColor'], function(i,attr){
+               $.fx.step[attr] = function(fx) {
+                               if ( fx.state == 0 ) {
+                                               fx.start = getColor( fx.elem, attr );
+                                               fx.end = getRGB( fx.end );
+                               }
+
+                               fx.elem.style[attr] = "rgb(" + [
+                                               Math.max(Math.min( parseInt((fx.pos * (fx.end[0] - fx.start[0])) + fx.start[0],10), 255), 0),
+                                               Math.max(Math.min( parseInt((fx.pos * (fx.end[1] - fx.start[1])) + fx.start[1],10), 255), 0),
+                                               Math.max(Math.min( parseInt((fx.pos * (fx.end[2] - fx.start[2])) + fx.start[2],10), 255), 0)
+                               ].join(",") + ")";
+                       };
+});
+
+// Color Conversion functions from highlightFade
+// By Blair Mitchelmore
+// http://jquery.offput.ca/highlightFade/
+
+// Parse strings looking for color tuples [255,255,255]
+function getRGB(color) {
+               var result;
+
+               // Check if we're already dealing with an array of colors
+               if ( color && color.constructor == Array && color.length == 3 )
+                               return color;
+
+               // Look for rgb(num,num,num)
+               if (result = /rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(color))
+                               return [parseInt(result[1],10), parseInt(result[2],10), parseInt(result[3],10)];
+
+               // Look for rgb(num%,num%,num%)
+               if (result = /rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(color))
+                               return [parseFloat(result[1])*2.55, parseFloat(result[2])*2.55, parseFloat(result[3])*2.55];
+
+               // Look for #a0b1c2
+               if (result = /#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(color))
+                               return [parseInt(result[1],16), parseInt(result[2],16), parseInt(result[3],16)];
+
+               // Look for #fff
+               if (result = /#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(color))
+                               return [parseInt(result[1]+result[1],16), parseInt(result[2]+result[2],16), parseInt(result[3]+result[3],16)];
+
+               // Look for rgba(0, 0, 0, 0) == transparent in Safari 3
+               if (result = /rgba\(0, 0, 0, 0\)/.exec(color))
+                               return colors['transparent'];
+
+               // Otherwise, we're most likely dealing with a named color
+               return colors[$.trim(color).toLowerCase()];
+}
+
+function getColor(elem, attr) {
+               var color;
+
+               do {
+                               color = $.curCSS(elem, attr);
+
+                               // Keep going until we find an element that has color, or we hit the body
+                               if ( color != '' && color != 'transparent' || $.nodeName(elem, "body") )
+                                               break;
+
+                               attr = "backgroundColor";
+               } while ( elem = elem.parentNode );
+
+               return getRGB(color);
+};
+
+// Some named colors to work with
+// From Interface by Stefan Petre
+// http://interface.eyecon.ro/
+
+var colors = {
+       aqua:[0,255,255],
+       azure:[240,255,255],
+       beige:[245,245,220],
+       black:[0,0,0],
+       blue:[0,0,255],
+       brown:[165,42,42],
+       cyan:[0,255,255],
+       darkblue:[0,0,139],
+       darkcyan:[0,139,139],
+       darkgrey:[169,169,169],
+       darkgreen:[0,100,0],
+       darkkhaki:[189,183,107],
+       darkmagenta:[139,0,139],
+       darkolivegreen:[85,107,47],
+       darkorange:[255,140,0],
+       darkorchid:[153,50,204],
+       darkred:[139,0,0],
+       darksalmon:[233,150,122],
+       darkviolet:[148,0,211],
+       fuchsia:[255,0,255],
+       gold:[255,215,0],
+       green:[0,128,0],
+       indigo:[75,0,130],
+       khaki:[240,230,140],
+       lightblue:[173,216,230],
+       lightcyan:[224,255,255],
+       lightgreen:[144,238,144],
+       lightgrey:[211,211,211],
+       lightpink:[255,182,193],
+       lightyellow:[255,255,224],
+       lime:[0,255,0],
+       magenta:[255,0,255],
+       maroon:[128,0,0],
+       navy:[0,0,128],
+       olive:[128,128,0],
+       orange:[255,165,0],
+       pink:[255,192,203],
+       purple:[128,0,128],
+       violet:[128,0,128],
+       red:[255,0,0],
+       silver:[192,192,192],
+       white:[255,255,255],
+       yellow:[255,255,0],
+       transparent: [255,255,255]
+};
+
+/*
+ * jQuery Easing v1.3 - http://gsgd.co.uk/sandbox/jquery/easing/
+ *
+ * Uses the built in easing capabilities added In jQuery 1.1
+ * to offer multiple easing options
+ *
+ * TERMS OF USE - jQuery Easing
+ *
+ * Open source under the BSD License.
+ *
+ * Copyright 2008 George McGinley Smith
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice, this list
+ * of conditions and the following disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * Neither the name of the author nor the names of contributors may be used to endorse
+ * or promote products derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+*/
+
+// t: current time, b: begInnIng value, c: change In value, d: duration
+$.easing.jswing = $.easing.swing;
+
+$.extend($.easing,
+{
+       def: 'easeOutQuad',
+       swing: function (x, t, b, c, d) {
+               //alert($.easing.default);
+               return $.easing[$.easing.def](x, t, b, c, d);
+       },
+       easeInQuad: function (x, t, b, c, d) {
+               return c*(t/=d)*t + b;
+       },
+       easeOutQuad: function (x, t, b, c, d) {
+               return -c *(t/=d)*(t-2) + b;
+       },
+       easeInOutQuad: function (x, t, b, c, d) {
+               if ((t/=d/2) < 1) return c/2*t*t + b;
+               return -c/2 * ((--t)*(t-2) - 1) + b;
+       },
+       easeInCubic: function (x, t, b, c, d) {
+               return c*(t/=d)*t*t + b;
+       },
+       easeOutCubic: function (x, t, b, c, d) {
+               return c*((t=t/d-1)*t*t + 1) + b;
+       },
+       easeInOutCubic: function (x, t, b, c, d) {
+               if ((t/=d/2) < 1) return c/2*t*t*t + b;
+               return c/2*((t-=2)*t*t + 2) + b;
+       },
+       easeInQuart: function (x, t, b, c, d) {
+               return c*(t/=d)*t*t*t + b;
+       },
+       easeOutQuart: function (x, t, b, c, d) {
+               return -c * ((t=t/d-1)*t*t*t - 1) + b;
+       },
+       easeInOutQuart: function (x, t, b, c, d) {
+               if ((t/=d/2) < 1) return c/2*t*t*t*t + b;
+               return -c/2 * ((t-=2)*t*t*t - 2) + b;
+       },
+       easeInQuint: function (x, t, b, c, d) {
+               return c*(t/=d)*t*t*t*t + b;
+       },
+       easeOutQuint: function (x, t, b, c, d) {
+               return c*((t=t/d-1)*t*t*t*t + 1) + b;
+       },
+       easeInOutQuint: function (x, t, b, c, d) {
+               if ((t/=d/2) < 1) return c/2*t*t*t*t*t + b;
+               return c/2*((t-=2)*t*t*t*t + 2) + b;
+       },
+       easeInSine: function (x, t, b, c, d) {
+               return -c * Math.cos(t/d * (Math.PI/2)) + c + b;
+       },
+       easeOutSine: function (x, t, b, c, d) {
+               return c * Math.sin(t/d * (Math.PI/2)) + b;
+       },
+       easeInOutSine: function (x, t, b, c, d) {
+               return -c/2 * (Math.cos(Math.PI*t/d) - 1) + b;
+       },
+       easeInExpo: function (x, t, b, c, d) {
+               return (t==0) ? b : c * Math.pow(2, 10 * (t/d - 1)) + b;
+       },
+       easeOutExpo: function (x, t, b, c, d) {
+               return (t==d) ? b+c : c * (-Math.pow(2, -10 * t/d) + 1) + b;
+       },
+       easeInOutExpo: function (x, t, b, c, d) {
+               if (t==0) return b;
+               if (t==d) return b+c;
+               if ((t/=d/2) < 1) return c/2 * Math.pow(2, 10 * (t - 1)) + b;
+               return c/2 * (-Math.pow(2, -10 * --t) + 2) + b;
+       },
+       easeInCirc: function (x, t, b, c, d) {
+               return -c * (Math.sqrt(1 - (t/=d)*t) - 1) + b;
+       },
+       easeOutCirc: function (x, t, b, c, d) {
+               return c * Math.sqrt(1 - (t=t/d-1)*t) + b;
+       },
+       easeInOutCirc: function (x, t, b, c, d) {
+               if ((t/=d/2) < 1) return -c/2 * (Math.sqrt(1 - t*t) - 1) + b;
+               return c/2 * (Math.sqrt(1 - (t-=2)*t) + 1) + b;
+       },
+       easeInElastic: function (x, t, b, c, d) {
+               var s=1.70158;var p=0;var a=c;
+               if (t==0) return b;  if ((t/=d)==1) return b+c;  if (!p) p=d*.3;
+               if (a < Math.abs(c)) { a=c; var s=p/4; }
+               else var s = p/(2*Math.PI) * Math.asin (c/a);
+               return -(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b;
+       },
+       easeOutElastic: function (x, t, b, c, d) {
+               var s=1.70158;var p=0;var a=c;
+               if (t==0) return b;  if ((t/=d)==1) return b+c;  if (!p) p=d*.3;
+               if (a < Math.abs(c)) { a=c; var s=p/4; }
+               else var s = p/(2*Math.PI) * Math.asin (c/a);
+               return a*Math.pow(2,-10*t) * Math.sin( (t*d-s)*(2*Math.PI)/p ) + c + b;
+       },
+       easeInOutElastic: function (x, t, b, c, d) {
+               var s=1.70158;var p=0;var a=c;
+               if (t==0) return b;  if ((t/=d/2)==2) return b+c;  if (!p) p=d*(.3*1.5);
+               if (a < Math.abs(c)) { a=c; var s=p/4; }
+               else var s = p/(2*Math.PI) * Math.asin (c/a);
+               if (t < 1) return -.5*(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b;
+               return a*Math.pow(2,-10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )*.5 + c + b;
+       },
+       easeInBack: function (x, t, b, c, d, s) {
+               if (s == undefined) s = 1.70158;
+               return c*(t/=d)*t*((s+1)*t - s) + b;
+       },
+       easeOutBack: function (x, t, b, c, d, s) {
+               if (s == undefined) s = 1.70158;
+               return c*((t=t/d-1)*t*((s+1)*t + s) + 1) + b;
+       },
+       easeInOutBack: function (x, t, b, c, d, s) {
+               if (s == undefined) s = 1.70158;
+               if ((t/=d/2) < 1) return c/2*(t*t*(((s*=(1.525))+1)*t - s)) + b;
+               return c/2*((t-=2)*t*(((s*=(1.525))+1)*t + s) + 2) + b;
+       },
+       easeInBounce: function (x, t, b, c, d) {
+               return c - $.easing.easeOutBounce (x, d-t, 0, c, d) + b;
+       },
+       easeOutBounce: function (x, t, b, c, d) {
+               if ((t/=d) < (1/2.75)) {
+                       return c*(7.5625*t*t) + b;
+               } else if (t < (2/2.75)) {
+                       return c*(7.5625*(t-=(1.5/2.75))*t + .75) + b;
+               } else if (t < (2.5/2.75)) {
+                       return c*(7.5625*(t-=(2.25/2.75))*t + .9375) + b;
+               } else {
+                       return c*(7.5625*(t-=(2.625/2.75))*t + .984375) + b;
+               }
+       },
+       easeInOutBounce: function (x, t, b, c, d) {
+               if (t < d/2) return $.easing.easeInBounce (x, t*2, 0, c, d) * .5 + b;
+               return $.easing.easeOutBounce (x, t*2-d, 0, c, d) * .5 + c*.5 + b;
+       }
+});
+
+/*
+ *
+ * TERMS OF USE - EASING EQUATIONS
+ *
+ * Open source under the BSD License.
+ *
+ * Copyright 2001 Robert Penner
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice, this list
+ * of conditions and the following disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * Neither the name of the author nor the names of contributors may be used to endorse
+ * or promote products derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+})(jQuery);
diff --git a/Toolkit/PHPImageEditor/javascript/joomla_editimagelink.js b/Toolkit/PHPImageEditor/javascript/joomla_editimagelink.js
new file mode 100644 (file)
index 0000000..2270ab6
--- /dev/null
@@ -0,0 +1,167 @@
+String.prototype.phpimageeditor_add_editlink_endswith = function(str) 
+{
+       return (this.match(str+"$")==str);
+}
+
+function phpimageeditor_add_editlink(pathToEditor, pathToPlugin, hostPath, editImageText, language)
+{
+       var mediamanagerForm = document.getElementById("mediamanager-form");
+       if (mediamanagerForm != null)
+       {
+               var trs = mediamanagerForm.getElementsByTagName("tr");
+               var modeDetailed = (trs.length > 0);
+               
+               if (modeDetailed)
+               {
+                       var isTableHeader = true;
+                       
+                       for(var i=0;i<trs.length;i++)
+                       {
+                               if (isTableHeader)
+                               {
+                                       var th = document.createElement("th");
+                                       th.appendChild(document.createTextNode(editImageText));
+                                       trs[i].appendChild(th);
+                               }
+                               else 
+                               {
+                                       var td = document.createElement("td");
+                                       
+                                       var imageSrcDetailed = "";
+                                       var links = trs[i].getElementsByTagName("a");
+                                       var foundDetailedImage = false;
+
+                                       for(var c=0;c<links.length;c++)
+                                       {
+                                               if (links[c].className == 'img-preview')
+                                               {
+                                                       imageSrcDetailed = phpimageeditor_urlencode(links[c].href.replace(hostPath,'../../../')); 
+                                                       
+                                                       if (phpimageeditor_file_is_image(imageSrcDetailed))
+                                                       {
+                                                               if (trs[i].innerHTML.indexOf('folderup_16.png') == -1 && trs[i].innerHTML.indexOf('folder_sm.png') == -1)
+                                                               {
+                                                                       td.innerHTML = '<div><a style="background-position: 5px 0; background-image: url('+pathToPlugin+'images/edit.gif); background-repeat: no-repeat; padding-bottom: 4px; padding-left: 22px;" href="'+pathToEditor+imageSrcDetailed+'&language='+language+'" target="_blank">'+editImageText+'</a></div>';
+                                                                       foundDetailedImage = true;
+                                                                       break;
+                                                               }
+                                                       }
+                                               }
+                                       }
+                                       
+                                       if (!foundDetailedImage)
+                                               td.innerHTML = "&nbsp;";
+
+                                       trs[i].appendChild(td);
+                               }
+                               
+                               isTableHeader = false;                  
+                       }
+               }
+               else
+               {
+                       var e = mediamanagerForm.getElementsByTagName("div");
+                       
+                       for(var i=0;i<e.length;i++)
+                       {
+                               if (e[i].className == 'imgOutline' && e[i].innerHTML.indexOf('folderup_32.png') == -1 && e[i].innerHTML.indexOf('folder.png') == -1)
+                               {
+                                       var images = e[i].getElementsByTagName("img");
+                                       var imageSrc = "";
+                                       
+                                       if (images.length > 0)
+                                       {
+                                               imageSrc = phpimageeditor_urlencode(images[0].src.replace(hostPath,'../../../'));
+               
+                                               if (phpimageeditor_file_is_image(imageSrc))
+                                                       e[i].innerHTML += '<a style="background-position: 5px 0; background-image: url('+pathToPlugin+'images/edit.gif); background-repeat: no-repeat; padding-bottom: 4px; padding-left: 22px; display: block;" href="'+pathToEditor+imageSrc+'&language='+language+'" target="_blank">'+editImageText+'</a>';
+                                       }
+                               }
+                       }
+               }
+       }
+       else
+       {
+               var articledivs = document.getElementsByTagName("div");
+               var foundManager = false;
+               var foundItem = false;
+               for(var i=0;i<articledivs.length;i++)
+               {
+                       if (articledivs[i].className == 'manager')
+                               foundManager = true;
+                       else if (articledivs[i].className == 'item')
+                               foundItem = true;
+                               
+                       if (foundManager && foundItem)
+                               break;
+               }               
+                       
+               if (foundManager && foundItem)
+               {
+                       for(var i=0;i<articledivs.length;i++)
+                       {
+                               if (articledivs[i].className == 'item')
+                               {
+                                       var imagesArticle = articledivs[i].getElementsByTagName("img");
+                                       var imageSrcArticle = "";
+                                       
+                                       if (imagesArticle.length > 0 && imagesArticle[0].src.indexOf('folder.gif') == -1 && phpimageeditor_file_is_image(imagesArticle[0].src))
+                                       {
+                                               imageSrcArticle = phpimageeditor_urlencode(imagesArticle[0].src.replace(hostPath,'../../../'));
+                                               articledivs[i].innerHTML = '<a style="position: absolute; top: 0; left: 0; background-color: #eeeeee; display: block; line-height: 15px; height: auto;" href="'+pathToEditor+imageSrcArticle+'&language='+language+'" target="_blank">'+editImageText+'</a>' + articledivs[i].innerHTML;
+                                       }
+                               }
+                       }
+               }
+       }
+}
+
+function phpimageeditor_urlencode(str) 
+{
+    // http://kevin.vanzonneveld.net
+    // +   original by: Philip Peterson
+    // +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
+    // +      input by: AJ
+    // +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
+    // %          note: info on what encoding functions to use from: http://xkr.us/articles/javascript/encode-compare/
+    // *     example 1: urlencode('Kevin van Zonneveld!');
+    // *     returns 1: 'Kevin+van+Zonneveld%21'
+    // *     example 2: urlencode('http://kevin.vanzonneveld.net/');
+    // *     returns 2: 'http%3A%2F%2Fkevin.vanzonneveld.net%2F'
+    // *     example 3: urlencode('http://www.google.nl/search?q=php.js&ie=utf-8&oe=utf-8&aq=t&rls=com.ubuntu:en-US:unofficial&client=firefox-a');
+    // *     returns 3: 'http%3A%2F%2Fwww.google.nl%2Fsearch%3Fq%3Dphp.js%26ie%3Dutf-8%26oe%3Dutf-8%26aq%3Dt%26rls%3Dcom.ubuntu%3Aen-US%3Aunofficial%26client%3Dfirefox-a'
+                                     
+    var histogram = {}, histogram_r = {}, code = 0, tmp_arr = [];
+    var ret = str.toString();
+    
+    var replacer = function(search, replace, str) {
+        var tmp_arr = [];
+        tmp_arr = str.split(search);
+        return tmp_arr.join(replace);
+    };
+    
+    // The histogram is identical to the one in urldecode.
+    histogram['!']   = '%21';
+    histogram['%20'] = '+';
+    
+    // Begin with encodeURIComponent, which most resembles PHP's encoding functions
+    ret = encodeURIComponent(ret);
+    
+    for (search in histogram) {
+        replace = histogram[search];
+        ret = replacer(search, replace, ret) // Custom replace. No regexing
+    }
+    
+    // Uppercase for full PHP compatibility
+    return ret.replace('/(\%([a-z0-9]{2}))/g', function(full, m1, m2) {
+        return "%"+m2.toUpperCase();
+    });
+    
+    return ret;
+}
+
+function phpimageeditor_file_is_image(filePath)
+{
+       filePath = filePath.toLowerCase();
+       return (filePath.indexOf('com_media') == -1 && (filePath.phpimageeditor_add_editlink_endswith("jpg") || filePath.phpimageeditor_add_editlink_endswith("gif") || filePath.phpimageeditor_add_editlink_endswith("png")));
+}
\ No newline at end of file
diff --git a/Toolkit/PHPImageEditor/javascript/jquery-1.3.2.min.js b/Toolkit/PHPImageEditor/javascript/jquery-1.3.2.min.js
new file mode 100644 (file)
index 0000000..b1ae21d
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * jQuery JavaScript Library v1.3.2
+ * http://jquery.com/
+ *
+ * Copyright (c) 2009 John Resig
+ * Dual licensed under the MIT and GPL licenses.
+ * http://docs.jquery.com/License
+ *
+ * Date: 2009-02-19 17:34:21 -0500 (Thu, 19 Feb 2009)
+ * Revision: 6246
+ */
+(function(){var l=this,g,y=l.jQuery,p=l.$,o=l.jQuery=l.$=function(E,F){return new o.fn.init(E,F)},D=/^[^<]*(<(.|\s)+>)[^>]*$|^#([\w-]+)$/,f=/^.[^:#\[\.,]*$/;o.fn=o.prototype={init:function(E,H){E=E||document;if(E.nodeType){this[0]=E;this.length=1;this.context=E;return this}if(typeof E==="string"){var G=D.exec(E);if(G&&(G[1]||!H)){if(G[1]){E=o.clean([G[1]],H)}else{var I=document.getElementById(G[3]);if(I&&I.id!=G[3]){return o().find(E)}var F=o(I||[]);F.context=document;F.selector=E;return F}}else{return o(H).find(E)}}else{if(o.isFunction(E)){return o(document).ready(E)}}if(E.selector&&E.context){this.selector=E.selector;this.context=E.context}return this.setArray(o.isArray(E)?E:o.makeArray(E))},selector:"",jquery:"1.3.2",size:function(){return this.length},get:function(E){return E===g?Array.prototype.slice.call(this):this[E]},pushStack:function(F,H,E){var G=o(F);G.prevObject=this;G.context=this.context;if(H==="find"){G.selector=this.selector+(this.selector?" ":"")+E}else{if(H){G.selector=this.selector+"."+H+"("+E+")"}}return G},setArray:function(E){this.length=0;Array.prototype.push.apply(this,E);return this},each:function(F,E){return o.each(this,F,E)},index:function(E){return o.inArray(E&&E.jquery?E[0]:E,this)},attr:function(F,H,G){var E=F;if(typeof F==="string"){if(H===g){return this[0]&&o[G||"attr"](this[0],F)}else{E={};E[F]=H}}return this.each(function(I){for(F in E){o.attr(G?this.style:this,F,o.prop(this,E[F],G,I,F))}})},css:function(E,F){if((E=="width"||E=="height")&&parseFloat(F)<0){F=g}return this.attr(E,F,"curCSS")},text:function(F){if(typeof F!=="object"&&F!=null){return this.empty().append((this[0]&&this[0].ownerDocument||document).createTextNode(F))}var E="";o.each(F||this,function(){o.each(this.childNodes,function(){if(this.nodeType!=8){E+=this.nodeType!=1?this.nodeValue:o.fn.text([this])}})});return E},wrapAll:function(E){if(this[0]){var F=o(E,this[0].ownerDocument).clone();if(this[0].parentNode){F.insertBefore(this[0])}F.map(function(){var G=this;while(G.firstChild){G=G.firstChild}return G}).append(this)}return this},wrapInner:function(E){return this.each(function(){o(this).contents().wrapAll(E)})},wrap:function(E){return this.each(function(){o(this).wrapAll(E)})},append:function(){return this.domManip(arguments,true,function(E){if(this.nodeType==1){this.appendChild(E)}})},prepend:function(){return this.domManip(arguments,true,function(E){if(this.nodeType==1){this.insertBefore(E,this.firstChild)}})},before:function(){return this.domManip(arguments,false,function(E){this.parentNode.insertBefore(E,this)})},after:function(){return this.domManip(arguments,false,function(E){this.parentNode.insertBefore(E,this.nextSibling)})},end:function(){return this.prevObject||o([])},push:[].push,sort:[].sort,splice:[].splice,find:function(E){if(this.length===1){var F=this.pushStack([],"find",E);F.length=0;o.find(E,this[0],F);return F}else{return this.pushStack(o.unique(o.map(this,function(G){return o.find(E,G)})),"find",E)}},clone:function(G){var E=this.map(function(){if(!o.support.noCloneEvent&&!o.isXMLDoc(this)){var I=this.outerHTML;if(!I){var J=this.ownerDocument.createElement("div");J.appendChild(this.cloneNode(true));I=J.innerHTML}return o.clean([I.replace(/ jQuery\d+="(?:\d+|null)"/g,"").replace(/^\s*/,"")])[0]}else{return this.cloneNode(true)}});if(G===true){var H=this.find("*").andSelf(),F=0;E.find("*").andSelf().each(function(){if(this.nodeName!==H[F].nodeName){return}var I=o.data(H[F],"events");for(var K in I){for(var J in I[K]){o.event.add(this,K,I[K][J],I[K][J].data)}}F++})}return E},filter:function(E){return this.pushStack(o.isFunction(E)&&o.grep(this,function(G,F){return E.call(G,F)})||o.multiFilter(E,o.grep(this,function(F){return F.nodeType===1})),"filter",E)},closest:function(E){var G=o.expr.match.POS.test(E)?o(E):null,F=0;return this.map(function(){var H=this;while(H&&H.ownerDocument){if(G?G.index(H)>-1:o(H).is(E)){o.data(H,"closest",F);return H}H=H.parentNode;F++}})},not:function(E){if(typeof E==="string"){if(f.test(E)){return this.pushStack(o.multiFilter(E,this,true),"not",E)}else{E=o.multiFilter(E,this)}}var F=E.length&&E[E.length-1]!==g&&!E.nodeType;return this.filter(function(){return F?o.inArray(this,E)<0:this!=E})},add:function(E){return this.pushStack(o.unique(o.merge(this.get(),typeof E==="string"?o(E):o.makeArray(E))))},is:function(E){return !!E&&o.multiFilter(E,this).length>0},hasClass:function(E){return !!E&&this.is("."+E)},val:function(K){if(K===g){var E=this[0];if(E){if(o.nodeName(E,"option")){return(E.attributes.value||{}).specified?E.value:E.text}if(o.nodeName(E,"select")){var I=E.selectedIndex,L=[],M=E.options,H=E.type=="select-one";if(I<0){return null}for(var F=H?I:0,J=H?I+1:M.length;F<J;F++){var G=M[F];if(G.selected){K=o(G).val();if(H){return K}L.push(K)}}return L}return(E.value||"").replace(/\r/g,"")}return g}if(typeof K==="number"){K+=""}return this.each(function(){if(this.nodeType!=1){return}if(o.isArray(K)&&/radio|checkbox/.test(this.type)){this.checked=(o.inArray(this.value,K)>=0||o.inArray(this.name,K)>=0)}else{if(o.nodeName(this,"select")){var N=o.makeArray(K);o("option",this).each(function(){this.selected=(o.inArray(this.value,N)>=0||o.inArray(this.text,N)>=0)});if(!N.length){this.selectedIndex=-1}}else{this.value=K}}})},html:function(E){return E===g?(this[0]?this[0].innerHTML.replace(/ jQuery\d+="(?:\d+|null)"/g,""):null):this.empty().append(E)},replaceWith:function(E){return this.after(E).remove()},eq:function(E){return this.slice(E,+E+1)},slice:function(){return this.pushStack(Array.prototype.slice.apply(this,arguments),"slice",Array.prototype.slice.call(arguments).join(","))},map:function(E){return this.pushStack(o.map(this,function(G,F){return E.call(G,F,G)}))},andSelf:function(){return this.add(this.prevObject)},domManip:function(J,M,L){if(this[0]){var I=(this[0].ownerDocument||this[0]).createDocumentFragment(),F=o.clean(J,(this[0].ownerDocument||this[0]),I),H=I.firstChild;if(H){for(var G=0,E=this.length;G<E;G++){L.call(K(this[G],H),this.length>1||G>0?I.cloneNode(true):I)}}if(F){o.each(F,z)}}return this;function K(N,O){return M&&o.nodeName(N,"table")&&o.nodeName(O,"tr")?(N.getElementsByTagName("tbody")[0]||N.appendChild(N.ownerDocument.createElement("tbody"))):N}}};o.fn.init.prototype=o.fn;function z(E,F){if(F.src){o.ajax({url:F.src,async:false,dataType:"script"})}else{o.globalEval(F.text||F.textContent||F.innerHTML||"")}if(F.parentNode){F.parentNode.removeChild(F)}}function e(){return +new Date}o.extend=o.fn.extend=function(){var J=arguments[0]||{},H=1,I=arguments.length,E=false,G;if(typeof J==="boolean"){E=J;J=arguments[1]||{};H=2}if(typeof J!=="object"&&!o.isFunction(J)){J={}}if(I==H){J=this;--H}for(;H<I;H++){if((G=arguments[H])!=null){for(var F in G){var K=J[F],L=G[F];if(J===L){continue}if(E&&L&&typeof L==="object"&&!L.nodeType){J[F]=o.extend(E,K||(L.length!=null?[]:{}),L)}else{if(L!==g){J[F]=L}}}}}return J};var b=/z-?index|font-?weight|opacity|zoom|line-?height/i,q=document.defaultView||{},s=Object.prototype.toString;o.extend({noConflict:function(E){l.$=p;if(E){l.jQuery=y}return o},isFunction:function(E){return s.call(E)==="[object Function]"},isArray:function(E){return s.call(E)==="[object Array]"},isXMLDoc:function(E){return E.nodeType===9&&E.documentElement.nodeName!=="HTML"||!!E.ownerDocument&&o.isXMLDoc(E.ownerDocument)},globalEval:function(G){if(G&&/\S/.test(G)){var F=document.getElementsByTagName("head")[0]||document.documentElement,E=document.createElement("script");E.type="text/javascript";if(o.support.scriptEval){E.appendChild(document.createTextNode(G))}else{E.text=G}F.insertBefore(E,F.firstChild);F.removeChild(E)}},nodeName:function(F,E){return F.nodeName&&F.nodeName.toUpperCase()==E.toUpperCase()},each:function(G,K,F){var E,H=0,I=G.length;if(F){if(I===g){for(E in G){if(K.apply(G[E],F)===false){break}}}else{for(;H<I;){if(K.apply(G[H++],F)===false){break}}}}else{if(I===g){for(E in G){if(K.call(G[E],E,G[E])===false){break}}}else{for(var J=G[0];H<I&&K.call(J,H,J)!==false;J=G[++H]){}}}return G},prop:function(H,I,G,F,E){if(o.isFunction(I)){I=I.call(H,F)}return typeof I==="number"&&G=="curCSS"&&!b.test(E)?I+"px":I},className:{add:function(E,F){o.each((F||"").split(/\s+/),function(G,H){if(E.nodeType==1&&!o.className.has(E.className,H)){E.className+=(E.className?" ":"")+H}})},remove:function(E,F){if(E.nodeType==1){E.className=F!==g?o.grep(E.className.split(/\s+/),function(G){return !o.className.has(F,G)}).join(" "):""}},has:function(F,E){return F&&o.inArray(E,(F.className||F).toString().split(/\s+/))>-1}},swap:function(H,G,I){var E={};for(var F in G){E[F]=H.style[F];H.style[F]=G[F]}I.call(H);for(var F in G){H.style[F]=E[F]}},css:function(H,F,J,E){if(F=="width"||F=="height"){var L,G={position:"absolute",visibility:"hidden",display:"block"},K=F=="width"?["Left","Right"]:["Top","Bottom"];function I(){L=F=="width"?H.offsetWidth:H.offsetHeight;if(E==="border"){return}o.each(K,function(){if(!E){L-=parseFloat(o.curCSS(H,"padding"+this,true))||0}if(E==="margin"){L+=parseFloat(o.curCSS(H,"margin"+this,true))||0}else{L-=parseFloat(o.curCSS(H,"border"+this+"Width",true))||0}})}if(H.offsetWidth!==0){I()}else{o.swap(H,G,I)}return Math.max(0,Math.round(L))}return o.curCSS(H,F,J)},curCSS:function(I,F,G){var L,E=I.style;if(F=="opacity"&&!o.support.opacity){L=o.attr(E,"opacity");return L==""?"1":L}if(F.match(/float/i)){F=w}if(!G&&E&&E[F]){L=E[F]}else{if(q.getComputedStyle){if(F.match(/float/i)){F="float"}F=F.replace(/([A-Z])/g,"-$1").toLowerCase();var M=q.getComputedStyle(I,null);if(M){L=M.getPropertyValue(F)}if(F=="opacity"&&L==""){L="1"}}else{if(I.currentStyle){var J=F.replace(/\-(\w)/g,function(N,O){return O.toUpperCase()});L=I.currentStyle[F]||I.currentStyle[J];if(!/^\d+(px)?$/i.test(L)&&/^\d/.test(L)){var H=E.left,K=I.runtimeStyle.left;I.runtimeStyle.left=I.currentStyle.left;E.left=L||0;L=E.pixelLeft+"px";E.left=H;I.runtimeStyle.left=K}}}}return L},clean:function(F,K,I){K=K||document;if(typeof K.createElement==="undefined"){K=K.ownerDocument||K[0]&&K[0].ownerDocument||document}if(!I&&F.length===1&&typeof F[0]==="string"){var H=/^<(\w+)\s*\/?>$/.exec(F[0]);if(H){return[K.createElement(H[1])]}}var G=[],E=[],L=K.createElement("div");o.each(F,function(P,S){if(typeof S==="number"){S+=""}if(!S){return}if(typeof S==="string"){S=S.replace(/(<(\w+)[^>]*?)\/>/g,function(U,V,T){return T.match(/^(abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i)?U:V+"></"+T+">"});var O=S.replace(/^\s+/,"").substring(0,10).toLowerCase();var Q=!O.indexOf("<opt")&&[1,"<select multiple='multiple'>","</select>"]||!O.indexOf("<leg")&&[1,"<fieldset>","</fieldset>"]||O.match(/^<(thead|tbody|tfoot|colg|cap)/)&&[1,"<table>","</table>"]||!O.indexOf("<tr")&&[2,"<table><tbody>","</tbody></table>"]||(!O.indexOf("<td")||!O.indexOf("<th"))&&[3,"<table><tbody><tr>","</tr></tbody></table>"]||!O.indexOf("<col")&&[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"]||!o.support.htmlSerialize&&[1,"div<div>","</div>"]||[0,"",""];L.innerHTML=Q[1]+S+Q[2];while(Q[0]--){L=L.lastChild}if(!o.support.tbody){var R=/<tbody/i.test(S),N=!O.indexOf("<table")&&!R?L.firstChild&&L.firstChild.childNodes:Q[1]=="<table>"&&!R?L.childNodes:[];for(var M=N.length-1;M>=0;--M){if(o.nodeName(N[M],"tbody")&&!N[M].childNodes.length){N[M].parentNode.removeChild(N[M])}}}if(!o.support.leadingWhitespace&&/^\s/.test(S)){L.insertBefore(K.createTextNode(S.match(/^\s*/)[0]),L.firstChild)}S=o.makeArray(L.childNodes)}if(S.nodeType){G.push(S)}else{G=o.merge(G,S)}});if(I){for(var J=0;G[J];J++){if(o.nodeName(G[J],"script")&&(!G[J].type||G[J].type.toLowerCase()==="text/javascript")){E.push(G[J].parentNode?G[J].parentNode.removeChild(G[J]):G[J])}else{if(G[J].nodeType===1){G.splice.apply(G,[J+1,0].concat(o.makeArray(G[J].getElementsByTagName("script"))))}I.appendChild(G[J])}}return E}return G},attr:function(J,G,K){if(!J||J.nodeType==3||J.nodeType==8){return g}var H=!o.isXMLDoc(J),L=K!==g;G=H&&o.props[G]||G;if(J.tagName){var F=/href|src|style/.test(G);if(G=="selected"&&J.parentNode){J.parentNode.selectedIndex}if(G in J&&H&&!F){if(L){if(G=="type"&&o.nodeName(J,"input")&&J.parentNode){throw"type property can't be changed"}J[G]=K}if(o.nodeName(J,"form")&&J.getAttributeNode(G)){return J.getAttributeNode(G).nodeValue}if(G=="tabIndex"){var I=J.getAttributeNode("tabIndex");return I&&I.specified?I.value:J.nodeName.match(/(button|input|object|select|textarea)/i)?0:J.nodeName.match(/^(a|area)$/i)&&J.href?0:g}return J[G]}if(!o.support.style&&H&&G=="style"){return o.attr(J.style,"cssText",K)}if(L){J.setAttribute(G,""+K)}var E=!o.support.hrefNormalized&&H&&F?J.getAttribute(G,2):J.getAttribute(G);return E===null?g:E}if(!o.support.opacity&&G=="opacity"){if(L){J.zoom=1;J.filter=(J.filter||"").replace(/alpha\([^)]*\)/,"")+(parseInt(K)+""=="NaN"?"":"alpha(opacity="+K*100+")")}return J.filter&&J.filter.indexOf("opacity=")>=0?(parseFloat(J.filter.match(/opacity=([^)]*)/)[1])/100)+"":""}G=G.replace(/-([a-z])/ig,function(M,N){return N.toUpperCase()});if(L){J[G]=K}return J[G]},trim:function(E){return(E||"").replace(/^\s+|\s+$/g,"")},makeArray:function(G){var E=[];if(G!=null){var F=G.length;if(F==null||typeof G==="string"||o.isFunction(G)||G.setInterval){E[0]=G}else{while(F){E[--F]=G[F]}}}return E},inArray:function(G,H){for(var E=0,F=H.length;E<F;E++){if(H[E]===G){return E}}return -1},merge:function(H,E){var F=0,G,I=H.length;if(!o.support.getAll){while((G=E[F++])!=null){if(G.nodeType!=8){H[I++]=G}}}else{while((G=E[F++])!=null){H[I++]=G}}return H},unique:function(K){var F=[],E={};try{for(var G=0,H=K.length;G<H;G++){var J=o.data(K[G]);if(!E[J]){E[J]=true;F.push(K[G])}}}catch(I){F=K}return F},grep:function(F,J,E){var G=[];for(var H=0,I=F.length;H<I;H++){if(!E!=!J(F[H],H)){G.push(F[H])}}return G},map:function(E,J){var F=[];for(var G=0,H=E.length;G<H;G++){var I=J(E[G],G);if(I!=null){F[F.length]=I}}return F.concat.apply([],F)}});var C=navigator.userAgent.toLowerCase();o.browser={version:(C.match(/.+(?:rv|it|ra|ie)[\/: ]([\d.]+)/)||[0,"0"])[1],safari:/webkit/.test(C),opera:/opera/.test(C),msie:/msie/.test(C)&&!/opera/.test(C),mozilla:/mozilla/.test(C)&&!/(compatible|webkit)/.test(C)};o.each({parent:function(E){return E.parentNode},parents:function(E){return o.dir(E,"parentNode")},next:function(E){return o.nth(E,2,"nextSibling")},prev:function(E){return o.nth(E,2,"previousSibling")},nextAll:function(E){return o.dir(E,"nextSibling")},prevAll:function(E){return o.dir(E,"previousSibling")},siblings:function(E){return o.sibling(E.parentNode.firstChild,E)},children:function(E){return o.sibling(E.firstChild)},contents:function(E){return o.nodeName(E,"iframe")?E.contentDocument||E.contentWindow.document:o.makeArray(E.childNodes)}},function(E,F){o.fn[E]=function(G){var H=o.map(this,F);if(G&&typeof G=="string"){H=o.multiFilter(G,H)}return this.pushStack(o.unique(H),E,G)}});o.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(E,F){o.fn[E]=function(G){var J=[],L=o(G);for(var K=0,H=L.length;K<H;K++){var I=(K>0?this.clone(true):this).get();o.fn[F].apply(o(L[K]),I);J=J.concat(I)}return this.pushStack(J,E,G)}});o.each({removeAttr:function(E){o.attr(this,E,"");if(this.nodeType==1){this.removeAttribute(E)}},addClass:function(E){o.className.add(this,E)},removeClass:function(E){o.className.remove(this,E)},toggleClass:function(F,E){if(typeof E!=="boolean"){E=!o.className.has(this,F)}o.className[E?"add":"remove"](this,F)},remove:function(E){if(!E||o.filter(E,[this]).length){o("*",this).add([this]).each(function(){o.event.remove(this);o.removeData(this)});if(this.parentNode){this.parentNode.removeChild(this)}}},empty:function(){o(this).children().remove();while(this.firstChild){this.removeChild(this.firstChild)}}},function(E,F){o.fn[E]=function(){return this.each(F,arguments)}});function j(E,F){return E[0]&&parseInt(o.curCSS(E[0],F,true),10)||0}var h="jQuery"+e(),v=0,A={};o.extend({cache:{},data:function(F,E,G){F=F==l?A:F;var H=F[h];if(!H){H=F[h]=++v}if(E&&!o.cache[H]){o.cache[H]={}}if(G!==g){o.cache[H][E]=G}return E?o.cache[H][E]:H},removeData:function(F,E){F=F==l?A:F;var H=F[h];if(E){if(o.cache[H]){delete o.cache[H][E];E="";for(E in o.cache[H]){break}if(!E){o.removeData(F)}}}else{try{delete F[h]}catch(G){if(F.removeAttribute){F.removeAttribute(h)}}delete o.cache[H]}},queue:function(F,E,H){if(F){E=(E||"fx")+"queue";var G=o.data(F,E);if(!G||o.isArray(H)){G=o.data(F,E,o.makeArray(H))}else{if(H){G.push(H)}}}return G},dequeue:function(H,G){var E=o.queue(H,G),F=E.shift();if(!G||G==="fx"){F=E[0]}if(F!==g){F.call(H)}}});o.fn.extend({data:function(E,G){var H=E.split(".");H[1]=H[1]?"."+H[1]:"";if(G===g){var F=this.triggerHandler("getData"+H[1]+"!",[H[0]]);if(F===g&&this.length){F=o.data(this[0],E)}return F===g&&H[1]?this.data(H[0]):F}else{return this.trigger("setData"+H[1]+"!",[H[0],G]).each(function(){o.data(this,E,G)})}},removeData:function(E){return this.each(function(){o.removeData(this,E)})},queue:function(E,F){if(typeof E!=="string"){F=E;E="fx"}if(F===g){return o.queue(this[0],E)}return this.each(function(){var G=o.queue(this,E,F);if(E=="fx"&&G.length==1){G[0].call(this)}})},dequeue:function(E){return this.each(function(){o.dequeue(this,E)})}});
+/*
+ * Sizzle CSS Selector Engine - v0.9.3
+ *  Copyright 2009, The Dojo Foundation
+ *  Released under the MIT, BSD, and GPL Licenses.
+ *  More information: http://sizzlejs.com/
+ */
+(function(){var R=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?/g,L=0,H=Object.prototype.toString;var F=function(Y,U,ab,ac){ab=ab||[];U=U||document;if(U.nodeType!==1&&U.nodeType!==9){return[]}if(!Y||typeof Y!=="string"){return ab}var Z=[],W,af,ai,T,ad,V,X=true;R.lastIndex=0;while((W=R.exec(Y))!==null){Z.push(W[1]);if(W[2]){V=RegExp.rightContext;break}}if(Z.length>1&&M.exec(Y)){if(Z.length===2&&I.relative[Z[0]]){af=J(Z[0]+Z[1],U)}else{af=I.relative[Z[0]]?[U]:F(Z.shift(),U);while(Z.length){Y=Z.shift();if(I.relative[Y]){Y+=Z.shift()}af=J(Y,af)}}}else{var ae=ac?{expr:Z.pop(),set:E(ac)}:F.find(Z.pop(),Z.length===1&&U.parentNode?U.parentNode:U,Q(U));af=F.filter(ae.expr,ae.set);if(Z.length>0){ai=E(af)}else{X=false}while(Z.length){var ah=Z.pop(),ag=ah;if(!I.relative[ah]){ah=""}else{ag=Z.pop()}if(ag==null){ag=U}I.relative[ah](ai,ag,Q(U))}}if(!ai){ai=af}if(!ai){throw"Syntax error, unrecognized expression: "+(ah||Y)}if(H.call(ai)==="[object Array]"){if(!X){ab.push.apply(ab,ai)}else{if(U.nodeType===1){for(var aa=0;ai[aa]!=null;aa++){if(ai[aa]&&(ai[aa]===true||ai[aa].nodeType===1&&K(U,ai[aa]))){ab.push(af[aa])}}}else{for(var aa=0;ai[aa]!=null;aa++){if(ai[aa]&&ai[aa].nodeType===1){ab.push(af[aa])}}}}}else{E(ai,ab)}if(V){F(V,U,ab,ac);if(G){hasDuplicate=false;ab.sort(G);if(hasDuplicate){for(var aa=1;aa<ab.length;aa++){if(ab[aa]===ab[aa-1]){ab.splice(aa--,1)}}}}}return ab};F.matches=function(T,U){return F(T,null,null,U)};F.find=function(aa,T,ab){var Z,X;if(!aa){return[]}for(var W=0,V=I.order.length;W<V;W++){var Y=I.order[W],X;if((X=I.match[Y].exec(aa))){var U=RegExp.leftContext;if(U.substr(U.length-1)!=="\\"){X[1]=(X[1]||"").replace(/\\/g,"");Z=I.find[Y](X,T,ab);if(Z!=null){aa=aa.replace(I.match[Y],"");break}}}}if(!Z){Z=T.getElementsByTagName("*")}return{set:Z,expr:aa}};F.filter=function(ad,ac,ag,W){var V=ad,ai=[],aa=ac,Y,T,Z=ac&&ac[0]&&Q(ac[0]);while(ad&&ac.length){for(var ab in I.filter){if((Y=I.match[ab].exec(ad))!=null){var U=I.filter[ab],ah,af;T=false;if(aa==ai){ai=[]}if(I.preFilter[ab]){Y=I.preFilter[ab](Y,aa,ag,ai,W,Z);if(!Y){T=ah=true}else{if(Y===true){continue}}}if(Y){for(var X=0;(af=aa[X])!=null;X++){if(af){ah=U(af,Y,X,aa);var ae=W^!!ah;if(ag&&ah!=null){if(ae){T=true}else{aa[X]=false}}else{if(ae){ai.push(af);T=true}}}}}if(ah!==g){if(!ag){aa=ai}ad=ad.replace(I.match[ab],"");if(!T){return[]}break}}}if(ad==V){if(T==null){throw"Syntax error, unrecognized expression: "+ad}else{break}}V=ad}return aa};var I=F.selectors={order:["ID","NAME","TAG"],match:{ID:/#((?:[\w\u00c0-\uFFFF_-]|\\.)+)/,CLASS:/\.((?:[\w\u00c0-\uFFFF_-]|\\.)+)/,NAME:/\[name=['"]*((?:[\w\u00c0-\uFFFF_-]|\\.)+)['"]*\]/,ATTR:/\[\s*((?:[\w\u00c0-\uFFFF_-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,TAG:/^((?:[\w\u00c0-\uFFFF\*_-]|\\.)+)/,CHILD:/:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/,POS:/:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/,PSEUDO:/:((?:[\w\u00c0-\uFFFF_-]|\\.)+)(?:\((['"]*)((?:\([^\)]+\)|[^\2\(\)]*)+)\2\))?/},attrMap:{"class":"className","for":"htmlFor"},attrHandle:{href:function(T){return T.getAttribute("href")}},relative:{"+":function(aa,T,Z){var X=typeof T==="string",ab=X&&!/\W/.test(T),Y=X&&!ab;if(ab&&!Z){T=T.toUpperCase()}for(var W=0,V=aa.length,U;W<V;W++){if((U=aa[W])){while((U=U.previousSibling)&&U.nodeType!==1){}aa[W]=Y||U&&U.nodeName===T?U||false:U===T}}if(Y){F.filter(T,aa,true)}},">":function(Z,U,aa){var X=typeof U==="string";if(X&&!/\W/.test(U)){U=aa?U:U.toUpperCase();for(var V=0,T=Z.length;V<T;V++){var Y=Z[V];if(Y){var W=Y.parentNode;Z[V]=W.nodeName===U?W:false}}}else{for(var V=0,T=Z.length;V<T;V++){var Y=Z[V];if(Y){Z[V]=X?Y.parentNode:Y.parentNode===U}}if(X){F.filter(U,Z,true)}}},"":function(W,U,Y){var V=L++,T=S;if(!U.match(/\W/)){var X=U=Y?U:U.toUpperCase();T=P}T("parentNode",U,V,W,X,Y)},"~":function(W,U,Y){var V=L++,T=S;if(typeof U==="string"&&!U.match(/\W/)){var X=U=Y?U:U.toUpperCase();T=P}T("previousSibling",U,V,W,X,Y)}},find:{ID:function(U,V,W){if(typeof V.getElementById!=="undefined"&&!W){var T=V.getElementById(U[1]);return T?[T]:[]}},NAME:function(V,Y,Z){if(typeof Y.getElementsByName!=="undefined"){var U=[],X=Y.getElementsByName(V[1]);for(var W=0,T=X.length;W<T;W++){if(X[W].getAttribute("name")===V[1]){U.push(X[W])}}return U.length===0?null:U}},TAG:function(T,U){return U.getElementsByTagName(T[1])}},preFilter:{CLASS:function(W,U,V,T,Z,aa){W=" "+W[1].replace(/\\/g,"")+" ";if(aa){return W}for(var X=0,Y;(Y=U[X])!=null;X++){if(Y){if(Z^(Y.className&&(" "+Y.className+" ").indexOf(W)>=0)){if(!V){T.push(Y)}}else{if(V){U[X]=false}}}}return false},ID:function(T){return T[1].replace(/\\/g,"")},TAG:function(U,T){for(var V=0;T[V]===false;V++){}return T[V]&&Q(T[V])?U[1]:U[1].toUpperCase()},CHILD:function(T){if(T[1]=="nth"){var U=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(T[2]=="even"&&"2n"||T[2]=="odd"&&"2n+1"||!/\D/.test(T[2])&&"0n+"+T[2]||T[2]);T[2]=(U[1]+(U[2]||1))-0;T[3]=U[3]-0}T[0]=L++;return T},ATTR:function(X,U,V,T,Y,Z){var W=X[1].replace(/\\/g,"");if(!Z&&I.attrMap[W]){X[1]=I.attrMap[W]}if(X[2]==="~="){X[4]=" "+X[4]+" "}return X},PSEUDO:function(X,U,V,T,Y){if(X[1]==="not"){if(X[3].match(R).length>1||/^\w/.test(X[3])){X[3]=F(X[3],null,null,U)}else{var W=F.filter(X[3],U,V,true^Y);if(!V){T.push.apply(T,W)}return false}}else{if(I.match.POS.test(X[0])||I.match.CHILD.test(X[0])){return true}}return X},POS:function(T){T.unshift(true);return T}},filters:{enabled:function(T){return T.disabled===false&&T.type!=="hidden"},disabled:function(T){return T.disabled===true},checked:function(T){return T.checked===true},selected:function(T){T.parentNode.selectedIndex;return T.selected===true},parent:function(T){return !!T.firstChild},empty:function(T){return !T.firstChild},has:function(V,U,T){return !!F(T[3],V).length},header:function(T){return/h\d/i.test(T.nodeName)},text:function(T){return"text"===T.type},radio:function(T){return"radio"===T.type},checkbox:function(T){return"checkbox"===T.type},file:function(T){return"file"===T.type},password:function(T){return"password"===T.type},submit:function(T){return"submit"===T.type},image:function(T){return"image"===T.type},reset:function(T){return"reset"===T.type},button:function(T){return"button"===T.type||T.nodeName.toUpperCase()==="BUTTON"},input:function(T){return/input|select|textarea|button/i.test(T.nodeName)}},setFilters:{first:function(U,T){return T===0},last:function(V,U,T,W){return U===W.length-1},even:function(U,T){return T%2===0},odd:function(U,T){return T%2===1},lt:function(V,U,T){return U<T[3]-0},gt:function(V,U,T){return U>T[3]-0},nth:function(V,U,T){return T[3]-0==U},eq:function(V,U,T){return T[3]-0==U}},filter:{PSEUDO:function(Z,V,W,aa){var U=V[1],X=I.filters[U];if(X){return X(Z,W,V,aa)}else{if(U==="contains"){return(Z.textContent||Z.innerText||"").indexOf(V[3])>=0}else{if(U==="not"){var Y=V[3];for(var W=0,T=Y.length;W<T;W++){if(Y[W]===Z){return false}}return true}}}},CHILD:function(T,W){var Z=W[1],U=T;switch(Z){case"only":case"first":while(U=U.previousSibling){if(U.nodeType===1){return false}}if(Z=="first"){return true}U=T;case"last":while(U=U.nextSibling){if(U.nodeType===1){return false}}return true;case"nth":var V=W[2],ac=W[3];if(V==1&&ac==0){return true}var Y=W[0],ab=T.parentNode;if(ab&&(ab.sizcache!==Y||!T.nodeIndex)){var X=0;for(U=ab.firstChild;U;U=U.nextSibling){if(U.nodeType===1){U.nodeIndex=++X}}ab.sizcache=Y}var aa=T.nodeIndex-ac;if(V==0){return aa==0}else{return(aa%V==0&&aa/V>=0)}}},ID:function(U,T){return U.nodeType===1&&U.getAttribute("id")===T},TAG:function(U,T){return(T==="*"&&U.nodeType===1)||U.nodeName===T},CLASS:function(U,T){return(" "+(U.className||U.getAttribute("class"))+" ").indexOf(T)>-1},ATTR:function(Y,W){var V=W[1],T=I.attrHandle[V]?I.attrHandle[V](Y):Y[V]!=null?Y[V]:Y.getAttribute(V),Z=T+"",X=W[2],U=W[4];return T==null?X==="!=":X==="="?Z===U:X==="*="?Z.indexOf(U)>=0:X==="~="?(" "+Z+" ").indexOf(U)>=0:!U?Z&&T!==false:X==="!="?Z!=U:X==="^="?Z.indexOf(U)===0:X==="$="?Z.substr(Z.length-U.length)===U:X==="|="?Z===U||Z.substr(0,U.length+1)===U+"-":false},POS:function(X,U,V,Y){var T=U[2],W=I.setFilters[T];if(W){return W(X,V,U,Y)}}}};var M=I.match.POS;for(var O in I.match){I.match[O]=RegExp(I.match[O].source+/(?![^\[]*\])(?![^\(]*\))/.source)}var E=function(U,T){U=Array.prototype.slice.call(U);if(T){T.push.apply(T,U);return T}return U};try{Array.prototype.slice.call(document.documentElement.childNodes)}catch(N){E=function(X,W){var U=W||[];if(H.call(X)==="[object Array]"){Array.prototype.push.apply(U,X)}else{if(typeof X.length==="number"){for(var V=0,T=X.length;V<T;V++){U.push(X[V])}}else{for(var V=0;X[V];V++){U.push(X[V])}}}return U}}var G;if(document.documentElement.compareDocumentPosition){G=function(U,T){var V=U.compareDocumentPosition(T)&4?-1:U===T?0:1;if(V===0){hasDuplicate=true}return V}}else{if("sourceIndex" in document.documentElement){G=function(U,T){var V=U.sourceIndex-T.sourceIndex;if(V===0){hasDuplicate=true}return V}}else{if(document.createRange){G=function(W,U){var V=W.ownerDocument.createRange(),T=U.ownerDocument.createRange();V.selectNode(W);V.collapse(true);T.selectNode(U);T.collapse(true);var X=V.compareBoundaryPoints(Range.START_TO_END,T);if(X===0){hasDuplicate=true}return X}}}}(function(){var U=document.createElement("form"),V="script"+(new Date).getTime();U.innerHTML="<input name='"+V+"'/>";var T=document.documentElement;T.insertBefore(U,T.firstChild);if(!!document.getElementById(V)){I.find.ID=function(X,Y,Z){if(typeof Y.getElementById!=="undefined"&&!Z){var W=Y.getElementById(X[1]);return W?W.id===X[1]||typeof W.getAttributeNode!=="undefined"&&W.getAttributeNode("id").nodeValue===X[1]?[W]:g:[]}};I.filter.ID=function(Y,W){var X=typeof Y.getAttributeNode!=="undefined"&&Y.getAttributeNode("id");return Y.nodeType===1&&X&&X.nodeValue===W}}T.removeChild(U)})();(function(){var T=document.createElement("div");T.appendChild(document.createComment(""));if(T.getElementsByTagName("*").length>0){I.find.TAG=function(U,Y){var X=Y.getElementsByTagName(U[1]);if(U[1]==="*"){var W=[];for(var V=0;X[V];V++){if(X[V].nodeType===1){W.push(X[V])}}X=W}return X}}T.innerHTML="<a href='#'></a>";if(T.firstChild&&typeof T.firstChild.getAttribute!=="undefined"&&T.firstChild.getAttribute("href")!=="#"){I.attrHandle.href=function(U){return U.getAttribute("href",2)}}})();if(document.querySelectorAll){(function(){var T=F,U=document.createElement("div");U.innerHTML="<p class='TEST'></p>";if(U.querySelectorAll&&U.querySelectorAll(".TEST").length===0){return}F=function(Y,X,V,W){X=X||document;if(!W&&X.nodeType===9&&!Q(X)){try{return E(X.querySelectorAll(Y),V)}catch(Z){}}return T(Y,X,V,W)};F.find=T.find;F.filter=T.filter;F.selectors=T.selectors;F.matches=T.matches})()}if(document.getElementsByClassName&&document.documentElement.getElementsByClassName){(function(){var T=document.createElement("div");T.innerHTML="<div class='test e'></div><div class='test'></div>";if(T.getElementsByClassName("e").length===0){return}T.lastChild.className="e";if(T.getElementsByClassName("e").length===1){return}I.order.splice(1,0,"CLASS");I.find.CLASS=function(U,V,W){if(typeof V.getElementsByClassName!=="undefined"&&!W){return V.getElementsByClassName(U[1])}}})()}function P(U,Z,Y,ad,aa,ac){var ab=U=="previousSibling"&&!ac;for(var W=0,V=ad.length;W<V;W++){var T=ad[W];if(T){if(ab&&T.nodeType===1){T.sizcache=Y;T.sizset=W}T=T[U];var X=false;while(T){if(T.sizcache===Y){X=ad[T.sizset];break}if(T.nodeType===1&&!ac){T.sizcache=Y;T.sizset=W}if(T.nodeName===Z){X=T;break}T=T[U]}ad[W]=X}}}function S(U,Z,Y,ad,aa,ac){var ab=U=="previousSibling"&&!ac;for(var W=0,V=ad.length;W<V;W++){var T=ad[W];if(T){if(ab&&T.nodeType===1){T.sizcache=Y;T.sizset=W}T=T[U];var X=false;while(T){if(T.sizcache===Y){X=ad[T.sizset];break}if(T.nodeType===1){if(!ac){T.sizcache=Y;T.sizset=W}if(typeof Z!=="string"){if(T===Z){X=true;break}}else{if(F.filter(Z,[T]).length>0){X=T;break}}}T=T[U]}ad[W]=X}}}var K=document.compareDocumentPosition?function(U,T){return U.compareDocumentPosition(T)&16}:function(U,T){return U!==T&&(U.contains?U.contains(T):true)};var Q=function(T){return T.nodeType===9&&T.documentElement.nodeName!=="HTML"||!!T.ownerDocument&&Q(T.ownerDocument)};var J=function(T,aa){var W=[],X="",Y,V=aa.nodeType?[aa]:aa;while((Y=I.match.PSEUDO.exec(T))){X+=Y[0];T=T.replace(I.match.PSEUDO,"")}T=I.relative[T]?T+"*":T;for(var Z=0,U=V.length;Z<U;Z++){F(T,V[Z],W)}return F.filter(X,W)};o.find=F;o.filter=F.filter;o.expr=F.selectors;o.expr[":"]=o.expr.filters;F.selectors.filters.hidden=function(T){return T.offsetWidth===0||T.offsetHeight===0};F.selectors.filters.visible=function(T){return T.offsetWidth>0||T.offsetHeight>0};F.selectors.filters.animated=function(T){return o.grep(o.timers,function(U){return T===U.elem}).length};o.multiFilter=function(V,T,U){if(U){V=":not("+V+")"}return F.matches(V,T)};o.dir=function(V,U){var T=[],W=V[U];while(W&&W!=document){if(W.nodeType==1){T.push(W)}W=W[U]}return T};o.nth=function(X,T,V,W){T=T||1;var U=0;for(;X;X=X[V]){if(X.nodeType==1&&++U==T){break}}return X};o.sibling=function(V,U){var T=[];for(;V;V=V.nextSibling){if(V.nodeType==1&&V!=U){T.push(V)}}return T};return;l.Sizzle=F})();o.event={add:function(I,F,H,K){if(I.nodeType==3||I.nodeType==8){return}if(I.setInterval&&I!=l){I=l}if(!H.guid){H.guid=this.guid++}if(K!==g){var G=H;H=this.proxy(G);H.data=K}var E=o.data(I,"events")||o.data(I,"events",{}),J=o.data(I,"handle")||o.data(I,"handle",function(){return typeof o!=="undefined"&&!o.event.triggered?o.event.handle.apply(arguments.callee.elem,arguments):g});J.elem=I;o.each(F.split(/\s+/),function(M,N){var O=N.split(".");N=O.shift();H.type=O.slice().sort().join(".");var L=E[N];if(o.event.specialAll[N]){o.event.specialAll[N].setup.call(I,K,O)}if(!L){L=E[N]={};if(!o.event.special[N]||o.event.special[N].setup.call(I,K,O)===false){if(I.addEventListener){I.addEventListener(N,J,false)}else{if(I.attachEvent){I.attachEvent("on"+N,J)}}}}L[H.guid]=H;o.event.global[N]=true});I=null},guid:1,global:{},remove:function(K,H,J){if(K.nodeType==3||K.nodeType==8){return}var G=o.data(K,"events"),F,E;if(G){if(H===g||(typeof H==="string"&&H.charAt(0)==".")){for(var I in G){this.remove(K,I+(H||""))}}else{if(H.type){J=H.handler;H=H.type}o.each(H.split(/\s+/),function(M,O){var Q=O.split(".");O=Q.shift();var N=RegExp("(^|\\.)"+Q.slice().sort().join(".*\\.")+"(\\.|$)");if(G[O]){if(J){delete G[O][J.guid]}else{for(var P in G[O]){if(N.test(G[O][P].type)){delete G[O][P]}}}if(o.event.specialAll[O]){o.event.specialAll[O].teardown.call(K,Q)}for(F in G[O]){break}if(!F){if(!o.event.special[O]||o.event.special[O].teardown.call(K,Q)===false){if(K.removeEventListener){K.removeEventListener(O,o.data(K,"handle"),false)}else{if(K.detachEvent){K.detachEvent("on"+O,o.data(K,"handle"))}}}F=null;delete G[O]}}})}for(F in G){break}if(!F){var L=o.data(K,"handle");if(L){L.elem=null}o.removeData(K,"events");o.removeData(K,"handle")}}},trigger:function(I,K,H,E){var G=I.type||I;if(!E){I=typeof I==="object"?I[h]?I:o.extend(o.Event(G),I):o.Event(G);if(G.indexOf("!")>=0){I.type=G=G.slice(0,-1);I.exclusive=true}if(!H){I.stopPropagation();if(this.global[G]){o.each(o.cache,function(){if(this.events&&this.events[G]){o.event.trigger(I,K,this.handle.elem)}})}}if(!H||H.nodeType==3||H.nodeType==8){return g}I.result=g;I.target=H;K=o.makeArray(K);K.unshift(I)}I.currentTarget=H;var J=o.data(H,"handle");if(J){J.apply(H,K)}if((!H[G]||(o.nodeName(H,"a")&&G=="click"))&&H["on"+G]&&H["on"+G].apply(H,K)===false){I.result=false}if(!E&&H[G]&&!I.isDefaultPrevented()&&!(o.nodeName(H,"a")&&G=="click")){this.triggered=true;try{H[G]()}catch(L){}}this.triggered=false;if(!I.isPropagationStopped()){var F=H.parentNode||H.ownerDocument;if(F){o.event.trigger(I,K,F,true)}}},handle:function(K){var J,E;K=arguments[0]=o.event.fix(K||l.event);K.currentTarget=this;var L=K.type.split(".");K.type=L.shift();J=!L.length&&!K.exclusive;var I=RegExp("(^|\\.)"+L.slice().sort().join(".*\\.")+"(\\.|$)");E=(o.data(this,"events")||{})[K.type];for(var G in E){var H=E[G];if(J||I.test(H.type)){K.handler=H;K.data=H.data;var F=H.apply(this,arguments);if(F!==g){K.result=F;if(F===false){K.preventDefault();K.stopPropagation()}}if(K.isImmediatePropagationStopped()){break}}}},props:"altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode metaKey newValue originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),fix:function(H){if(H[h]){return H}var F=H;H=o.Event(F);for(var G=this.props.length,J;G;){J=this.props[--G];H[J]=F[J]}if(!H.target){H.target=H.srcElement||document}if(H.target.nodeType==3){H.target=H.target.parentNode}if(!H.relatedTarget&&H.fromElement){H.relatedTarget=H.fromElement==H.target?H.toElement:H.fromElement}if(H.pageX==null&&H.clientX!=null){var I=document.documentElement,E=document.body;H.pageX=H.clientX+(I&&I.scrollLeft||E&&E.scrollLeft||0)-(I.clientLeft||0);H.pageY=H.clientY+(I&&I.scrollTop||E&&E.scrollTop||0)-(I.clientTop||0)}if(!H.which&&((H.charCode||H.charCode===0)?H.charCode:H.keyCode)){H.which=H.charCode||H.keyCode}if(!H.metaKey&&H.ctrlKey){H.metaKey=H.ctrlKey}if(!H.which&&H.button){H.which=(H.button&1?1:(H.button&2?3:(H.button&4?2:0)))}return H},proxy:function(F,E){E=E||function(){return F.apply(this,arguments)};E.guid=F.guid=F.guid||E.guid||this.guid++;return E},special:{ready:{setup:B,teardown:function(){}}},specialAll:{live:{setup:function(E,F){o.event.add(this,F[0],c)},teardown:function(G){if(G.length){var E=0,F=RegExp("(^|\\.)"+G[0]+"(\\.|$)");o.each((o.data(this,"events").live||{}),function(){if(F.test(this.type)){E++}});if(E<1){o.event.remove(this,G[0],c)}}}}}};o.Event=function(E){if(!this.preventDefault){return new o.Event(E)}if(E&&E.type){this.originalEvent=E;this.type=E.type}else{this.type=E}this.timeStamp=e();this[h]=true};function k(){return false}function u(){return true}o.Event.prototype={preventDefault:function(){this.isDefaultPrevented=u;var E=this.originalEvent;if(!E){return}if(E.preventDefault){E.preventDefault()}E.returnValue=false},stopPropagation:function(){this.isPropagationStopped=u;var E=this.originalEvent;if(!E){return}if(E.stopPropagation){E.stopPropagation()}E.cancelBubble=true},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=u;this.stopPropagation()},isDefaultPrevented:k,isPropagationStopped:k,isImmediatePropagationStopped:k};var a=function(F){var E=F.relatedTarget;while(E&&E!=this){try{E=E.parentNode}catch(G){E=this}}if(E!=this){F.type=F.data;o.event.handle.apply(this,arguments)}};o.each({mouseover:"mouseenter",mouseout:"mouseleave"},function(F,E){o.event.special[E]={setup:function(){o.event.add(this,F,a,E)},teardown:function(){o.event.remove(this,F,a)}}});o.fn.extend({bind:function(F,G,E){return F=="unload"?this.one(F,G,E):this.each(function(){o.event.add(this,F,E||G,E&&G)})},one:function(G,H,F){var E=o.event.proxy(F||H,function(I){o(this).unbind(I,E);return(F||H).apply(this,arguments)});return this.each(function(){o.event.add(this,G,E,F&&H)})},unbind:function(F,E){return this.each(function(){o.event.remove(this,F,E)})},trigger:function(E,F){return this.each(function(){o.event.trigger(E,F,this)})},triggerHandler:function(E,G){if(this[0]){var F=o.Event(E);F.preventDefault();F.stopPropagation();o.event.trigger(F,G,this[0]);return F.result}},toggle:function(G){var E=arguments,F=1;while(F<E.length){o.event.proxy(G,E[F++])}return this.click(o.event.proxy(G,function(H){this.lastToggle=(this.lastToggle||0)%F;H.preventDefault();return E[this.lastToggle++].apply(this,arguments)||false}))},hover:function(E,F){return this.mouseenter(E).mouseleave(F)},ready:function(E){B();if(o.isReady){E.call(document,o)}else{o.readyList.push(E)}return this},live:function(G,F){var E=o.event.proxy(F);E.guid+=this.selector+G;o(document).bind(i(G,this.selector),this.selector,E);return this},die:function(F,E){o(document).unbind(i(F,this.selector),E?{guid:E.guid+this.selector+F}:null);return this}});function c(H){var E=RegExp("(^|\\.)"+H.type+"(\\.|$)"),G=true,F=[];o.each(o.data(this,"events").live||[],function(I,J){if(E.test(J.type)){var K=o(H.target).closest(J.data)[0];if(K){F.push({elem:K,fn:J})}}});F.sort(function(J,I){return o.data(J.elem,"closest")-o.data(I.elem,"closest")});o.each(F,function(){if(this.fn.call(this.elem,H,this.fn.data)===false){return(G=false)}});return G}function i(F,E){return["live",F,E.replace(/\./g,"`").replace(/ /g,"|")].join(".")}o.extend({isReady:false,readyList:[],ready:function(){if(!o.isReady){o.isReady=true;if(o.readyList){o.each(o.readyList,function(){this.call(document,o)});o.readyList=null}o(document).triggerHandler("ready")}}});var x=false;function B(){if(x){return}x=true;if(document.addEventListener){document.addEventListener("DOMContentLoaded",function(){document.removeEventListener("DOMContentLoaded",arguments.callee,false);o.ready()},false)}else{if(document.attachEvent){document.attachEvent("onreadystatechange",function(){if(document.readyState==="complete"){document.detachEvent("onreadystatechange",arguments.callee);o.ready()}});if(document.documentElement.doScroll&&l==l.top){(function(){if(o.isReady){return}try{document.documentElement.doScroll("left")}catch(E){setTimeout(arguments.callee,0);return}o.ready()})()}}}o.event.add(l,"load",o.ready)}o.each(("blur,focus,load,resize,scroll,unload,click,dblclick,mousedown,mouseup,mousemove,mouseover,mouseout,mouseenter,mouseleave,change,select,submit,keydown,keypress,keyup,error").split(","),function(F,E){o.fn[E]=function(G){return G?this.bind(E,G):this.trigger(E)}});o(l).bind("unload",function(){for(var E in o.cache){if(E!=1&&o.cache[E].handle){o.event.remove(o.cache[E].handle.elem)}}});(function(){o.support={};var F=document.documentElement,G=document.createElement("script"),K=document.createElement("div"),J="script"+(new Date).getTime();K.style.display="none";K.innerHTML='   <link/><table></table><a href="/a" style="color:red;float:left;opacity:.5;">a</a><select><option>text</option></select><object><param/></object>';var H=K.getElementsByTagName("*"),E=K.getElementsByTagName("a")[0];if(!H||!H.length||!E){return}o.support={leadingWhitespace:K.firstChild.nodeType==3,tbody:!K.getElementsByTagName("tbody").length,objectAll:!!K.getElementsByTagName("object")[0].getElementsByTagName("*").length,htmlSerialize:!!K.getElementsByTagName("link").length,style:/red/.test(E.getAttribute("style")),hrefNormalized:E.getAttribute("href")==="/a",opacity:E.style.opacity==="0.5",cssFloat:!!E.style.cssFloat,scriptEval:false,noCloneEvent:true,boxModel:null};G.type="text/javascript";try{G.appendChild(document.createTextNode("window."+J+"=1;"))}catch(I){}F.insertBefore(G,F.firstChild);if(l[J]){o.support.scriptEval=true;delete l[J]}F.removeChild(G);if(K.attachEvent&&K.fireEvent){K.attachEvent("onclick",function(){o.support.noCloneEvent=false;K.detachEvent("onclick",arguments.callee)});K.cloneNode(true).fireEvent("onclick")}o(function(){var L=document.createElement("div");L.style.width=L.style.paddingLeft="1px";document.body.appendChild(L);o.boxModel=o.support.boxModel=L.offsetWidth===2;document.body.removeChild(L).style.display="none"})})();var w=o.support.cssFloat?"cssFloat":"styleFloat";o.props={"for":"htmlFor","class":"className","float":w,cssFloat:w,styleFloat:w,readonly:"readOnly",maxlength:"maxLength",cellspacing:"cellSpacing",rowspan:"rowSpan",tabindex:"tabIndex"};o.fn.extend({_load:o.fn.load,load:function(G,J,K){if(typeof G!=="string"){return this._load(G)}var I=G.indexOf(" ");if(I>=0){var E=G.slice(I,G.length);G=G.slice(0,I)}var H="GET";if(J){if(o.isFunction(J)){K=J;J=null}else{if(typeof J==="object"){J=o.param(J);H="POST"}}}var F=this;o.ajax({url:G,type:H,dataType:"html",data:J,complete:function(M,L){if(L=="success"||L=="notmodified"){F.html(E?o("<div/>").append(M.responseText.replace(/<script(.|\s)*?\/script>/g,"")).find(E):M.responseText)}if(K){F.each(K,[M.responseText,L,M])}}});return this},serialize:function(){return o.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?o.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||/select|textarea/i.test(this.nodeName)||/text|hidden|password|search/i.test(this.type))}).map(function(E,F){var G=o(this).val();return G==null?null:o.isArray(G)?o.map(G,function(I,H){return{name:F.name,value:I}}):{name:F.name,value:G}}).get()}});o.each("ajaxStart,ajaxStop,ajaxComplete,ajaxError,ajaxSuccess,ajaxSend".split(","),function(E,F){o.fn[F]=function(G){return this.bind(F,G)}});var r=e();o.extend({get:function(E,G,H,F){if(o.isFunction(G)){H=G;G=null}return o.ajax({type:"GET",url:E,data:G,success:H,dataType:F})},getScript:function(E,F){return o.get(E,null,F,"script")},getJSON:function(E,F,G){return o.get(E,F,G,"json")},post:function(E,G,H,F){if(o.isFunction(G)){H=G;G={}}return o.ajax({type:"POST",url:E,data:G,success:H,dataType:F})},ajaxSetup:function(E){o.extend(o.ajaxSettings,E)},ajaxSettings:{url:location.href,global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:function(){return l.ActiveXObject?new ActiveXObject("Microsoft.XMLHTTP"):new XMLHttpRequest()},accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},ajax:function(M){M=o.extend(true,M,o.extend(true,{},o.ajaxSettings,M));var W,F=/=\?(&|$)/g,R,V,G=M.type.toUpperCase();if(M.data&&M.processData&&typeof M.data!=="string"){M.data=o.param(M.data)}if(M.dataType=="jsonp"){if(G=="GET"){if(!M.url.match(F)){M.url+=(M.url.match(/\?/)?"&":"?")+(M.jsonp||"callback")+"=?"}}else{if(!M.data||!M.data.match(F)){M.data=(M.data?M.data+"&":"")+(M.jsonp||"callback")+"=?"}}M.dataType="json"}if(M.dataType=="json"&&(M.data&&M.data.match(F)||M.url.match(F))){W="jsonp"+r++;if(M.data){M.data=(M.data+"").replace(F,"="+W+"$1")}M.url=M.url.replace(F,"="+W+"$1");M.dataType="script";l[W]=function(X){V=X;I();L();l[W]=g;try{delete l[W]}catch(Y){}if(H){H.removeChild(T)}}}if(M.dataType=="script"&&M.cache==null){M.cache=false}if(M.cache===false&&G=="GET"){var E=e();var U=M.url.replace(/(\?|&)_=.*?(&|$)/,"$1_="+E+"$2");M.url=U+((U==M.url)?(M.url.match(/\?/)?"&":"?")+"_="+E:"")}if(M.data&&G=="GET"){M.url+=(M.url.match(/\?/)?"&":"?")+M.data;M.data=null}if(M.global&&!o.active++){o.event.trigger("ajaxStart")}var Q=/^(\w+:)?\/\/([^\/?#]+)/.exec(M.url);if(M.dataType=="script"&&G=="GET"&&Q&&(Q[1]&&Q[1]!=location.protocol||Q[2]!=location.host)){var H=document.getElementsByTagName("head")[0];var T=document.createElement("script");T.src=M.url;if(M.scriptCharset){T.charset=M.scriptCharset}if(!W){var O=false;T.onload=T.onreadystatechange=function(){if(!O&&(!this.readyState||this.readyState=="loaded"||this.readyState=="complete")){O=true;I();L();T.onload=T.onreadystatechange=null;H.removeChild(T)}}}H.appendChild(T);return g}var K=false;var J=M.xhr();if(M.username){J.open(G,M.url,M.async,M.username,M.password)}else{J.open(G,M.url,M.async)}try{if(M.data){J.setRequestHeader("Content-Type",M.contentType)}if(M.ifModified){J.setRequestHeader("If-Modified-Since",o.lastModified[M.url]||"Thu, 01 Jan 1970 00:00:00 GMT")}J.setRequestHeader("X-Requested-With","XMLHttpRequest");J.setRequestHeader("Accept",M.dataType&&M.accepts[M.dataType]?M.accepts[M.dataType]+", */*":M.accepts._default)}catch(S){}if(M.beforeSend&&M.beforeSend(J,M)===false){if(M.global&&!--o.active){o.event.trigger("ajaxStop")}J.abort();return false}if(M.global){o.event.trigger("ajaxSend",[J,M])}var N=function(X){if(J.readyState==0){if(P){clearInterval(P);P=null;if(M.global&&!--o.active){o.event.trigger("ajaxStop")}}}else{if(!K&&J&&(J.readyState==4||X=="timeout")){K=true;if(P){clearInterval(P);P=null}R=X=="timeout"?"timeout":!o.httpSuccess(J)?"error":M.ifModified&&o.httpNotModified(J,M.url)?"notmodified":"success";if(R=="success"){try{V=o.httpData(J,M.dataType,M)}catch(Z){R="parsererror"}}if(R=="success"){var Y;try{Y=J.getResponseHeader("Last-Modified")}catch(Z){}if(M.ifModified&&Y){o.lastModified[M.url]=Y}if(!W){I()}}else{o.handleError(M,J,R)}L();if(X){J.abort()}if(M.async){J=null}}}};if(M.async){var P=setInterval(N,13);if(M.timeout>0){setTimeout(function(){if(J&&!K){N("timeout")}},M.timeout)}}try{J.send(M.data)}catch(S){o.handleError(M,J,null,S)}if(!M.async){N()}function I(){if(M.success){M.success(V,R)}if(M.global){o.event.trigger("ajaxSuccess",[J,M])}}function L(){if(M.complete){M.complete(J,R)}if(M.global){o.event.trigger("ajaxComplete",[J,M])}if(M.global&&!--o.active){o.event.trigger("ajaxStop")}}return J},handleError:function(F,H,E,G){if(F.error){F.error(H,E,G)}if(F.global){o.event.trigger("ajaxError",[H,F,G])}},active:0,httpSuccess:function(F){try{return !F.status&&location.protocol=="file:"||(F.status>=200&&F.status<300)||F.status==304||F.status==1223}catch(E){}return false},httpNotModified:function(G,E){try{var H=G.getResponseHeader("Last-Modified");return G.status==304||H==o.lastModified[E]}catch(F){}return false},httpData:function(J,H,G){var F=J.getResponseHeader("content-type"),E=H=="xml"||!H&&F&&F.indexOf("xml")>=0,I=E?J.responseXML:J.responseText;if(E&&I.documentElement.tagName=="parsererror"){throw"parsererror"}if(G&&G.dataFilter){I=G.dataFilter(I,H)}if(typeof I==="string"){if(H=="script"){o.globalEval(I)}if(H=="json"){I=l["eval"]("("+I+")")}}return I},param:function(E){var G=[];function H(I,J){G[G.length]=encodeURIComponent(I)+"="+encodeURIComponent(J)}if(o.isArray(E)||E.jquery){o.each(E,function(){H(this.name,this.value)})}else{for(var F in E){if(o.isArray(E[F])){o.each(E[F],function(){H(F,this)})}else{H(F,o.isFunction(E[F])?E[F]():E[F])}}}return G.join("&").replace(/%20/g,"+")}});var m={},n,d=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];function t(F,E){var G={};o.each(d.concat.apply([],d.slice(0,E)),function(){G[this]=F});return G}o.fn.extend({show:function(J,L){if(J){return this.animate(t("show",3),J,L)}else{for(var H=0,F=this.length;H<F;H++){var E=o.data(this[H],"olddisplay");this[H].style.display=E||"";if(o.css(this[H],"display")==="none"){var G=this[H].tagName,K;if(m[G]){K=m[G]}else{var I=o("<"+G+" />").appendTo("body");K=I.css("display");if(K==="none"){K="block"}I.remove();m[G]=K}o.data(this[H],"olddisplay",K)}}for(var H=0,F=this.length;H<F;H++){this[H].style.display=o.data(this[H],"olddisplay")||""}return this}},hide:function(H,I){if(H){return this.animate(t("hide",3),H,I)}else{for(var G=0,F=this.length;G<F;G++){var E=o.data(this[G],"olddisplay");if(!E&&E!=="none"){o.data(this[G],"olddisplay",o.css(this[G],"display"))}}for(var G=0,F=this.length;G<F;G++){this[G].style.display="none"}return this}},_toggle:o.fn.toggle,toggle:function(G,F){var E=typeof G==="boolean";return o.isFunction(G)&&o.isFunction(F)?this._toggle.apply(this,arguments):G==null||E?this.each(function(){var H=E?G:o(this).is(":hidden");o(this)[H?"show":"hide"]()}):this.animate(t("toggle",3),G,F)},fadeTo:function(E,G,F){return this.animate({opacity:G},E,F)},animate:function(I,F,H,G){var E=o.speed(F,H,G);return this[E.queue===false?"each":"queue"](function(){var K=o.extend({},E),M,L=this.nodeType==1&&o(this).is(":hidden"),J=this;for(M in I){if(I[M]=="hide"&&L||I[M]=="show"&&!L){return K.complete.call(this)}if((M=="height"||M=="width")&&this.style){K.display=o.css(this,"display");K.overflow=this.style.overflow}}if(K.overflow!=null){this.style.overflow="hidden"}K.curAnim=o.extend({},I);o.each(I,function(O,S){var R=new o.fx(J,K,O);if(/toggle|show|hide/.test(S)){R[S=="toggle"?L?"show":"hide":S](I)}else{var Q=S.toString().match(/^([+-]=)?([\d+-.]+)(.*)$/),T=R.cur(true)||0;if(Q){var N=parseFloat(Q[2]),P=Q[3]||"px";if(P!="px"){J.style[O]=(N||1)+P;T=((N||1)/R.cur(true))*T;J.style[O]=T+P}if(Q[1]){N=((Q[1]=="-="?-1:1)*N)+T}R.custom(T,N,P)}else{R.custom(T,S,"")}}});return true})},stop:function(F,E){var G=o.timers;if(F){this.queue([])}this.each(function(){for(var H=G.length-1;H>=0;H--){if(G[H].elem==this){if(E){G[H](true)}G.splice(H,1)}}});if(!E){this.dequeue()}return this}});o.each({slideDown:t("show",1),slideUp:t("hide",1),slideToggle:t("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"}},function(E,F){o.fn[E]=function(G,H){return this.animate(F,G,H)}});o.extend({speed:function(G,H,F){var E=typeof G==="object"?G:{complete:F||!F&&H||o.isFunction(G)&&G,duration:G,easing:F&&H||H&&!o.isFunction(H)&&H};E.duration=o.fx.off?0:typeof E.duration==="number"?E.duration:o.fx.speeds[E.duration]||o.fx.speeds._default;E.old=E.complete;E.complete=function(){if(E.queue!==false){o(this).dequeue()}if(o.isFunction(E.old)){E.old.call(this)}};return E},easing:{linear:function(G,H,E,F){return E+F*G},swing:function(G,H,E,F){return((-Math.cos(G*Math.PI)/2)+0.5)*F+E}},timers:[],fx:function(F,E,G){this.options=E;this.elem=F;this.prop=G;if(!E.orig){E.orig={}}}});o.fx.prototype={update:function(){if(this.options.step){this.options.step.call(this.elem,this.now,this)}(o.fx.step[this.prop]||o.fx.step._default)(this);if((this.prop=="height"||this.prop=="width")&&this.elem.style){this.elem.style.display="block"}},cur:function(F){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null)){return this.elem[this.prop]}var E=parseFloat(o.css(this.elem,this.prop,F));return E&&E>-10000?E:parseFloat(o.curCSS(this.elem,this.prop))||0},custom:function(I,H,G){this.startTime=e();this.start=I;this.end=H;this.unit=G||this.unit||"px";this.now=this.start;this.pos=this.state=0;var E=this;function F(J){return E.step(J)}F.elem=this.elem;if(F()&&o.timers.push(F)&&!n){n=setInterval(function(){var K=o.timers;for(var J=0;J<K.length;J++){if(!K[J]()){K.splice(J--,1)}}if(!K.length){clearInterval(n);n=g}},13)}},show:function(){this.options.orig[this.prop]=o.attr(this.elem.style,this.prop);this.options.show=true;this.custom(this.prop=="width"||this.prop=="height"?1:0,this.cur());o(this.elem).show()},hide:function(){this.options.orig[this.prop]=o.attr(this.elem.style,this.prop);this.options.hide=true;this.custom(this.cur(),0)},step:function(H){var G=e();if(H||G>=this.options.duration+this.startTime){this.now=this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;var E=true;for(var F in this.options.curAnim){if(this.options.curAnim[F]!==true){E=false}}if(E){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;this.elem.style.display=this.options.display;if(o.css(this.elem,"display")=="none"){this.elem.style.display="block"}}if(this.options.hide){o(this.elem).hide()}if(this.options.hide||this.options.show){for(var I in this.options.curAnim){o.attr(this.elem.style,I,this.options.orig[I])}}this.options.complete.call(this.elem)}return false}else{var J=G-this.startTime;this.state=J/this.options.duration;this.pos=o.easing[this.options.easing||(o.easing.swing?"swing":"linear")](this.state,J,0,1,this.options.duration);this.now=this.start+((this.end-this.start)*this.pos);this.update()}return true}};o.extend(o.fx,{speeds:{slow:600,fast:200,_default:400},step:{opacity:function(E){o.attr(E.elem.style,"opacity",E.now)},_default:function(E){if(E.elem.style&&E.elem.style[E.prop]!=null){E.elem.style[E.prop]=E.now+E.unit}else{E.elem[E.prop]=E.now}}}});if(document.documentElement.getBoundingClientRect){o.fn.offset=function(){if(!this[0]){return{top:0,left:0}}if(this[0]===this[0].ownerDocument.body){return o.offset.bodyOffset(this[0])}var G=this[0].getBoundingClientRect(),J=this[0].ownerDocument,F=J.body,E=J.documentElement,L=E.clientTop||F.clientTop||0,K=E.clientLeft||F.clientLeft||0,I=G.top+(self.pageYOffset||o.boxModel&&E.scrollTop||F.scrollTop)-L,H=G.left+(self.pageXOffset||o.boxModel&&E.scrollLeft||F.scrollLeft)-K;return{top:I,left:H}}}else{o.fn.offset=function(){if(!this[0]){return{top:0,left:0}}if(this[0]===this[0].ownerDocument.body){return o.offset.bodyOffset(this[0])}o.offset.initialized||o.offset.initialize();var J=this[0],G=J.offsetParent,F=J,O=J.ownerDocument,M,H=O.documentElement,K=O.body,L=O.defaultView,E=L.getComputedStyle(J,null),N=J.offsetTop,I=J.offsetLeft;while((J=J.parentNode)&&J!==K&&J!==H){M=L.getComputedStyle(J,null);N-=J.scrollTop,I-=J.scrollLeft;if(J===G){N+=J.offsetTop,I+=J.offsetLeft;if(o.offset.doesNotAddBorder&&!(o.offset.doesAddBorderForTableAndCells&&/^t(able|d|h)$/i.test(J.tagName))){N+=parseInt(M.borderTopWidth,10)||0,I+=parseInt(M.borderLeftWidth,10)||0}F=G,G=J.offsetParent}if(o.offset.subtractsBorderForOverflowNotVisible&&M.overflow!=="visible"){N+=parseInt(M.borderTopWidth,10)||0,I+=parseInt(M.borderLeftWidth,10)||0}E=M}if(E.position==="relative"||E.position==="static"){N+=K.offsetTop,I+=K.offsetLeft}if(E.position==="fixed"){N+=Math.max(H.scrollTop,K.scrollTop),I+=Math.max(H.scrollLeft,K.scrollLeft)}return{top:N,left:I}}}o.offset={initialize:function(){if(this.initialized){return}var L=document.body,F=document.createElement("div"),H,G,N,I,M,E,J=L.style.marginTop,K='<div style="position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;"><div></div></div><table style="position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;" cellpadding="0" cellspacing="0"><tr><td></td></tr></table>';M={position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",height:"1px",visibility:"hidden"};for(E in M){F.style[E]=M[E]}F.innerHTML=K;L.insertBefore(F,L.firstChild);H=F.firstChild,G=H.firstChild,I=H.nextSibling.firstChild.firstChild;this.doesNotAddBorder=(G.offsetTop!==5);this.doesAddBorderForTableAndCells=(I.offsetTop===5);H.style.overflow="hidden",H.style.position="relative";this.subtractsBorderForOverflowNotVisible=(G.offsetTop===-5);L.style.marginTop="1px";this.doesNotIncludeMarginInBodyOffset=(L.offsetTop===0);L.style.marginTop=J;L.removeChild(F);this.initialized=true},bodyOffset:function(E){o.offset.initialized||o.offset.initialize();var G=E.offsetTop,F=E.offsetLeft;if(o.offset.doesNotIncludeMarginInBodyOffset){G+=parseInt(o.curCSS(E,"marginTop",true),10)||0,F+=parseInt(o.curCSS(E,"marginLeft",true),10)||0}return{top:G,left:F}}};o.fn.extend({position:function(){var I=0,H=0,F;if(this[0]){var G=this.offsetParent(),J=this.offset(),E=/^body|html$/i.test(G[0].tagName)?{top:0,left:0}:G.offset();J.top-=j(this,"marginTop");J.left-=j(this,"marginLeft");E.top+=j(G,"borderTopWidth");E.left+=j(G,"borderLeftWidth");F={top:J.top-E.top,left:J.left-E.left}}return F},offsetParent:function(){var E=this[0].offsetParent||document.body;while(E&&(!/^body|html$/i.test(E.tagName)&&o.css(E,"position")=="static")){E=E.offsetParent}return o(E)}});o.each(["Left","Top"],function(F,E){var G="scroll"+E;o.fn[G]=function(H){if(!this[0]){return null}return H!==g?this.each(function(){this==l||this==document?l.scrollTo(!F?H:o(l).scrollLeft(),F?H:o(l).scrollTop()):this[G]=H}):this[0]==l||this[0]==document?self[F?"pageYOffset":"pageXOffset"]||o.boxModel&&document.documentElement[G]||document.body[G]:this[0][G]}});o.each(["Height","Width"],function(I,G){var E=I?"Left":"Top",H=I?"Right":"Bottom",F=G.toLowerCase();o.fn["inner"+G]=function(){return this[0]?o.css(this[0],F,false,"padding"):null};o.fn["outer"+G]=function(K){return this[0]?o.css(this[0],F,false,K?"margin":"border"):null};var J=G.toLowerCase();o.fn[J]=function(K){return this[0]==l?document.compatMode=="CSS1Compat"&&document.documentElement["client"+G]||document.body["client"+G]:this[0]==document?Math.max(document.documentElement["client"+G],document.body["scroll"+G],document.documentElement["scroll"+G],document.body["offset"+G],document.documentElement["offset"+G]):K===g?(this.length?o.css(this[0],J):null):this.css(J,typeof K==="string"?K:K+"px")}})})();
\ No newline at end of file
diff --git a/Toolkit/PHPImageEditor/javascript/jquery.jcrop.js b/Toolkit/PHPImageEditor/javascript/jquery.jcrop.js
new file mode 100644 (file)
index 0000000..ad261f9
--- /dev/null
@@ -0,0 +1,1197 @@
+/**
+ * jquery.Jcrop.js v0.9.8
+ * jQuery Image Cropping Plugin
+ * @author Kelly Hallman <khallman@gmail.com>
+ * Copyright (c) 2008-2009 Kelly Hallman - released under MIT License {{{
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+
+ * }}}
+ */
+
+(function($) {
+
+$.Jcrop = function(obj,opt)
+{
+       // Initialization {{{
+
+       // Sanitize some options {{{
+       var obj = obj, opt = opt;
+
+       if (typeof(obj) !== 'object') obj = $(obj)[0];
+       if (typeof(opt) !== 'object') opt = { };
+
+       // Some on-the-fly fixes for MSIE...sigh
+       if (!('trackDocument' in opt))
+       {
+               opt.trackDocument = $.browser.msie ? false : true;
+               if ($.browser.msie && $.browser.version.split('.')[0] == '8')
+                       opt.trackDocument = true;
+       }
+
+       if (!('keySupport' in opt))
+                       opt.keySupport = $.browser.msie ? false : true;
+               
+       // }}}
+       // Extend the default options {{{
+       var defaults = {
+
+               // Basic Settings
+               trackDocument:          false,
+               baseClass:                      'jcrop',
+               addClass:                       null,
+
+               // Styling Options
+               bgColor:                        'black',
+               bgOpacity:                      .6,
+               borderOpacity:          .4,
+               handleOpacity:          .5,
+
+               handlePad:                      5,
+               handleSize:                     9,
+               handleOffset:           5,
+               edgeMargin:                     14,
+
+               aspectRatio:            0,
+               keySupport:                     true,
+               cornerHandles:          true,
+               sideHandles:            true,
+               drawBorders:            true,
+               dragEdges:                      true,
+
+               boxWidth:                       0,
+               boxHeight:                      0,
+
+               boundary:                       8,
+               animationDelay:         20,
+               swingSpeed:                     3,
+
+               allowSelect:            true,
+               allowMove:                      true,
+               allowResize:            true,
+
+               minSelect:                      [ 0, 0 ],
+               maxSize:                        [ 0, 0 ],
+               minSize:                        [ 0, 0 ],
+
+               // Callbacks / Event Handlers
+               onChange: function() { },
+               onSelect: function() { }
+
+       };
+       var options = defaults;
+       setOptions(opt);
+
+       // }}}
+       // Initialize some jQuery objects {{{
+
+       var $origimg = $(obj);
+       var $img = $origimg.clone().removeAttr('id').css({ position: 'absolute' });
+
+       $img.width($origimg.width());
+       $img.height($origimg.height());
+       $origimg.after($img).hide();
+
+       presize($img,options.boxWidth,options.boxHeight);
+
+       var boundx = $img.width(),
+               boundy = $img.height(),
+
+               $div = $('<div />')
+                       .width(boundx).height(boundy)
+                       .addClass(cssClass('holder'))
+                       .css({
+                               position: 'relative',
+                               backgroundColor: options.bgColor
+                       }).insertAfter($origimg).append($img);
+       ;
+       
+       if (options.addClass) $div.addClass(options.addClass);
+       //$img.wrap($div);
+
+       var $img2 = $('<img />')/*{{{*/
+                       .attr('src',$img.attr('src'))
+                       .css('position','absolute')
+                       .width(boundx).height(boundy)
+       ;/*}}}*/
+       var $img_holder = $('<div />')/*{{{*/
+               .width(pct(100)).height(pct(100))
+               .css({
+                       zIndex: 310,
+                       position: 'absolute',
+                       overflow: 'hidden'
+               })
+               .append($img2)
+       ;/*}}}*/
+       var $hdl_holder = $('<div />')/*{{{*/
+               .width(pct(100)).height(pct(100))
+               .css('zIndex',320);
+       /*}}}*/
+       var $sel = $('<div />')/*{{{*/
+               .css({
+                       position: 'absolute',
+                       zIndex: 300
+               })
+               .insertBefore($img)
+               .append($img_holder,$hdl_holder)
+       ;/*}}}*/
+
+       var bound = options.boundary;
+       var $trk = newTracker().width(boundx+(bound*2)).height(boundy+(bound*2))
+               .css({ position: 'absolute', top: px(-bound), left: px(-bound), zIndex: 290 })
+               .mousedown(newSelection);       
+       
+       /* }}} */
+       // Set more variables {{{
+
+       var xlimit, ylimit, xmin, ymin;
+       var xscale, yscale, enabled = true;
+       var docOffset = getPos($img),
+               // Internal states
+               btndown, lastcurs, dimmed, animating,
+               shift_down;
+
+       // }}}
+               
+
+               // }}}
+       // Internal Modules {{{
+
+       var Coords = function()/*{{{*/
+       {
+               var x1 = 0, y1 = 0, x2 = 0, y2 = 0, ox, oy;
+
+               function setPressed(pos)/*{{{*/
+               {
+                       var pos = rebound(pos);
+                       x2 = x1 = pos[0];
+                       y2 = y1 = pos[1];
+               };
+               /*}}}*/
+               function setCurrent(pos)/*{{{*/
+               {
+                       var pos = rebound(pos);
+                       ox = pos[0] - x2;
+                       oy = pos[1] - y2;
+                       x2 = pos[0];
+                       y2 = pos[1];
+               };
+               /*}}}*/
+               function getOffset()/*{{{*/
+               {
+                       return [ ox, oy ];
+               };
+               /*}}}*/
+               function moveOffset(offset)/*{{{*/
+               {
+                       var ox = offset[0], oy = offset[1];
+
+                       if (0 > x1 + ox) ox -= ox + x1;
+                       if (0 > y1 + oy) oy -= oy + y1;
+
+                       if (boundy < y2 + oy) oy += boundy - (y2 + oy);
+                       if (boundx < x2 + ox) ox += boundx - (x2 + ox);
+
+                       x1 += ox;
+                       x2 += ox;
+                       y1 += oy;
+                       y2 += oy;
+               };
+               /*}}}*/
+               function getCorner(ord)/*{{{*/
+               {
+                       var c = getFixed();
+                       switch(ord)
+                       {
+                               case 'ne': return [ c.x2, c.y ];
+                               case 'nw': return [ c.x, c.y ];
+                               case 'se': return [ c.x2, c.y2 ];
+                               case 'sw': return [ c.x, c.y2 ];
+                       }
+               };
+               /*}}}*/
+               function getFixed()/*{{{*/
+               {
+                       if (!options.aspectRatio) return getRect();
+                       // This function could use some optimization I think...
+                       var aspect = options.aspectRatio,
+                               min_x = options.minSize[0]/xscale, 
+                               min_y = options.minSize[1]/yscale,
+                               max_x = options.maxSize[0]/xscale, 
+                               max_y = options.maxSize[1]/yscale,
+                               rw = x2 - x1,
+                               rh = y2 - y1,
+                               rwa = Math.abs(rw),
+                               rha = Math.abs(rh),
+                               real_ratio = rwa / rha,
+                               xx, yy
+                       ;
+                       if (max_x == 0) { max_x = boundx * 10 }
+                       if (max_y == 0) { max_y = boundy * 10 }
+                       if (real_ratio < aspect)
+                       {
+                               yy = y2;
+                               w = rha * aspect;
+                               xx = rw < 0 ? x1 - w : w + x1;
+
+                               if (xx < 0)
+                               {
+                                       xx = 0;
+                                       h = Math.abs((xx - x1) / aspect);
+                                       yy = rh < 0 ? y1 - h: h + y1;
+                               }
+                               else if (xx > boundx)
+                               {
+                                       xx = boundx;
+                                       h = Math.abs((xx - x1) / aspect);
+                                       yy = rh < 0 ? y1 - h : h + y1;
+                               }
+                       }
+                       else
+                       {
+                               xx = x2;
+                               h = rwa / aspect;
+                               yy = rh < 0 ? y1 - h : y1 + h;
+                               if (yy < 0)
+                               {
+                                       yy = 0;
+                                       w = Math.abs((yy - y1) * aspect);
+                                       xx = rw < 0 ? x1 - w : w + x1;
+                               }
+                               else if (yy > boundy)
+                               {
+                                       yy = boundy;
+                                       w = Math.abs(yy - y1) * aspect;
+                                       xx = rw < 0 ? x1 - w : w + x1;
+                               }
+                       }
+
+                       // Magic %-)
+                       if(xx > x1) { // right side
+                         if(xx - x1 < min_x) {
+                               xx = x1 + min_x;
+                         } else if (xx - x1 > max_x) {
+                               xx = x1 + max_x;
+                         }
+                         if(yy > y1) {
+                               yy = y1 + (xx - x1)/aspect;
+                         } else {
+                               yy = y1 - (xx - x1)/aspect;
+                         }
+                       } else if (xx < x1) { // left side
+                         if(x1 - xx < min_x) {
+                               xx = x1 - min_x
+                         } else if (x1 - xx > max_x) {
+                               xx = x1 - max_x;
+                         }
+                         if(yy > y1) {
+                               yy = y1 + (x1 - xx)/aspect;
+                         } else {
+                               yy = y1 - (x1 - xx)/aspect;
+                         }
+                       }
+
+                       if(xx < 0) {
+                               x1 -= xx;
+                               xx = 0;
+                       } else  if (xx > boundx) {
+                               x1 -= xx - boundx;
+                               xx = boundx;
+                       }
+
+                       if(yy < 0) {
+                               y1 -= yy;
+                               yy = 0;
+                       } else  if (yy > boundy) {
+                               y1 -= yy - boundy;
+                               yy = boundy;
+                       }
+
+                       return last = makeObj(flipCoords(x1,y1,xx,yy));
+               };
+               /*}}}*/
+               function rebound(p)/*{{{*/
+               {
+                       if (p[0] < 0) p[0] = 0;
+                       if (p[1] < 0) p[1] = 0;
+
+                       if (p[0] > boundx) p[0] = boundx;
+                       if (p[1] > boundy) p[1] = boundy;
+
+                       return [ p[0], p[1] ];
+               };
+               /*}}}*/
+               function flipCoords(x1,y1,x2,y2)/*{{{*/
+               {
+                       var xa = x1, xb = x2, ya = y1, yb = y2;
+                       if (x2 < x1)
+                       {
+                               xa = x2;
+                               xb = x1;
+                       }
+                       if (y2 < y1)
+                       {
+                               ya = y2;
+                               yb = y1;
+                       }
+                       return [ Math.round(xa), Math.round(ya), Math.round(xb), Math.round(yb) ];
+               };
+               /*}}}*/
+               function getRect()/*{{{*/
+               {
+                       var xsize = x2 - x1;
+                       var ysize = y2 - y1;
+
+                       if (xlimit && (Math.abs(xsize) > xlimit))
+                               x2 = (xsize > 0) ? (x1 + xlimit) : (x1 - xlimit);
+                       if (ylimit && (Math.abs(ysize) > ylimit))
+                               y2 = (ysize > 0) ? (y1 + ylimit) : (y1 - ylimit);
+
+                       if (ymin && (Math.abs(ysize) < ymin))
+                               y2 = (ysize > 0) ? (y1 + ymin) : (y1 - ymin);
+                       if (xmin && (Math.abs(xsize) < xmin))
+                               x2 = (xsize > 0) ? (x1 + xmin) : (x1 - xmin);
+
+                       if (x1 < 0) { x2 -= x1; x1 -= x1; }
+                       if (y1 < 0) { y2 -= y1; y1 -= y1; }
+                       if (x2 < 0) { x1 -= x2; x2 -= x2; }
+                       if (y2 < 0) { y1 -= y2; y2 -= y2; }
+                       if (x2 > boundx) { var delta = x2 - boundx; x1 -= delta; x2 -= delta; }
+                       if (y2 > boundy) { var delta = y2 - boundy; y1 -= delta; y2 -= delta; }
+                       if (x1 > boundx) { var delta = x1 - boundy; y2 -= delta; y1 -= delta; }
+                       if (y1 > boundy) { var delta = y1 - boundy; y2 -= delta; y1 -= delta; }
+
+                       return makeObj(flipCoords(x1,y1,x2,y2));
+               };
+               /*}}}*/
+               function makeObj(a)/*{{{*/
+               {
+                       return { x: a[0], y: a[1], x2: a[2], y2: a[3],
+                               w: a[2] - a[0], h: a[3] - a[1] };
+               };
+               /*}}}*/
+
+               return {
+                       flipCoords: flipCoords,
+                       setPressed: setPressed,
+                       setCurrent: setCurrent,
+                       getOffset: getOffset,
+                       moveOffset: moveOffset,
+                       getCorner: getCorner,
+                       getFixed: getFixed
+               };
+       }();
+
+       /*}}}*/
+       var Selection = function()/*{{{*/
+       {
+               var start, end, dragmode, awake, hdep = 370;
+               var borders = { };
+               var handle = { };
+               var seehandles = false;
+               var hhs = options.handleOffset;
+
+               /* Insert draggable elements {{{*/
+
+               // Insert border divs for outline
+               if (options.drawBorders) {
+                       borders = {
+                                       top: insertBorder('hline')
+                                               .css('top',$.browser.msie?px(-1):px(0)),
+                                       bottom: insertBorder('hline'),
+                                       left: insertBorder('vline'),
+                                       right: insertBorder('vline')
+                       };
+               }
+
+               // Insert handles on edges
+               if (options.dragEdges) {
+                       handle.t = insertDragbar('n');
+                       handle.b = insertDragbar('s');
+                       handle.r = insertDragbar('e');
+                       handle.l = insertDragbar('w');
+               }
+
+               // Insert side handles
+               options.sideHandles &&
+                       createHandles(['n','s','e','w']);
+
+               // Insert corner handles
+               options.cornerHandles &&
+                       createHandles(['sw','nw','ne','se']);
+
+               /*}}}*/
+               // Private Methods
+               function insertBorder(type)/*{{{*/
+               {
+                       var jq = $('<div />')
+                               .css({position: 'absolute', opacity: options.borderOpacity })
+                               .addClass(cssClass(type));
+                       $img_holder.append(jq);
+                       return jq;
+               };
+               /*}}}*/
+               function dragDiv(ord,zi)/*{{{*/
+               {
+                       var jq = $('<div />')
+                               .mousedown(createDragger(ord))
+                               .css({
+                                       cursor: ord+'-resize',
+                                       position: 'absolute',
+                                       zIndex: zi 
+                               })
+                       ;
+                       $hdl_holder.append(jq);
+                       return jq;
+               };
+               /*}}}*/
+               function insertHandle(ord)/*{{{*/
+               {
+                       return dragDiv(ord,hdep++)
+                               .css({ top: px(-hhs+1), left: px(-hhs+1), opacity: options.handleOpacity })
+                               .addClass(cssClass('handle'));
+               };
+               /*}}}*/
+               function insertDragbar(ord)/*{{{*/
+               {
+                       var s = options.handleSize,
+                               o = hhs,
+                               h = s, w = s,
+                               t = o, l = o;
+
+                       switch(ord)
+                       {
+                               case 'n': case 's': w = pct(100); break;
+                               case 'e': case 'w': h = pct(100); break;
+                       }
+
+                       return dragDiv(ord,hdep++).width(w).height(h)
+                               .css({ top: px(-t+1), left: px(-l+1)});
+               };
+               /*}}}*/
+               function createHandles(li)/*{{{*/
+               {
+                       for(i in li) handle[li[i]] = insertHandle(li[i]);
+               };
+               /*}}}*/
+               function moveHandles(c)/*{{{*/
+               {
+                       var midvert  = Math.round((c.h / 2) - hhs),
+                               midhoriz = Math.round((c.w / 2) - hhs),
+                               north = west = -hhs+1,
+                               east = c.w - hhs,
+                               south = c.h - hhs,
+                               x, y;
+
+                       'e' in handle &&
+                               handle.e.css({ top: px(midvert), left: px(east) }) &&
+                               handle.w.css({ top: px(midvert) }) &&
+                               handle.s.css({ top: px(south), left: px(midhoriz) }) &&
+                               handle.n.css({ left: px(midhoriz) });
+
+                       'ne' in handle &&
+                               handle.ne.css({ left: px(east) }) &&
+                               handle.se.css({ top: px(south), left: px(east) }) &&
+                               handle.sw.css({ top: px(south) });
+
+                       'b' in handle &&
+                               handle.b.css({ top: px(south) }) &&
+                               handle.r.css({ left: px(east) });
+               };
+               /*}}}*/
+               function moveto(x,y)/*{{{*/
+               {
+                       $img2.css({ top: px(-y), left: px(-x) });
+                       $sel.css({ top: px(y), left: px(x) });
+               };
+               /*}}}*/
+               function resize(w,h)/*{{{*/
+               {
+                       $sel.width(w).height(h);
+               };
+               /*}}}*/
+               function refresh()/*{{{*/
+               {
+                       var c = Coords.getFixed();
+
+                       Coords.setPressed([c.x,c.y]);
+                       Coords.setCurrent([c.x2,c.y2]);
+
+                       updateVisible();
+               };
+               /*}}}*/
+
+               // Internal Methods
+               function updateVisible()/*{{{*/
+                       { if (awake) return update(); };
+               /*}}}*/
+               function update()/*{{{*/
+               {
+                       var c = Coords.getFixed();
+
+                       resize(c.w,c.h);
+                       moveto(c.x,c.y);
+
+                       options.drawBorders &&
+                               borders['right'].css({ left: px(c.w-1) }) &&
+                                       borders['bottom'].css({ top: px(c.h-1) });
+
+                       seehandles && moveHandles(c);
+                       awake || show();
+
+                       options.onChange(unscale(c));
+               };
+               /*}}}*/
+               function show()/*{{{*/
+               {
+                       $sel.show();
+                       $img.css('opacity',options.bgOpacity);
+                       awake = true;
+               };
+               /*}}}*/
+               function release()/*{{{*/
+               {
+                       disableHandles();
+                       $sel.hide();
+                       $img.css('opacity',1);
+                       awake = false;
+               };
+               /*}}}*/
+               function showHandles()//{{{
+               {
+                       if (seehandles)
+                       {
+                               moveHandles(Coords.getFixed());
+                               $hdl_holder.show();
+                       }
+               };
+               //}}}
+               function enableHandles()/*{{{*/
+               { 
+                       seehandles = true;
+                       if (options.allowResize)
+                       {
+                               moveHandles(Coords.getFixed());
+                               $hdl_holder.show();
+                               return true;
+                       }
+               };
+               /*}}}*/
+               function disableHandles()/*{{{*/
+               {
+                       seehandles = false;
+                       $hdl_holder.hide();
+               };
+               /*}}}*/
+               function animMode(v)/*{{{*/
+               {
+                       (animating = v) ? disableHandles(): enableHandles();
+               };
+               /*}}}*/
+               function done()/*{{{*/
+               {
+                       animMode(false);
+                       refresh();
+               };
+               /*}}}*/
+
+               var $track = newTracker().mousedown(createDragger('move'))
+                               .css({ cursor: 'move', position: 'absolute', zIndex: 360 })
+
+               $img_holder.append($track);
+               disableHandles();
+
+               return {
+                       updateVisible: updateVisible,
+                       update: update,
+                       release: release,
+                       refresh: refresh,
+                       setCursor: function (cursor) { $track.css('cursor',cursor); },
+                       enableHandles: enableHandles,
+                       enableOnly: function() { seehandles = true; },
+                       showHandles: showHandles,
+                       disableHandles: disableHandles,
+                       animMode: animMode,
+                       done: done
+               };
+       }();
+       /*}}}*/
+       var Tracker = function()/*{{{*/
+       {
+               var onMove              = function() { },
+                       onDone          = function() { },
+                       trackDoc        = options.trackDocument;
+
+               if (!trackDoc)
+               {
+                       $trk
+                               .mousemove(trackMove)
+                               .mouseup(trackUp)
+                               .mouseout(trackUp)
+                       ;
+               }
+
+               function toFront()/*{{{*/
+               {
+                       $trk.css({zIndex:450});
+                       if (trackDoc)
+                       {
+                               $(document)
+                                       .mousemove(trackMove)
+                                       .mouseup(trackUp)
+                               ;
+                       }
+               }
+               /*}}}*/
+               function toBack()/*{{{*/
+               {
+                       $trk.css({zIndex:290});
+                       if (trackDoc)
+                       {
+                               $(document)
+                                       .unbind('mousemove',trackMove)
+                                       .unbind('mouseup',trackUp)
+                               ;
+                       }
+               }
+               /*}}}*/
+               function trackMove(e)/*{{{*/
+               {
+                       onMove(mouseAbs(e));
+               };
+               /*}}}*/
+               function trackUp(e)/*{{{*/
+               {
+                       e.preventDefault();
+                       e.stopPropagation();
+
+                       if (btndown)
+                       {
+                               btndown = false;
+
+                               onDone(mouseAbs(e));
+                               options.onSelect(unscale(Coords.getFixed()));
+                               toBack();
+                               onMove = function() { };
+                               onDone = function() { };
+                       }
+
+                       return false;
+               };
+               /*}}}*/
+
+               function activateHandlers(move,done)/* {{{ */
+               {
+                       btndown = true;
+                       onMove = move;
+                       onDone = done;
+                       toFront();
+                       return false;
+               };
+               /* }}} */
+
+               function setCursor(t) { $trk.css('cursor',t); };
+
+               $img.before($trk);
+               return {
+                       activateHandlers: activateHandlers,
+                       setCursor: setCursor
+               };
+       }();
+       /*}}}*/
+       var KeyManager = function()/*{{{*/
+       {
+               var $keymgr = $('<input type="radio" />')
+                               .css({ position: 'absolute', left: '-30px' })
+                               .keypress(parseKey)
+                               .blur(onBlur),
+
+                       $keywrap = $('<div />')
+                               .css({
+                                       position: 'absolute',
+                                       overflow: 'hidden'
+                               })
+                               .append($keymgr)
+               ;
+
+               function watchKeys()/*{{{*/
+               {
+                       if (options.keySupport)
+                       {
+                               $keymgr.show();
+                               $keymgr.focus();
+                       }
+               };
+               /*}}}*/
+               function onBlur(e)/*{{{*/
+               {
+                       $keymgr.hide();
+               };
+               /*}}}*/
+               function doNudge(e,x,y)/*{{{*/
+               {
+                       if (options.allowMove) {
+                               Coords.moveOffset([x,y]);
+                               Selection.updateVisible();
+                       };
+                       e.preventDefault();
+                       e.stopPropagation();
+               };
+               /*}}}*/
+               function parseKey(e)/*{{{*/
+               {
+                       if (e.ctrlKey) return true;
+                       shift_down = e.shiftKey ? true : false;
+                       var nudge = shift_down ? 10 : 1;
+                       switch(e.keyCode)
+                       {
+                               case 37: doNudge(e,-nudge,0); break;
+                               case 39: doNudge(e,nudge,0); break;
+                               case 38: doNudge(e,0,-nudge); break;
+                               case 40: doNudge(e,0,nudge); break;
+
+                               case 27: Selection.release(); break;
+
+                               case 9: return true;
+                       }
+
+                       return nothing(e);
+               };
+               /*}}}*/
+               
+               if (options.keySupport) $keywrap.insertBefore($img);
+               return {
+                       watchKeys: watchKeys
+               };
+       }();
+       /*}}}*/
+
+       // }}}
+       // Internal Methods {{{
+
+       function px(n) { return '' + parseInt(n) + 'px'; };
+       function pct(n) { return '' + parseInt(n) + '%'; };
+       function cssClass(cl) { return options.baseClass + '-' + cl; };
+       function getPos(obj)/*{{{*/
+       {
+               // Updated in v0.9.4 to use built-in dimensions plugin
+               var pos = $(obj).offset();
+               return [ pos.left, pos.top ];
+       };
+       /*}}}*/
+       function mouseAbs(e)/*{{{*/
+       {
+               return [ (e.pageX - docOffset[0]), (e.pageY - docOffset[1]) ];
+       };
+       /*}}}*/
+       function myCursor(type)/*{{{*/
+       {
+               if (type != lastcurs)
+               {
+                       Tracker.setCursor(type);
+                       //Handles.xsetCursor(type);
+                       lastcurs = type;
+               }
+       };
+       /*}}}*/
+       function startDragMode(mode,pos)/*{{{*/
+       {
+               docOffset = getPos($img);
+               Tracker.setCursor(mode=='move'?mode:mode+'-resize');
+
+               if (mode == 'move')
+                       return Tracker.activateHandlers(createMover(pos), doneSelect);
+
+               var fc = Coords.getFixed();
+               var opp = oppLockCorner(mode);
+               var opc = Coords.getCorner(oppLockCorner(opp));
+
+               Coords.setPressed(Coords.getCorner(opp));
+               Coords.setCurrent(opc);
+
+               Tracker.activateHandlers(dragmodeHandler(mode,fc),doneSelect);
+       };
+       /*}}}*/
+       function dragmodeHandler(mode,f)/*{{{*/
+       {
+               return function(pos) {
+                       if (!options.aspectRatio) switch(mode)
+                       {
+                               case 'e': pos[1] = f.y2; break;
+                               case 'w': pos[1] = f.y2; break;
+                               case 'n': pos[0] = f.x2; break;
+                               case 's': pos[0] = f.x2; break;
+                       }
+                       else switch(mode)
+                       {
+                               case 'e': pos[1] = f.y+1; break;
+                               case 'w': pos[1] = f.y+1; break;
+                               case 'n': pos[0] = f.x+1; break;
+                               case 's': pos[0] = f.x+1; break;
+                       }
+                       Coords.setCurrent(pos);
+                       Selection.update();
+               };
+       };
+       /*}}}*/
+       function createMover(pos)/*{{{*/
+       {
+               var lloc = pos;
+               KeyManager.watchKeys();
+
+               return function(pos)
+               {
+                       Coords.moveOffset([pos[0] - lloc[0], pos[1] - lloc[1]]);
+                       lloc = pos;
+                       
+                       Selection.update();
+               };
+       };
+       /*}}}*/
+       function oppLockCorner(ord)/*{{{*/
+       {
+               switch(ord)
+               {
+                       case 'n': return 'sw';
+                       case 's': return 'nw';
+                       case 'e': return 'nw';
+                       case 'w': return 'ne';
+                       case 'ne': return 'sw';
+                       case 'nw': return 'se';
+                       case 'se': return 'nw';
+                       case 'sw': return 'ne';
+               };
+       };
+       /*}}}*/
+       function createDragger(ord)/*{{{*/
+       {
+               return function(e) {
+                       if (options.disabled) return false;
+                       if ((ord == 'move') && !options.allowMove) return false;
+                       btndown = true;
+                       startDragMode(ord,mouseAbs(e));
+                       e.stopPropagation();
+                       e.preventDefault();
+                       return false;
+               };
+       };
+       /*}}}*/
+       function presize($obj,w,h)/*{{{*/
+       {
+               var nw = $obj.width(), nh = $obj.height();
+               if ((nw > w) && w > 0)
+               {
+                       nw = w;
+                       nh = (w/$obj.width()) * $obj.height();
+               }
+               if ((nh > h) && h > 0)
+               {
+                       nh = h;
+                       nw = (h/$obj.height()) * $obj.width();
+               }
+               xscale = $obj.width() / nw;
+               yscale = $obj.height() / nh;
+               $obj.width(nw).height(nh);
+       };
+       /*}}}*/
+       function unscale(c)/*{{{*/
+       {
+               return {
+                       x: parseInt(c.x * xscale), y: parseInt(c.y * yscale), 
+                       x2: parseInt(c.x2 * xscale), y2: parseInt(c.y2 * yscale), 
+                       w: parseInt(c.w * xscale), h: parseInt(c.h * yscale)
+               };
+       };
+       /*}}}*/
+       function doneSelect(pos)/*{{{*/
+       {
+               var c = Coords.getFixed();
+               if (c.w > options.minSelect[0] && c.h > options.minSelect[1])
+               {
+                       Selection.enableHandles();
+                       Selection.done();
+               }
+               else
+               {
+                       Selection.release();
+               }
+               Tracker.setCursor( options.allowSelect?'crosshair':'default' );
+       };
+       /*}}}*/
+       function newSelection(e)/*{{{*/
+       {
+               if (options.disabled) return false;
+               if (!options.allowSelect) return false;
+               btndown = true;
+               docOffset = getPos($img);
+               Selection.disableHandles();
+               myCursor('crosshair');
+               var pos = mouseAbs(e);
+               Coords.setPressed(pos);
+               Tracker.activateHandlers(selectDrag,doneSelect);
+               KeyManager.watchKeys();
+               Selection.update();
+
+               e.stopPropagation();
+               e.preventDefault();
+               return false;
+       };
+       /*}}}*/
+       function selectDrag(pos)/*{{{*/
+       {
+               Coords.setCurrent(pos);
+               Selection.update();
+       };
+       /*}}}*/
+       function newTracker()
+       {
+               var trk = $('<div></div>').addClass(cssClass('tracker'));
+               $.browser.msie && trk.css({ opacity: 0, backgroundColor: 'white' });
+               return trk;
+       };
+
+       // }}}
+       // API methods {{{
+               
+       function animateTo(a)/*{{{*/
+       {
+               var x1 = a[0] / xscale,
+                       y1 = a[1] / yscale,
+                       x2 = a[2] / xscale,
+                       y2 = a[3] / yscale;
+
+               if (animating) return;
+
+               var animto = Coords.flipCoords(x1,y1,x2,y2);
+               var c = Coords.getFixed();
+               var animat = initcr = [ c.x, c.y, c.x2, c.y2 ];
+               var interv = options.animationDelay;
+
+               var x = animat[0];
+               var y = animat[1];
+               var x2 = animat[2];
+               var y2 = animat[3];
+               var ix1 = animto[0] - initcr[0];
+               var iy1 = animto[1] - initcr[1];
+               var ix2 = animto[2] - initcr[2];
+               var iy2 = animto[3] - initcr[3];
+               var pcent = 0;
+               var velocity = options.swingSpeed;
+
+               Selection.animMode(true);
+
+               var animator = function()
+               {
+                       return function()
+                       {
+                               pcent += (100 - pcent) / velocity;
+
+                               animat[0] = x + ((pcent / 100) * ix1);
+                               animat[1] = y + ((pcent / 100) * iy1);
+                               animat[2] = x2 + ((pcent / 100) * ix2);
+                               animat[3] = y2 + ((pcent / 100) * iy2);
+
+                               if (pcent < 100) animateStart();
+                                       else Selection.done();
+
+                               if (pcent >= 99.8) pcent = 100;
+
+                               setSelectRaw(animat);
+                       };
+               }();
+
+               function animateStart()
+                       { window.setTimeout(animator,interv); };
+
+               animateStart();
+       };
+       /*}}}*/
+       function setSelect(rect)//{{{
+       {
+               setSelectRaw([rect[0]/xscale,rect[1]/yscale,rect[2]/xscale,rect[3]/yscale]);
+       };
+       //}}}
+       function setSelectRaw(l) /*{{{*/
+       {
+               Coords.setPressed([l[0],l[1]]);
+               Coords.setCurrent([l[2],l[3]]);
+               Selection.update();
+       };
+       /*}}}*/
+       function setOptions(opt)/*{{{*/
+       {
+               if (typeof(opt) != 'object') opt = { };
+               options = $.extend(options,opt);
+
+               if (typeof(options.onChange)!=='function')
+                       options.onChange = function() { };
+
+               if (typeof(options.onSelect)!=='function')
+                       options.onSelect = function() { };
+
+       };
+       /*}}}*/
+       function tellSelect()/*{{{*/
+       {
+               return unscale(Coords.getFixed());
+       };
+       /*}}}*/
+       function tellScaled()/*{{{*/
+       {
+               return Coords.getFixed();
+       };
+       /*}}}*/
+       function setOptionsNew(opt)/*{{{*/
+       {
+               setOptions(opt);
+               interfaceUpdate();
+       };
+       /*}}}*/
+       function disableCrop()//{{{
+       {
+               options.disabled = true;
+               Selection.disableHandles();
+               Selection.setCursor('default');
+               Tracker.setCursor('default');
+       };
+       //}}}
+       function enableCrop()//{{{
+       {
+               options.disabled = false;
+               interfaceUpdate();
+       };
+       //}}}
+       function cancelCrop()//{{{
+       {
+               Selection.done();
+               Tracker.activateHandlers(null,null);
+       };
+       //}}}
+       function destroy()//{{{
+       {
+               $div.remove();
+               $origimg.show();
+       };
+       //}}}
+
+       function interfaceUpdate(alt)//{{{
+       // This method tweaks the interface based on options object.
+       // Called when options are changed and at end of initialization.
+       {
+               options.allowResize ?
+                       alt?Selection.enableOnly():Selection.enableHandles():
+                       Selection.disableHandles();
+
+               Tracker.setCursor( options.allowSelect? 'crosshair': 'default' );
+               Selection.setCursor( options.allowMove? 'move': 'default' );
+
+               $div.css('backgroundColor',options.bgColor);
+
+               if ('setSelect' in options) {
+                       setSelect(opt.setSelect);
+                       Selection.done();
+                       delete(options.setSelect);
+               }
+
+               if ('trueSize' in options) {
+                       xscale = options.trueSize[0] / boundx;
+                       yscale = options.trueSize[1] / boundy;
+               }
+
+               xlimit = options.maxSize[0] || 0;
+               ylimit = options.maxSize[1] || 0;
+               xmin = options.minSize[0] || 0;
+               ymin = options.minSize[1] || 0;
+
+               if ('outerImage' in options)
+               {
+                       $img.attr('src',options.outerImage);
+                       delete(options.outerImage);
+               }
+
+               Selection.refresh();
+       };
+       //}}}
+
+       // }}}
+
+       $hdl_holder.hide();
+       interfaceUpdate(true);
+       
+       var api = {
+               animateTo: animateTo,
+               setSelect: setSelect,
+               setOptions: setOptionsNew,
+               tellSelect: tellSelect,
+               tellScaled: tellScaled,
+
+               disable: disableCrop,
+               enable: enableCrop,
+               cancel: cancelCrop,
+
+               focus: KeyManager.watchKeys,
+
+               getBounds: function() { return [ boundx * xscale, boundy * yscale ]; },
+               getWidgetSize: function() { return [ boundx, boundy ]; },
+
+               release: Selection.release,
+               destroy: destroy
+
+       };
+
+       $origimg.data('Jcrop',api);
+       return api;
+};
+
+$.fn.Jcrop = function(options)/*{{{*/
+{
+       function attachWhenDone(from)/*{{{*/
+       {
+               var loadsrc = options.useImg || from.src;
+               var img = new Image();
+               img.onload = function() { $.Jcrop(from,options); };
+               img.src = loadsrc;
+       };
+       /*}}}*/
+       if (typeof(options) !== 'object') options = { };
+
+       // Iterate over each object, attach Jcrop
+       this.each(function()
+       {
+               // If we've already attached to this object
+               if ($(this).data('Jcrop'))
+               {
+                       // The API can be requested this way (undocumented)
+                       if (options == 'api') return $(this).data('Jcrop');
+                       // Otherwise, we just reset the options...
+                       else $(this).data('Jcrop').setOptions(options);
+               }
+               // If we haven't been attached, preload and attach
+               else attachWhenDone(this);
+       });
+
+       // Return "this" so we're chainable a la jQuery plugin-style!
+       return this;
+};
+/*}}}*/
+
+})(jQuery);
diff --git a/Toolkit/PHPImageEditor/javascript/jquery.numeric.js b/Toolkit/PHPImageEditor/javascript/jquery.numeric.js
new file mode 100644 (file)
index 0000000..5c5aae7
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ *
+ * Copyright (c) 2006/2007 Sam Collett (http://www.texotela.co.uk)
+ * Licensed under the MIT License:
+ * http://www.opensource.org/licenses/mit-license.php
+ * 
+ * Version 1.0
+ * Demo: http://www.texotela.co.uk/code/jquery/numeric/
+ *
+ * $LastChangedDate: 2007-05-29 11:31:36 +0100 (Tue, 29 May 2007) $
+ * $Rev: 2005 $
+ */
+/*
+ * Allows only valid characters to be entered into input boxes.
+ * Note: does not validate that the final text is a valid number
+ * (that could be done by another script, or server-side)
+ *
+ * @name     numeric
+ * @param    decimal      Decimal separator (e.g. '.' or ',' - default is '.')
+ * @param    callback     A function that runs if the number is not valid (fires onblur)
+ * @author   Sam Collett (http://www.texotela.co.uk)
+ * @example  $(".numeric").numeric();
+ * @example  $(".numeric").numeric(",");
+ * @example  $(".numeric").numeric(null, callback);
+ *
+ */
+jQuery.fn.numeric = function(decimal, callback)
+{
+       decimal = decimal || ".";
+       callback = typeof callback == "function" ? callback : function(){};
+       this.keypress(
+               function(e)
+               {
+                       var key = e.charCode ? e.charCode : e.keyCode ? e.keyCode : 0;
+                       // allow enter/return key (only when in an input box)
+                       if(key == 13 && this.nodeName.toLowerCase() == "input")
+                       {
+                               return true;
+                       }
+                       else if(key == 13)
+                       {
+                               return false;
+                       }
+                       var allow = false;
+                       // allow Ctrl+A
+                       if((e.ctrlKey && key == 97 /* firefox */) || (e.ctrlKey && key == 65) /* opera */) return true;
+                       // allow Ctrl+X (cut)
+                       if((e.ctrlKey && key == 120 /* firefox */) || (e.ctrlKey && key == 88) /* opera */) return true;
+                       // allow Ctrl+C (copy)
+                       if((e.ctrlKey && key == 99 /* firefox */) || (e.ctrlKey && key == 67) /* opera */) return true;
+                       // allow Ctrl+Z (undo)
+                       if((e.ctrlKey && key == 122 /* firefox */) || (e.ctrlKey && key == 90) /* opera */) return true;
+                       // allow or deny Ctrl+V (paste), Shift+Ins
+                       if((e.ctrlKey && key == 118 /* firefox */) || (e.ctrlKey && key == 86) /* opera */
+                       || (e.shiftKey && key == 45)) return true;
+                       // if a number was not pressed
+                       if(key < 48 || key > 57)
+                       {
+                               /* '-' only allowed at start */
+                               if(key == 45 && this.value.length == 0) return true;
+                               /* only one decimal separator allowed */
+                               if(key == decimal.charCodeAt(0) && this.value.indexOf(decimal) != -1)
+                               {
+                                       allow = false;
+                               }
+                               // check for other keys that have special purposes
+                               if(
+                                       key != 8 /* backspace */ &&
+                                       key != 9 /* tab */ &&
+                                       key != 13 /* enter */ &&
+                                       key != 35 /* end */ &&
+                                       key != 36 /* home */ &&
+                                       key != 37 /* left */ &&
+                                       key != 39 /* right */ &&
+                                       key != 46 /* del */
+                               )
+                               {
+                                       allow = false;
+                               }
+                               else
+                               {
+                                       // for detecting special keys (listed above)
+                                       // IE does not support 'charCode' and ignores them in keypress anyway
+                                       if(typeof e.charCode != "undefined")
+                                       {
+                                               // special keys have 'keyCode' and 'which' the same (e.g. backspace)
+                                               if(e.keyCode == e.which && e.which != 0)
+                                               {
+                                                       allow = true;
+                                               }
+                                               // or keyCode != 0 and 'charCode'/'which' = 0
+                                               else if(e.keyCode != 0 && e.charCode == 0 && e.which == 0)
+                                               {
+                                                       allow = true;
+                                               }
+                                       }
+                               }
+                               // if key pressed is the decimal and it is not already in the field
+                               if(key == decimal.charCodeAt(0) && this.value.indexOf(decimal) == -1)
+                               {
+                                       allow = true;
+                               }
+                       }
+                       else
+                       {
+                               allow = true;
+                       }
+                       return allow;
+               }
+       )
+       .blur(
+               function()
+               {
+                       var val = jQuery(this).val();
+                       if(val != "")
+                       {
+                               var re = new RegExp("^\\d+$|\\d*" + decimal + "\\d+");
+                               if(!re.exec(val))
+                               {
+                                       callback.apply(this);
+                               }
+                       }
+               }
+       );
+       return this;
+}
\ No newline at end of file
diff --git a/Toolkit/PHPImageEditor/javascript/phpimageeditor.js b/Toolkit/PHPImageEditor/javascript/phpimageeditor.js
new file mode 100644 (file)
index 0000000..bb37f83
--- /dev/null
@@ -0,0 +1,711 @@
+var objCrop = null;
+
+function ajax_post()
+{
+       if ($('#loading').css('display') == 'none') {
+               $('#width').attr('disabled','disabled');
+               $('#height').attr('disabled','disabled');
+               $('#keepproportions').attr('disabled','disabled');
+               $('#btnRotateLeft').attr('disabled','disabled');
+               $('#btnRotateRight').attr('disabled','disabled');
+               $('#croptop').attr('disabled','disabled');
+               $('#cropbottom').attr('disabled','disabled');
+               $('#cropleft').attr('disabled','disabled');
+               $('#cropright').attr('disabled','disabled');
+               $('#grayscale').attr('disabled','disabled');
+               $('#btnupdate').attr('disabled','disabled');
+               $('#btnsave').attr('disabled','disabled');
+               $('#btnundo').attr('disabled','disabled');
+               $('#brightness_slider_track').slider('disable');
+               $('#contrast_slider_track').slider('disable');
+       
+               $('#loading').css('display','block');
+               $('#loading_bar').width(0);
+               $('#loading_bar').animate({width: document.getElementById('loading').offsetWidth-30}, document.getElementById('loading').offsetWidth*10);
+               
+               $.ajax({
+            timeout: AjaxPostTimeoutMs,
+            type: "POST",
+            url: FormAction,
+            data: "grayscaleval="+$('#grayscaleval').val()+
+                "&keepproportionsval="+$('#keepproportionsval').val()+
+                "&image_name="+$('#image_name').val()+
+                "&width="+$('#width').val()+
+                "&widthoriginal="+$('#widthoriginal').val()+
+                "&height="+$('#height').val()+
+                "&heightoriginal="+$('#heightoriginal').val()+
+                "&rotate="+$('#rotate').val()+
+                "&croptop="+$('#croptop').val()+
+                "&cropleft="+$('#cropleft').val()+
+                "&cropright="+$('#cropright').val()+
+                "&cropbottom="+$('#cropbottom').val()+
+                "&contrast="+$('#contrast').val()+
+                "&brightness="+$('#brightness').val()+
+                "&actiontype="+$('#actiontype').val()+
+                "&panel="+$('#panel').val()+
+                "&language="+$('#language').val()+
+                "&actions="+$('#actions').val()+
+                "&widthlast="+$('#widthlast').val()+
+                "&heightlast="+$('#heightlast').val()+
+                "&userid="+$('#userid').val()+
+                "&contrastlast="+$('#contrastlast').val()+
+                "&brightnesslast="+$('#brightnesslast').val()+
+                "&widthlastbeforeresize="+$('#widthlastbeforeresize').val()+
+                "&heightlastbeforeresize="+$('#heightlastbeforeresize').val()+
+                "&cropkeepproportionsval="+$('#cropkeepproportionsval').val()+
+                "&cropkeepproportionsratio="+$('#cropkeepproportionsratio').val()+
+                "&isajaxpost=true",
+               success: function(data)
+               {       
+                       $('#phpImageEditor').html(data);
+                       activate_form();
+                       phpimageeditor_crop_activator(parseInt($('#panel').val()));
+                       phpimageeditor_init();
+                       $('#loading_bar').stop();
+                       $('#loading').css('display','none');
+               },
+           error: function(XMLHttpRequest, textStatus, errorThrown)
+           {
+                       activate_form();
+                       $('#ulJsErrors').html("");
+                       adderror(TextAnUnexpectedError);
+                       $('#divJsErrors').css('display','block');
+                       $('#ulJsErrors').css('display','block');
+                       phpimageeditor_crop_activator(parseInt($('#panel').val()));
+                       $('#loading_bar').stop();
+                       $('#loading').css('display','none');
+           }
+               });
+               
+       }
+}
+
+function isinteger(s)
+{
+    var i;
+
+    if (isempty(s))
+    if (isinteger.arguments.length == 1) return 0;
+    else return (isinteger.arguments[1] == true);
+
+    for (i = 0; i < s.length; i++)
+    {
+       var c = s.charAt(i);
+
+       if (!isdigit(c)) return false;
+    }
+
+    return true;
+}
+
+function focus_on_enter(element, event)
+{
+       if(event.keyCode == 13)
+               element.focus();
+}
+
+function reload_mouse_crop()
+{
+       objCrop.destroy();
+       objCrop = $.Jcrop('#image',{onChange: set_crop_values,onSelect: set_crop_values, aspectRatio: $("input#cropkeepproportions").attr('checked') ? $("#cropkeepproportionsratio").val() : 0});              
+       $(".jcrop-holder").css("display", "none");
+       $("#image").css("display", "block");                    
+}
+
+function update_width(InputWidth, EditForm)
+{
+       if (isinteger(InputWidth.val()))
+       {
+               var Width = parseInt($('#width').val());
+               var Height = parseInt($('#height').val());
+               
+               $('#image').css('width',Width+'px');
+               
+               if ($("input#keepproportions").attr('checked'))
+               {
+                       $('#image').css('height', get_proportional_height(Width, EditForm) + "px");
+                       $('#height').val(get_proportional_height(Width, EditForm));
+               }
+               
+               update_mouse_resizer();
+               reload_mouse_crop();
+       }
+}
+
+function update_mouse_resizer()
+{
+       $('#imageResizerKeepProportions').css('width',$('#width').val()+'px');  
+       $('#imageResizerKeepProportions').css('height',$('#height').val()+'px');
+       $('#imageResizerNoProportions').css('width',$('#width').val()+'px');    
+       $('#imageResizerNoProportions').css('height',$('#height').val()+'px');
+}
+
+function update_height(InputHeight, EditForm)
+{
+       if (isinteger(InputHeight.val()))
+       {
+               var Height = parseInt($('#height').val());
+               var Width = parseInt($('#width').val());
+               
+               $('#image').css('height',$('#height').val()+'px');
+               
+               if ($("input#keepproportions").attr('checked'))
+               {
+                       $('#image').css('width',get_proportional_width(Height, EditForm)+'px');
+                       $('#width').val(get_proportional_width(Height, EditForm));
+               }
+               
+               update_mouse_resizer();
+               reload_mouse_crop();
+       }
+}
+
+function isempty(s)
+{
+       return ((s == null) || (s.length == 0))
+}
+
+function isdigit (c)
+{
+       return ((c >= "0") && (c <= "9"))
+}
+
+function isintegerinrange(s, a, b)
+{   
+       if (isempty(s))
+         if (isintegerinrange.arguments.length == 1) return false;
+         else return (isintegerinrange.arguments[1] == true);
+
+      // Catch non-integer strings to avoid creating a NaN below,
+      // which isn't available on JavaScript 1.0 for Windows.
+      if (!isinteger(s, false)) return false;
+
+      // Now, explicitly change the type to integer via parseInt
+      // so that the comparison code below will work both on
+      // JavaScript 1.2 (which typechecks in equality comparisons)
+      // and JavaScript 1.1 and before (which doesn't).
+      var num = parseInt (s);
+      return ((num >= a) && (num <= b));
+}
+
+function validate_form()
+{
+       var sendForm = true;
+       
+       var width = $('#width').val();
+       var height = $('#height').val();
+       var cropleft = $('#cropleft').val();
+       var cropright = $('#cropright').val();
+       var croptop = $('#croptop').val();
+       var cropbottom = $('#cropbottom').val();
+    var imageName = $('#image_name').val();
+       
+       $('#divJsErrors').css('display','none');
+       $('#ulJsErrors').css('display','none');
+       
+       $('#ulJsErrors').html("");
+
+    if (imageName == "") {
+        adderror("\"Image Name\" is Required!" );
+        sendForm = false;
+    }
+
+       if (width == "")
+       {
+               adderror("\"" + TextWidth + "\" " + TextIsRequired)
+               sendForm = false;
+       }
+       
+       if (height == "")
+       {
+               adderror("\"" + TextHeight + "\" " + TextIsRequired)
+               sendForm = false;
+       }
+       
+       if (!sendForm)
+       {
+               $('#divJsErrors').css('display','block');
+               $('#ulJsErrors').css('display','block');
+
+               return sendForm;
+       }
+       
+       if (!isinteger(width))
+       {
+               adderror("\"" + TextWidth + "\" " + TextMustBeNumeric)
+               sendForm = false;
+       }
+       
+       if (!isinteger(height))
+       {
+               adderror("\"" + TextHeight + "\" " + TextMustBeNumeric)
+               sendForm = false;
+       }
+       
+       if (!sendForm)
+       {
+               $('#divJsErrors').css('display','block');
+               $('#ulJsErrors').css('display','block');
+               return sendForm;
+       }
+
+       width = parseInt(width);
+       height = parseInt(height);
+
+       if (!isintegerinrange(width, 1, ImageMaxWidth))
+       {
+               adderror("\"" + TextWidth + "\" " + TextNotInRange + ": 1 - " + ImageMaxWidth)
+               sendForm = false;
+       }
+       
+       if (!isintegerinrange(height, 1, ImageMaxHeight))
+       {
+               adderror("\"" + TextHeight + "\" " + TextNotInRange + ": 1 - " + ImageMaxHeight)
+               sendForm = false;
+       }
+               
+       if (!sendForm)
+       {
+               $('#divJsErrors').css('display','block');
+               $('#ulJsErrors').css('display','block');
+       }
+
+       return sendForm;
+}
+
+function adderror(ErrorText)
+{
+       $('#ulJsErrors').html($('#ulJsErrors').html()+'<li>'+ErrorText+'</li>');
+}
+
+function get_proportional_width(Height, EditForm)
+{
+       var HeightOriginal = parseInt($("#heightlastbeforeresize").val());
+       var WidthOriginal = parseInt($("#widthlastbeforeresize").val());
+       
+       return Math.round((WidthOriginal/HeightOriginal)*Height);
+}
+
+function get_proportional_height(Width, EditForm)
+{
+       var HeightOriginal = parseInt($("#heightlastbeforeresize").val());
+       var WidthOriginal = parseInt($("#widthlastbeforeresize").val());
+       
+       return Math.round((HeightOriginal/WidthOriginal)*Width);
+}
+
+function remove_px(Value)
+{
+       return Value.replace("px","");
+}
+
+function activate_form()
+{
+       $('#width').removeAttr('disabled');
+       $('#height').removeAttr('disabled');
+       $('#keepproportions').removeAttr('disabled');
+       $('#btnRotateLeft').removeAttr('disabled');
+       $('#btnRotateRight').removeAttr('disabled');
+       $('#croptop').removeAttr('disabled');
+       $('#cropbottom').removeAttr('disabled');
+       $('#cropleft').removeAttr('disabled');
+       $('#cropright').removeAttr('disabled');
+       $('#grayscale').removeAttr('disabled');
+       $('#btnupdate').removeAttr('disabled');
+       $('#btnsave').removeAttr('disabled');
+       $('#btnundo').removeAttr('disabled');
+       $('#brightness_slider_track').slider('enable');
+       $('#contrast_slider_track').slider('enable');
+       
+       if ($('#actions').val() == '')
+       {
+               $('#btnundo').attr('disabled','disabled');
+               $('#btnsave').attr('disabled','disabled');
+       }
+       else
+       {
+               $('#btnundo').removeAttr('disabled');
+               $('#btnsave').removeAttr('disabled');
+       }       
+}
+
+
+function set_crop_values(c)
+{
+       if (isinteger($("#height").val()) && isinteger($("#width").val()))
+       {
+               $("#croptop").val(c.y);
+               $("#cropbottom").val(parseInt($("#height").val()) - (c.y + c.h));
+               $("#cropleft").val(c.x);
+               $("#cropright").val(parseInt($("#width").val()) - (c.x + c.w));
+               $("#cropwidth").html(c.w);
+               $("#cropheight").html(c.h);
+       }
+}
+
+function phpimageeditor_resize_activator(selectedIndex)
+{
+       if (selectedIndex == MenuResize)
+       {
+               if ($('#keepproportionsval').val() == "1")
+               {
+                       $("#imageResizerKeepProportions").css("display", "block");
+                       $("#imageResizerNoProportions").css("display", "none");
+               }
+               else
+               {
+                       $("#imageResizerKeepProportions").css("display", "none");
+                       $("#imageResizerNoProportions").css("display", "block");
+               }
+       }
+       else
+       {
+               $("#imageResizerKeepProportions").css("display", "none");
+               $("#imageResizerNoProportions").css("display", "none");
+       }
+}
+
+function phpimageeditor_panelfade(selectedIndex)
+{
+       for (i = 0; i < 4; i++)
+       {
+               if ($('#panel_'+i) != null)
+               {
+                       $("#panel_"+i).css('opacity','0.0');
+       
+                       if (i == selectedIndex)
+                       {
+                               $("#menuitem_"+i).removeClass("not-selected");                          
+                               $("#menuitem_"+i).addClass("selected");
+                               $("#panel_"+i).css('display','block');
+                               $("#panel_"+i).fadeTo("normal", 1.0);
+                       }
+                       else
+                       {
+                               $("#menuitem_"+i).removeClass("selected");
+                               $("#menuitem_"+i).addClass("not-selected");                             
+                               $("#panel_"+i).css('display','none');
+                       }
+               }
+       }
+       
+       if (selectedIndex != MenuCrop)
+       {
+               $("#cropleft").val("0");
+               $("#cropright").val("0");
+               $("#croptop").val("0");
+               $("#cropbottom").val("0");
+               $("#cropwidth").html("0");
+               $("#cropheight").html("0");
+       }
+       
+       phpimageeditor_resize_activator(selectedIndex);
+       phpimageeditor_crop_activator(selectedIndex);
+               
+       $("#panel").val(selectedIndex);
+}
+
+function phpimageeditor_crop_activator(selectedIndex)
+{
+       if (objCrop != null)
+       {       
+               if (selectedIndex != MenuCrop)
+               {
+                       objCrop.release();
+                       objCrop.disable();
+                       $(".jcrop-holder").css("display", "none");
+                       $("#image").css("display", "block");
+               }
+               else
+               {
+                       objCrop.release();
+                       objCrop.enable();
+                       $(".jcrop-holder").css("display", "block");
+                       $("#image").css("display", "block");
+               }
+       }
+}
+
+function phpimageeditor_init()
+{
+    objCrop = $.Jcrop('#image',{onChange: set_crop_values,onSelect: set_crop_values, aspectRatio: $("input#cropkeepproportions").attr('checked') ? $("input#cropkeepproportionsratio").val() : 0});
+
+    $("#imageResizerKeepProportions").resizable(
+    {
+        aspectRatio: parseFloat($("input#widthlastbeforeresize").val()) / parseFloat($("input#heightlastbeforeresize").val()),
+        stop: function(event,ui)
+        {        
+            var resize_width = parseInt(remove_px($("#imageResizerKeepProportions").css("width")));
+            var resize_height = parseInt(remove_px($("#imageResizerKeepProportions").css("height")));
+                        
+            $("#image").css("width", resize_width+"px");
+            $("#image").css("height", resize_height+"px");
+            $("#width").val(resize_width);
+            $("#height").val(resize_height);
+
+            update_mouse_resizer();
+
+            $("#imageResizerKeepProportions").css("opacity", "0.0");
+                        
+            reload_mouse_crop();
+        },
+        start: function(event,ui)
+        {        
+            $("#imageResizerKeepProportions").css("opacity", "0.5");
+        }
+    });
+                
+    $("#imageResizerNoProportions").resizable(
+    {
+        aspectRatio: false,
+        stop: function(event,ui)
+        {        
+            var resize_width = parseInt(remove_px($("#imageResizerNoProportions").css("width")));
+            var resize_height = parseInt(remove_px($("#imageResizerNoProportions").css("height")));
+                        
+            $("#image").css("width", resize_width+"px");
+            $("#image").css("height", resize_height+"px");
+            $("#width").val(resize_width);
+            $("#height").val(resize_height);
+                        
+            update_mouse_resizer();
+
+            $("#widthlastbeforeresize").val(resize_width);
+            $("#heightlastbeforeresize").val(resize_height);
+            $("#imageResizerKeepProportions").resizable('option','aspectRatio',parseFloat($('#widthlastbeforeresize').val())/parseFloat($('#heightlastbeforeresize').val()));
+
+            $("#imageResizerNoProportions").css("opacity", "0.0");
+
+            reload_mouse_crop();
+        },
+        start: function(event,ui)
+        {        
+            $("#imageResizerNoProportions").css("opacity", "0.5");
+        }
+    });
+    
+    $("#contrast_slider_track").slider({value: parseInt($("#contrast").val()) + ContrastMax, min: 1, max: ((ContrastMax*2)+1), step: 1,
+       stop: function(event,ui)
+        {        
+            if (validate_form())
+                ajax_post();
+        },
+        slide: function(event, ui) 
+        {
+            $("#contrast").val(parseInt(ui.value)-((ContrastMax)+1));
+        }
+    });
+    
+    $("#brightness_slider_track").slider({value: (parseInt($("input#brightness").val())+BrightnessMax),min: 1,max: ((BrightnessMax*2)+1),step: 1,
+       stop: function(event,ui)
+        {        
+            if (validate_form())
+                ajax_post();
+        },
+        slide: function(event, ui) 
+        {
+            $("#brightness").val(parseInt(ui.value)-(BrightnessMax+1));
+        }
+    });
+                
+    $("#grayscale").click(function()
+    {
+        if (validate_form())
+        {
+            $("#grayscale").attr('checked') ? $('#grayscaleval').val('1') : $('#grayscaleval').val('0');
+            ajax_post();
+        }
+    });
+                
+    $("#btnupdate").click(function()
+    {
+        if (validate_form())
+        {
+               $("#actiontype").val(ActionUpdate);                                      
+            ajax_post();
+        }
+    });
+                
+    $("#btnundo").click(function()
+    {
+        if (validate_form())
+        {
+            $("#actiontype").val(ActionUndo);                                    
+            ajax_post();
+        }
+    });
+
+    $("#btnsave").click(function()
+    {
+        if (validate_form())
+        {
+            $("#actiontype").val(ActionSaveAndClose);                                    
+            ajax_post();
+        }
+    });
+
+    $("#btnRotateLeft").click(function()
+    {
+        if (validate_form())
+        {
+            $("#rotate").val(ActionRotateLeft);
+            ajax_post();
+        }
+    });
+
+    $("#btnRotateRight").click(function()
+    {
+        if (validate_form())
+        {
+            $("#rotate").val(ActionRotateRight);
+            ajax_post();
+        }
+    });
+                
+    $("form#" + FormId).submit(function()
+    {
+        if (validate_form())
+        {
+            $("#actiontype").val(ActionSaveAndClose);
+            return true;
+        }
+        return false;
+    });
+                
+    $("#width").keydown(function(event)
+    {
+        focus_on_enter($("input#btnupdate"), event);
+    });
+                
+    $("#width").keyup(function()
+    {
+        update_width($(this),$("form#" + FormId));
+    });
+                
+    $("#height").keydown(function(event)
+    {
+        focus_on_enter($("input#btnupdate"), event);
+    });
+                
+    $("#height").keyup(function()
+    {
+        update_height($(this),$("form#" + FormId));
+    });
+                
+    $("#keepproportions").click(function()
+    {
+        if ($(this).attr('checked'))
+        {
+            $('#keepproportionsval').val('1');
+            $('#imageResizerKeepProportions').css('display','block');
+            $('#imageResizerNoProportions').css('display','none');  
+        }
+        else
+        {
+            $('#keepproportionsval').val('0'); 
+            $('#imageResizerKeepProportions').css('display','none');
+            $('#imageResizerNoProportions').css('display','block'); 
+        }
+    });
+         
+
+    $("#cropkeepproportions").click(function()
+    {
+        if ($(this).attr('checked'))
+        {
+            $('#cropkeepproportionsval').val('1');
+        }
+        else
+        {
+            $('#cropkeepproportionsval').val('0'); 
+        }
+    });
+    
+    $("#menuitem_" + MenuResize).click(function()
+    {
+        if ($('#panel').val() != MenuResize)
+        {
+            phpimageeditor_panelfade(MenuResize);
+        }
+    });
+                
+   $("#menuitem_" + MenuRotate).click(function()
+   {
+       if ($('#panel').val() != MenuRotate)
+       {
+            phpimageeditor_panelfade(MenuRotate);
+        }
+    });
+            
+    $("#menuitem_" + MenuCrop).click(function()
+    {
+        if ($('#panel').val() != MenuCrop)
+        {
+            phpimageeditor_panelfade(MenuCrop);
+        }
+    });
+            
+    if ($("#menuitem_" + MenuEffects) != null)
+    {
+        $("#menuitem_" + MenuEffects).click(function()
+        {
+            if ($('#panel').val() != MenuEffects)
+            {
+                phpimageeditor_panelfade(MenuEffects);
+            }
+        });
+    }
+
+    $('#cropkeepproportions').change(function(e) 
+    {
+        if (objCrop != null)
+        {
+            if (this.checked && parseFloat($("#cropwidth").html()) != 0 && parseFloat($("#cropheight").html()) != 0)
+            {   
+                var aspectRatio = parseFloat($("#cropwidth").html()) / parseFloat($("#cropheight").html());
+                objCrop.setOptions({ aspectRatio: aspectRatio });
+                $("#cropkeepproportionsratio").val(aspectRatio);
+            }
+            else if (this.checked && parseFloat($("#cropwidth").html()) == 0 && parseFloat($("#cropheight").html()) == 0)
+            {
+                objCrop.setOptions({ aspectRatio: $("#cropkeepproportionsratio").val() });
+            }   
+            else
+            {   
+                objCrop.setOptions({ aspectRatio: 0 });
+            }
+        }
+    });
+
+    $("input#width").numeric();
+    $("input#height").numeric();
+                
+    var selectedIndex = parseInt($('#panel').val());
+    for (i = 0; i < 4; i++)
+    {
+        if ($('#panel_'+i) != null)
+        {
+            if (i == selectedIndex)
+            {
+                $("#panel_"+i).css('opacity','1.0');
+                $("#panel_"+i).css('display','block');
+            }
+            else
+            {
+                $("#panel_"+i).css('opacity','0.0');
+                $("#panel_"+i).css('display','none');
+            }
+        }
+    }   
+    
+    phpimageeditor_crop_activator(selectedIndex);
+    phpimageeditor_resize_activator(selectedIndex);
+}
+
+$(document).ready(function()
+{
+       phpimageeditor_init();
+});define('CKIMAGE_ORIGINAL', TOOLBOX_ORIGINAL);
+define('CKIMAGE_RESIZED', TOOLBOX_RESIZED);
+define('CKIMAGE_MIDSIZED', TOOLBOX_MIDSIZED);
+define('CKIMAGE_THUMB', TOOLBOX_THUMB);
\ No newline at end of file
diff --git a/Toolkit/PHPImageEditor/javascript/ui.core.js b/Toolkit/PHPImageEditor/javascript/ui.core.js
new file mode 100644 (file)
index 0000000..89300d9
--- /dev/null
@@ -0,0 +1,519 @@
+/*
+ * jQuery UI 1.7
+ *
+ * Copyright (c) 2009 AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT (MIT-LICENSE.txt)
+ * and GPL (GPL-LICENSE.txt) licenses.
+ *
+ * http://docs.jquery.com/UI
+ */
+;jQuery.ui || (function($) {
+
+var _remove = $.fn.remove,
+       isFF2 = $.browser.mozilla && (parseFloat($.browser.version) < 1.9);
+
+//Helper functions and ui object
+$.ui = {
+       version: "1.7",
+
+       // $.ui.plugin is deprecated.  Use the proxy pattern instead.
+       plugin: {
+               add: function(module, option, set) {
+                       var proto = $.ui[module].prototype;
+                       for(var i in set) {
+                               proto.plugins[i] = proto.plugins[i] || [];
+                               proto.plugins[i].push([option, set[i]]);
+                       }
+               },
+               call: function(instance, name, args) {
+                       var set = instance.plugins[name];
+                       if(!set || !instance.element[0].parentNode) { return; }
+
+                       for (var i = 0; i < set.length; i++) {
+                               if (instance.options[set[i][0]]) {
+                                       set[i][1].apply(instance.element, args);
+                               }
+                       }
+               }
+       },
+
+       contains: function(a, b) {
+               return document.compareDocumentPosition
+                       ? a.compareDocumentPosition(b) & 16
+                       : a !== b && a.contains(b);
+       },
+
+       hasScroll: function(el, a) {
+
+               //If overflow is hidden, the element might have extra content, but the user wants to hide it
+               if ($(el).css('overflow') == 'hidden') { return false; }
+
+               var scroll = (a && a == 'left') ? 'scrollLeft' : 'scrollTop',
+                       has = false;
+
+               if (el[scroll] > 0) { return true; }
+
+               // TODO: determine which cases actually cause this to happen
+               // if the element doesn't have the scroll set, see if it's possible to
+               // set the scroll
+               el[scroll] = 1;
+               has = (el[scroll] > 0);
+               el[scroll] = 0;
+               return has;
+       },
+
+       isOverAxis: function(x, reference, size) {
+               //Determines when x coordinate is over "b" element axis
+               return (x > reference) && (x < (reference + size));
+       },
+
+       isOver: function(y, x, top, left, height, width) {
+               //Determines when x, y coordinates is over "b" element
+               return $.ui.isOverAxis(y, top, height) && $.ui.isOverAxis(x, left, width);
+       },
+
+       keyCode: {
+               BACKSPACE: 8,
+               CAPS_LOCK: 20,
+               COMMA: 188,
+               CONTROL: 17,
+               DELETE: 46,
+               DOWN: 40,
+               END: 35,
+               ENTER: 13,
+               ESCAPE: 27,
+               HOME: 36,
+               INSERT: 45,
+               LEFT: 37,
+               NUMPAD_ADD: 107,
+               NUMPAD_DECIMAL: 110,
+               NUMPAD_DIVIDE: 111,
+               NUMPAD_ENTER: 108,
+               NUMPAD_MULTIPLY: 106,
+               NUMPAD_SUBTRACT: 109,
+               PAGE_DOWN: 34,
+               PAGE_UP: 33,
+               PERIOD: 190,
+               RIGHT: 39,
+               SHIFT: 16,
+               SPACE: 32,
+               TAB: 9,
+               UP: 38
+       }
+};
+
+// WAI-ARIA normalization
+if (isFF2) {
+       var attr = $.attr,
+               removeAttr = $.fn.removeAttr,
+               ariaNS = "http://www.w3.org/2005/07/aaa",
+               ariaState = /^aria-/,
+               ariaRole = /^wairole:/;
+
+       $.attr = function(elem, name, value) {
+               var set = value !== undefined;
+
+               return (name == 'role'
+                       ? (set
+                               ? attr.call(this, elem, name, "wairole:" + value)
+                               : (attr.apply(this, arguments) || "").replace(ariaRole, ""))
+                       : (ariaState.test(name)
+                               ? (set
+                                       ? elem.setAttributeNS(ariaNS,
+                                               name.replace(ariaState, "aaa:"), value)
+                                       : attr.call(this, elem, name.replace(ariaState, "aaa:")))
+                               : attr.apply(this, arguments)));
+       };
+
+       $.fn.removeAttr = function(name) {
+               return (ariaState.test(name)
+                       ? this.each(function() {
+                               this.removeAttributeNS(ariaNS, name.replace(ariaState, ""));
+                       }) : removeAttr.call(this, name));
+       };
+}
+
+//jQuery plugins
+$.fn.extend({
+       remove: function() {
+               // Safari has a native remove event which actually removes DOM elements,
+               // so we have to use triggerHandler instead of trigger (#3037).
+               $("*", this).add(this).each(function() {
+                       $(this).triggerHandler("remove");
+               });
+               return _remove.apply(this, arguments );
+       },
+
+       enableSelection: function() {
+               return this
+                       .attr('unselectable', 'off')
+                       .css('MozUserSelect', '')
+                       .unbind('selectstart.ui');
+       },
+
+       disableSelection: function() {
+               return this
+                       .attr('unselectable', 'on')
+                       .css('MozUserSelect', 'none')
+                       .bind('selectstart.ui', function() { return false; });
+       },
+
+       scrollParent: function() {
+               var scrollParent;
+               if(($.browser.msie && (/(static|relative)/).test(this.css('position'))) || (/absolute/).test(this.css('position'))) {
+                       scrollParent = this.parents().filter(function() {
+                               return (/(relative|absolute|fixed)/).test($.curCSS(this,'position',1)) && (/(auto|scroll)/).test($.curCSS(this,'overflow',1)+$.curCSS(this,'overflow-y',1)+$.curCSS(this,'overflow-x',1));
+                       }).eq(0);
+               } else {
+                       scrollParent = this.parents().filter(function() {
+                               return (/(auto|scroll)/).test($.curCSS(this,'overflow',1)+$.curCSS(this,'overflow-y',1)+$.curCSS(this,'overflow-x',1));
+                       }).eq(0);
+               }
+
+               return (/fixed/).test(this.css('position')) || !scrollParent.length ? $(document) : scrollParent;
+       }
+});
+
+
+//Additional selectors
+$.extend($.expr[':'], {
+       data: function(elem, i, match) {
+               return !!$.data(elem, match[3]);
+       },
+
+       focusable: function(element) {
+               var nodeName = element.nodeName.toLowerCase(),
+                       tabIndex = $.attr(element, 'tabindex');
+               return (/input|select|textarea|button|object/.test(nodeName)
+                       ? !element.disabled
+                       : 'a' == nodeName || 'area' == nodeName
+                               ? element.href || !isNaN(tabIndex)
+                               : !isNaN(tabIndex))
+                       // the element and all of its ancestors must be visible
+                       // the browser may report that the area is hidden
+                       && !$(element)['area' == nodeName ? 'parents' : 'closest'](':hidden').length;
+       },
+
+       tabbable: function(element) {
+               var tabIndex = $.attr(element, 'tabindex');
+               return (isNaN(tabIndex) || tabIndex >= 0) && $(element).is(':focusable');
+       }
+});
+
+
+// $.widget is a factory to create jQuery plugins
+// taking some boilerplate code out of the plugin code
+function getter(namespace, plugin, method, args) {
+       function getMethods(type) {
+               var methods = $[namespace][plugin][type] || [];
+               return (typeof methods == 'string' ? methods.split(/,?\s+/) : methods);
+       }
+
+       var methods = getMethods('getter');
+       if (args.length == 1 && typeof args[0] == 'string') {
+               methods = methods.concat(getMethods('getterSetter'));
+       }
+       return ($.inArray(method, methods) != -1);
+}
+
+$.widget = function(name, prototype) {
+       var namespace = name.split(".")[0];
+       name = name.split(".")[1];
+
+       // create plugin method
+       $.fn[name] = function(options) {
+               var isMethodCall = (typeof options == 'string'),
+                       args = Array.prototype.slice.call(arguments, 1);
+
+               // prevent calls to internal methods
+               if (isMethodCall && options.substring(0, 1) == '_') {
+                       return this;
+               }
+
+               // handle getter methods
+               if (isMethodCall && getter(namespace, name, options, args)) {
+                       var instance = $.data(this[0], name);
+                       return (instance ? instance[options].apply(instance, args)
+                               : undefined);
+               }
+
+               // handle initialization and non-getter methods
+               return this.each(function() {
+                       var instance = $.data(this, name);
+
+                       // constructor
+                       (!instance && !isMethodCall &&
+                               $.data(this, name, new $[namespace][name](this, options))._init());
+
+                       // method call
+                       (instance && isMethodCall && $.isFunction(instance[options]) &&
+                               instance[options].apply(instance, args));
+               });
+       };
+
+       // create widget constructor
+       $[namespace] = $[namespace] || {};
+       $[namespace][name] = function(element, options) {
+               var self = this;
+
+               this.namespace = namespace;
+               this.widgetName = name;
+               this.widgetEventPrefix = $[namespace][name].eventPrefix || name;
+               this.widgetBaseClass = namespace + '-' + name;
+
+               this.options = $.extend({},
+                       $.widget.defaults,
+                       $[namespace][name].defaults,
+                       $.metadata && $.metadata.get(element)[name],
+                       options);
+
+               this.element = $(element)
+                       .bind('setData.' + name, function(event, key, value) {
+                               if (event.target == element) {
+                                       return self._setData(key, value);
+                               }
+                       })
+                       .bind('getData.' + name, function(event, key) {
+                               if (event.target == element) {
+                                       return self._getData(key);
+                               }
+                       })
+                       .bind('remove', function() {
+                               return self.destroy();
+                       });
+       };
+
+       // add widget prototype
+       $[namespace][name].prototype = $.extend({}, $.widget.prototype, prototype);
+
+       // TODO: merge getter and getterSetter properties from widget prototype
+       // and plugin prototype
+       $[namespace][name].getterSetter = 'option';
+};
+
+$.widget.prototype = {
+       _init: function() {},
+       destroy: function() {
+               this.element.removeData(this.widgetName)
+                       .removeClass(this.widgetBaseClass + '-disabled' + ' ' + this.namespace + '-state-disabled')
+                       .removeAttr('aria-disabled');
+       },
+
+       option: function(key, value) {
+               var options = key,
+                       self = this;
+
+               if (typeof key == "string") {
+                       if (value === undefined) {
+                               return this._getData(key);
+                       }
+                       options = {};
+                       options[key] = value;
+               }
+
+               $.each(options, function(key, value) {
+                       self._setData(key, value);
+               });
+       },
+       _getData: function(key) {
+               return this.options[key];
+       },
+       _setData: function(key, value) {
+               this.options[key] = value;
+
+               if (key == 'disabled') {
+                       this.element
+                               [value ? 'addClass' : 'removeClass'](
+                                       this.widgetBaseClass + '-disabled' + ' ' +
+                                       this.namespace + '-state-disabled')
+                               .attr("aria-disabled", value);
+               }
+       },
+
+       enable: function() {
+               this._setData('disabled', false);
+       },
+       disable: function() {
+               this._setData('disabled', true);
+       },
+
+       _trigger: function(type, event, data) {
+               var callback = this.options[type],
+                       eventName = (type == this.widgetEventPrefix
+                               ? type : this.widgetEventPrefix + type);
+
+               event = $.Event(event);
+               event.type = eventName;
+
+               // copy original event properties over to the new event
+               // this would happen if we could call $.event.fix instead of $.Event
+               // but we don't have a way to force an event to be fixed multiple times
+               if (event.originalEvent) {
+                       for (var i = $.event.props.length, prop; i;) {
+                               prop = $.event.props[--i];
+                               event[prop] = event.originalEvent[prop];
+                       }
+               }
+
+               this.element.trigger(event, data);
+
+               return !($.isFunction(callback) && callback.call(this.element[0], event, data) === false
+                       || event.isDefaultPrevented());
+       }
+};
+
+$.widget.defaults = {
+       disabled: false
+};
+
+
+/** Mouse Interaction Plugin **/
+
+$.ui.mouse = {
+       _mouseInit: function() {
+               var self = this;
+
+               this.element
+                       .bind('mousedown.'+this.widgetName, function(event) {
+                               return self._mouseDown(event);
+                       })
+                       .bind('click.'+this.widgetName, function(event) {
+                               if(self._preventClickEvent) {
+                                       self._preventClickEvent = false;
+                                       event.stopImmediatePropagation();
+                                       return false;
+                               }
+                       });
+
+               // Prevent text selection in IE
+               if ($.browser.msie) {
+                       this._mouseUnselectable = this.element.attr('unselectable');
+                       this.element.attr('unselectable', 'on');
+               }
+
+               this.started = false;
+       },
+
+       // TODO: make sure destroying one instance of mouse doesn't mess with
+       // other instances of mouse
+       _mouseDestroy: function() {
+               this.element.unbind('.'+this.widgetName);
+
+               // Restore text selection in IE
+               ($.browser.msie
+                       && this.element.attr('unselectable', this._mouseUnselectable));
+       },
+
+       _mouseDown: function(event) {
+               // don't let more than one widget handle mouseStart
+               // TODO: figure out why we have to use originalEvent
+               event.originalEvent = event.originalEvent || {};
+               if (event.originalEvent.mouseHandled) { return; }
+
+               // we may have missed mouseup (out of window)
+               (this._mouseStarted && this._mouseUp(event));
+
+               this._mouseDownEvent = event;
+
+               var self = this,
+                       btnIsLeft = (event.which == 1),
+                       elIsCancel = (typeof this.options.cancel == "string" ? $(event.target).parents().add(event.target).filter(this.options.cancel).length : false);
+               if (!btnIsLeft || elIsCancel || !this._mouseCapture(event)) {
+                       return true;
+               }
+
+               this.mouseDelayMet = !this.options.delay;
+               if (!this.mouseDelayMet) {
+                       this._mouseDelayTimer = setTimeout(function() {
+                               self.mouseDelayMet = true;
+                       }, this.options.delay);
+               }
+
+               if (this._mouseDistanceMet(event) && this._mouseDelayMet(event)) {
+                       this._mouseStarted = (this._mouseStart(event) !== false);
+                       if (!this._mouseStarted) {
+                               event.preventDefault();
+                               return true;
+                       }
+               }
+
+               // these delegates are required to keep context
+               this._mouseMoveDelegate = function(event) {
+                       return self._mouseMove(event);
+               };
+               this._mouseUpDelegate = function(event) {
+                       return self._mouseUp(event);
+               };
+               $(document)
+                       .bind('mousemove.'+this.widgetName, this._mouseMoveDelegate)
+                       .bind('mouseup.'+this.widgetName, this._mouseUpDelegate);
+
+               // preventDefault() is used to prevent the selection of text here -
+               // however, in Safari, this causes select boxes not to be selectable
+               // anymore, so this fix is needed
+               ($.browser.safari || event.preventDefault());
+
+               event.originalEvent.mouseHandled = true;
+               return true;
+       },
+
+       _mouseMove: function(event) {
+               // IE mouseup check - mouseup happened when mouse was out of window
+               if ($.browser.msie && !event.button) {
+                       return this._mouseUp(event);
+               }
+
+               if (this._mouseStarted) {
+                       this._mouseDrag(event);
+                       return event.preventDefault();
+               }
+
+               if (this._mouseDistanceMet(event) && this._mouseDelayMet(event)) {
+                       this._mouseStarted =
+                               (this._mouseStart(this._mouseDownEvent, event) !== false);
+                       (this._mouseStarted ? this._mouseDrag(event) : this._mouseUp(event));
+               }
+
+               return !this._mouseStarted;
+       },
+
+       _mouseUp: function(event) {
+               $(document)
+                       .unbind('mousemove.'+this.widgetName, this._mouseMoveDelegate)
+                       .unbind('mouseup.'+this.widgetName, this._mouseUpDelegate);
+
+               if (this._mouseStarted) {
+                       this._mouseStarted = false;
+                       this._preventClickEvent = (event.target == this._mouseDownEvent.target);
+                       this._mouseStop(event);
+               }
+
+               return false;
+       },
+
+       _mouseDistanceMet: function(event) {
+               return (Math.max(
+                               Math.abs(this._mouseDownEvent.pageX - event.pageX),
+                               Math.abs(this._mouseDownEvent.pageY - event.pageY)
+                       ) >= this.options.distance
+               );
+       },
+
+       _mouseDelayMet: function(event) {
+               return this.mouseDelayMet;
+       },
+
+       // These are placeholder methods, to be overriden by extending plugin
+       _mouseStart: function(event) {},
+       _mouseDrag: function(event) {},
+       _mouseStop: function(event) {},
+       _mouseCapture: function(event) { return true; }
+};
+
+$.ui.mouse.defaults = {
+       cancel: null,
+       distance: 1,
+       delay: 0
+};
+
+})(jQuery);
diff --git a/Toolkit/PHPImageEditor/javascript/ui.resizable.js b/Toolkit/PHPImageEditor/javascript/ui.resizable.js
new file mode 100644 (file)
index 0000000..0e4b894
--- /dev/null
@@ -0,0 +1,800 @@
+/*
+ * jQuery UI Resizable 1.7
+ *
+ * Copyright (c) 2009 AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT (MIT-LICENSE.txt)
+ * and GPL (GPL-LICENSE.txt) licenses.
+ *
+ * http://docs.jquery.com/UI/Resizables
+ *
+ * Depends:
+ *     ui.core.js
+ */
+(function($) {
+
+$.widget("ui.resizable", $.extend({}, $.ui.mouse, {
+
+       _init: function() {
+
+               var self = this, o = this.options;
+               this.element.addClass("ui-resizable");
+
+               $.extend(this, {
+                       _aspectRatio: !!(o.aspectRatio),
+                       aspectRatio: o.aspectRatio,
+                       originalElement: this.element,
+                       _proportionallyResizeElements: [],
+                       _helper: o.helper || o.ghost || o.animate ? o.helper || 'ui-resizable-helper' : null
+               });
+
+               //Wrap the element if it cannot hold child nodes
+               if(this.element[0].nodeName.match(/canvas|textarea|input|select|button|img/i)) {
+
+                       //Opera fix for relative positioning
+                       if (/relative/.test(this.element.css('position')) && $.browser.opera)
+                               this.element.css({ position: 'relative', top: 'auto', left: 'auto' });
+
+                       //Create a wrapper element and set the wrapper to the new current internal element
+                       this.element.wrap(
+                               $('<div class="ui-wrapper" style="overflow: hidden;"></div>').css({
+                                       position: this.element.css('position'),
+                                       width: this.element.outerWidth(),
+                                       height: this.element.outerHeight(),
+                                       top: this.element.css('top'),
+                                       left: this.element.css('left')
+                               })
+                       );
+
+                       //Overwrite the original this.element
+                       this.element = this.element.parent().data(
+                               "resizable", this.element.data('resizable')
+                       );
+
+                       this.elementIsWrapper = true;
+
+                       //Move margins to the wrapper
+                       this.element.css({ marginLeft: this.originalElement.css("marginLeft"), marginTop: this.originalElement.css("marginTop"), marginRight: this.originalElement.css("marginRight"), marginBottom: this.originalElement.css("marginBottom") });
+                       this.originalElement.css({ marginLeft: 0, marginTop: 0, marginRight: 0, marginBottom: 0});
+
+                       //Prevent Safari textarea resize
+                       this.originalResizeStyle = this.originalElement.css('resize');
+                       this.originalElement.css('resize', 'none');
+
+                       //Push the actual element to our proportionallyResize internal array
+                       this._proportionallyResizeElements.push(this.originalElement.css({ position: 'static', zoom: 1, display: 'block' }));
+
+                       // avoid IE jump (hard set the margin)
+                       this.originalElement.css({ margin: this.originalElement.css('margin') });
+
+                       // fix handlers offset
+                       this._proportionallyResize();
+
+               }
+
+               this.handles = o.handles || (!$('.ui-resizable-handle', this.element).length ? "e,s,se" : { n: '.ui-resizable-n', e: '.ui-resizable-e', s: '.ui-resizable-s', w: '.ui-resizable-w', se: '.ui-resizable-se', sw: '.ui-resizable-sw', ne: '.ui-resizable-ne', nw: '.ui-resizable-nw' });
+               if(this.handles.constructor == String) {
+
+                       if(this.handles == 'all') this.handles = 'n,e,s,w,se,sw,ne,nw';
+                       var n = this.handles.split(","); this.handles = {};
+
+                       for(var i = 0; i < n.length; i++) {
+
+                               var handle = $.trim(n[i]), hname = 'ui-resizable-'+handle;
+                               var axis = $('<div class="ui-resizable-handle ' + hname + '"></div>');
+
+                               // increase zIndex of sw, se, ne, nw axis
+                               //TODO : this modifies original option
+                               if(/sw|se|ne|nw/.test(handle)) axis.css({ zIndex: ++o.zIndex });
+
+                               //TODO : What's going on here?
+                               if ('se' == handle) {
+                                       axis.addClass('ui-icon ui-icon-gripsmall-diagonal-se');
+                               };
+
+                               //Insert into internal handles object and append to element
+                               this.handles[handle] = '.ui-resizable-'+handle;
+                               this.element.append(axis);
+                       }
+
+               }
+
+               this._renderAxis = function(target) {
+
+                       target = target || this.element;
+
+                       for(var i in this.handles) {
+
+                               if(this.handles[i].constructor == String)
+                                       this.handles[i] = $(this.handles[i], this.element).show();
+
+                               //Apply pad to wrapper element, needed to fix axis position (textarea, inputs, scrolls)
+                               if (this.elementIsWrapper && this.originalElement[0].nodeName.match(/textarea|input|select|button/i)) {
+
+                                       var axis = $(this.handles[i], this.element), padWrapper = 0;
+
+                                       //Checking the correct pad and border
+                                       padWrapper = /sw|ne|nw|se|n|s/.test(i) ? axis.outerHeight() : axis.outerWidth();
+
+                                       //The padding type i have to apply...
+                                       var padPos = [ 'padding',
+                                               /ne|nw|n/.test(i) ? 'Top' :
+                                               /se|sw|s/.test(i) ? 'Bottom' :
+                                               /^e$/.test(i) ? 'Right' : 'Left' ].join("");
+
+                                       target.css(padPos, padWrapper);
+
+                                       this._proportionallyResize();
+
+                               }
+
+                               //TODO: What's that good for? There's not anything to be executed left
+                               if(!$(this.handles[i]).length)
+                                       continue;
+
+                       }
+               };
+
+               //TODO: make renderAxis a prototype function
+               this._renderAxis(this.element);
+
+               this._handles = $('.ui-resizable-handle', this.element)
+                       .disableSelection();
+
+               //Matching axis name
+               this._handles.mouseover(function() {
+                       if (!self.resizing) {
+                               if (this.className)
+                                       var axis = this.className.match(/ui-resizable-(se|sw|ne|nw|n|e|s|w)/i);
+                               //Axis, default = se
+                               self.axis = axis && axis[1] ? axis[1] : 'se';
+                       }
+               });
+
+               //If we want to auto hide the elements
+               if (o.autoHide) {
+                       this._handles.hide();
+                       $(this.element)
+                               .addClass("ui-resizable-autohide")
+                               .hover(function() {
+                                       $(this).removeClass("ui-resizable-autohide");
+                                       self._handles.show();
+                               },
+                               function(){
+                                       if (!self.resizing) {
+                                               $(this).addClass("ui-resizable-autohide");
+                                               self._handles.hide();
+                                       }
+                               });
+               }
+
+               //Initialize the mouse interaction
+               this._mouseInit();
+
+       },
+
+       destroy: function() {
+
+               this._mouseDestroy();
+
+               var _destroy = function(exp) {
+                       $(exp).removeClass("ui-resizable ui-resizable-disabled ui-resizable-resizing")
+                               .removeData("resizable").unbind(".resizable").find('.ui-resizable-handle').remove();
+               };
+
+               //TODO: Unwrap at same DOM position
+               if (this.elementIsWrapper) {
+                       _destroy(this.element);
+                       var wrapper = this.element;
+                       wrapper.parent().append(
+                               this.originalElement.css({
+                                       position: wrapper.css('position'),
+                                       width: wrapper.outerWidth(),
+                                       height: wrapper.outerHeight(),
+                                       top: wrapper.css('top'),
+                                       left: wrapper.css('left')
+                               })
+                       ).end().remove();
+               }
+
+               this.originalElement.css('resize', this.originalResizeStyle);
+               _destroy(this.originalElement);
+
+       },
+
+       _mouseCapture: function(event) {
+
+               var handle = false;
+               for(var i in this.handles) {
+                       if($(this.handles[i])[0] == event.target) handle = true;
+               }
+
+               return this.options.disabled || !!handle;
+
+       },
+
+       _mouseStart: function(event) {
+
+               var o = this.options, iniPos = this.element.position(), el = this.element;
+
+               this.resizing = true;
+               this.documentScroll = { top: $(document).scrollTop(), left: $(document).scrollLeft() };
+
+               // bugfix for http://dev.jquery.com/ticket/1749
+               if (el.is('.ui-draggable') || (/absolute/).test(el.css('position'))) {
+                       el.css({ position: 'absolute', top: iniPos.top, left: iniPos.left });
+               }
+
+               //Opera fixing relative position
+               if ($.browser.opera && (/relative/).test(el.css('position')))
+                       el.css({ position: 'relative', top: 'auto', left: 'auto' });
+
+               this._renderProxy();
+
+               var curleft = num(this.helper.css('left')), curtop = num(this.helper.css('top'));
+
+               if (o.containment) {
+                       curleft += $(o.containment).scrollLeft() || 0;
+                       curtop += $(o.containment).scrollTop() || 0;
+               }
+
+               //Store needed variables
+               this.offset = this.helper.offset();
+               this.position = { left: curleft, top: curtop };
+               this.size = this._helper ? { width: el.outerWidth(), height: el.outerHeight() } : { width: el.width(), height: el.height() };
+               this.originalSize = this._helper ? { width: el.outerWidth(), height: el.outerHeight() } : { width: el.width(), height: el.height() };
+               this.originalPosition = { left: curleft, top: curtop };
+               this.sizeDiff = { width: el.outerWidth() - el.width(), height: el.outerHeight() - el.height() };
+               this.originalMousePosition = { left: event.pageX, top: event.pageY };
+
+               //Aspect Ratio
+               this.aspectRatio = (typeof o.aspectRatio == 'number') ? o.aspectRatio : ((this.originalSize.width / this.originalSize.height) || 1);
+
+           var cursor = $('.ui-resizable-' + this.axis).css('cursor');
+           $('body').css('cursor', cursor == 'auto' ? this.axis + '-resize' : cursor);
+
+               el.addClass("ui-resizable-resizing");
+               this._propagate("start", event);
+               return true;
+       },
+
+       _mouseDrag: function(event) {
+
+               //Increase performance, avoid regex
+               var el = this.helper, o = this.options, props = {},
+                       self = this, smp = this.originalMousePosition, a = this.axis;
+
+               var dx = (event.pageX-smp.left)||0, dy = (event.pageY-smp.top)||0;
+               var trigger = this._change[a];
+               if (!trigger) return false;
+
+               // Calculate the attrs that will be change
+               var data = trigger.apply(this, [event, dx, dy]), ie6 = $.browser.msie && $.browser.version < 7, csdif = this.sizeDiff;
+
+               if (this._aspectRatio || event.shiftKey)
+                       data = this._updateRatio(data, event);
+
+               data = this._respectSize(data, event);
+
+               // plugins callbacks need to be called first
+               this._propagate("resize", event);
+
+               el.css({
+                       top: this.position.top + "px", left: this.position.left + "px",
+                       width: this.size.width + "px", height: this.size.height + "px"
+               });
+
+               if (!this._helper && this._proportionallyResizeElements.length)
+                       this._proportionallyResize();
+
+               this._updateCache(data);
+
+               // calling the user callback at the end
+               this._trigger('resize', event, this.ui());
+
+               return false;
+       },
+
+       _mouseStop: function(event) {
+
+               this.resizing = false;
+               var o = this.options, self = this;
+
+               if(this._helper) {
+                       var pr = this._proportionallyResizeElements, ista = pr.length && (/textarea/i).test(pr[0].nodeName),
+                                               soffseth = ista && $.ui.hasScroll(pr[0], 'left') /* TODO - jump height */ ? 0 : self.sizeDiff.height,
+                                                       soffsetw = ista ? 0 : self.sizeDiff.width;
+
+                       var s = { width: (self.size.width - soffsetw), height: (self.size.height - soffseth) },
+                               left = (parseInt(self.element.css('left'), 10) + (self.position.left - self.originalPosition.left)) || null,
+                               top = (parseInt(self.element.css('top'), 10) + (self.position.top - self.originalPosition.top)) || null;
+
+                       if (!o.animate)
+                               this.element.css($.extend(s, { top: top, left: left }));
+
+                       self.helper.height(self.size.height);
+                       self.helper.width(self.size.width);
+
+                       if (this._helper && !o.animate) this._proportionallyResize();
+               }
+
+               $('body').css('cursor', 'auto');
+
+               this.element.removeClass("ui-resizable-resizing");
+
+               this._propagate("stop", event);
+
+               if (this._helper) this.helper.remove();
+               return false;
+
+       },
+
+       _updateCache: function(data) {
+               var o = this.options;
+               this.offset = this.helper.offset();
+               if (isNumber(data.left)) this.position.left = data.left;
+               if (isNumber(data.top)) this.position.top = data.top;
+               if (isNumber(data.height)) this.size.height = data.height;
+               if (isNumber(data.width)) this.size.width = data.width;
+       },
+
+       _updateRatio: function(data, event) {
+
+               var o = this.options, cpos = this.position, csize = this.size, a = this.axis;
+
+               if (data.height) data.width = (csize.height * this.aspectRatio);
+               else if (data.width) data.height = (csize.width / this.aspectRatio);
+
+               if (a == 'sw') {
+                       data.left = cpos.left + (csize.width - data.width);
+                       data.top = null;
+               }
+               if (a == 'nw') {
+                       data.top = cpos.top + (csize.height - data.height);
+                       data.left = cpos.left + (csize.width - data.width);
+               }
+
+               return data;
+       },
+
+       _respectSize: function(data, event) {
+
+               var el = this.helper, o = this.options, pRatio = this._aspectRatio || event.shiftKey, a = this.axis,
+                               ismaxw = isNumber(data.width) && o.maxWidth && (o.maxWidth < data.width), ismaxh = isNumber(data.height) && o.maxHeight && (o.maxHeight < data.height),
+                                       isminw = isNumber(data.width) && o.minWidth && (o.minWidth > data.width), isminh = isNumber(data.height) && o.minHeight && (o.minHeight > data.height);
+
+               if (isminw) data.width = o.minWidth;
+               if (isminh) data.height = o.minHeight;
+               if (ismaxw) data.width = o.maxWidth;
+               if (ismaxh) data.height = o.maxHeight;
+
+               var dw = this.originalPosition.left + this.originalSize.width, dh = this.position.top + this.size.height;
+               var cw = /sw|nw|w/.test(a), ch = /nw|ne|n/.test(a);
+
+               if (isminw && cw) data.left = dw - o.minWidth;
+               if (ismaxw && cw) data.left = dw - o.maxWidth;
+               if (isminh && ch)       data.top = dh - o.minHeight;
+               if (ismaxh && ch)       data.top = dh - o.maxHeight;
+
+               // fixing jump error on top/left - bug #2330
+               var isNotwh = !data.width && !data.height;
+               if (isNotwh && !data.left && data.top) data.top = null;
+               else if (isNotwh && !data.top && data.left) data.left = null;
+
+               return data;
+       },
+
+       _proportionallyResize: function() {
+
+               var o = this.options;
+               if (!this._proportionallyResizeElements.length) return;
+               var element = this.helper || this.element;
+
+               for (var i=0; i < this._proportionallyResizeElements.length; i++) {
+
+                       var prel = this._proportionallyResizeElements[i];
+
+                       if (!this.borderDif) {
+                               var b = [prel.css('borderTopWidth'), prel.css('borderRightWidth'), prel.css('borderBottomWidth'), prel.css('borderLeftWidth')],
+                                       p = [prel.css('paddingTop'), prel.css('paddingRight'), prel.css('paddingBottom'), prel.css('paddingLeft')];
+
+                               this.borderDif = $.map(b, function(v, i) {
+                                       var border = parseInt(v,10)||0, padding = parseInt(p[i],10)||0;
+                                       return border + padding;
+                               });
+                       }
+
+                       if ($.browser.msie && !(!($(element).is(':hidden') || $(element).parents(':hidden').length)))
+                               continue;
+
+                       prel.css({
+                               height: (element.height() - this.borderDif[0] - this.borderDif[2]) || 0,
+                               width: (element.width() - this.borderDif[1] - this.borderDif[3]) || 0
+                       });
+
+               };
+
+       },
+
+       _renderProxy: function() {
+
+               var el = this.element, o = this.options;
+               this.elementOffset = el.offset();
+
+               if(this._helper) {
+
+                       this.helper = this.helper || $('<div style="overflow:hidden;"></div>');
+
+                       // fix ie6 offset TODO: This seems broken
+                       var ie6 = $.browser.msie && $.browser.version < 7, ie6offset = (ie6 ? 1 : 0),
+                       pxyoffset = ( ie6 ? 2 : -1 );
+
+                       this.helper.addClass(this._helper).css({
+                               width: this.element.outerWidth() + pxyoffset,
+                               height: this.element.outerHeight() + pxyoffset,
+                               position: 'absolute',
+                               left: this.elementOffset.left - ie6offset +'px',
+                               top: this.elementOffset.top - ie6offset +'px',
+                               zIndex: ++o.zIndex //TODO: Don't modify option
+                       });
+
+                       this.helper
+                               .appendTo("body")
+                               .disableSelection();
+
+               } else {
+                       this.helper = this.element;
+               }
+
+       },
+
+       _change: {
+               e: function(event, dx, dy) {
+                       return { width: this.originalSize.width + dx };
+               },
+               w: function(event, dx, dy) {
+                       var o = this.options, cs = this.originalSize, sp = this.originalPosition;
+                       return { left: sp.left + dx, width: cs.width - dx };
+               },
+               n: function(event, dx, dy) {
+                       var o = this.options, cs = this.originalSize, sp = this.originalPosition;
+                       return { top: sp.top + dy, height: cs.height - dy };
+               },
+               s: function(event, dx, dy) {
+                       return { height: this.originalSize.height + dy };
+               },
+               se: function(event, dx, dy) {
+                       return $.extend(this._change.s.apply(this, arguments), this._change.e.apply(this, [event, dx, dy]));
+               },
+               sw: function(event, dx, dy) {
+                       return $.extend(this._change.s.apply(this, arguments), this._change.w.apply(this, [event, dx, dy]));
+               },
+               ne: function(event, dx, dy) {
+                       return $.extend(this._change.n.apply(this, arguments), this._change.e.apply(this, [event, dx, dy]));
+               },
+               nw: function(event, dx, dy) {
+                       return $.extend(this._change.n.apply(this, arguments), this._change.w.apply(this, [event, dx, dy]));
+               }
+       },
+
+       _propagate: function(n, event) {
+               $.ui.plugin.call(this, n, [event, this.ui()]);
+               (n != "resize" && this._trigger(n, event, this.ui()));
+       },
+
+       plugins: {},
+
+       ui: function() {
+               return {
+                       originalElement: this.originalElement,
+                       element: this.element,
+                       helper: this.helper,
+                       position: this.position,
+                       size: this.size,
+                       originalSize: this.originalSize,
+                       originalPosition: this.originalPosition
+               };
+       }
+
+}));
+
+$.extend($.ui.resizable, {
+       version: "1.7",
+       eventPrefix: "resize",
+       defaults: {
+               alsoResize: false,
+               animate: false,
+               animateDuration: "slow",
+               animateEasing: "swing",
+               aspectRatio: false,
+               autoHide: false,
+               cancel: ":input,option",
+               containment: false,
+               delay: 0,
+               distance: 1,
+               ghost: false,
+               grid: false,
+               handles: "e,s,se",
+               helper: false,
+               maxHeight: null,
+               maxWidth: null,
+               minHeight: 10,
+               minWidth: 10,
+               zIndex: 1000
+       }
+});
+
+/*
+ * Resizable Extensions
+ */
+
+$.ui.plugin.add("resizable", "alsoResize", {
+
+       start: function(event, ui) {
+
+               var self = $(this).data("resizable"), o = self.options;
+
+               _store = function(exp) {
+                       $(exp).each(function() {
+                               $(this).data("resizable-alsoresize", {
+                                       width: parseInt($(this).width(), 10), height: parseInt($(this).height(), 10),
+                                       left: parseInt($(this).css('left'), 10), top: parseInt($(this).css('top'), 10)
+                               });
+                       });
+               };
+
+               if (typeof(o.alsoResize) == 'object' && !o.alsoResize.parentNode) {
+                       if (o.alsoResize.length) { o.alsoResize = o.alsoResize[0];      _store(o.alsoResize); }
+                       else { $.each(o.alsoResize, function(exp, c) { _store(exp); }); }
+               }else{
+                       _store(o.alsoResize);
+               }
+       },
+
+       resize: function(event, ui){
+               var self = $(this).data("resizable"), o = self.options, os = self.originalSize, op = self.originalPosition;
+
+               var delta = {
+                       height: (self.size.height - os.height) || 0, width: (self.size.width - os.width) || 0,
+                       top: (self.position.top - op.top) || 0, left: (self.position.left - op.left) || 0
+               },
+
+               _alsoResize = function(exp, c) {
+                       $(exp).each(function() {
+                               var el = $(this), start = $(this).data("resizable-alsoresize"), style = {}, css = c && c.length ? c : ['width', 'height', 'top', 'left'];
+
+                               $.each(css || ['width', 'height', 'top', 'left'], function(i, prop) {
+                                       var sum = (start[prop]||0) + (delta[prop]||0);
+                                       if (sum && sum >= 0)
+                                               style[prop] = sum || null;
+                               });
+
+                               //Opera fixing relative position
+                               if (/relative/.test(el.css('position')) && $.browser.opera) {
+                                       self._revertToRelativePosition = true;
+                                       el.css({ position: 'absolute', top: 'auto', left: 'auto' });
+                               }
+
+                               el.css(style);
+                       });
+               };
+
+               if (typeof(o.alsoResize) == 'object' && !o.alsoResize.nodeType) {
+                       $.each(o.alsoResize, function(exp, c) { _alsoResize(exp, c); });
+               }else{
+                       _alsoResize(o.alsoResize);
+               }
+       },
+
+       stop: function(event, ui){
+               var self = $(this).data("resizable");
+
+               //Opera fixing relative position
+               if (self._revertToRelativePosition && $.browser.opera) {
+                       self._revertToRelativePosition = false;
+                       el.css({ position: 'relative' });
+               }
+
+               $(this).removeData("resizable-alsoresize-start");
+       }
+});
+
+$.ui.plugin.add("resizable", "animate", {
+
+       stop: function(event, ui) {
+               var self = $(this).data("resizable"), o = self.options;
+
+               var pr = self._proportionallyResizeElements, ista = pr.length && (/textarea/i).test(pr[0].nodeName),
+                                       soffseth = ista && $.ui.hasScroll(pr[0], 'left') /* TODO - jump height */ ? 0 : self.sizeDiff.height,
+                                               soffsetw = ista ? 0 : self.sizeDiff.width;
+
+               var style = { width: (self.size.width - soffsetw), height: (self.size.height - soffseth) },
+                                       left = (parseInt(self.element.css('left'), 10) + (self.position.left - self.originalPosition.left)) || null,
+                                               top = (parseInt(self.element.css('top'), 10) + (self.position.top - self.originalPosition.top)) || null;
+
+               self.element.animate(
+                       $.extend(style, top && left ? { top: top, left: left } : {}), {
+                               duration: o.animateDuration,
+                               easing: o.animateEasing,
+                               step: function() {
+
+                                       var data = {
+                                               width: parseInt(self.element.css('width'), 10),
+                                               height: parseInt(self.element.css('height'), 10),
+                                               top: parseInt(self.element.css('top'), 10),
+                                               left: parseInt(self.element.css('left'), 10)
+                                       };
+
+                                       if (pr && pr.length) $(pr[0]).css({ width: data.width, height: data.height });
+
+                                       // propagating resize, and updating values for each animation step
+                                       self._updateCache(data);
+                                       self._propagate("resize", event);
+
+                               }
+                       }
+               );
+       }
+
+});
+
+$.ui.plugin.add("resizable", "containment", {
+
+       start: function(event, ui) {
+               var self = $(this).data("resizable"), o = self.options, el = self.element;
+               var oc = o.containment, ce = (oc instanceof $) ? oc.get(0) : (/parent/.test(oc)) ? el.parent().get(0) : oc;
+               if (!ce) return;
+
+               self.containerElement = $(ce);
+
+               if (/document/.test(oc) || oc == document) {
+                       self.containerOffset = { left: 0, top: 0 };
+                       self.containerPosition = { left: 0, top: 0 };
+
+                       self.parentData = {
+                               element: $(document), left: 0, top: 0,
+                               width: $(document).width(), height: $(document).height() || document.body.parentNode.scrollHeight
+                       };
+               }
+
+               // i'm a node, so compute top, left, right, bottom
+               else {
+                       var element = $(ce), p = [];
+                       $([ "Top", "Right", "Left", "Bottom" ]).each(function(i, name) { p[i] = num(element.css("padding" + name)); });
+
+                       self.containerOffset = element.offset();
+                       self.containerPosition = element.position();
+                       self.containerSize = { height: (element.innerHeight() - p[3]), width: (element.innerWidth() - p[1]) };
+
+                       var co = self.containerOffset, ch = self.containerSize.height,  cw = self.containerSize.width,
+                                               width = ($.ui.hasScroll(ce, "left") ? ce.scrollWidth : cw ), height = ($.ui.hasScroll(ce) ? ce.scrollHeight : ch);
+
+                       self.parentData = {
+                               element: ce, left: co.left, top: co.top, width: width, height: height
+                       };
+               }
+       },
+
+       resize: function(event, ui) {
+               var self = $(this).data("resizable"), o = self.options,
+                               ps = self.containerSize, co = self.containerOffset, cs = self.size, cp = self.position,
+                               pRatio = o._aspectRatio || event.shiftKey, cop = { top:0, left:0 }, ce = self.containerElement;
+
+               if (ce[0] != document && (/static/).test(ce.css('position'))) cop = co;
+
+               if (cp.left < (self._helper ? co.left : 0)) {
+                       self.size.width = self.size.width + (self._helper ? (self.position.left - co.left) : (self.position.left - cop.left));
+                       if (pRatio) self.size.height = self.size.width / o.aspectRatio;
+                       self.position.left = o.helper ? co.left : 0;
+               }
+
+               if (cp.top < (self._helper ? co.top : 0)) {
+                       self.size.height = self.size.height + (self._helper ? (self.position.top - co.top) : self.position.top);
+                       if (pRatio) self.size.width = self.size.height * o.aspectRatio;
+                       self.position.top = self._helper ? co.top : 0;
+               }
+
+               self.offset.left = self.parentData.left+self.position.left;
+               self.offset.top = self.parentData.top+self.position.top;
+
+               var woset = Math.abs( (self._helper ? self.offset.left - cop.left : (self.offset.left - cop.left)) + self.sizeDiff.width ),
+                                       hoset = Math.abs( (self._helper ? self.offset.top - cop.top : (self.offset.top - co.top)) + self.sizeDiff.height );
+
+               var isParent = self.containerElement.get(0) == self.element.parent().get(0),
+                   isOffsetRelative = /relative|absolute/.test(self.containerElement.css('position'));
+
+               if(isParent && isOffsetRelative) woset -= self.parentData.left;
+
+               if (woset + self.size.width >= self.parentData.width) {
+                       self.size.width = self.parentData.width - woset;
+                       if (pRatio) self.size.height = self.size.width / o.aspectRatio;
+               }
+
+               if (hoset + self.size.height >= self.parentData.height) {
+                       self.size.height = self.parentData.height - hoset;
+                       if (pRatio) self.size.width = self.size.height * o.aspectRatio;
+               }
+       },
+
+       stop: function(event, ui){
+               var self = $(this).data("resizable"), o = self.options, cp = self.position,
+                               co = self.containerOffset, cop = self.containerPosition, ce = self.containerElement;
+
+               var helper = $(self.helper), ho = helper.offset(), w = helper.outerWidth() - self.sizeDiff.width, h = helper.outerHeight() - self.sizeDiff.height;
+
+               if (self._helper && !o.animate && (/relative/).test(ce.css('position')))
+                       $(this).css({ left: ho.left - cop.left - co.left, width: w, height: h });
+
+               if (self._helper && !o.animate && (/static/).test(ce.css('position')))
+                       $(this).css({ left: ho.left - cop.left - co.left, width: w, height: h });
+
+       }
+});
+
+$.ui.plugin.add("resizable", "ghost", {
+
+       start: function(event, ui) {
+
+               var self = $(this).data("resizable"), o = self.options, cs = self.size;
+
+               self.ghost = self.originalElement.clone();
+               self.ghost
+                       .css({ opacity: .25, display: 'block', position: 'relative', height: cs.height, width: cs.width, margin: 0, left: 0, top: 0 })
+                       .addClass('ui-resizable-ghost')
+                       .addClass(typeof o.ghost == 'string' ? o.ghost : '');
+
+               self.ghost.appendTo(self.helper);
+
+       },
+
+       resize: function(event, ui){
+               var self = $(this).data("resizable"), o = self.options;
+               if (self.ghost) self.ghost.css({ position: 'relative', height: self.size.height, width: self.size.width });
+       },
+
+       stop: function(event, ui){
+               var self = $(this).data("resizable"), o = self.options;
+               if (self.ghost && self.helper) self.helper.get(0).removeChild(self.ghost.get(0));
+       }
+
+});
+
+$.ui.plugin.add("resizable", "grid", {
+
+       resize: function(event, ui) {
+               var self = $(this).data("resizable"), o = self.options, cs = self.size, os = self.originalSize, op = self.originalPosition, a = self.axis, ratio = o._aspectRatio || event.shiftKey;
+               o.grid = typeof o.grid == "number" ? [o.grid, o.grid] : o.grid;
+               var ox = Math.round((cs.width - os.width) / (o.grid[0]||1)) * (o.grid[0]||1), oy = Math.round((cs.height - os.height) / (o.grid[1]||1)) * (o.grid[1]||1);
+
+               if (/^(se|s|e)$/.test(a)) {
+                       self.size.width = os.width + ox;
+                       self.size.height = os.height + oy;
+               }
+               else if (/^(ne)$/.test(a)) {
+                       self.size.width = os.width + ox;
+                       self.size.height = os.height + oy;
+                       self.position.top = op.top - oy;
+               }
+               else if (/^(sw)$/.test(a)) {
+                       self.size.width = os.width + ox;
+                       self.size.height = os.height + oy;
+                       self.position.left = op.left - ox;
+               }
+               else {
+                       self.size.width = os.width + ox;
+                       self.size.height = os.height + oy;
+                       self.position.top = op.top - oy;
+                       self.position.left = op.left - ox;
+               }
+       }
+
+});
+
+var num = function(v) {
+       return parseInt(v, 10) || 0;
+};
+
+var isNumber = function(value) {
+       return !isNaN(parseInt(value, 10));
+};
+
+})(jQuery);
diff --git a/Toolkit/PHPImageEditor/javascript/ui.slider.js b/Toolkit/PHPImageEditor/javascript/ui.slider.js
new file mode 100644 (file)
index 0000000..a7b4d0c
--- /dev/null
@@ -0,0 +1,511 @@
+/*
+ * jQuery UI Slider 1.7
+ *
+ * Copyright (c) 2009 AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT (MIT-LICENSE.txt)
+ * and GPL (GPL-LICENSE.txt) licenses.
+ *
+ * http://docs.jquery.com/UI/Slider
+ *
+ * Depends:
+ *     ui.core.js
+ */
+
+(function($) {
+
+$.widget("ui.slider", $.extend({}, $.ui.mouse, {
+
+       _init: function() {
+
+               var self = this, o = this.options;
+               this._keySliding = false;
+               this._handleIndex = null;
+               this._detectOrientation();
+               this._mouseInit();
+
+               this.element
+                       .addClass("ui-slider"
+                               + " ui-slider-" + this.orientation
+                               + " ui-widget"
+                               + " ui-widget-content"
+                               + " ui-corner-all");
+
+               this.range = $([]);
+
+               if (o.range) {
+
+                       if (o.range === true) {
+                               this.range = $('<div></div>');
+                               if (!o.values) o.values = [this._valueMin(), this._valueMin()];
+                               if (o.values.length && o.values.length != 2) {
+                                       o.values = [o.values[0], o.values[0]];
+                               }
+                       } else {
+                               this.range = $('<div></div>');
+                       }
+
+                       this.range
+                               .appendTo(this.element)
+                               .addClass("ui-slider-range");
+
+                       if (o.range == "min" || o.range == "max") {
+                               this.range.addClass("ui-slider-range-" + o.range);
+                       }
+
+                       // note: this isn't the most fittingly semantic framework class for this element,
+                       // but worked best visually with a variety of themes
+                       this.range.addClass("ui-widget-header");
+
+               }
+
+               if ($(".ui-slider-handle", this.element).length == 0)
+                       $('<a href="#"></a>')
+                               .appendTo(this.element)
+                               .addClass("ui-slider-handle");
+
+               if (o.values && o.values.length) {
+                       while ($(".ui-slider-handle", this.element).length < o.values.length)
+                               $('<a href="#"></a>')
+                                       .appendTo(this.element)
+                                       .addClass("ui-slider-handle");
+               }
+
+               this.handles = $(".ui-slider-handle", this.element)
+                       .addClass("ui-state-default"
+                               + " ui-corner-all");
+
+               this.handle = this.handles.eq(0);
+
+               this.handles.add(this.range).filter("a")
+                       .click(function(event) { event.preventDefault(); })
+                       .hover(function() { $(this).addClass('ui-state-hover'); }, function() { $(this).removeClass('ui-state-hover'); })
+                       .focus(function() { $(".ui-slider .ui-state-focus").removeClass('ui-state-focus'); $(this).addClass('ui-state-focus'); })
+                       .blur(function() { $(this).removeClass('ui-state-focus'); });
+
+               this.handles.each(function(i) {
+                       $(this).data("index.ui-slider-handle", i);
+               });
+
+               this.handles.keydown(function(event) {
+
+                       var ret = true;
+
+                       var index = $(this).data("index.ui-slider-handle");
+
+                       if (self.options.disabled)
+                               return;
+
+                       switch (event.keyCode) {
+                               case $.ui.keyCode.HOME:
+                               case $.ui.keyCode.END:
+                               case $.ui.keyCode.UP:
+                               case $.ui.keyCode.RIGHT:
+                               case $.ui.keyCode.DOWN:
+                               case $.ui.keyCode.LEFT:
+                                       ret = false;
+                                       if (!self._keySliding) {
+                                               self._keySliding = true;
+                                               $(this).addClass("ui-state-active");
+                                               self._start(event, index);
+                                       }
+                                       break;
+                       }
+
+                       var curVal, newVal, step = self._step();
+                       if (self.options.values && self.options.values.length) {
+                               curVal = newVal = self.values(index);
+                       } else {
+                               curVal = newVal = self.value();
+                       }
+
+                       switch (event.keyCode) {
+                               case $.ui.keyCode.HOME:
+                                       newVal = self._valueMin();
+                                       break;
+                               case $.ui.keyCode.END:
+                                       newVal = self._valueMax();
+                                       break;
+                               case $.ui.keyCode.UP:
+                               case $.ui.keyCode.RIGHT:
+                                       if(curVal == self._valueMax()) return;
+                                       newVal = curVal + step;
+                                       break;
+                               case $.ui.keyCode.DOWN:
+                               case $.ui.keyCode.LEFT:
+                                       if(curVal == self._valueMin()) return;
+                                       newVal = curVal - step;
+                                       break;
+                       }
+
+                       self._slide(event, index, newVal);
+
+                       return ret;
+
+               }).keyup(function(event) {
+
+                       var index = $(this).data("index.ui-slider-handle");
+
+                       if (self._keySliding) {
+                               self._stop(event, index);
+                               self._change(event, index);
+                               self._keySliding = false;
+                               $(this).removeClass("ui-state-active");
+                       }
+
+               });
+
+               this._refreshValue();
+
+       },
+
+       destroy: function() {
+
+               this.handles.remove();
+
+               this.element
+                       .removeClass("ui-slider"
+                               + " ui-slider-horizontal"
+                               + " ui-slider-vertical"
+                               + " ui-slider-disabled"
+                               + " ui-widget"
+                               + " ui-widget-content"
+                               + " ui-corner-all")
+                       .removeData("slider")
+                       .unbind(".slider");
+
+               this._mouseDestroy();
+
+       },
+
+       _mouseCapture: function(event) {
+
+               var o = this.options;
+
+               if (o.disabled)
+                       return false;
+
+               this.elementSize = {
+                       width: this.element.outerWidth(),
+                       height: this.element.outerHeight()
+               };
+               this.elementOffset = this.element.offset();
+
+               var position = { x: event.pageX, y: event.pageY };
+               var normValue = this._normValueFromMouse(position);
+
+               var distance = this._valueMax() + 1, closestHandle;
+               var self = this, index;
+               this.handles.each(function(i) {
+                       var thisDistance = Math.abs(normValue - self.values(i));
+                       if (distance > thisDistance) {
+                               distance = thisDistance;
+                               closestHandle = $(this);
+                               index = i;
+                       }
+               });
+
+               // workaround for bug #3736 (if both handles of a range are at 0,
+               // the first is always used as the one with least distance,
+               // and moving it is obviously prevented by preventing negative ranges)
+               if(o.range == true && this.values(1) == o.min) {
+                       closestHandle = $(this.handles[++index]);
+               }
+
+               this._start(event, index);
+
+               self._handleIndex = index;
+
+               closestHandle
+                       .addClass("ui-state-active")
+                       .focus();
+               
+               var offset = closestHandle.offset();
+               var mouseOverHandle = !$(event.target).parents().andSelf().is('.ui-slider-handle');
+               this._clickOffset = mouseOverHandle ? { left: 0, top: 0 } : {
+                       left: event.pageX - offset.left - (closestHandle.width() / 2),
+                       top: event.pageY - offset.top
+                               - (closestHandle.height() / 2)
+                               - (parseInt(closestHandle.css('borderTopWidth'),10) || 0)
+                               - (parseInt(closestHandle.css('borderBottomWidth'),10) || 0)
+                               + (parseInt(closestHandle.css('marginTop'),10) || 0)
+               };
+
+               normValue = this._normValueFromMouse(position);
+               this._slide(event, index, normValue);
+               return true;
+
+       },
+
+       _mouseStart: function(event) {
+               return true;
+       },
+
+       _mouseDrag: function(event) {
+
+               var position = { x: event.pageX, y: event.pageY };
+               var normValue = this._normValueFromMouse(position);
+               
+               this._slide(event, this._handleIndex, normValue);
+
+               return false;
+
+       },
+
+       _mouseStop: function(event) {
+
+               this.handles.removeClass("ui-state-active");
+               this._stop(event, this._handleIndex);
+               this._change(event, this._handleIndex);
+               this._handleIndex = null;
+               this._clickOffset = null;
+
+               return false;
+
+       },
+       
+       _detectOrientation: function() {
+               this.orientation = this.options.orientation == 'vertical' ? 'vertical' : 'horizontal';
+       },
+
+       _normValueFromMouse: function(position) {
+
+               var pixelTotal, pixelMouse;
+               if ('horizontal' == this.orientation) {
+                       pixelTotal = this.elementSize.width;
+                       pixelMouse = position.x - this.elementOffset.left - (this._clickOffset ? this._clickOffset.left : 0);
+               } else {
+                       pixelTotal = this.elementSize.height;
+                       pixelMouse = position.y - this.elementOffset.top - (this._clickOffset ? this._clickOffset.top : 0);
+               }
+
+               var percentMouse = (pixelMouse / pixelTotal);
+               if (percentMouse > 1) percentMouse = 1;
+               if (percentMouse < 0) percentMouse = 0;
+               if ('vertical' == this.orientation)
+                       percentMouse = 1 - percentMouse;
+
+               var valueTotal = this._valueMax() - this._valueMin(),
+                       valueMouse = percentMouse * valueTotal,
+                       valueMouseModStep = valueMouse % this.options.step,
+                       normValue = this._valueMin() + valueMouse - valueMouseModStep;
+
+               if (valueMouseModStep > (this.options.step / 2))
+                       normValue += this.options.step;
+
+               // Since JavaScript has problems with large floats, round
+               // the final value to 5 digits after the decimal point (see #4124)
+               return parseFloat(normValue.toFixed(5));
+
+       },
+
+       _start: function(event, index) {
+               this._trigger("start", event, this._uiHash(index));
+       },
+
+       _slide: function(event, index, newVal) {
+
+               var handle = this.handles[index];
+
+               if (this.options.values && this.options.values.length) {
+
+                       var otherVal = this.values(index ? 0 : 1);
+
+                       if ((index == 0 && newVal >= otherVal) || (index == 1 && newVal <= otherVal))
+                               newVal = otherVal;
+
+                       if (newVal != this.values(index)) {
+                               var newValues = this.values();
+                               newValues[index] = newVal;
+                               // A slide can be canceled by returning false from the slide callback
+                               var allowed = this._trigger("slide", event, this._uiHash(index, newVal, newValues));
+                               var otherVal = this.values(index ? 0 : 1);
+                               if (allowed !== false) {
+                                       this.values(index, newVal, ( event.type == 'mousedown' && this.options.animate ), true);
+                               }
+                       }
+
+               } else {
+
+                       if (newVal != this.value()) {
+                               // A slide can be canceled by returning false from the slide callback
+                               var allowed = this._trigger("slide", event, this._uiHash(index, newVal));
+                               if (allowed !== false) {
+                                       this._setData('value', newVal, ( event.type == 'mousedown' && this.options.animate ));
+                               }
+                                       
+                       }
+
+               }
+
+       },
+
+       _stop: function(event, index) {
+               this._trigger("stop", event, this._uiHash(index));
+       },
+
+       _change: function(event, index) {
+               this._trigger("change", event, this._uiHash(index));
+       },
+
+       value: function(newValue) {
+
+               if (arguments.length) {
+                       this._setData("value", newValue);
+                       this._change(null, 0);
+               }
+
+               return this._value();
+
+       },
+
+       values: function(index, newValue, animated, noPropagation) {
+
+               if (arguments.length > 1) {
+                       this.options.values[index] = newValue;
+                       this._refreshValue(animated);
+                       if(!noPropagation) this._change(null, index);
+               }
+
+               if (arguments.length) {
+                       if (this.options.values && this.options.values.length) {
+                               return this._values(index);
+                       } else {
+                               return this.value();
+                       }
+               } else {
+                       return this._values();
+               }
+
+       },
+
+       _setData: function(key, value, animated) {
+
+               $.widget.prototype._setData.apply(this, arguments);
+
+               switch (key) {
+                       case 'orientation':
+
+                               this._detectOrientation();
+                               
+                               this.element
+                                       .removeClass("ui-slider-horizontal ui-slider-vertical")
+                                       .addClass("ui-slider-" + this.orientation);
+                               this._refreshValue(animated);
+                               break;
+                       case 'value':
+                               this._refreshValue(animated);
+                               break;
+               }
+
+       },
+
+       _step: function() {
+               var step = this.options.step;
+               return step;
+       },
+
+       _value: function() {
+
+               var val = this.options.value;
+               if (val < this._valueMin()) val = this._valueMin();
+               if (val > this._valueMax()) val = this._valueMax();
+
+               return val;
+
+       },
+
+       _values: function(index) {
+
+               if (arguments.length) {
+                       var val = this.options.values[index];
+                       if (val < this._valueMin()) val = this._valueMin();
+                       if (val > this._valueMax()) val = this._valueMax();
+
+                       return val;
+               } else {
+                       return this.options.values;
+               }
+
+       },
+
+       _valueMin: function() {
+               var valueMin = this.options.min;
+               return valueMin;
+       },
+
+       _valueMax: function() {
+               var valueMax = this.options.max;
+               return valueMax;
+       },
+
+       _refreshValue: function(animate) {
+
+               var oRange = this.options.range, o = this.options, self = this;
+
+               if (this.options.values && this.options.values.length) {
+                       var vp0, vp1;
+                       this.handles.each(function(i, j) {
+                               var valPercent = (self.values(i) - self._valueMin()) / (self._valueMax() - self._valueMin()) * 100;
+                               var _set = {}; _set[self.orientation == 'horizontal' ? 'left' : 'bottom'] = valPercent + '%';
+                               $(this).stop(1,1)[animate ? 'animate' : 'css'](_set, o.animate);
+                               if (self.options.range === true) {
+                                       if (self.orientation == 'horizontal') {
+                                               (i == 0) && self.range.stop(1,1)[animate ? 'animate' : 'css']({ left: valPercent + '%' }, o.animate);
+                                               (i == 1) && self.range[animate ? 'animate' : 'css']({ width: (valPercent - lastValPercent) + '%' }, { queue: false, duration: o.animate });
+                                       } else {
+                                               (i == 0) && self.range.stop(1,1)[animate ? 'animate' : 'css']({ bottom: (valPercent) + '%' }, o.animate);
+                                               (i == 1) && self.range[animate ? 'animate' : 'css']({ height: (valPercent - lastValPercent) + '%' }, { queue: false, duration: o.animate });
+                                       }
+                               }
+                               lastValPercent = valPercent;
+                       });
+               } else {
+                       var value = this.value(),
+                               valueMin = this._valueMin(),
+                               valueMax = this._valueMax(),
+                               valPercent = valueMax != valueMin
+                                       ? (value - valueMin) / (valueMax - valueMin) * 100
+                                       : 0;
+                       var _set = {}; _set[self.orientation == 'horizontal' ? 'left' : 'bottom'] = valPercent + '%';
+                       this.handle.stop(1,1)[animate ? 'animate' : 'css'](_set, o.animate);
+
+                       (oRange == "min") && (this.orientation == "horizontal") && this.range.stop(1,1)[animate ? 'animate' : 'css']({ width: valPercent + '%' }, o.animate);
+                       (oRange == "max") && (this.orientation == "horizontal") && this.range[animate ? 'animate' : 'css']({ width: (100 - valPercent) + '%' }, { queue: false, duration: o.animate });
+                       (oRange == "min") && (this.orientation == "vertical") && this.range.stop(1,1)[animate ? 'animate' : 'css']({ height: valPercent + '%' }, o.animate);
+                       (oRange == "max") && (this.orientation == "vertical") && this.range[animate ? 'animate' : 'css']({ height: (100 - valPercent) + '%' }, { queue: false, duration: o.animate });
+               }
+
+       },
+       
+       _uiHash: function(index, value, values) {
+               
+               var multiple = this.options.values && this.options.values.length;
+               return {
+                       handle: this.handles[index],
+                       value: value || (multiple ? this.values(index) : this.value()),
+                       values: values || (multiple && this.values())
+               };
+
+       }
+
+}));
+
+$.extend($.ui.slider, {
+       getter: "value values",
+       version: "1.7",
+       eventPrefix: "slide",
+       defaults: {
+               animate: false,
+               delay: 0,
+               distance: 0,
+               max: 100,
+               min: 0,
+               orientation: 'horizontal',
+               range: false,
+               step: 1,
+               value: 0,
+               values: null
+       }
+});
+
+})(jQuery);
diff --git a/Toolkit/PHPImageEditor/language/en-GB/en-GB.com_admin.ini b/Toolkit/PHPImageEditor/language/en-GB/en-GB.com_admin.ini
new file mode 100644 (file)
index 0000000..0217e7e
--- /dev/null
@@ -0,0 +1,37 @@
+#PHP IMAGE EDITOR TEXTS
+EDIT IMAGE=Edit Image
+RESIZE IMAGE=Resize Image
+WIDTH=Width
+HEIGHT=Height
+KEEP PROPORTIONS=Keep Proportions
+ROTATE IMAGE=Rotate Image
+LEFT 90 DEGREES=Left 90 Degrees
+RIGHT 90 DEGREES=Right 90 Degrees
+CROP IMAGE=Crop Image
+EFFECTS=Effects
+SELECT EFFECT=Select Effect
+SAVE AND CLOSE=Save and Close
+GRAYSCALE=Grayscale
+CONTRAST=Contrast
+BRIGHTNESS=Brightness                  
+DARKER=Darker
+IS REQUIRED=is required
+MUST BE NUMERIC=must be numeric
+NOT NEGATIVE=must be a positive number
+NOT IN RANGE=is not in valid range
+CANT BE LARGER THEN=can´t be larger then
+NO PROVIDED IMAGE=No image has been provided.
+IMAGE DOES NOT EXIST=The image doesn´t exist.
+INVALID IMAGE TYPE=Image must be of type jpeg, png or gif.
+OLD PHP VERSION=is too old php version. Minimum is:
+OLD GD VERSION=is too old GD version. Minimum is:
+UNDO=Undo
+UPDATE=Update
+LOADING=Loading
+AN UNEXPECTED ERROR=An unexpected error has occured, please try again...
+RESIZE HELP=Update Width and Height fields.<br/>Or drag and drop in the right or bottom side of the image.
+CROP HELP=Drag and drop to create a crop area on the image.
+CROP WIDTH=Crop Width
+CROP HEIGHT=Crop Height
+CROP KEEP PROPORTIONS=Keep Crop Proportions
+INSTRUCTIONS=Instructions
\ No newline at end of file
diff --git a/Toolkit/Page.php b/Toolkit/Page.php
new file mode 100755 (executable)
index 0000000..3972e47
--- /dev/null
@@ -0,0 +1,932 @@
+<?php
+
+/**
+ * Page.php
+ *
+ * PHP version 5
+ *
+ * All rights reserved.
+ *
+ * @category  Toolkit
+ * @package   Page
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2009 Gaslight Media
+ * @license   http://demo.gaslightmedia.com Gaslight Media
+ * @version   CVS: $Id: Page.php,v 1.80 2010/08/16 17:39:58 jamie Exp $
+ * @link      http://demo.gaslightmedia.com
+ */
+/**
+ * Page for Error Doc
+ */
+define("ERROR_DOCUMENT", "404.html");
+
+/**
+ * The home page template
+ */
+define("HOME_TEMPLATE", "template.html");
+
+/**
+ * inside page template
+ */
+define("INSIDE_TEMPLATE", "template.html");
+
+/**
+ * Template for the error doc
+ */
+define("ERROR_DOCUMENT_TEMPLATE", "404-template.html");
+
+/**
+ * page title default
+ */
+define("PAGE_TITLE", SITENAME);
+
+/**
+ * Toolkit_Page
+ *
+ * A class Object for use with merging into the flexy template for output.
+ * Sets up the $page object used for merging with the flexy template
+ * to output the toolbox and members admin content
+ *
+ * @category  Toolkit
+ * @package   Page
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2009 Gaslight Media
+ * @license   http://demo.gaslightmedia.com Gaslight Media
+ * @link      http://demo.gaslightmedia.com
+ */
+class Toolkit_Page
+{
+
+    /**
+     * used on img src and style href tags
+     * @var    $baseURL
+     * @access public
+     */
+    public $mediaBaseURL;
+
+    /**
+     * used in href's
+     * @var    $baseURL
+     * @access public
+     */
+    public $baseURLDefined;
+
+    /**
+     * meta tag description element
+     * @var    $metaTags
+     * @access public
+     */
+    public $metaTags;
+
+    /**
+     * title tag node
+     * @var    string
+     * @access public
+     */
+    public $pageTitle;
+
+    /**
+     * The main content of website
+     * @var    $toolboxContent
+     * @access public
+     */
+    public $toolboxContent;
+    private $_breadCrumbs;
+    private $_toolboxPage;
+    private $_pageGateway;
+    private $_paragraphGateway;
+    private $_navigationFactory;
+    private $_keywordReplacement;
+    private $_catid;
+
+    /**
+     * Creates objects for class Page
+     *
+     * @param Toolkit_Template_Page                  $toolboxPage             Toolbox Page
+     * @param Toolkit_BreadCrumbsFactory             $breadCrumbsFactory      BreadCrumbes
+     * @param Toolkit_Toolbox_GatewayFactoryAbstract $pageGatewayFactory      Page Gateway
+     * @param Toolkit_Toolbox_GatewayFactoryAbstract $paragraphGatewayFactory Paragraph
+     * @param Toolkit_NavigationFactoryAbstract      $navFactory              Navigation
+     * @param Toolkit_Template_KeywordReplacement    $keywordReplacement      Keyword replacements
+     * @param type                                   $catid                   catid
+     *
+     * @return void
+     */
+    public function __construct(
+        Toolkit_Template_Page $toolboxPage,
+        Toolkit_BreadCrumbsFactory $breadCrumbsFactory,
+        Toolkit_Toolbox_GatewayFactoryAbstract $pageGatewayFactory,
+        Toolkit_Toolbox_GatewayFactoryAbstract $paragraphGatewayFactory,
+        Toolkit_NavigationFactoryAbstract $navFactory,
+        Toolkit_Template_KeywordReplacement $keywordReplacement, $catid
+    ) {
+        $this->_toolboxPage = $toolboxPage;
+        $this->_breadCrumbs = $breadCrumbsFactory;
+        $this->_pageGateway = $pageGatewayFactory->createGateway();
+        $this->_paragraphGateway = $paragraphGatewayFactory->createGateway();
+        $this->_navigationFactory = $navFactory;
+        $this->_keywordReplacement = $keywordReplacement;
+        $this->_catid = $catid;
+
+        // determine the base url to use (for images and stylesheet urls)
+        $this->mediaBaseURL = MEDIA_BASE_URL;
+
+        $this->baseURLDefined = BASE_URL;
+
+        // determine the base url to use (for MEDIA_APP_BASE_URL)
+        $this->glmAppBaseURL = MEDIA_APP_BASE_URL;
+
+        $this->sitemapURL = $this->BaseURLDefined . 'site-map';
+
+        $this->homePageUrl
+            = ($_ENV['GLM_HOST_ID'] == 'PRODUCTION')
+            ? BASE_URL
+            : BASE_URL . 'index.php';
+
+        // Resources needed for every page.
+        $GLOBALS['styleSheets'] = array();
+        // Resources needed for every page.
+        $GLOBALS['topScripts'][] = JQUERY_CDN_JS;
+        $GLOBALS['topScripts'][] = JQUERY_CDN_MIGRATE_JS;
+    }
+
+    private function _video()
+    {
+        //$GLOBALS['styleSheets'][]
+        //   = GLM_APP_BASE_URL . 'gallery/colorbox/colorbox.css';
+        //$GLOBALS['bottomScripts'][]
+        //    = GLM_APP_BASE_URL . 'libjs/plugins/colorbox/1.3.15/jquery.colorbox-min.js';
+        //$GLOBALS['bottomScripts'][]
+        //    = BASE_URL . 'Toolkit/Videos/libjs/thickbox.js';
+        $GLOBALS['bottomScripts'][]
+            = BASE_URL . 'Toolkit/Videos/libjs/videos.js';
+        $dbh = Toolkit_Database::getInstance();
+        $sql = "
+          SELECT id
+            FROM videos
+           WHERE featured = true
+             AND active = true
+        ORDER BY pos
+           LIMIT 1
+          OFFSET 0";
+        $row = $dbh->query($sql)->fetch(PDO::FETCH_ASSOC);
+        if ($row) {
+            $video = new Toolkit_Videos_WebDecorator();
+            $this->video = $video->toHtml(
+                Toolkit_Database::getInstance(),
+                $this->_pageGateway
+            );
+        }
+    }
+
+    /**
+     * Add Banner module
+     *
+     * @param array $page toolbox page
+     *
+     * @return void
+     * @access public
+     */
+    public function banners(array $page)
+    {
+        $bannerConf = new Config;
+        $bannerRoot = & $bannerConf->parseConfig(
+            BASE . 'Toolkit/Banners/config.ini', 'IniFile'
+        );
+        $dbh     = Toolkit_Database::getInstance();
+
+        // top banners
+        $banners = & Toolkit_Banners_BannersIterator::create('Bottom');
+        if (defined('MEMBERS_DB') && MEMBERS_DB) {
+            $categoriesIterator = Toolkit_Members_CategoriesIterator::create();
+            $banners->fetchAllAvailable(
+                $dbh, $bannerRoot, $categoriesIterator, $page['id']
+            );
+        } else {
+            $banners->fetchAllAssignedToPage($dbh, $bannerRoot, $page['id']);
+        }
+
+        $availableBanners = $banners->getAvailable();
+
+        //    Are there any banners even available
+        if (count($availableBanners)) {
+            $GLOBALS['bottomScripts'][]
+                = $this->glmAppBaseURL . 'libjs/banner-link.js';
+            $decoratorsTop = Toolkit_Banners_StaticBannersDecorator::create();
+            foreach ($availableBanners as $i) {
+                $decorator = Toolkit_Banners_HorizontalDecorator::create($i);
+                $decoratorsTop->add($decorator);
+            }
+
+            //  application configuration
+            $conf        = new Config;
+            $bannersRoot = & $conf->parseConfig(
+                BASE . 'Toolkit/Banners/config.ini', 'IniFile'
+            );
+
+            $this->hasBanner = count($decoratorsTop->getIterator());
+
+            if ($this->hasBanner) {
+
+                $mailerFactory = new Toolkit_Banners_MailerFactory(
+                    $bannersRoot,
+                    $GLOBALS['flexyOptions']
+                );
+
+                $notifier = new Toolkit_Banners_Notifier($mailerFactory, $dbh);
+
+                $this->bannerAds = $decoratorsTop->toHtml(
+                    $dbh,
+                    new Toolkit_Image_Server(),
+                    $notifier,
+                    $page['id'],
+                    'top'
+                );
+            }
+        }
+        // side banners
+        $banners = & Toolkit_Banners_BannersIterator::create('Side');
+        if (defined('MEMBERS_DB') && MEMBERS_DB) {
+            $categoriesIterator = Toolkit_Members_CategoriesIterator::create();
+            $banners->fetchAllAvailable(
+                $dbh, $bannerRoot, $categoriesIterator, $page['id']
+            );
+        } else {
+            $banners->fetchAllAssignedToPage($dbh, $bannerRoot, $page['id']);
+        }
+
+        $availableSideBanners = $banners->getAvailable();
+
+        //    Are there any banners even available
+        if (count($availableSideBanners)) {
+            $GLOBALS['bottomScripts'][]
+                = $this->glmAppBaseURL . 'libjs/banner-link.js';
+            $decoratorsSide = Toolkit_Banners_StaticBannersDecorator::create();
+            foreach ($availableSideBanners as $i) {
+                $decorator = Toolkit_Banners_VerticalDecorator::create($i);
+                $decoratorsSide->add($decorator);
+            }
+
+            //  application configuration
+            $conf        = new Config;
+            $bannersRoot = & $conf->parseConfig(
+                BASE . 'Toolkit/Banners/config.ini', 'IniFile'
+            );
+
+            $this->hasSideBanner = count($decoratorsSide->getIterator());
+            if ($this->hasSideBanner) {
+
+                $mailerFactory = new Toolkit_Banners_MailerFactory(
+                    $bannersRoot,
+                    $GLOBALS['flexyOptions']
+                );
+
+                $notifier = new Toolkit_Banners_Notifier($mailerFactory, $dbh);
+
+                $this->sideBannerAds = $decoratorsSide->toHtml(
+                    $dbh,
+                    new Toolkit_Image_Server(),
+                    $notifier,
+                    $page['id'],
+                    'side'
+                );
+            }
+        }
+    }
+
+    /**
+     * Create the error document page
+     *
+     * @return void
+     * @access public
+     */
+    function createErrorDocument()
+    {
+        $this->baseURL = MEDIA_BASE_URL;
+        $this->homeURL = $this->toolbox->get_seo_url(HOME_ID);
+        ;
+        // Initiate HTML_Template_Flexy.
+        $template = new HTML_Template_Flexy($GLOBALS['flexyOptions']);
+
+        // Create a template object for the 404 content part
+        $errorDocumentTemplate = new HTML_Template_Flexy($GLOBALS['flexyOptions']);
+        $errorDocumentTemplate->compile(ERROR_DOCUMENT_TEMPLATE);
+        $errorDocumentContents = $errorDocumentTemplate->bufferedOutputObject($this);
+
+        // set defaults
+        $this->toolboxContent = $errorDocumentContents;
+        $this->pageTitle = '404 Not Found - ' . PAGE_TITLE;
+        $this->hasHeadlines = false;
+        $this->hasAreaEvents = false;
+        $this->hasNews = false;
+        $this->sideNav = null;
+        $this->mainNav = $this->toolbox->get_main_nav();
+        $this->hasWeather = false;
+        $this->isHomePage = false;
+        $this->sitemapURL = false;
+
+        // compile the flexy template
+        $template->compile(INSIDE_TEMPLATE);
+
+        // Merge compiled template with the object.
+        $fileContents = $template->bufferedOutputObject($this);
+        file_put_contents(BASE . ERROR_DOCUMENT, $fileContents);
+    }
+
+    /**
+     * Add Event module
+     *
+     * @param Toolkit_Template_Page           $toolboxPage page template
+     * @param Toolkit_Toolbox_GatewayAbstract $pageGateway page gateway
+     *
+     * @return void
+     * @access private
+     */
+    private function _events(
+        Toolkit_Template_Page $toolboxPage,
+        Toolkit_Toolbox_GatewayAbstract $pageGateway
+    ) {
+        if (defined('COMMON_EVENTS') && COMMON_EVENTS) {
+            if (!defined('COMMON_APP_BASE')) {
+                define('COMMON_APP_BASE', '/var/www/server/CommonApps/');
+            }
+            $this->events = array();
+            define('COMMON_EVENTS_SCHEMA', 'events');
+            require_once COMMON_APP_BASE . 'EventCalendar/V1/models/EventMapper.php';
+            $eventMapper = new EventMapper(Toolkit_Database::getInstance());
+
+            $events = $eventMapper->fetchFilteredEvents(
+                "slideshow is true"
+            );
+            if (is_array($events)) {
+                foreach ($events as $event) {
+                    $this->events[] = array(
+                        'id'     => $event->getId(),
+                        'href'   => $event->getHref(),
+                        'bdate'  => $event->getDates(),
+                        'header' => strip_tags($event->getHeader()),
+                        'descr'     => substr(strip_tags($event->getDescription()), 0, 150),
+                        's-img'  => (($event->getImage())
+                            ? FILE_SERVER_URL . IS_OWNER_ID
+                                . '/eventSlider/' . $event->getImage()
+                            :''),
+                        't-img'  => (($event->getImage())
+                            ? FILE_SERVER_URL . IS_OWNER_ID
+                                . '/eventFeaturedThumb/' . $event->getImage()
+                            :''),
+                        'f-img'  => (($event->getImage())
+                            ? FILE_SERVER_URL . IS_OWNER_ID
+                                . '/eventSlider/' . $event->getImage()
+                            :''),
+                    );
+                }
+            }
+        } else {
+            $events = new Toolkit_Events_HomeEvents(
+                Toolkit_Database::getInstance()
+            );
+            $this->events = $events->getHomeEvents();
+        }
+        $this->hasEvents = !empty($this->events);
+        $this->eventsUrl = Toolkit_Template_Page::getSeoUrl(
+            $pageGateway, EVENT_PAGE
+        );
+    }
+
+    /**
+     * Setup the page with all applications (banners home events navigation)
+     *
+     * @return void
+     */
+    public function fetchPage()
+    {
+        if (defined('MEMBERS_DB') && MEMBERS_DB) {
+            // check to see if this page is a member only page
+            $memberOnlyPage = Toolkit_Template_Page::isMemberOnly(
+                $this->_pageGateway, $this->_catid
+            );
+            if ($memberOnlyPage
+                && !strpos($_SERVER['REQUEST_URI'], 'members-only-area')
+            ) {
+                header('Location: ' . $this->getToolboxUrl(MEMBERS_CATEGORY));
+            }
+        }
+        $this->pageTitle = $this->_getPageTitle($this->_catid);
+
+        $bodyFactory = new Toolkit_Template_Page_BodyFactory(
+            $this->_breadCrumbs,
+            $this->_pageGateway,
+            $this->_paragraphGateway,
+            $this->_keywordReplacement,
+            new Cache_Lite($GLOBALS['cacheOptions'])
+        );
+
+        $this->toolboxContent = $this->_toolboxPage->getBody(
+            $this->_catid, $bodyFactory
+        );
+        $page = $this->_pageGateway->find($this->_catid);
+        $this->topParentId = $this->_pageGateway->findTopParent($this->_catid);
+
+        // set the metaTags
+        if ($page['meta_description']) {
+            $this->metaTags
+                = htmlentities(
+                    strip_tags($page['meta_description']), ENT_QUOTES, 'UTF-8'
+                );
+        } else {
+            $this->metaTags
+                = htmlentities(
+                    substr(
+                        trim(strip_tags($page['description'])), 0, 250
+                    ), ENT_QUOTES, 'UTF-8'
+                );
+        }
+
+        if (filter_var($_REQUEST['sitemap'], FILTER_VALIDATE_INT)) {
+            $this->isHomePage = false;
+        } elseif (HOME_ID == $page['id']) {
+            $this->isHomePage = true;
+        } else {
+            $this->isHomePage = false;
+        }
+        if ($this->_catid != HOME_ID) {
+            $this->parentPageId = $this->_pageGateway->findTopParent($this->_catid);
+            $this->pageId = $this->_catid;
+        }
+        $this->_navigationFactory->setGateway($this->_pageGateway);
+        $this->mainNav = $this->_getMainNav($this->_catid);
+        $this->sideNav = $this->_getSideNav($this->_catid);
+
+        $this->newsletterAction = Toolkit_Template_Page::getSeoUrl(
+            $this->_pageGateway, 10
+        );
+
+        $this->tripPlannerCount = isset($_SESSION['wish_list'])
+            ? count($_SESSION['wish_list'])
+            : 0;
+        $this->tripPlannerUrl   = Toolkit_Template_Page::getSeoUrl(
+            $this->_pageGateway, MEMBER_SESSION_PAGE
+        );
+
+        // check if define for GOOGLE_SEARCH is set
+        if (defined("GOOGLE_SEARCH")
+            && GOOGLE_SEARCH
+            && isset($_REQUEST['query'])
+        ) {
+            $this->_googleSearch();
+        }
+
+        if (   defined("VIDEOS")
+            && VIDEOS
+            && $this->_catid == HOME_ID
+        ) {
+            $this->_video();
+        }
+
+        // check if define for GLM_SEARCH is set
+        if (defined("GLM_SEARCH")
+            && GLM_SEARCH
+        ) {
+            $this->glmSearch = GLM_SEARCH;
+            $this->glmSearchSite = GLM_SEARCH_SITE;
+            $this->glmSearchLogin = GLM_SEARCH_LOGIN;
+            $this->glmSearchKey = GLM_SEARCH_KEY;
+
+            // Check if GLMSearch page called
+            if ($_REQUEST['GLMSearch'] == 'true') {
+                $this->isHomePage = false;
+            }
+        }
+
+        // check if define for HOME_EVENTS is set
+        if (defined("HOME_EVENTS")
+            && HOME_EVENTS
+            && !filter_var($_REQUEST[ 'sitemap'], FILTER_VALIDATE_INT)
+            && !isset($_REQUEST['query'])
+            && $this->isHomePage
+        ) {
+            $this->_events($this->_toolboxPage, $this->_pageGateway);
+        }
+
+        if (   defined("GLM_BLOCKS")
+            && GLM_BLOCKS
+            && !filter_var($_REQUEST[ 'sitemap'], FILTER_VALIDATE_INT)
+            && !isset($_REQUEST['query'])
+        ) {
+            $this->_getBlocks();
+        }
+
+        if (   defined("PRESS_DB")
+            && PRESS_DB
+            && $page['id'] == PRESS_PAGE_ID
+        ) {
+            $this->_getNewsPage();
+        }
+        // check if define for WEATHER is set
+        if (defined("WEATHER") && WEATHER) {
+            $this->hasWeather = true;
+            $this->_weather();
+        }
+
+        // check if define for BANNERS is set
+        if (defined("BANNERS") && BANNERS && is_array($page)) {
+            $this->banners($page);
+        }
+
+        if (defined("ROTATING_IMAGES")
+            && ROTATING_IMAGES
+            && !filter_var($_REQUEST['sitemap'], FILTER_VALIDATE_INT)
+            && !isset($_REQUEST['query'])
+        ) {
+            $this->_rotatingImages();
+        }
+
+        if (defined('SEASONS') && SEASONS) {
+            $this->_seasons();
+        }
+
+        if (defined("VIDEOS")
+            && VIDEOS
+            && !filter_var($_REQUEST[ 'sitemap'], FILTER_VALIDATE_INT)
+            && !isset($_GET['query'])
+            && in_array($page['id'], array(HOME_ID))
+        ) {
+            $this->_getFeaturedVideo();
+        }
+
+    }
+
+    /**
+     * Returns page main navigation
+     *
+     * @param type $catid page catid
+     *
+     * @return type
+     */
+    private function _getMainNav($catid)
+    {
+        $nav          = $this->_navigationFactory->createMainNav();
+        $mainNavArray = $nav->getNavStructure($this->_pageGateway, $catid);
+
+        return $nav->renderPageNav($mainNavArray, 'tree');
+    }
+
+        private function _getArrayToListNav($catid)
+    {
+        $sideNav = new Toolkit_Template_Navigation_AllInOneSideNav(
+            $this->_pageGateway
+        );
+        $nav  = $sideNav->getNavigation();
+        $html = $this->_arrayToListHTML($nav, 0, "mainNav");
+        return ($html) ? $html : '';
+    }
+
+    private function _arrayToListHTML($array, $level = 0, $className = null)
+    {
+        static $tab = "\t", $format = '<a href="%s"%s>%s</a>';
+
+        if (empty($array)) {
+            return;
+        }
+        $tabs = str_repeat($tab, $level * 2);
+        $mainClass = ($level == 0 && $className) ? ' class="'.$className.'"': '';
+        $result = "{$tabs}<ul{$mainClass}>\n";
+        foreach ($array as $i => $node) {
+            $class
+                = ($node['class'] == 'current')
+                ? ' class="'.$node['class'].'"'
+                : '';
+            $parent
+                = ($node['class'] == 'parent')
+                ? ' class="'.$node['class'].'"'
+                : '';
+            $link = sprintf(
+                $format,
+                $node['uri'],
+                $class,
+                $node['label']
+            );
+            $result .= "{$tabs}{$tab}<li{$parent}>\n{$tabs}{$tab}{$tab}"
+                . "{$link}\n"
+                . $this->_arrayToListHTML($node['pages'], $level + 1)
+                . "{$tabs}{$tab}</li>\n";
+        }
+        $result .= "{$tabs}</ul>\n";
+        return $result;
+    }
+
+    /**
+     * Returns page title
+     *
+     * @param type $id page id
+     *
+     * @return string
+     */
+    private function _getPageTitle($id)
+    {
+        if (filter_input(INPUT_GET, 'member_id', FILTER_VALIDATE_INT)) {
+            // Member profile pages can't have search page title tags
+            return SITENAME;
+        }
+
+        $page = $this->_pageGateway->find($id);
+
+        if (!empty($page['meta_title'])) {
+            $title = $page['meta_title'];
+        } elseif (!empty($page['title'])) {
+            $title = $page['title'];
+        } elseif (!empty($page['navigation_name'])) {
+            $title = $page['navigation_name'];
+        }
+
+        $title = strip_tags($title);
+
+        $title = isset($title)
+            ? htmlentities("$title - ", ENT_QUOTES, 'UTF-8') . SITENAME
+            : SITENAME;
+
+        return $title;
+    }
+
+    /**
+     * Returns side navigation
+     *
+     * @param type $catid page catid
+     *
+     * @return type
+     */
+    private function _getSideNav($catid)
+    {
+        if ($catid == HOME_ID) {
+            return;
+        } else {
+            // side nav
+            $nav
+                = $this->_navigationFactory->createSideNav($this->_pageGateway);
+            $sideNavArray = $nav->getNavStructure(
+                $this->_pageGateway, $catid
+            );
+            return (!empty($sideNavArray))
+                ? $nav->renderPageNav($sideNavArray, 'tree')
+                : '';
+        }
+    }
+
+    /**
+     * Add Google Search module
+     *
+     * @return void
+     * @access private
+     */
+    private function _googleSearch()
+    {
+        $this->gSearchOn = false;
+        $this->isHomePage = false;
+        if (isset($_REQUEST['query']) && $_REQUEST['query']) {
+            $this->gSearchOn = true;
+            $query                      = urlencode($_REQUEST['query']);
+            $GLOBALS['styleSheets'][]   = MEDIA_BASE_URL . 'css/gsearch.css';
+            $GLOBALS['bottomScripts'][]
+                = 'http://www.google.com/uds/api?file=uds.js&v=1.0&key=' .
+                GSEARCH_API;
+            $GLOBALS['bottomScripts'][]
+                = $this->mediaBaseURL . 'js/gsearch.php?query=' . $query;
+        }
+    }
+
+    /**
+     * create code for conditional styles
+     *
+     * @return type
+     */
+    public function getConditionalStyles()
+    {
+        return '
+        <!--[if lte IE 7]>
+        <link rel="stylesheet" type="text/css" href="' . MEDIA_BASE_URL . 'css/ie7.css">
+        <![endif]-->';
+    }
+
+    /**
+     * grab the navigation name for the id
+     *
+     * @param integer $id id for pages table
+     *
+     * @return string page nav name
+     * @access public
+     */
+    function getToolboxName($id)
+    {
+        $page = $this->_pageGateway->find($id);
+        return $page['navigation_name'];
+    }
+
+    /**
+     * grab the navigation url for the id
+     *
+     * @param integer $id id for pages table
+     *
+     * @return string url
+     * @access public
+     */
+    public function getToolboxUrl($id)
+    {
+        return Toolkit_Template_Page::getSeoUrl($this->_pageGateway, $id);
+    }
+
+    /**
+     * grab the home page headlines
+     *
+     * @return void
+     * @access private
+     */
+    private function _headlines()
+    {
+        // get headlines aka 'Quick Links'
+        $this->headlines = $this->_pageGateway->getHomePageHeadlines();
+        // boolean for if toolbox has headlines
+        $this->hasHeadlines = !empty($this->headlines);
+    }
+
+    /**
+     * grab the GLM Blocks
+     *
+     * @return void
+     * @access private
+     */
+    private function _getBlocks()
+    {
+        $cache = new Cache_Lite($GLOBALS['cacheOptions']);
+        if ($blockContent = $cache->get($this->_catid, 'Block')) {
+            $this->headlines = unserialize($blockContent);
+        } else {
+            $blocks = new Toolkit_Blocks_Display(
+                Toolkit_Database::getInstance()
+            );
+            $this->headlines
+                = $blocks->getPageBlocksAsArray((int)$this->_catid);
+            $cache->save(serialize($this->headlines), $this->_catid, 'Block');
+        }
+        $this->hasHeadlines = !empty($this->headlines);
+    }
+
+    /**
+     * Check if this page has photo gallery in it
+     *
+     * @return boolean If this page has a photo gallery assigned to it
+     * @access public
+     */
+    function isPhotoGalleryPage()
+    {
+        $sql = "
+            SELECT photocat_id
+              FROM photo_category_bus
+             WHERE buscat_id = {$this->toolbox->catid}";
+        if ($pData = $this->toolbox->DB->select($sql)) {
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * grab the home page newsletter/press info
+     *
+     * @param object $toolbox Toolbox object
+     *
+     * @return void
+     * @access public
+     */
+    public function news($toolbox)
+    {
+        if (HOME_ID == $toolbox->catid) {
+            $this->news    = $toolbox->get_news();
+            $this->hasNews = (!empty($this->news));
+        } else {
+            $this->hasNews = false;
+        }
+    }
+
+    /**
+     * Sets toolboxContent with news
+     *
+     * @return void
+     */
+    private function _getNewsPage()
+    {
+        $dbh = Toolkit_Database::getInstance();
+        $press = new Toolkit_Press_Newsletter();
+        $this->toolboxContent = $press->toHtml($dbh);
+    }
+
+    /**
+     * Sets rotatingImages
+     *
+     * @return void
+     */
+    private function _rotatingImages()
+    {
+        $dbh = Toolkit_Database::getInstance();
+        $sql = "
+        SELECT id
+          FROM rotatingimages.application
+         WHERE page = " . $this->_catid;
+        $appId = $dbh->query($sql)->fetchColumn();
+        if ($appId) {
+            define('SLIDESHOW_APP_ID', $appId);
+        } else {
+            return;
+        }
+        $gateway       = new Toolkit_RotatingImages_Gateway(
+            Toolkit_Database::getInstance()
+        );
+        $nodesIterator = new Toolkit_RotatingImages_NodesIterator();
+        $images        = $gateway->findAll();
+        foreach ($images as $image) {
+            $nodesIterator->addImage($image);
+        }
+        $is        = new Toolkit_FileServer_ImageAdapter();
+        $decorator = new Toolkit_RotatingImages_Decorator_User();
+        foreach ($nodesIterator as $v) {
+            if ($v->getActive()) {
+                $imgDecorator = new Toolkit_RotatingImages_Decorator_Image($v);
+                if ($v->isAnchor()) {
+                    $imgDecorator = new Toolkit_RotatingImages_Decorator_Anchor(
+                        $imgDecorator,
+                        $v
+                    );
+                }
+                $decorator->add($imgDecorator);
+            }
+        }
+        $this->rotatingImages = $decorator->toHtml($is);
+    }
+
+    /**
+     * Add Weather module
+     *
+     * @return void
+     * @access private
+     */
+    private function _weather()
+    {
+        $weather = new Toolkit_Weather();
+        if (PEAR::isError($weather->currentCond)) {
+            return false;
+        }
+
+        $this->weather  = $weather->currentCond['weather'];
+        $this->tempF    = $weather->currentCond['temp_f'];
+        $this->location = $weather->currentCond['location'];
+        $this->iconUrlName
+            = ($weather->currentCond['icon_url_name'])
+            ? $weather->currentCond['icon_url_name']
+            : 'na.png';
+
+        $iconUrl
+            = ($_SERVER['HTTPS'] == 'on')
+            ? 'https://app.gaslightmedia.com/weather/'
+            : 'http://app.gaslightmedia.com/weather/';
+        $this->fullIconUrl = $iconUrl . $this->iconUrlName;
+    }
+
+    private function _seasons()
+    {
+        $seasonMapper = new Toolkit_Seasons_Season();
+        $season = $seasonMapper->fetchById(
+            Toolkit_Database::getInstance(),
+            1
+        );
+        $GLOBALS['styleSheets'][] = $this->mediaBaseURL . 'css/' .
+            $season->getName() . '.css?_=' . strtotime($season->getLast_update());
+    }
+
+    /**
+     * Sets the video url for the embed video if not found then set as empty
+     *
+     * @return void
+     */
+    private function _getFeaturedVideo()
+    {
+        try {
+            $dbh = Toolkit_Database::getInstance();
+            $sql = "
+              SELECT id
+                FROM videos
+               WHERE featured = true
+            ORDER BY pos
+               LIMIT 1
+              OFFSET 0";
+            $row = $dbh->query($sql)->fetch(PDO::FETCH_ASSOC);
+            if ($row) {
+                $videoMapper = new Toolkit_Videos_VideoMapper($dbh);
+                $video = $videoMapper->getVideoById($row['id'], false);
+                $this->featuredVideoCode
+                    = ($video)
+                    ? $video->getVideoCode()
+                    : '';
+            }
+        } catch(PDOException $e) {
+            Toolkit_Common::handle_error($e);
+        }
+    }
+}
diff --git a/Toolkit/Photos/Admin/FileUploader.php b/Toolkit/Photos/Admin/FileUploader.php
new file mode 100644 (file)
index 0000000..b81c390
--- /dev/null
@@ -0,0 +1,458 @@
+<?php
+/**
+ * FileUploader.php
+ *
+ * PHP version 5
+ *
+ * @category  Toolkit
+ * @package   Photos/Admin
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2009 Gaslight Media
+ * @license   Gaslight Media
+ * @version   CVS: $Id: ShortURL.php,v 1.5 2010/05/25 14:07:22 jamie Exp $
+ * @link      <>
+ */
+
+define('YAHOO_YUI_BASE', MEDIA_APP_BASE_URL . 'yui/build/');
+define('PHOTOS_UPLOAD_PATH', BASE . 'admin/Photos/uploads/');
+
+/**
+ * Toolkit_Photos_Admin_FileUploader
+ *
+ * Description of Toolkit_Photos_Admin_FileUploader
+ *
+ * @category  Toolkit
+ * @package   Photos/Admin
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2014 Gaslight Media
+ * @license   Gaslight Media
+ * @release   Release: $id$
+ * @link      <>
+ */
+class Toolkit_Photos_Admin_FileUploader
+    extends Toolkit_FormBuilder
+{
+    // {{{ Class Properties
+    /**
+     * Description of $debug
+     * @var boolean
+     */
+    public $debug = false;
+
+    /**
+     * Message for successful upload
+     * @var string
+     */
+    protected $successMsg
+        = '<div id="form-success-top">
+               You have successfully uploaded the files to your photo album.
+           </div>';
+    // }}}
+
+    //    {{{    configureDefaults()
+
+    /**
+     * Initializes default form values
+     *
+     * @return void
+     * @access public
+     */
+    public function configureDefaults()
+    {
+        $defaults = array('catid' => $_REQUEST['catid']);
+
+        $this->setupDefaults($defaults);
+    }
+
+    //    }}}
+    //    {{{    configureElements()
+
+    /**
+     * Form element definitions
+     *
+     * @return void
+     * @access public
+     */
+    public function configureElements()
+    {
+        $e = array();
+        $categories = $this->getPhotoCategories();
+
+        //    All Elements are created here.  This includes group element definitions.
+        $e[] = array(
+            'type'    => 'header',
+            'name'    => 'albumHdr_rmv',
+            'display' => '
+            <p style="width: 700px; padding-left: 5px;">
+                <strong>USE:</strong>
+            Hold down the Ctrl key to select or unselect more than one photo
+            at a time. Hold down the Shift key to select a range.</p>
+            <div style="position: absolute; top: 320px;width: 700px; border: 1px dashed #9FD8EF;padding: 20px; margin: 0 0 20px 0;">
+            This feature (Multiple Uploads) is only supported in certain modern browsers:
+            <ul>
+                <li>Internet Explorer 10</li>
+                <li>FireFox 8 and up</li>
+                <li>Safari 5 and up</li>
+                <li>Google Chrome 17 and up</li>
+            </ul>
+            </div>'
+        );
+        $e[] = array(
+            'type' => 'hidden',
+            'req'  => false,
+            'name' => 'catid'
+        );
+        $e[] = array(
+            'type'    => 'file',
+            'name'    => 'files[]',
+            'display' => 'Files to Upload',
+            'opts'    => array(
+                'id'       => 'myuploader',
+                'multiple' => 'multiple',
+                'accept'   => 'image/*'
+            )
+        );
+
+        $e[] = array(
+            'type'    => 'submit',
+            'req'     => false,
+            'name'    => 'submit_rmv',
+            'display' => 'Submit Form'
+        );
+
+        $this->setupElements($e);
+    }
+
+    //    }}}
+    //    {{{    configureFilters()
+
+    /**
+     * Form filter definitions
+     *
+     * Applies a data filter for the given fields when the form is submitted
+     *
+     * @return void
+     * @access public
+     */
+    public function configureFilters()
+    {
+        $f = array();
+
+        $f[] = array(
+            'element' => '__ALL__',
+            'filter' => 'trim'
+        );
+
+        $this->setupFilters($f);
+    }
+
+    //    }}}
+    //  {{{ configureForm()
+
+    /**
+     * Helper function, configures the entire form
+     *
+     * @return void
+     * @access public
+     */
+    public function configureForm()
+    {
+        $this->configureElements();
+        $this->configureFilters();
+        $this->configureRules();
+        $this->configureDefaults();
+    }
+
+    //  }}}
+    //    {{{    configureRules()
+
+    /**
+     * Form rule definitions
+     *
+     * Adds validation rules for the given fields
+     *
+     * @return void
+     * @access public
+     */
+    public function configureRules()
+    {
+        $r = array();
+        $this->setupRules($r);
+    }
+
+    //    }}}
+
+    // {{{ getFileData()
+    /**
+     * Get file data
+     *
+     * @param string $fileName Filename
+     * @param string $path     File path
+     *
+     * @return array|boolean
+     * @access public
+     */
+    function getFileData($fileName, $path)
+    {
+        $filename = $path . '/' . $fileName;
+        if (is_file($filename) && is_readable($filename)) {
+            return array(
+                'name' => $fileName,
+                'tmp_name' => $filename,
+                'size' => filesize($filename),
+                'type' => mime_content_type($filename)
+            );
+        } else {
+            return false;
+        }
+    }
+    // }}}
+    // {{{ getPhotoCategorieso()
+
+    /**
+     * Get photo categories
+     *
+     * @return array
+     * @access public
+     */
+    function getPhotoCategories()
+    {
+        try {
+            $sql = "
+              SELECT id, category
+                FROM photo_category
+            ORDER BY category";
+            $stmt = $this->dbh->query($sql);
+            while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                $categories[$row['id']] = $row['category'];
+            }
+            return $categories;
+        } catch(PDOExecption $e) {
+            Toolkit_Common::handleError($e);
+        }
+    }// }}}
+    // {{{ getCategoryMaxPos()
+    /**
+     * get max pos plus one
+     *
+     * @param mixed $id photo category id
+     *
+     * @access public
+     * @return string
+     */
+    function getCategoryMaxPos($id)
+    {
+        try {
+            $sql = "
+            SELECT COALESCE(max(pos) + 1, 1)
+              FROM photo2category
+             WHERE category = :catid";
+            $stmt = $this->dbh->prepare($sql);
+            $stmt->bindParam(":catid", $id, PDO::PARAM_INT);
+            $stmt->execute();
+            return $stmt->fetchColumn();
+        } catch(PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+    }// }}}
+
+    //    {{{    processData()
+
+    /**
+     * Handles how to process the form when submitted
+     *
+     * @param array $values Form submitted values
+     *
+     * @return array     Result of Insert / Update function
+     * @access public
+     */
+    public function processData($values)
+    {
+        if (   is_array($values['files'])
+            && !empty($values['files'])) {
+            $this->processFilesArray($values);
+        } else {
+            $this->processFolder($values);
+        }
+        return true;
+    }
+
+    //    }}}
+
+    /**
+     * Process files array
+     *
+     * @param array $values Values array
+     *
+     * @return void
+     * @access public
+     */
+    public function processFilesArray($values)
+    {
+        $images = array();
+        $photos = array();
+        $acceptedFiles = array(
+            'image/jpeg',
+            'image/gif',
+            'image/png'
+        );
+        $is = new Toolkit_FileServer_ImageAdapter(IS_OWNER_ID, IS_OWNER_PW);
+        $mediaMapper = new Toolkit_Photos_Models_MediaMapper(
+            Toolkit_Database::getInstance()
+        );
+        $pos = $this->getCategoryMaxPos($values['catid']);
+        if (is_array($values['files']['name'])) {
+            for ($i = 0; $i < count($values['files']['name']); ++$i) {
+                // check that it's an image
+                // jpg jpeg gif or png
+                if (   !$values['files']['error'][$i]
+                    && in_array($values['files']['type'][$i], $acceptedFiles)
+                ) {
+                    $images[] = array(
+                        'name'     => $values['files']['name'][$i],
+                        'tmp_name' => $values['files']['tmp_name'][$i],
+                        'size'     => $values['files']['size'][$i]
+                    );
+                }
+            }
+        }
+        if (!empty($images)) {
+            foreach ($images as $img) {
+                $imgData = $is->uploadImageFile($img['tmp_name']);
+                $photo = Toolkit_Photos_Models_Photo::createByValues(
+                    array(
+                        'image' => $imgData['name'],
+                        'catid' => $values['catid']
+                    )
+                );
+                $photoId = $mediaMapper->savePhoto($photo);
+                $photo2Category
+                    = Toolkit_Photos_Models_Photo2Category::createByValues(
+                        array(
+                            'photo'    => $photoId,
+                            'category' => $values['catid'],
+                            'pos'      => $pos
+                        )
+                    );
+                $mediaMapper->savePhoto2Category($photo2Category);
+                ++$pos;
+            }
+        }
+    }
+
+    /**
+     * Process folder
+     *
+     * @param array $values Values array
+     *
+     * @throws Exception
+     * @return void
+     * @access public
+     */
+    public function processFolder($values)
+    {
+        $is  = new Toolkit_Image_Server();
+        $mediaMapper = new Toolkit_Photos_Models_MediaMapper(
+            Toolkit_Database::getInstance()
+        );
+        try {
+            if (!is_dir($_SESSION['uploadFolder'])) {
+                throw new Exception('No Folder exists');
+            }
+        } catch (Exception $e) {
+            Toolkit_Common::handleError($e);
+        }
+
+        $d   = dir($_SESSION['uploadFolder']);
+        $pos = $this->getCategoryMaxPos($_REQUEST['catid']);
+        $i = 0;
+        while (false !== ($entry = $d->read())) {
+            if (!in_array($entry, array('.','..','.svn','CVS'))) {
+                // before we can send the image to the image class we need
+                // to inject the data for the images into the $_FILES array
+                $_FILES['img' . $i] = $this->getFileData($entry, $_SESSION['uploadFolder']);
+                // send to image server and insert values
+                $image_name = $is->imageUpload('img' . $i, $_SESSION['uploadFolder']);
+                $photo = Toolkit_Photos_Models_Photo::createByValues(
+                    array(
+                        'image' => $image_name,
+                        'catid' => $_REQUEST['catid']
+                    )
+                );
+                $photoId = $mediaMapper->savePhoto($photo);
+                $photo2Category
+                    = Toolkit_Photos_Models_Photo2Category::createByValues(
+                        array(
+                            'photo'    => $photoId,
+                            'category' => $_REQUEST['catid'],
+                            'pos'      => $pos
+                        )
+                    );
+                $mediaMapper->savePhoto2Category($photo2Category);
+                ++$pos;
+                unlink($_SESSION['uploadFolder'] . '/'. $entry);
+                ++$i;
+            }
+        }
+        $d->close();
+        if (is_dir($_SESSION['uploadFolder'])) {
+            rmdir($_SESSION['uploadFolder']);
+        }
+    }
+
+    //    {{{    setupRenderers()
+    //  @codeCoverageIgnoreStart
+
+    /**
+     * Custom rendering templates for special fields on the form
+     *
+     * @return void
+     * @access protected
+     */
+    protected function setupRenderers()
+    {
+        parent::setupRenderers();
+        $renderer =& $this->defaultRenderer();
+        $required = '<!-- BEGIN required --><span class="req"> * </span><!-- END required -->';
+        $error    = '<!-- BEGIN error --><div class="req"> {error} </div><!-- END error -->';
+    }
+
+    //  @codeCoverageIgnoreEnd
+    //    }}}
+
+    //    {{{    toHtml()
+
+    /**
+     * Handles how to display the current step the user is at in the form
+     *
+     * destroying and resetting the captcha value dis-allows someone from
+     * re-sending a form on a previous captcha.
+     *
+     * @return string form HTML state
+     * @access public
+     */
+    public function toHtml()
+    {
+        $this->setupRenderers();
+        if ($this->validate()) {
+            $this->cleanForm();
+            if ($this->process(array(&$this, 'processData'), $this->mergeFiles)) {
+                $this->freeze();
+                HTTP_Session2::unregister('uploadFolder');
+                header("Location: photos.php?ac=editCategory&id=" . $_REQUEST['catid']);
+                exit;
+            }
+
+        } elseif ($this->isSubmitted()) {
+            $output  = $this->errorMsg;
+            $output .= parent::toHTML();
+            $output = '<pre>'.print_r($this, true).'</pre>';
+        } else {
+            $output = parent::toHTML();
+        }
+        return $output;
+    }
+
+    //    }}}
+}
+?>
diff --git a/Toolkit/Photos/Admin/PageTree.php b/Toolkit/Photos/Admin/PageTree.php
new file mode 100644 (file)
index 0000000..9e52bfa
--- /dev/null
@@ -0,0 +1,145 @@
+<?php
+
+/**
+ * PageTree.php
+ *
+ * PHP version 5.2
+ *
+ * @category  Toolkit
+ * @package   Blocks
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @version   SVN: (0.1)
+ * @link      <>
+ */
+
+/**
+ * Toolkit_Package_PageTree
+ *
+ * Display the toolbox page as ul lil list for jQuery-Column viewer
+ *
+ * @category  Toolkit
+ * @package   Blocks
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @release   Release: (0.1)
+ * @link      <>
+ */
+class Toolkit_Photos_Admin_PageTree
+{
+    private $_dbh;
+    private $_rootNodeStart = "<ul class=\"menu\" id=\"demo1\">\n";
+    private $_leafStartExpanded = "\n\t<li class=\"expanded\" %s>\n";
+    private $_leafStartLeaf = "\n\t<li class=\"leaf\" %s>\n";
+    private $_subTreeStart = "\n<ul class=\"menu\">\n";
+    private $_treeEnd = "\n</ul>\n";
+    private $_leafEnd = "\n\t</li>\n";
+    private $_tree;
+
+    public function __construct(PDO $dbh)
+    {
+        $this->_dbh = $dbh;
+    }
+
+    /**
+     * creates and executes the sql query for getting the pages
+     *
+     * @return array | null
+     */
+    private function _findAll()
+    {
+        try {
+            $members
+                = (filter_var(MEMBERS_CATEGORY, FILTER_VALIDATE_INT))
+                ? "WHERE id NOT IN (" . MEMBERS_CATEGORY . ")
+                     AND parent NOT IN (" . MEMBERS_CATEGORY . ")"
+                : '';
+            $sql = "
+                SELECT id,parent,navigation_name
+                  FROM pages
+                $members
+                 ORDER by parent, pos";
+
+            return $this->_dbh
+                ->query($sql)
+                ->fetchAll(PDO::FETCH_ASSOC);
+        } catch (PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+    }
+
+    /**
+     * Get all pages for the tree
+     *
+     * @return array
+     */
+    private function _fetchPages()
+    {
+        $pages = $this->_findAll();
+        if (is_array($pages)) {
+            $threads = array();
+            foreach ($pages as $page) {
+                $page['children'] = array();
+                $threads[] = $page;
+            }
+
+            $children = array();
+            while (list($key, $value) = each ($threads)) {
+                $children[$value['parent']][$value['id']] = $value;
+            }
+
+            $this->_tree = $children;
+        } else {
+            $this->_tree = array();
+        }
+    }
+
+    /**
+     * Create html of the pages tree for jqueyr.columnview
+     *
+     * @return string
+     */
+    public function toHtml()
+    {
+        $this->_fetchPages();
+        if (is_array($this->_tree)) {
+            $html = $this->createTree($this->_tree, reset($this->_tree));
+        }
+        return $html;
+    }
+
+    /**
+     * Creates the tree structure for the pages jquery column view
+     *
+     * @param array $tree  Array for tree
+     * @param type  $leaf  Array for leaf
+     * @param type  $level tree level
+     *
+     * @return string
+     */
+    protected function createTree(array $tree, $leaf, $level = 0)
+    {
+        $html = !$level ? $this->_rootNodeStart : $this->_subTreeStart;
+        if (is_array($leaf) && !empty($leaf)) {
+            while (list($parent, $branch) = each($leaf)) {
+                $pageName = htmlspecialchars($branch['navigation_name']);
+                if ($tree[$parent]) {
+                    $html .= sprintf($this->_leafStartExpanded, null);
+                    $html .= "<a href=\"#\" data-page=\"{$branch['id']}\" data-name=\"{$pageName}\">{$branch['navigation_name']} </a> ";
+                    $html .= $this->createTree($tree, $tree[$parent], $level + 1);
+                } else {
+                    $html .= sprintf($this->_leafStartLeaf, null);
+                    $html .= "<a href=\"#\" data-page=\"{$branch['id']}\" data-name=\"{$pageName}\">{$branch['navigation_name']} </a> ";
+                    $html .= $this->_leafEnd;
+                }
+            }
+        }
+        $html .= $this->_treeEnd;
+        if ($level) {
+            $html .= $this->_leafEnd;
+        }
+        return $html;
+    }
+}
diff --git a/Toolkit/Photos/Auth.php b/Toolkit/Photos/Auth.php
new file mode 100644 (file)
index 0000000..8b5e31e
--- /dev/null
@@ -0,0 +1,98 @@
+<?php
+//    vim:set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker syntax=php:
+
+/**
+ * Member Authentication
+ *
+ * PHP version 5
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @version   CVS: $Id: Auth.php,v 1.22 2010/08/10 18:08:44 jamie Exp $
+ * @link      http://demo.gaslightmedia.com
+ * @see       Toolkit_Members_Auth-LoginForm, Toolkit_Members_Auth-PasswordForm
+ */
+
+require_once 'Auth.php';
+
+/**
+ * Methods for the memberdb authentication system
+ *
+ * Handles Cookie and session generation, id challenges and security for
+ * the memberdb application
+ *
+ * @category  MembersDB
+ * @package   Toolkit_Members
+ * @author    Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @link      http://demo.gaslightmedia.com
+ * @see       Toolkit_Members_Auth-LoginForm, Toolkit_Members_Auth-PasswordForm
+ */
+class Toolkit_Photos_Auth extends Auth
+{
+    //    {{{    properties
+
+    /**
+     * Maximum idle time
+     *
+     * If more seconds pass before a new page request, then the user
+     * will have to re-authenticate back into the application.
+     * 1800 = 30 min
+     * 3600 = 1 hr
+     *
+     * @var    integer
+     * @access protected
+     */
+    protected $idleTime = 3600;
+
+    //    }}}
+    //    {{{    __construct()
+
+    /**
+     * Constructor
+     *
+     * Sets up the storage driver
+     *
+     * @param Config_Container              $c             Configuration object
+     * @param Toolkit_Members_AuthContainer $storageDriver storage driver
+     * @param string                        $loginFunction (optional)Name of the function
+     *                                                     that creates the login form
+     * @param boolean                       $showLogin     (optional)Should the login form
+     *                                                     be displayed if neccessary?
+     *
+     * @return void
+     * @access public
+     */
+    public function __construct(
+        Auth_Container $storageDriver,
+        $loginFunction = '',
+        $showLogin = false
+    ) {
+        parent::Auth($storageDriver, '', $loginFunction, $showLogin);
+    }
+
+    //    }}}
+
+    //    {{{    setIdle()
+
+    /**
+     * Set the maximum idle time
+     *
+     * @param integer $time time in seconds
+     * @param boolean $add  (optional)add time to current maximum idle time or not
+     *
+     * @return void
+     * @access public
+     */
+    public function setIdle($time = null, $add = false)
+    {
+        $time = is_null($time) ? $this->idleTime : $time;
+        parent::setIdle($time, $add);
+    }
+
+    //    }}}
+}
diff --git a/Toolkit/Photos/AuthContainer.php b/Toolkit/Photos/AuthContainer.php
new file mode 100644 (file)
index 0000000..a3eed86
--- /dev/null
@@ -0,0 +1,193 @@
+<?php
+
+/**
+ * AuthContainer.php
+ *
+ * PHP version 5.3
+ *
+ * @category  Toolkit
+ * @package   Photos
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2013 Gaslight Media
+ * @license   Gaslight Media
+ * @version   SVN: (0.1)
+ * @link      <>
+ */
+
+/**
+ * Toolkit_Package_AuthContainer
+ *
+ * Description of AuthContainer
+ *
+ * @category  Toolkit
+ * @package   Photos
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2013 Gaslight Media
+ * @license   Gaslight Media
+ * @release   Release: (0.1)
+ * @link      <>
+ */
+class Toolkit_Photos_AuthContainer
+    extends Auth_Container
+{
+    //    {{{    properties
+
+    /**
+     * Database handler
+     * @var    PDO
+     * @access private
+     */
+    private $_dbh;
+
+    /**
+     * Addition options for the storage container
+     * @var array
+     * @access private
+     */
+    private $_options = array();
+
+    //    }}}
+    //    {{{    __construct()
+
+    /**
+     * Constructor
+     *
+     * @param PDO $dbh Database handler
+     *
+     * @return void
+     * @access public
+     */
+    public function __construct(PDO $dbh, array $options = null)
+    {
+        $this->_dbh = $dbh;
+        $this->_setDefaults();
+        if (is_array($options)) {
+            $this->_parseOptions($options);
+        }
+    }
+
+    //    }}}
+    //    {{{    _setDefaults()
+
+    /**
+     * Set some default options
+     *
+     * @access private
+     * @return void
+     */
+    private function _setDefaults()
+    {
+        $this->_options['sessionName'] = 'glmMedia';
+        $this->_options['table']       = 'contact';
+        $this->_options['usernamecol'] = 'email';
+        $this->_options['passwordcol'] = 'media_pass';
+        $this->_options['db_fields']   = array('id', 'fname', 'lname');
+        $this->_options['cryptType']   = 'none';
+        $this->_options['db_where']    = ' AND media = true AND approved = true'
+            . ' AND (expire IS NULL OR expire > current_date)';
+    }
+
+    //    }}}
+    //    {{{    _parseOptions()
+
+    /**
+     * Parse options passed to the container class
+     *
+     * @param array $array options for class
+     *
+     * @access private
+     * @return void
+     */
+    private function _parseOptions($array)
+    {
+        foreach ($array as $key => $value) {
+            if (isset($this->_options[$key])) {
+                $this->_options[$key] = $value;
+            }
+        }
+    }
+
+    //    }}}
+    //    {{{    fetchData()
+
+    /**
+     * Get the user information from the database
+     *
+     * @param string $username username to authenticate
+     * @param string $password password to authenticate against username
+     *
+     * @return boolean If the user was authenticated or not
+     * @access public
+     * @throws Toolkit_Members_Exception upon error querying DB for user
+     */
+    public function fetchData($username, $password)
+    {
+        if (   is_string($this->_options['db_fields'])
+            && strstr($this->_options['db_fields'], '*')
+        ) {
+            $sqlFrom = '*';
+        } else {
+            $sqlFrom  = $this->_options['usernamecol'];
+
+            if (strlen($fields = $this->_getDBFields()) > 0) {
+                $sqlFrom .= ", $fields";
+            }
+
+        }
+
+        $pword = ($this->_options['cryptType'] == 'md5') ? 'MD5(:pword)' : ':pword';
+
+        $sql = "
+            SELECT $sqlFrom
+              FROM {$this->_options['table']}
+             WHERE {$this->_options['usernamecol']} = :uname
+               AND {$this->_options['passwordcol']} = $pword";
+
+        if ($this->_options['db_where']) {
+            $sql .= $this->_options['db_where'];
+        }
+
+        try {
+            $stmt = $this->_dbh->prepare($sql);
+            $stmt->bindParam(':uname', $username, PDO::PARAM_STR);
+            $stmt->bindParam(':pword', $password, PDO::PARAM_STR);
+            $stmt->execute();
+            $row = $stmt->fetch(PDO::FETCH_ASSOC);
+
+            if ($row !== FALSE) {
+                foreach ($row as $key => $value) {
+                    $this->_auth_obj->setAuthData($key, $value);
+                }
+                return true;
+            }
+
+            return false;
+        } catch (PDOException $e) {
+            Toolkit_Logger::logException('DB Error', $e);
+            throw new Toolkit_Members_Exception(
+                "Error validating user `$username` - `$password`"
+            );
+        }
+    }
+
+    //    }}}
+    //    {{{    _getDBFields()
+
+    /**
+     * Get extra db fields to fetch and set in the auth data
+     *
+     * @return array comma separated string of extra db fields for a SQL query
+     * @access private
+     */
+    private function _getDBFields()
+    {
+        if (isset($this->_options['db_fields'])) {
+            if (is_array($this->_options['db_fields'])) {
+                return implode(', ', $this->_options['db_fields']);
+            }
+        }
+    }
+
+    //    }}}
+}
+
diff --git a/Toolkit/Photos/Controllers/IndexController.php b/Toolkit/Photos/Controllers/IndexController.php
new file mode 100644 (file)
index 0000000..1a6f3e8
--- /dev/null
@@ -0,0 +1,514 @@
+<?php
+
+/**
+ * IndexController.php
+ *
+ * PHP version 5.3
+ *
+ * @category  Toolkit
+ * @package   Media
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2013 Gaslight Media
+ * @license   Gaslight Media
+ * @version   SVN: (0.1)
+ * @link      <>
+ */
+
+/**
+ * Toolkit_Package_IndexController
+ *
+ * Description of IndexController
+ *
+ * @category  Toolkit
+ * @package   Media
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2013 Gaslight Media
+ * @license   Gaslight Media
+ * @release   Release: (0.1)
+ * @link      <>
+ */
+class Toolkit_Photos_Controllers_IndexController
+    extends Toolkit_BaseControllerAbstract
+    implements Toolkit_IController
+{
+
+    const LIST_CATEGORY_TPL = 'Admin/listCategories.html';
+    const LIST_PHOTOS_TPL   = 'Admin/listPhotos.html';
+    const LIST_USERS_TPL    = 'Admin/listUsers.html';
+    const EDIT_CATEGORY_TPL = 'Admin/editCategory.html';
+    const EDIT_PHOTO_TPL    = 'Admin/editPhoto.html';
+    const EDIT_USER_TPL     = 'Admin/editUser.html';
+    const ADMIN_PAGE_TPL    = 'Admin/index.html';
+
+    public function indexAction()
+    {
+        $pageUrl = $this->registry->page;
+        $flexyOptions = $this->registry->config->flexyOptions->toArray();
+        $tpl = new HTML_Template_Flexy($flexyOptions);
+        $tpl->compile(self::LIST_CATEGORY_TPL);
+        $mediaMapper = new Toolkit_Photos_Models_MediaMapper(
+            $this->registry->dbh
+        );
+        $page = new stdClass();
+        $page->imgPath = PHOTO_SMALL_URL;
+
+        // list template
+        $page->editUrl       = $pageUrl . '?ac=editCategory&id=';
+        $page->deleteUrl     = $pageUrl . '?ac=deleteCategory&id=';
+        $page->categories    = $mediaMapper->fetchAllCategories();
+        return $this->_createPage($tpl->bufferedOutputObject($page));
+    }
+
+    public function ajaxPhotoSearchAction()
+    {
+        $jsonData = array();
+        $mediaMapper = new Toolkit_Photos_Models_MediaMapper(
+            $this->registry->dbh
+        );
+        $photoSearch = filter_var($_REQUEST['photoName'], FILTER_SANITIZE_STRING);
+        $photos      = $mediaMapper->fetchPhotosByName(true);
+        if ($photos) {
+            foreach ($photos as $photo) {
+                $jsonData[] = $photo->getTitle();
+            }
+        }
+        return json_encode($jsonData);
+    }
+
+    public function multipleUploadAction()
+    {
+        define('PHOTOS_UPLOAD_PATH', BASE . 'uploads/photos/');
+
+        HTTP_Session2::useCookies(false);
+        HTTP_Session2::start('photoUploader');
+        $form = new Toolkit_Photos_Admin_FileUploader('file_upload_form');
+        $form->debug = YUI_UPLOADER_DEBUG;
+        $form->configureForm();
+        $formOut = $form->toHtml();
+        return $this->_createPage($formOut);
+    }
+
+    private function _createPage($html)
+    {
+        $GLOBALS['topScripts'][]
+            = MEDIA_APP_BASE_URL . 'libjs/jquery/jquery-1.7.2.min.js';
+        $GLOBALS['topScripts'][]
+            = MEDIA_BASE_URL . 'admin/Photos/checkBrowserSupport.js';
+        $GLOBALS['styleSheets'][]
+            = MEDIA_APP_BASE_URL . 'libjs/jqueryui/1.8.13/development-bundle/themes/base/jquery.ui.all.css';
+        $GLOBALS['styleSheets'][]
+            = MEDIA_BASE_URL . 'Toolkit/Photos/css/style.css';
+        $GLOBALS['styleSheets'][]
+            = MEDIA_BASE_URL . 'css/contactform.css';
+        $GLOBALS['topScripts'][]
+            = MEDIA_APP_BASE_URL . 'libjs/jqueryui/1.8.13/js/jquery-ui-1.8.13.custom.min.js';
+        $mediaMapper      = new Toolkit_Photos_Models_MediaMapper(
+            $this->registry->dbh
+        );
+        $tpl              = new HTML_Template_Flexy(
+            $this->registry->config->flexyOptions->toArray()
+        );
+        $page = new stdClass();
+        $ac    = filter_var($_REQUEST['ac'], FILTER_SANITIZE_STRING);
+        $catId = filter_var($_REQUEST['id'], FILTER_VALIDATE_INT);
+        if ($ac && ($ac == 'editCategory') && $catId) {
+            $page->categoryId = $catId;
+        }
+
+        $page->categorySearchUrl
+            = MEDIA_BASE_URL . 'admin/photos.php';
+        $this->_setNav($page);
+        $page->topScripts    = $this->_getTopScripts();
+        $page->bottomScripts = $this->_getBottomScripts();
+        $page->styles        = $this->_getCss();
+        $page->categories    = $mediaMapper->fetchAllCategories();
+        $page->AppName       = $this->registry->config->application->name;
+        $page->content       = $html;
+        $tpl->compile(self::ADMIN_PAGE_TPL);
+        return $tpl->bufferedOutputObject($page);
+    }
+
+    private function _setNav(&$page)
+    {
+        $page->userAdminUrl = MEDIA_BASE_URL . 'admin/photos.php';
+        $pageUrl = $this->registry->page;
+        $ac      = filter_var($_REQUEST['ac'], FILTER_SANITIZE_STRING);
+
+        // navigation urls
+        if ($ac == 'editCategory') {
+            $catId = filter_var($_REQUEST['id'], FILTER_VALIDATE_INT);
+            if ($catId) {
+                $page->addPhotoUrl
+                    = $pageUrl . '?ac=editPhoto&catid=' . $catId;
+                $page->addMultipleUrl
+                    = $pageUrl . '?ac=multipleUpload&catid=' . $catId;
+            }
+        }
+        if ($ac == 'multipleUpload') {
+            $catId = filter_var($_REQUEST['catid'], FILTER_VALIDATE_INT);
+            if ($catId) {
+                $page->addPhotoUrl
+                    = $pageUrl . '?ac=editPhoto&catid=' . $catId;
+                $page->addMultipleUrl
+                    = $pageUrl . '?ac=multipleUpload&catid=' . $catId;
+            }
+        }
+        $page->addCategoryUrl = $pageUrl . '?ac=editCategory';
+        $page->listCategoryUrl = $pageUrl;
+        if ($this->registry->config->settings->mediaExclusive) {
+            $page->listUsersUrl    = $pageUrl . '?ac=listUsers';
+        }
+    }
+
+    public function searchPhotosAction()
+    {
+       $mediaMapper = new Toolkit_Photos_Models_MediaMapper(
+            $this->registry->dbh
+        );
+        $flexyOptions
+            = $this->registry->config->flexyOptions->toArray();
+        $tpl
+            = new HTML_Template_Flexy($flexyOptions);
+        $page
+            = new stdClass();
+        $page->imgPath = PHOTO_SMALL_URL;
+        $page->photos = $mediaMapper->fetchPhotosByName();
+        $page->editUrl = $this->registry->page
+            . '?ac=editPhoto';
+        $this->_setNav($page);
+        $tpl->compile(self::LIST_PHOTOS_TPL);
+        return $this->_createPage($tpl->bufferedOutputObject($page));
+    }
+
+    public function listUsersAction()
+    {
+        $flexyOptions = $this->registry->config->flexyOptions->toArray();
+        $tpl = new HTML_Template_Flexy($flexyOptions);
+        $tpl->compile(self::LIST_USERS_TPL);
+        $mediaMapper = new Toolkit_Photos_Models_MediaMapper(
+            $this->registry->dbh
+        );
+        $page = new stdClass();
+        $this->_setNav($page);
+        $page->editUrl = $this->registry->page
+            . '?ac=editUser&catid=' . $catId
+            . '&id=';
+        $status = filter_var($_REQUEST['userStatus'], FILTER_SANITIZE_STRING);
+        $filters = array();
+        switch ($status) {
+        case 'approved':
+            $page->statusText = 'Approved';
+            $filters[] = "approved = true";
+            $filters[] = "(expire > current_date OR expire IS NULL)";
+            break;
+        case 'denied':
+            $page->statusText = 'Denied';
+            $filters[] = "denied = true";
+            break;
+        case 'expired':
+            $page->statusText = 'Expired';
+            $filters[] = "expire is not null";
+            $filters[] = "expire < current_date";
+            break;
+        default:
+            $page->statusText = 'Pending';
+            $filters[] = "((approved is null
+                AND denied IS NULL)
+                OR (denied = false AND approved = false))";
+            break;
+        }
+        $page->users = $mediaMapper->fetchUsers($filters);
+        return $this->_createPage($tpl->bufferedOutputObject($page));
+    }
+
+    public function showPagesAction()
+    {
+        $pageTree = new Toolkit_Photos_Admin_PageTree($this->registry->dbh);
+        return $pageTree->toHtml();
+    }
+
+    public function editCategoryAction()
+    {
+        $mediaMapper = new Toolkit_Photos_Models_MediaMapper(
+            $this->registry->dbh
+        );
+        if ($_POST) {
+            $saved = $mediaMapper->saveCategoryWithPost();
+            if ($saved) {
+                header('Location: ' . $this->registry->page);
+                exit;
+            }
+        }
+        $categoryId = filter_var($_REQUEST['id'], FILTER_VALIDATE_INT);
+
+        $GLOBALS['styleSheets'][]
+            = MEDIA_BASE_URL . 'css/contactform.css';
+        $GLOBALS['styleSheets'][]
+            = MEDIA_APP_BASE_URL
+            . 'libjs/jqueryui/1.8.13/development-bundle/themes/base/jquery.ui.all.css';
+        $GLOBALS['bottomScripts'][]
+            = MEDIA_BASE_URL . 'Toolkit/Photos/js/jquery.columnview.js';
+        $flexyOptions
+            = $this->registry->config->flexyOptions->toArray();
+        $tpl
+            = new HTML_Template_Flexy($flexyOptions);
+        $page
+            = new stdClass();
+        $page->mediaExclusive
+            = $this->registry->config->settings->mediaExclusive;
+        $page->slideShow
+            = $this->registry->config->settings->slideShowOption;
+        $page->pages2Categories
+            = ($categoryId)
+            ? $mediaMapper->fetchCategoryPages($categoryId)
+            : null;
+        $page->imgPath = PHOTO_SMALL_URL;
+        $page->photos
+            = ($categoryId)
+            ? $mediaMapper->fetchAllPhotosByCatid($categoryId)
+            : null;
+        $page->categoryId = $categoryId;
+        $page->editUrl = $this->registry->page
+            . '?ac=editPhoto&catid=' . $categoryId
+            . '&id=';
+        $this->_setNav($page);
+        if ($categoryId) {
+            $page->category = $mediaMapper->fetchCategoryById($categoryId);
+        }
+        $page->formAction = $this->registry->page . '?ac=editCategory';
+        $tpl->compile(self::EDIT_CATEGORY_TPL);
+        return $this->_createPage($tpl->bufferedOutputObject($page));
+    }
+
+    public function editPhotoAction()
+    {
+        $mediaMapper = new Toolkit_Photos_Models_MediaMapper(
+            $this->registry->dbh
+        );
+        $catId   = filter_var($_REQUEST['catid'], FILTER_VALIDATE_INT);
+        // grab the category
+        if ($catId) {
+            $photoCategory = $mediaMapper->fetchCategoryById($catId);
+        }
+        $pageUrl = $this->registry->page;
+
+        if ($_POST) {
+            $saved = $mediaMapper->savePhotoWithPost();
+            if ($saved) {
+                header('Location: '
+                    . $this->registry->page.'?ac=editCategory&id='
+                    . $catId);
+                exit;
+            }
+            exit;
+        }
+        $photoId = filter_var($_REQUEST['id'], FILTER_VALIDATE_INT);
+
+        $GLOBALS['styleSheets'][]
+            = MEDIA_BASE_URL . 'css/contactform.css';
+        $GLOBALS['styleSheets'][]
+            = MEDIA_APP_BASE_URL . 'libjs/plugins/asmselect/1.0.4a/jquery.asmselect.css';
+        $GLOBALS['bottomScripts'][]
+            = MEDIA_APP_BASE_URL . 'libjs/plugins/asmselect/1.0.4a/jquery.asmselect.js';
+        $flexyOptions  = $this->registry->config->flexyOptions->toArray();
+        $tpl           = new HTML_Template_Flexy($flexyOptions);
+        $page          = new stdClass();
+        $page->mediaExclusive
+            = $this->registry->config->settings->mediaExclusive;
+        $page->catid   = $catId;
+        $page->photo2Categories
+            = ($photoId)
+            ? $mediaMapper->fetchPhoto2CategoryByPhotoId($photoId)
+            : new ArrayObject();
+        $page->imgPath = PHOTO_SMALL_URL;
+        $page->categoryName= $photoCategory->getCategory();
+        $this->_setNav($page);
+        if ($photoId) {
+            $page->photo = $mediaMapper->fetchPhotoById($photoId);
+        }
+        $page->categories = $mediaMapper->fetchAllCategories();
+        $page->formAction = $this->registry->page
+            . '?ac=editPhoto&catid=' . $catId;
+        $tpl->compile(self::EDIT_PHOTO_TPL);
+        return $this->_createPage($tpl->bufferedOutputObject($page));
+    }
+
+    public function editUserAction()
+    {
+        $GLOBALS['styleSheets'][] = MEDIA_BASE_URL . 'css/contactform.css';
+        $pageUrl = $this->registry->page;
+        $mediaMapper = new Toolkit_Photos_Models_MediaMapper(
+            $this->registry->dbh
+        );
+        if ($_POST) {
+            // if the user is beign approved
+            if ($_REQUEST['status'] == 'approved') {
+                // expire date needs to be greater or equal to current date
+                $expire = filter_var(
+                    $_REQUEST['expire'],
+                    FILTER_VALIDATE_REGEXP,
+                    array(
+                        'options' => array(
+                            'regexp' => '%[0-9]{2}/[0-9]{2}/[0-9]{4}%'
+                        )
+                    )
+                );
+                $isActiveDate
+                    = (!$expire || strtotime($expire) >= mktime());
+                if ($isActiveDate) {
+                    $this->emailUser('approved');
+                }
+            }
+            // if the user is being denied
+            if ($_REQUEST['status'] == 'denied') {
+                $this->emailUser('denied');
+            }
+
+            $saved = $mediaMapper->saveUserWithPost();
+            if ($saved) {
+                $status = filter_var(
+                    $_REQUEST['userStatus'], FILTER_SANITIZE_STRING
+                );
+                header(
+                    'Location: ' . $this->registry->page.'?ac=listUsers'
+                    . '&userStatus=' . $status
+                );
+                exit;
+            }
+            exit;
+        }
+        $userId
+            = filter_var($_REQUEST['id'], FILTER_VALIDATE_INT);
+        $flexyOptions
+            = $this->registry->config->flexyOptions->toArray();
+        $tpl  = new HTML_Template_Flexy($flexyOptions);
+        $page = new stdClass();
+        $this->_setNav($page);
+        if ($userId) {
+            $page->user = $mediaMapper->fetchUserById($userId);
+        }
+        $page->formAction = $this->registry->page . '?ac=editUser';
+        $tpl->compile(self::EDIT_USER_TPL);
+        return $this->_createPage($tpl->bufferedOutputObject($page));
+    }
+
+    public function emailUser($status)
+    {
+        $userId
+            = filter_var($_REQUEST['id'], FILTER_VALIDATE_INT);
+        $mediaMapper = new Toolkit_Photos_Models_MediaMapper(
+            $this->registry->dbh
+        );
+        $page          = new stdClass();
+        $page->user    = $mediaMapper->fetchUserById($userId);
+        if (!$page->user instanceof Toolkit_Photos_Models_User) {
+            return false;
+        }
+        if (!$page->user->getEmail()) {
+            return false;
+        }
+        $page->title      = SITENAME . ' Media Gallery';
+        $page->subject    = 'Your Media Request for Photos - Traverse City';
+        $page->email_from = MEDIA_REQUEST_FORM_EMAIL;
+        switch ($status) {
+        case 'approved' :
+            $page->approved = true;
+            break;
+        case 'denied' :
+            $page->denied = true;
+            break;
+        default :
+            return false;
+        }
+        $flexyOptions = $GLOBALS['flexyOptions'];
+        $flexyOptions['templateDir'] = BASE . 'Toolkit/Photos/templates/Admin';
+        $flexyOptions['compileDir'] = BASE . 'Toolkit/Photos/templates/compiled';
+        $tpl = new HTML_Template_Flexy($flexyOptions);
+        $tpl->compile('emailUser.tpl');
+        $htmlMsg = $tpl->bufferedOutputObject($page);
+        $msg = strip_tags($htmlMsg);
+        $mimeMail = new Mail_mime("\n");
+        $mimeMail->setFrom("Traverse City Media Gallery <".MEDIA_REQUEST_FORM_EMAIL.">");
+        $mimeMail->setSubject($page->subject);
+        $mimeMail->setHTMLBody($htmlMsg);
+        $mimeMail->setTXTBody($msg);
+
+        $mail =& Mail::factory('mail');
+        $body = $mimeMail->get();
+        $setHeader['Reply-To'] = "Traverse City Media Gallery <".MEDIA_REQUEST_FORM_EMAIL.">";
+
+        $headers = $mimeMail->headers($setHeader);
+
+        $res = $mail->send($page->user->getEmail(), $headers, $body);
+        if (PEAR::isError($res)) {
+            return Toolkit_Common::handleError($res);
+        } else {
+            return $res;
+        }
+        exit;
+    }
+
+    public function moveCategoriesAction()
+    {
+        $mediaMapper      = new Toolkit_Photos_Models_MediaMapper(
+            $this->registry->dbh
+        );
+        $values = filter_var_array(
+            $_REQUEST,
+            array(
+                'catPhotos' => array(
+                    'filter' => FILTER_VALIDATE_INT,
+                    'flags'  => FILTER_FORCE_ARRAY
+                )
+            )
+        );
+        if ($values['catPhotos']) {
+            $mediaMapper->saveCategoryPositionsByArray($values['catPhotos']);
+        }
+        return true;
+    }
+
+    public function movePhotosAction()
+    {
+        $categoryId  = filter_var($_REQUEST['categoryId'], FILTER_VALIDATE_INT);
+        if (!$categoryId) {
+            return false;
+        }
+        $mediaMapper = new Toolkit_Photos_Models_MediaMapper(
+            $this->registry->dbh
+        );
+        $values = filter_var_array(
+            $_REQUEST,
+            array(
+                'categoryId' => FILTER_VALIDATE_INT,
+                'photos' => array(
+                    'filter' => FILTER_VALIDATE_INT,
+                    'flags'  => FILTER_FORCE_ARRAY
+                )
+            )
+        );
+        if ($values['photos']) {
+            $mediaMapper->savePhotoPositionsByCategory(
+                $values['photos'],
+                $values['categoryId']
+            );
+        }
+        return '<pre>'.print_r($photo2categories, true).'</pre>';
+    }
+
+    private function _getCss()
+    {
+        return Toolkit_Common::getStyleSheets();
+    }
+
+    private function _getTopScripts()
+    {
+        return Toolkit_Common::getScripts($GLOBALS['topScripts']);
+    }
+
+    private function _getBottomScripts()
+    {
+        return Toolkit_Common::getScripts($GLOBALS['bottomScripts']);
+    }
+
+}
diff --git a/Toolkit/Photos/Database/addSlideShow.sql b/Toolkit/Photos/Database/addSlideShow.sql
new file mode 100644 (file)
index 0000000..3dc1a96
--- /dev/null
@@ -0,0 +1,7 @@
+--
+-- adding slideshow
+--
+
+ALTER TABLE photos.photo_category ADD slideshow BOOLEAN;
+ALTER TABLE photos.photo_category ALTER slideshow SET DEFAULT false;
+UPDATE photos.photo_category SET slideshow = false;
diff --git a/Toolkit/Photos/Database/application.sql b/Toolkit/Photos/Database/application.sql
new file mode 100644 (file)
index 0000000..eb1c72c
--- /dev/null
@@ -0,0 +1,10 @@
+CREATE SCHEMA photos;
+GRANT ALL ON SCHEMA photos TO nobody;
+--
+-- Tables
+--
+\i ./tables/photo_category.sql
+\i ./tables/photo.sql
+\i ./tables/photo_category_bus.sql
+\i ./tables/photo_default.sql
+\i ./tables/photo2category.sql
diff --git a/Toolkit/Photos/Database/removeApplication.sql b/Toolkit/Photos/Database/removeApplication.sql
new file mode 100644 (file)
index 0000000..7428041
--- /dev/null
@@ -0,0 +1,8 @@
+--
+--     This will drop everything in the photos schema.
+--     Nothing better be in here except photos related objects
+--     or it will be dropped
+--
+--     The force is strong w/ this one, use it wisely.
+--
+DROP SCHEMA IF EXISTS photos CASCADE;
diff --git a/Toolkit/Photos/Database/resetSequences.sql b/Toolkit/Photos/Database/resetSequences.sql
new file mode 100644 (file)
index 0000000..4cd9be2
--- /dev/null
@@ -0,0 +1,7 @@
+--
+-- Need to reset sequences for photos tables
+-- try running this file
+--
+SET search_path TO photos;
+SELECT setval('photo_id_seq', (SELECT max(id) FROM photos.photo));
+SELECT setval('photo_category_id_seq', (SELECT max(id) FROM photos.photo_category));
\ No newline at end of file
diff --git a/Toolkit/Photos/Database/tables/photo.sql b/Toolkit/Photos/Database/tables/photo.sql
new file mode 100755 (executable)
index 0000000..7ca56ad
--- /dev/null
@@ -0,0 +1,18 @@
+DROP TABLE IF EXISTS photos.photo CASCADE;
+
+CREATE TABLE photos.photo
+(id SERIAL,
+ title TEXT,
+ description TEXT,
+ image TEXT,
+ catid INTEGER NOT NULL
+    REFERENCES photos.photo_category (id)
+    ON UPDATE CASCADE
+    ON DELETE CASCADE,
+ pos INTEGER,
+ exclusive BOOLEAN DEFAULT false,
+ download BOOLEAN DEFAULT false,
+ PRIMARY KEY (id));
+
+GRANT ALL on photos.photo_id_seq to nobody;
+GRANT ALL on photos.photo to nobody;
diff --git a/Toolkit/Photos/Database/tables/photo2category.sql b/Toolkit/Photos/Database/tables/photo2category.sql
new file mode 100644 (file)
index 0000000..654915e
--- /dev/null
@@ -0,0 +1,13 @@
+DROP TABLE IF EXISTS photos.photo2category CASCADE;
+
+CREATE TABLE photos.photo2category
+(
+    id SERIAL,
+    photo INTEGER,
+    category INTEGER,
+    pos INTEGER,
+    PRIMARY KEY (id)
+);
+
+GRANT ALL ON photos.photo2category TO  nobody;
+GRANT ALL ON photos.photo2category_id_seq TO nobody;
\ No newline at end of file
diff --git a/Toolkit/Photos/Database/tables/photo_category.sql b/Toolkit/Photos/Database/tables/photo_category.sql
new file mode 100755 (executable)
index 0000000..bd22f55
--- /dev/null
@@ -0,0 +1,13 @@
+DROP TABLE IF EXISTS photos.photo_category CASCADE;
+
+CREATE TABLE photos.photo_category
+(id SERIAL,
+ category TEXT,
+ image TEXT,
+ pos INTEGER,
+ exclusive BOOLEAN DEFAULT false,
+ slideshow BOOLEAN DEFAULT false,
+ PRIMARY KEY (id));
+
+GRANT ALL ON photos.photo_category_id_seq TO nobody;
+GRANT ALL ON photos.photo_category TO nobody;
diff --git a/Toolkit/Photos/Database/tables/photo_category_bus.sql b/Toolkit/Photos/Database/tables/photo_category_bus.sql
new file mode 100755 (executable)
index 0000000..41592ec
--- /dev/null
@@ -0,0 +1,20 @@
+DROP TABLE IF EXISTS photos.photo_category_bus CASCADE;
+
+CREATE TABLE photos.photo_category_bus
+(id SERIAL,
+ photocat_id INTEGER
+       REFERENCES photos.photo_category (id) 
+       ON UPDATE CASCADE
+       ON DELETE CASCADE,
+ buscat_id INTEGER
+       REFERENCES toolbox.pages (id)
+       ON UPDATE CASCADE
+       ON DELETE CASCADE,
+ pos INTEGER,
+ PRIMARY KEY (id)
+);
+
+GRANT ALL ON photos.photo_category_bus_id_seq TO nobody;
+GRANT ALL ON photos.photo_category_bus TO  nobody;
+
+CREATE UNIQUE INDEX photo_category_bus_bus_phot_indx on photos.photo_category_bus (buscat_id ,photocat_id);
diff --git a/Toolkit/Photos/Database/tables/photo_default.sql b/Toolkit/Photos/Database/tables/photo_default.sql
new file mode 100755 (executable)
index 0000000..8dc9467
--- /dev/null
@@ -0,0 +1,10 @@
+DROP TABLE IF EXISTS photos.photo_default CASCADE;
+
+CREATE TABLE photos.photo_default
+(id SERIAL,
+ header TEXT,
+ description TEXT, 
+ PRIMARY KEY (id));
+
+GRANT ALL ON photos.photo_default_id_seq TO nobody;
+GRANT ALL ON photos.photo_default TO nobody;
diff --git a/Toolkit/Photos/Database/updatePhotos.sql b/Toolkit/Photos/Database/updatePhotos.sql
new file mode 100644 (file)
index 0000000..b3ee1dc
--- /dev/null
@@ -0,0 +1,20 @@
+--
+-- Update photo tables
+--
+
+\i ./tables/photo2category.sql
+
+ALTER TABLE photos.photo_category ALTER pos SET DEFAULT 1;
+ALTER TABLE photos.photo_category ADD exclusive bool;
+ALTER TABLE photos.photo_category ALTER exclusive set default false;
+UPDATE photos.photo_category SET exclusive = false;
+
+ALTER TABLE photos.photo add exclusive bool;
+ALTER TABLE photos.photo ALTER exclusive set default false;
+UPDATE photos.photo SET exclusive = false;
+
+ALTER TABLE photos.photo add download bool;
+ALTER TABLE photos.photo ALTER download set default false;
+UPDATE photos.photo SET download = false;
+
+ALTER TABLE contacts.contact ADD title TEXT;
\ No newline at end of file
diff --git a/Toolkit/Photos/Database/upgradeApp.sql b/Toolkit/Photos/Database/upgradeApp.sql
new file mode 100644 (file)
index 0000000..d8e285b
--- /dev/null
@@ -0,0 +1,15 @@
+--
+-- Upgrade (movie schemas)
+--
+
+CREATE SCHEMA oldphotos;
+GRANT ALL ON oldphotos TO nobody;
+
+ALTER TABLE public.photo SET SCHEMA oldphotos;
+ALTER TABLE public.photo_category SET SCHEMA oldphotos;
+ALTER TABLE public.photo_category_bus SET SCHEMA oldphotos;
+ALTER TABLE public.photo_default SET SCHEMA oldphotos;
+
+INSERT INTO photos.photo_category (SELECT * FROM oldphotos.photo_category);
+INSERT INTO photos.photo (SELECT * FROM oldphotos.photo);
+INSERT INTO photos.photo_category_bus (SELECT * FROM oldphotos.photo_category_bus);
\ No newline at end of file
diff --git a/Toolkit/Photos/Display.php b/Toolkit/Photos/Display.php
new file mode 100644 (file)
index 0000000..5f88ba4
--- /dev/null
@@ -0,0 +1,384 @@
+<?php
+
+/**
+ * Display.php
+ *
+ * PHP version 5
+ *
+ * @category  Photos
+ * @package   Toolkit_Photos
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2009 Gaslight Media
+ * @license   Gaslight Media
+ * @version   CVS: $Id: Display.php,v 1.10 2010/07/04 23:55:12 jamie Exp $
+ * @link      <>
+ */
+
+/**
+ * Toolkit_Photos_Display
+ *
+ * Display the Photo Gallery assoc to a toolbox page
+ *
+ * @category  Photos
+ * @package   Toolkit_Photos
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2009 Gaslight Media
+ * @license   Gaslight Media
+ * @link      <>
+ */
+class Toolkit_Photos_Display
+{
+    // {{{ Properties
+
+    private $_dbh;
+
+    /**
+     * Options for Flexy Templates
+     * @var    array
+     * @access protected
+     */
+    protected $flexyOptions;
+
+    /**
+     * page name for form action and links
+     * @var    unknown
+     * @access protected
+     */
+    protected $pageName;
+
+    /**
+     * Photo Table Name
+     * @var    string
+     * @access protected
+     */
+    protected $photoTable = 'photo';
+
+    /**
+     * Photo Category Table Name
+     * @var    string
+     * @access protected
+     */
+    protected $categoryTable = 'photo_category';
+
+    /**
+     * rowCount
+     *
+     * @var float
+     * @access protected
+     */
+    protected $rowCount = 4;
+    protected $baseURL;
+    protected $glmAppBaseURL;
+    protected $pageId;
+    protected $config;
+
+    const PHOTO_GALLERY_WRAPPER_TPL = 'photoGalleryWrapper.html';
+    const PHOTO_SLIDER = 'photoSlider.html';
+    // }}}
+    // {{{ __construct()
+
+    /**
+     * __construct()
+     *
+     * @return void
+     * @access public
+     */
+    function __construct(PDO $dbh)
+    {
+        $this->pageId       = filter_var($_REQUEST['catid'], FILTER_VALIDATE_INT);
+        $this->_dbh         = $dbh;
+        // create a Zend Config Object and store into Registry
+        $config             = new Zend_Config_Ini(
+            BASE . 'Toolkit/Photos/application.ini',
+            strtolower($_ENV['GLM_HOST_ID'])
+        );
+        $this->config       = $config;
+        $this->flexyOptions = $config->flexyOptions->toArray();
+
+        // the main display page for events to link to
+        $this->pageName      = BASE_URL . 'index.php?catid=' . $_REQUEST['catid'];
+        $this->baseURL       = ($_SERVER['HTTPS'] == 'on')
+            ? BASE_SECURE_URL
+            : BASE_URL;
+        $this->glmAppBaseURL = ($_SERVER['HTTPS'] == 'on')
+            ? GLM_APP_BASE_SECURE_URL
+            : GLM_APP_BASE_URL;
+    }
+
+    // }}}
+    public function getSlideShow(array $photoCatIds, $galleries)
+    {
+        $GLOBALS['styleSheets'][]   = $this->glmAppBaseURL
+            . 'libjs/plugins/nivoslider/3.2/prod/nivo-slider.css';
+        $GLOBALS['styleSheets'][]   = $this->glmAppBaseURL
+            . 'libjs/plugins/nivoslider/themes/default/default.css';
+        $GLOBALS['bottomScripts'][] = $this->glmAppBaseURL
+            . 'libjs/plugins/nivoslider/3.2/prod/jquery.nivo.slider.js';
+        $tpl = new HTML_Template_Flexy($this->flexyOptions);
+        $tpl->compile(self::PHOTO_SLIDER);
+
+        $page                = new stdClass();
+
+        // Filter the categories so only slideshow ones are in $categories
+        $categories          = $this->getCategories($photoCatIds);
+        $iterator      = new Toolkit_Photos_SlideShowCategoryFilter(
+            $categories->getIterator()
+        );
+        $slideShowCategories = new ArrayObject();
+        $photoCatIds = array();
+        foreach ($iterator as $index) {
+            $slideShowCategories->append($index);
+            $photoCatIds[] = $index['id'];
+        }
+        if (empty($photoCatIds)) {
+            return false;
+        }
+
+        $page->categories    = $slideShowCategories;
+        $page->searchFormUrl = $this->pageName;
+        $page->pageId        = $this->pageId;
+        $page->base_url      = BASE_URL;
+        $page->webUrl        = BASE_URL . 'download-photo-web/';
+        $page->printUrl      = BASE_URL . 'download-photo-print/';
+        $page->photoUrlLarge = FILE_SERVER_URL . IS_OWNER_ID
+            . '/rotatingImagesResized/';
+        $page->photoUrlSmall = FILE_SERVER_URL . IS_OWNER_ID
+            . '/mediaGallery/';
+        $catid               = filter_var($_REQUEST['photoCategoryId']);
+
+        if (count($page->categories) == 1) {
+            $catid = $photoCatIds[0];
+        }
+        $photoCatSelected = ($catid)
+            ? $catid
+            : $photoCatIds[0];
+        $page->photos     = $this->getPhotosByCatid(
+            $photoCatSelected,
+            $this->getLoginStatus()
+        );
+
+
+        return $tpl->bufferedOutputObject($page);
+    }
+    // {{{ toHTML()
+
+    /**
+     * toHTML()
+     *
+     * call to listPhotos function for display of Photos
+     *
+     * @return void
+     * @access public
+     */
+    public function toHTML(array $photoCatIds, $galleries)
+    {
+        $GLOBALS['styleSheets'][]
+            = $this->baseURL . 'photoswipe/photoswipe.css';
+        $GLOBALS['styleSheets'][]
+            = $this->baseURL . 'Toolkit/Photos/css/gallery.css';
+        $GLOBALS['bottomScripts'][]
+            = $this->baseURL . 'photoswipe/lib/klass.min.js';
+        $GLOBALS['bottomScripts'][]
+            = $this->baseURL . 'photoswipe/code.photoswipe.jquery-3.0.5.js';
+        $GLOBALS['bottomScripts'][]
+            = $this->baseURL . 'Toolkit/Photos/js/photoGallery.js';
+
+        $GLOBALS['styleSheets'][] = $this->glmAppBaseURL
+            . 'libjs/jqueryui/1.8.13/development-bundle/themes/base/jquery.ui.all.css';
+        $GLOBALS['styleSheets'][] = $this->baseURL
+            . 'Toolkit/Photos/css/style.css';
+        $GLOBALS['styleSheets'][] = $this->baseURL
+            . 'css/contactform.css';
+        $GLOBALS['topScripts'][]  = $this->glmAppBaseURL
+            . 'libjs/jqueryui/1.8.13/js/jquery-ui-1.8.13.custom.min.js';
+
+        $tpl = new HTML_Template_Flexy($this->flexyOptions);
+        $tpl->compile(self::PHOTO_GALLERY_WRAPPER_TPL);
+
+        $page                             = new stdClass();
+        $page->mediaExclusive             = $this->config->settings->mediaExclusive;
+        $page->photoNameSearch            = $this->config->settings->photoNameSearch;
+        $page->galleries                  = $galleries;
+        $page->pageId                     = $this->pageId;
+        $page->base_url                   = BASE_URL;
+        $page->mediaGalleryRequestFormUrl = BASE_URL . 'media-gallery-request-form-'
+            . MEDIA_GALLERY_REQUEST_FORM . '/';
+        $page->isLoggedIn                 = $this->getLoginStatus();
+        $page->loginUrl                   = BASE_URL . 'Toolkit/Photos/login.php';
+        $page->logoutUrl                  = BASE_URL . 'Toolkit/Photos/login.php?catid='
+            . $_REQUEST['catid'] . '&logout=1';
+        $page->photoSearchFormUrl         = $this->pageName;
+        $page->photoUrlLarge              = PHOTO_LARGE_URL;
+        $page->photoUrlSmall              = FILE_SERVER_URL . IS_OWNER_ID . '/mediaGallery/';
+        $page->photoDownWeb               = BASE_URL . "download-photo-web/";
+        $page->photoDownPrint             = BASE_URL . "download-photo-print/";
+
+        // Filter out the slideshow categories
+        $categories = $this->getCategories($photoCatIds);
+        $iterator   = new Toolkit_Photos_GalleryCategoryFilter(
+            $categories->getIterator()
+        );
+        $galleryCategories = new ArrayObject();
+        $photoCatIds = array();
+        foreach ($iterator as $index) {
+            $galleryCategories->append($index);
+            $photoCatIds[] = $index['id'];
+        }
+        if (empty($photoCatIds)) {
+            return false;
+        }
+        $page->categories                 = $galleryCategories;
+
+        $failedStatus                     = $this->_getFailedLoginStatus();
+        $page->expired                    = false;
+        if ($failedStatus == -4) {
+            $page->failedStatus = '';
+            $page->expired      = true;
+        } else {
+            $page->failedStatus = $failedStatus;
+        }
+
+        $catid     = filter_var($_REQUEST['photoCategoryId'], FILTER_VALIDATE_INT);
+        $photoName = filter_var($_REQUEST['photo_name'], FILTER_SANITIZE_STRING);
+
+        if (count($page->categories) == 1) {
+            $catid = $photoCatIds[0];
+        }
+        if ($photoName) {
+            $mediaMapper  = new Toolkit_Photos_Models_MediaMapper(
+                $this->_dbh
+            );
+            $gallery      = new Toolkit_Photos_Gallery(
+                Toolkit_Database::getInstance(),
+                new Toolkit_Photos_Display(
+                    Toolkit_Database::getInstance()
+                )
+            );
+            $page->photos = $mediaMapper->fetchPhotosByNameAndCats(
+                $photoName, $gallery->getPageGalleries($this->pageId)
+            );
+        } else if ($catid) {
+            $photoCatSelected        = ($catid)
+                ? $catid
+                : $photoCatIds[0];
+            $photoCategory           = $this->getPhotoCategory($photoCatSelected);
+            $page->photoCategoryName = $photoCategory['category'];
+            $page->photos            = $this->getPhotosByCatid(
+                $photoCatSelected, $page->isLoggedIn
+            );
+        } else {
+            $page->photoUrl  = $this->pageName . '&amp;photoCategoryId=';
+            $page->catPhotos = $this->getCategoriesForPage($photoCatIds);
+        }
+
+        return $tpl->bufferedOutputObject($page);
+    }
+
+    // }}}
+
+    public function getLoginStatus()
+    {
+        $authContainer = new Toolkit_Photos_AuthContainer(
+            Toolkit_Database::getInstance()
+        );
+
+        $photoAuth = new Toolkit_Photos_Auth(
+            $authContainer, '', false
+        );
+        $photoAuth->start();
+        return $photoAuth->checkAuth();
+    }
+
+    private function _getFailedLoginStatus()
+    {
+        $failStatus = filter_var($_REQUEST['status'], FILTER_VALIDATE_INT);
+        switch ($failStatus) {
+        case -1:
+            $status = 'Idled';
+            break;
+        case -2:
+            $status = 'Expired';
+            break;
+        case -3:
+            $status = 'Incorrect Username/Password supplied';
+            break;
+        case -4:
+            return -4;
+            $url     = BASE_URL . 'media-gallery-request-form-'
+                . MEDIA_GALLERY_REQUEST_FORM . '/';
+            $status  = 'Your login has expired';
+            $status .= ' <a href="' . $url . '">Ask for more time</a>';
+            break;
+        default:
+            $status = '';
+            break;
+        }
+        return $status;
+    }
+
+    public function getPhotosByCatid($catid, $isLogedIn)
+    {
+        $filter = (!$isLogedIn)
+            ? array("exclusive <> true")
+            : '';
+
+        $mediaMapper = new Toolkit_Photos_Models_MediaMapper(
+            $this->_dbh
+        );
+        $photos      = $mediaMapper->fetchAllPhotosByCatid(
+            $catid, $filter
+        );
+        return $photos;
+    }
+
+    public function getCategoriesForPage($photoCats)
+    {
+        $categories  = new ArrayObject();
+        $mediaMapper = new Toolkit_Photos_Models_MediaMapper(
+            $this->_dbh
+        );
+        if (is_array($photoCats) && !empty($photoCats)) {
+            $index = 0;
+            foreach ($photoCats as $cat) {
+                $categories->offsetSet(
+                    $index, $mediaMapper->fetchCategoryById($cat)
+                );
+                ++$index;
+            }
+        }
+        return $categories;
+    }
+
+    public function getCategories($photoCats)
+    {
+        $categories = new ArrayObject();
+        if (is_array($photoCats) && !empty($photoCats)) {
+            $index = 0;
+            foreach ($photoCats as $cat) {
+                $categories->offsetSet(
+                    $index, $this->getPhotoCategory($cat)
+                );
+                ++$index;
+            }
+        }
+        return $categories;
+    }
+
+    public function getPhotoCategory($categoryId)
+    {
+        try {
+            $sql  = "
+              SELECT *
+                FROM photos.photo_category
+               WHERE id = :id
+            ORDER BY pos";
+            $stmt = $this->_dbh->prepare($sql);
+            $stmt->bindParam(':id', $categoryId, PDO::PARAM_INT);
+            $stmt->execute();
+            return $stmt->fetch(PDO::FETCH_ASSOC);
+        } catch (PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+    }
+
+}
diff --git a/Toolkit/Photos/Exception.php b/Toolkit/Photos/Exception.php
new file mode 100644 (file)
index 0000000..82dfa3c
--- /dev/null
@@ -0,0 +1,3 @@
+<?php
+class Toolkit_Photos_Exception extends Exception {}
+?>
diff --git a/Toolkit/Photos/Factory.php b/Toolkit/Photos/Factory.php
new file mode 100644 (file)
index 0000000..048c5f5
--- /dev/null
@@ -0,0 +1,21 @@
+<?php
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+/**
+ * Description of Factory
+ *
+ * @author steve
+ */
+class Toolkit_Photos_Factory
+{
+    static public function createPhotoFromValues($values)
+    {
+        $newPhoto = new Toolkit_Photos_Photo();
+        $photo = $newPhoto->createByValues($values);
+        return $photo;
+    }
+}
+?>
diff --git a/Toolkit/Photos/Gallery.php b/Toolkit/Photos/Gallery.php
new file mode 100644 (file)
index 0000000..e658738
--- /dev/null
@@ -0,0 +1,169 @@
+<?php
+
+/**
+ * Fetches photo galleries assigned to pages
+ *
+ * PHP version 5
+ *
+ * The license text...
+ *
+ * @category  Toolkit_Photos
+ * @package   Photos
+ * @author    Jamie Kahgee <jamie@gaslightmedia.com>
+ * @copyright 2010 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com/ Gaslightmedia
+ * @version   CVS: $Id: Gallery.php,v 1.1 2010/06/09 19:52:24 jamie Exp $
+ * @link      <>
+ * @see       References to other sections (if any)...
+ */
+
+/**
+ * Fetches photo galleries assigned to pages
+ *
+ * @category  Toolkit_Photos
+ * @package   Photos
+ * @author    Jamie Kahgee <jamie@gaslightmedia.com>
+ * @copyright 2010 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com/ Gaslightmedia
+ * @version   Release: @package_version@
+ * @link      <>
+ * @see       References to other sections (if any)...
+ */
+class Toolkit_Photos_Gallery
+{
+    //    {{{    properties
+
+    /**
+     * Database handler
+     * @var    object
+     * @access private
+     */
+    private $_dbh;
+
+    /**
+     * Toolkit_Photos_Display object
+     * @var    object
+     * @access private
+     */
+    private $_photoApp;
+
+    //    }}}
+    //    {{{    __construct()
+
+    /**
+     * Constructor
+     *
+     * @param PDO                    $dbh      Database handler
+     * @param Toolkit_Photos_Display $photoApp Photo Gallery Display object
+     * @return void
+     * @access public
+     */
+    public function __construct(PDO $dbh, Toolkit_Photos_Display $photoApp)
+    {
+        $this->_dbh      = $dbh;
+        $this->_photoApp = $photoApp;
+    }
+
+    //    }}}
+    //    {{{    _getGalleriesAssignedToPage()
+
+    /**
+     * Gets an array of photo galleries assigned to page
+     *
+     * If we were on a page that contained multiple galleries
+     * and we move into a single gallery page - this function
+     * returns that single gallery, so we can view all its photos
+     *
+     * @param integer $pageId Id of page we're on
+     *
+     * @return array multi-dimensional array of photo galleries assigned to page
+     * @access private
+     * @throws Toolkit_Photos_Exception If can't query for galleries on page
+     */
+    private function _getGalleriesAssignedToPage($pageId)
+    {
+        if (isset($_GET['photo_catid']) && ctype_digit($_GET['photo_catid'])) {
+            return array(
+                array('photocat_id' => $_GET['photo_catid'])
+            );
+        }
+
+        try {
+            $where
+                = (!$this->_photoApp->getLoginStatus())
+                ? " AND exclusive <> true"
+                : '';
+            $sql = "
+              SELECT pcb.photocat_id
+                FROM photo_category_bus pcb, photo_category pc
+               WHERE buscat_id = :pageId
+                 AND pc.id = pcb.photocat_id
+                 AND pc.id IN (
+                     SELECT distinct category
+                       FROM photo2category
+                     )
+                 $where
+            ORDER BY pc.pos";
+
+            $stmt = $this->_dbh->prepare($sql);
+            $stmt->bindParam(':pageId', $pageId, PDO::PARAM_INT);
+            $stmt->execute();
+            return $stmt->fetchAll(PDO::FETCH_ASSOC);
+        } catch (PDOException $e) {
+            Toolkit_Logger::logException('DB Error', $e);
+            throw new Toolkit_Photos_Exception(
+                "Unable to check if page has a gallery `$pageId`"
+            );
+        }
+    }
+
+    //    }}}
+    public function getPageGalleries($pageId)
+    {
+        $cats = array();
+        $galleries = $this->_getGalleriesAssignedToPage($pageId);
+        foreach ($galleries as $galley) {
+            $cats[] = $galley['photocat_id'];
+        }
+        return $cats;
+    }
+    //    {{{    getPageGallery()
+
+    /**
+     * Gets the page galleries / gallery
+     *
+     * @param integer $pageId Id of page we're on
+     *
+     * @return string photo gallery output
+     * @access public
+     */
+    public function getPageGallery($pageId)
+    {
+        $galleries = $this->_getGalleriesAssignedToPage($pageId);
+
+        $photoCatIds = array();
+        foreach ($galleries as $gallery) {
+            $photoCatIds[] = $gallery['photocat_id'];
+        }
+
+        return !empty($photoCatIds)
+            ? $this->_photoApp->toHTML($photoCatIds, $galleries)
+            : '';
+    }
+
+    //    }}}
+
+    public function getPageSlideShow($pageId)
+    {
+        $galleries = $this->_getGalleriesAssignedToPage($pageId);
+
+        $photoCatIds = array();
+        foreach ($galleries as $gallery) {
+            $photoCatIds[] = $gallery['photocat_id'];
+        }
+
+        return !empty($photoCatIds)
+            ? $this->_photoApp->getSlideShow($photoCatIds, $galleries)
+            : '';
+    }
+}
diff --git a/Toolkit/Photos/GalleryCategoryFilter.php b/Toolkit/Photos/GalleryCategoryFilter.php
new file mode 100644 (file)
index 0000000..d90e91e
--- /dev/null
@@ -0,0 +1,46 @@
+<?php
+
+/**
+ * GalleryCategoryFilter.php
+ *
+ * PHP version 5.3
+ *
+ * @category  Toolkit
+ * @package   Photos
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2013 Gaslight Media
+ * @license   Gaslight Media
+ * @version   SVN: (0.1)
+ * @link      <>
+ */
+
+/**
+ * Toolkit_Photos_GalleryCategoryFilter
+ *
+ * Description of SlideShowFilter
+ *
+ * @category  Toolkit
+ * @package   Photos
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2013 Gaslight Media
+ * @license   Gaslight Media
+ * @release   Release: (0.1)
+ * @link      <>
+ */
+class Toolkit_Photos_GalleryCategoryFilter
+    extends FilterIterator
+{
+    public function __construct(Iterator $iterator)
+    {
+        parent::__construct($iterator);
+    }
+
+    public function accept()
+    {
+        $category = $this->getInnerIterator()->current();
+        if (!$category['slideshow']) {
+            return true;
+        }
+        return false;
+    }
+}
diff --git a/Toolkit/Photos/Models/Category.php b/Toolkit/Photos/Models/Category.php
new file mode 100644 (file)
index 0000000..eccd7c8
--- /dev/null
@@ -0,0 +1,156 @@
+<?php
+
+/**
+ * Category.php
+ *
+ * PHP version 5.3
+ *
+ * @category  Toolkit
+ * @package   Media
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2013 Gaslight Media
+ * @license   Gaslight Media
+ * @version   SVN: (0.1)
+ * @link      <>
+ */
+
+/**
+ * Toolkit_Photos_Models_Category
+ *
+ * Represents the Category for Media Gallery
+ *
+ * @category  Toolkit
+ * @package   Media
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2013 Gaslight Media
+ * @license   Gaslight Media
+ * @release   Release: (0.1)
+ * @link      <>
+ */
+class Toolkit_Photos_Models_Category
+{
+    private $_id;
+    private $_category;
+    private $_exclusive;
+    private $_slideshow;
+    private $_pos;
+
+    const TABLE_NAME = 'photos.photo_category';
+    const PRI_KEY    = 'id';
+    const SORT       = 'pos';
+
+    private function __construct($values)
+    {
+        extract($values);
+        $this->setCategory($category)
+            ->setExclusive($exclusive)
+            ->setSlideShow($slideshow)
+            ->setPos($pos);
+        if ($id) {
+            $this->setId($id);
+        }
+    }
+
+    static public function createByValues($values)
+    {
+        return new Toolkit_Photos_Models_Category($values);
+    }
+
+    public function getFirstPhoto()
+    {
+        $dbh = Toolkit_Database::getInstance();
+        try {
+            $sql = "
+            SELECT p.image
+              FROM photos.photo p, photos.photo2category p2c
+             WHERE p2c.pos = 1
+               AND p2c.photo = p.id
+               AND p2c.category = :catid";
+            $stmt = $dbh->prepare($sql);
+            $stmt->bindParam(':catid', $this->getId(), PDO::PARAM_INT);
+            $stmt->execute();
+            return $stmt->fetchColumn();
+        } catch (PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+    }
+
+    public function getTotalNumberOfPhotos()
+    {
+        $dbh = Toolkit_Database::getInstance();
+        try {
+            $sql = "
+            SELECT count(p.id)
+              FROM photos.photo p, photos.photo2category p2c
+             WHERE p2c.photo = p.id
+               AND p2c.category = :catid";
+            $stmt = $dbh->prepare($sql);
+            $stmt->bindParam(':catid', $this->getId(), PDO::PARAM_INT);
+            $stmt->execute();
+            return $stmt->fetchColumn();
+        } catch (PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+    }
+
+    public function getId()
+    {
+        return (int)$this->_id;
+    }
+
+    public function setId($id)
+    {
+        $this->_id
+            = (filter_var($id, FILTER_VALIDATE_INT))
+            ? $id
+            : null;
+        return $this;
+    }
+
+    public function getPos()
+    {
+        return (int)$this->_pos;
+    }
+
+    public function setPos($pos)
+    {
+        $this->_pos = (int)$pos;
+        return $this;
+    }
+
+
+    public function getCategory()
+    {
+        return $this->_category;
+    }
+
+    public function setCategory($category)
+    {
+        $this->_category = $category;
+        return $this;
+    }
+
+    public function getExclusive()
+    {
+        return (bool)$this->_exclusive;
+    }
+
+    public function setExclusive($exclusive)
+    {
+        $this->_exclusive = (bool)$exclusive;
+        return $this;
+    }
+
+    public function getSlideShow()
+    {
+        return (bool)$this->_slideshow;
+    }
+
+    public function setSlideShow($slideshow)
+    {
+        $this->_slideshow = (bool)$slideshow;
+        return $this;
+    }
+
+
+}
diff --git a/Toolkit/Photos/Models/MediaMapper.php b/Toolkit/Photos/Models/MediaMapper.php
new file mode 100644 (file)
index 0000000..53226e7
--- /dev/null
@@ -0,0 +1,1003 @@
+<?php
+
+/**
+ * MediaMapper.php
+ *
+ * PHP version 5.3
+ *
+ * @category  Toolkit
+ * @package   Media
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2013 Gaslight Media
+ * @license   Gaslight Media
+ * @version   SVN: (0.1)
+ * @link      <>
+ */
+
+/**
+ * Toolkit_Photos_Models_MediaMapper
+ *
+ * Maps the Media Objects to methods to create and generate list of
+ * categories and photos.
+ *
+ * @category  Toolkit
+ * @package   Media
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2013 Gaslight Media
+ * @license   Gaslight Media
+ * @release   Release: (0.1)
+ * @link      <>
+ */
+class Toolkit_Photos_Models_MediaMapper
+{
+    private $_dbh;
+
+    public function __construct(PDO $dbh)
+    {
+        $this->_dbh = $dbh;
+    }
+
+    public function fetchAllCategories()
+    {
+        $categories = new ArrayObject();
+        try {
+            $sql = "
+              SELECT *
+                FROM " . Toolkit_Photos_Models_Category::TABLE_NAME . "
+            ORDER BY " . Toolkit_Photos_Models_Category::SORT;
+            $stmt = $this->_dbh->query($sql);
+            while ($category = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                $categories->append(
+                    Toolkit_Photos_Models_Category::createByValues($category)
+                );
+            }
+        } catch (PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+        return $categories;
+    }
+
+    public function fetchAllPhotos($catId)
+    {
+        $photos = new ArrayObject();
+        try {
+            $sql = "
+              SELECT *
+                FROM " . Toolkit_Photos_Models_Photo::TABLE_NAME . "
+               WHERE catid = :catid
+            ORDER BY " . Toolkit_Photos_Models_Photo::SORT;
+            $stmt = $this->_dbh->prepare($sql);
+            $stmt->bindParam(':catid', $catId, PDO::PARAM_INT);
+            $stmt->execute();
+            while ($photo = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                $photos->append(
+                    Toolkit_Photos_Models_Photo::createByValues($photo)
+                );
+            }
+        } catch (PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+        return $photos;
+    }
+
+    public function fetchPhotosByName($distinct = false)
+    {
+        $nameSearch = filter_var($_REQUEST['photoName'], FILTER_SANITIZE_STRING);
+        if (!$nameSearch) {
+            return false;
+        }
+        $photos     = new ArrayObject();
+        try {
+            $nameSearch = $this->_dbh->quote('%' . $nameSearch . '%');
+            $select
+                = (!$distinct)
+                ? '*'
+                : 'DISTINCT ON (title) title';
+            $orderBy
+                = (!$distinct)
+                ? Toolkit_Photos_Models_Photo::SORT
+                : 'title';
+            $sql = "
+              SELECT $select
+                FROM " . Toolkit_Photos_Models_Photo::TABLE_NAME . "
+               WHERE title ilike $nameSearch
+            ORDER BY $orderBy";
+            $stmt = $this->_dbh->query($sql);
+            while ($photo = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                $photos->append(
+                    Toolkit_Photos_Models_Photo::createByValues($photo)
+                );
+            }
+        } catch (PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+        return $photos;
+    }
+
+    public function fetchPhotosByNameAndCats(
+        $nameSearch,
+        $galleries,
+        $distinct = false
+    ) {
+        if (!$nameSearch) {
+            die('here');
+            return false;
+        }
+        $photos     = new ArrayObject();
+        try {
+            $nameSearch = $this->_dbh->quote('%' . $nameSearch . '%');
+            $select
+                = (!$distinct)
+                ? '*'
+                : 'DISTINCT ON (title) title';
+            $where
+                = (!empty($galleries))
+                ? " AND id IN (SELECT photo FROM photo2category WHERE category IN ("
+                . implode(",", $galleries) . "))"
+                : '';
+            $orderBy
+                = (!$distinct)
+                ? Toolkit_Photos_Models_Photo::SORT
+                : 'title';
+            $sql = "
+              SELECT $select
+                FROM " . Toolkit_Photos_Models_Photo::TABLE_NAME . "
+               WHERE title ilike $nameSearch
+                     $where
+            ORDER BY $orderBy";
+            $stmt = $this->_dbh->query($sql);
+            while ($photo = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                $photos->append(
+                    Toolkit_Photos_Models_Photo::createByValues($photo)
+                );
+            }
+        } catch (PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+        return $photos;
+    }
+
+    public function fetchAllPhotosByCatid($catId, $filter = null)
+    {
+        $photos = new ArrayObject();
+        try {
+            $where
+                = ($filter)
+                ? "AND " . implode(" AND ", $filter)
+                : '';
+            $sql = "
+              SELECT p.*
+                FROM " . Toolkit_Photos_Models_Photo::TABLE_NAME . " p,
+                     " . Toolkit_Photos_Models_Photo2Category::TABLE_NAME . " p2c
+               WHERE p2c.category = :catid
+                 AND p2c.photo = p.id
+                 $where
+            ORDER BY p2c.pos";
+            $stmt = $this->_dbh->prepare($sql);
+            $stmt->bindParam(':catid', $catId, PDO::PARAM_INT);
+            $stmt->execute();
+            while ($photo = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                $photos->append(
+                    Toolkit_Photos_Models_Photo::createByValues($photo)
+                );
+            }
+        } catch (PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+        return $photos;
+    }
+
+    public function fetchPhoto2CategoryByPhotoId($photoId)
+    {
+        $photo2Categories = new ArrayObject();
+        try {
+            $sql = "
+              SELECT *
+                FROM " . Toolkit_Photos_Models_Photo2Category::TABLE_NAME . "
+               WHERE photo = :photo
+            ORDER BY " . Toolkit_Photos_Models_Photo2Category::SORT;
+            $stmt = $this->_dbh->prepare($sql);
+            $stmt->bindParam(':photo', $photoId, PDO::PARAM_INT);
+            $stmt->execute();
+            while ($photo = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                $photo2Categories->offsetSet(
+                    $photo['category'],
+                    Toolkit_Photos_Models_Photo2Category::createByValues($photo)
+                );
+            }
+        } catch (PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+        return $photo2Categories;
+    }
+
+    public function fetchPhoto2Category($catId)
+    {
+        $photo2Categories = new ArrayObject();
+        try {
+            $sql = "
+              SELECT *
+                FROM " . Toolkit_Photos_Models_Photo2Category::TABLE_NAME . "
+               WHERE category = :catid
+            ORDER BY " . Toolkit_Photos_Models_Photo2Category::SORT;
+            $stmt = $this->_dbh->prepare($sql);
+            $stmt->bindParam(':catid', $catId, PDO::PARAM_INT);
+            $stmt->execute();
+            while ($photo = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                $photo2Categories->append(
+                    Toolkit_Photos_Models_Photo2Category::createByValues($photo)
+                );
+            }
+        } catch (PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+        return $photo2Categories;
+    }
+
+    public function fetchCategoryPages($catId)
+    {
+        $pages2Categories = new ArrayObject();
+        try {
+            $sql = "
+              SELECT pcb.buscat_id as page, p.navigation_name as page_name, pcb.pos as pos
+                FROM photo_category_bus pcb, photo_category pc, pages p
+               WHERE pc.id IN (
+                    SELECT photocat_id
+                      FROM photo_category_bus
+                     WHERE photocat_id = :catid)
+                 AND pcb.photocat_id = pc.id
+                 AND p.id = pcb.buscat_id
+            ORDER BY pcb.pos";
+            $stmt = $this->_dbh->prepare($sql);
+            $stmt->bindParam(':catid', $catId, PDO::PARAM_INT);
+            $stmt->execute();
+            while ($photo = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                $pages2Categories->append(
+                    $photo
+                );
+            }
+        } catch (PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+        return $pages2Categories;
+    }
+
+    public function fixAllPhoto2Cats()
+    {
+        $sql = "
+        DELETE FROM photos.photo2category";
+        $this->_dbh->query($sql);
+        // first grab all categories
+        $categories = $this->fetchAllCategories();
+        foreach ($categories as $category) {
+            var_dump($category);
+            $photos = $this->fetchAllPhotos($category->getId());
+            var_dump($photos);
+            if ($photos) {
+                foreach ($photos as $photo) {
+                    $photo2Category
+                        = Toolkit_Photos_Models_Photo2Category::createByValues(
+                            array(
+                                'category' => $category->getId(),
+                                'photo'    => $photo->getId(),
+                                'pos'      => $photo->getPos()
+                            )
+                        );
+                    $this->savePhoto2Category($photo2Category);
+                }
+            }
+        }
+        exit;
+    }
+
+    public function fetchPhotoById($photoId)
+    {
+        try {
+            $sql = "
+            SELECT *
+              FROM " . Toolkit_Photos_Models_Photo::TABLE_NAME . "
+             WHERE " . Toolkit_Photos_Models_Photo::PRI_KEY . " = :id";
+            $stmt = $this->_dbh->prepare($sql);
+            $stmt->bindParam(':id', $photoId, PDO::PARAM_INT);
+            $stmt->execute();
+            return Toolkit_Photos_Models_Photo::createByValues(
+                $stmt->fetch(PDO::FETCH_ASSOC)
+            );
+
+        } catch (PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+
+    }
+
+    public function saveCategoryWithPost()
+    {
+        if ($_REQUEST['Cancel']) {
+            return true;
+        }
+        if ($_REQUEST['Delete']) {
+            // delete the category
+            $category = $this->fetchCategoryById($_REQUEST['id']);
+            $this->removeCategory($category);
+            return true;
+        }
+        $postedData = filter_var_array(
+            $_POST,
+            array(
+                'id' => FILTER_VALIDATE_INT,
+                'pos' => FILTER_VALIDATE_INT,
+                'pages' => array(
+                    'filter' => FILTER_VALIDATE_INT,
+                    'flags'  => FILTER_FORCE_ARRAY
+                ),
+                'category' => array(
+                    'filter' => FILTER_SANITIZE_STRIPPED,
+                    'flags'  => FILTER_FLAG_NO_ENCODE_QUOTES
+                ),
+                'exclusive' => FILTER_VALIDATE_BOOLEAN,
+                'slideshow' => FILTER_VALIDATE_BOOLEAN
+            )
+        );
+        if (get_magic_quotes_gpc()) {
+            $postedData['category'] = stripslashes($postedData['category']);
+        }
+
+        $category = Toolkit_Photos_Models_Category::createByValues(
+            $postedData
+        );
+        //var_dump($category);exit;
+        $id = $this->saveCategory($category);
+
+        try {
+            $this->_dbh->beginTransaction();
+            if ($postedData['id']) {
+                $sql = "
+                DELETE
+                  FROM " . Toolkit_Photos_Models_Page2Category::TABLE_NAME . "
+                 WHERE photocat_id = :category";
+                $stmt = $this->_dbh->prepare($sql);
+                $stmt->bindParam(':category', $postedData['id'], PDO::PARAM_INT);
+                $stmt->execute();
+            }
+            if (   is_array($postedData['pages'])
+                && !empty($postedData['pages'])
+            ) {
+                foreach ($postedData['pages'] as $pageId) {
+                    $page2Category
+                        = Toolkit_Photos_Models_Page2Category::createByValues(
+                            array(
+                                'buscat_id'   => $pageId,
+                                'photocat_id' => $category->getId()
+                            )
+                    );
+                    $this->savePage2Category($page2Category);
+                }
+            }
+
+            $this->_dbh->commit();
+        } catch (PDOException $e) {
+            $this->_dbh->rollBack();
+            Toolkit_Common::handleError($e);
+        }
+
+        return true;
+    }
+
+    public function saveCategory(
+        Toolkit_Photos_Models_Category $category
+    ) {
+        if ($category->getId()) {
+            $sql = "
+            UPDATE " . Toolkit_Photos_Models_Category::TABLE_NAME . "
+               SET category = :category,
+                   exclusive = :exclusive,
+                   slideshow = :slideshow,
+                   pos = :pos
+             WHERE " . Toolkit_Photos_Models_Category::PRI_KEY . " = :id";
+
+        } else {
+            $sql = "
+            INSERT INTO " . Toolkit_Photos_Models_Category::TABLE_NAME . "
+            (category, exclusive, slideshow, pos)
+            VALUES
+            (:category, :exclusive, :slideshow, :pos)
+            RETURNING " . Toolkit_Photos_Models_Category::PRI_KEY;
+        }
+        $stmt = $this->_dbh->prepare($sql);
+        $stmt->bindParam(':category', $category->getCategory());
+        $stmt->bindParam(':pos', $category->getPos(), PDO::PARAM_INT);
+        $stmt->bindParam(':exclusive', $category->getExclusive(), PDO::PARAM_BOOL);
+        $stmt->bindParam(':slideshow', $category->getSlideShow(), PDO::PARAM_BOOL);
+        if ($category->getId()) {
+            $stmt->bindParam(':id', $category->getId(), PDO::PARAM_INT);
+        }
+        $stmt->execute();
+        if (!$category->getId()) {
+            $category->setId($stmt->fetchColumn());
+        }
+        return $category->getId();
+    }
+
+    public function savePage2Category(
+        Toolkit_Photos_Models_Page2Category $page2Category
+    ) {
+        if ($page2Category->getId()) {
+            $sql = "
+            UPDATE " . Toolkit_Photos_Models_Page2Category::TABLE_NAME . "
+               SET photocat_id = :category,
+                   buscat_id = :page
+             WHERE " . Toolkit_Photos_Models_Page2Category::PRI_KEY . " = :id";
+
+        } else {
+            $sql = "
+            INSERT INTO " . Toolkit_Photos_Models_Page2Category::TABLE_NAME . "
+            (photocat_id, buscat_id)
+            VALUES
+            (:category, :page)
+            RETURNING " . Toolkit_Photos_Models_Page2Category::PRI_KEY;
+        }
+        $stmt = $this->_dbh->prepare($sql);
+        $stmt->bindParam(':page', $page2Category->getBuscatId(), PDO::PARAM_INT);
+        $stmt->bindParam(':category', $page2Category->getPhotocatId(), PDO::PARAM_INT);
+        if ($page2Category->getId()) {
+            $stmt->bindParam(':id', $page2Category->getId(), PDO::PARAM_INT);
+        }
+        $stmt->execute();
+        return ($page2Category->getId())
+            ? $page2Category->getId()
+            : $stmt->fetchColumn();
+    }
+
+    public function savePhoto2Category(
+        Toolkit_Photos_Models_Photo2Category $photo2Category
+    ) {
+        if ($photo2Category->getId()) {
+            $sql = "
+            UPDATE " . Toolkit_Photos_Models_Photo2Category::TABLE_NAME . "
+               SET photo = :photo,
+                   category = :category,
+                   pos = :pos
+             WHERE " . Toolkit_Photos_Models_Photo2Category::PRI_KEY . " = :id";
+
+        } else {
+            $sql = "
+            INSERT INTO " . Toolkit_Photos_Models_Photo2Category::TABLE_NAME . "
+            (photo, category, pos)
+            VALUES
+            (:photo, :category, :pos)
+            RETURNING " . Toolkit_Photos_Models_Photo2Category::PRI_KEY;
+        }
+        $stmt = $this->_dbh->prepare($sql);
+        $stmt->bindParam(':photo', $photo2Category->getPhoto(), PDO::PARAM_INT);
+        $stmt->bindParam(':category', $photo2Category->getCategory(), PDO::PARAM_INT);
+        $stmt->bindParam(':pos', $photo2Category->getPos(), PDO::PARAM_INT);
+        if ($photo2Category->getId()) {
+            $stmt->bindParam(':id', $photo2Category->getId(), PDO::PARAM_INT);
+        }
+        $stmt->execute();
+        return ($photo2Category->getId())
+            ? $photo2Category->getId()
+            : $stmt->fetchColumn();
+    }
+
+    public function fetchCategoryById($categoryId)
+    {
+        try {
+            $sql = "
+            SELECT *
+              FROM " . Toolkit_Photos_Models_Category::TABLE_NAME . "
+             WHERE " . Toolkit_Photos_Models_Category::PRI_KEY . " = :id";
+            $stmt = $this->_dbh->prepare($sql);
+            $stmt->bindParam(':id', $categoryId, PDO::PARAM_INT);
+            $stmt->execute();
+            return Toolkit_Photos_Models_Category::createByValues(
+                $stmt->fetch(PDO::FETCH_ASSOC)
+            );
+
+        } catch (PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+
+    }
+
+    public function savePhotoPositionsByCategory($photos, $categoryId)
+    {
+        if (!is_array($photos) || empty($photos)) {
+            return false;
+        }
+        var_dump($photos);
+        var_dump($categoryId);
+        try {
+            $this->_dbh->beginTransaction();
+            $sql = "
+            DELETE
+              FROM " . Toolkit_Photos_Models_Photo2Category::TABLE_NAME . "
+             WHERE category = :category";
+            $stmt = $this->_dbh->prepare($sql);
+            $stmt->bindParam(':category', $categoryId, PDO::PARAM_INT);
+            $stmt->execute();
+
+            foreach ($photos as $position => $photoId) {
+                $photo2Category
+                    = Toolkit_Photos_Models_Photo2Category::createByValues(
+                        array(
+                            'photo'    => $photoId,
+                            'category' => $categoryId,
+                            'pos'      => $position + 1
+                        )
+                    );
+                $this->savePhoto2Category($photo2Category);
+                var_dump($photo2Category);
+            }
+
+            $this->_dbh->commit();
+        } catch (PDOException $e) {
+            $this->_dbh->rollBack();
+            Toolkit_Common::handleError($e);
+        }
+
+    }
+
+    public function saveCategoryPositionsByArray($cats)
+    {
+        if (!is_array($cats) || empty($cats)) {
+            return false;
+        }
+//        var_dump($cats);exit;
+        foreach ($cats as $position => $catId) {
+            $category = $this->fetchCategoryById($catId);
+            $category->setPos((int)$position + 1);
+            $this->saveCategory($category);
+        }
+    }
+
+    public function savePhotoWithPost()
+    {
+        if ($_REQUEST['Cancel']) {
+            return true;
+        }
+        if ($_REQUEST['Delete']) {
+            // delete the photo
+            $photo = $this->fetchPhotoById($_REQUEST['id']);
+            $this->removePhoto($photo);
+            return true;
+        }
+
+        if (   $_FILES['image']['name']
+            && !$_FILES['image']['error']
+        ) {
+            $fs = new Toolkit_FileServer_ImageAdapter();
+            $image = $fs->upload('image');
+        }
+        $postedData = filter_var_array(
+            $_POST,
+            array(
+                'id' => FILTER_VALIDATE_INT,
+                'catid' => FILTER_VALIDATE_INT,
+                'title' => array(
+                    'filter' => FILTER_SANITIZE_STRING,
+                    'flags'  => FILTER_FLAG_NO_ENCODE_QUOTES
+                ),
+                'image' => array(
+                    'filter' => FILTER_SANITIZE_STRING,
+                    'flags'  => FILTER_FLAG_NO_ENCODE_QUOTES
+                ),
+                'description' => array(
+                    'filter' => FILTER_SANITIZE_STRING,
+                    'flags'  => FILTER_FLAG_NO_ENCODE_QUOTES
+                ),
+                'photo2Categories' => array(
+                    'filter' => FILTER_VALIDATE_INT,
+                    'flags'  => FILTER_FORCE_ARRAY
+                ),
+                'pos'       => FILTER_VALIDATE_INT,
+                'del_img'   => FILTER_VALIDATE_BOOLEAN,
+                'download'  => FILTER_VALIDATE_BOOLEAN,
+                'exclusive' => FILTER_VALIDATE_BOOLEAN,
+            )
+        );
+
+        if (!$image && $postedData['del_img']) {
+            $postedData['image'] = '';
+        } else if ($image) {
+            $postedData['image'] = $image['name'];
+        }
+        $photo = Toolkit_Photos_Models_Photo::createByValues(
+            $postedData
+        );
+        $photoId = $this->savePhoto($photo);
+        $postedData['id'] = $photoId;
+        $this->savePhoto2CategoryData($postedData);
+        return $photoId;
+    }
+
+    public function savePhoto2CategoryData($data)
+    {
+        if (!is_array($data) || empty($data)) {
+            return false;
+        }
+//        var_dump($data);
+        // need to get the current photo2category records for this photo
+        $currentCategories = $this->fetchPhoto2CategoryByPhotoId($data['id']);
+//        var_dump($currentCategories);
+        foreach ($data['photo2Categories'] as $catId) {
+            // if the offest does not exists then the photo need to be
+            // added to the category
+            if (!$currentCategories->offsetExists($catId)) {
+                $photoCatsNew = $this->fetchPhoto2Category($catId);
+                $newPosition = $photoCatsNew->count() + 1;
+                // need to add photo to the photo2category record as last pos
+                $newPhoto2Cat
+                    = Toolkit_Photos_Models_Photo2Category::createByValues(
+                        array(
+                            'photo'    => $data['id'],
+                            'category' => $catId,
+                            'pos'      => $newPosition
+                        )
+                    );
+//                var_dump($newPhoto2Cat);
+                $this->savePhoto2Category($newPhoto2Cat);
+//                var_dump('adding record for category ' . $catId);
+            }
+            // for each step through remove the iteration in the ArrayObject
+            // that way we know if we have to remove any records and reorder
+            $currentCategories->offsetUnset($catId);
+        }
+        foreach ($currentCategories as $photo2Category) {
+//            var_dump($photo2Category);
+            $this->removePhoto2Category($photo2Category);
+        }
+    }
+
+    public function removeCategory(
+        Toolkit_Photos_Models_Category $category
+    ) {
+        // if there's any photos in the category that are only in that
+        // category then it will need to delete them.
+        try {
+            // 1. delete this category
+            $sql = "
+            DELETE
+              FROM photos.photo_category
+             WHERE id = :id";
+            $stmt = $this->_dbh->prepare($sql);
+            $stmt->bindParam(':id', $category->getId(), PDO::PARAM_INT);
+            $stmt->execute();
+            // 2. delete all references in photo2category table for this category
+            $sql = "
+            DELETE
+              FROM photos.photo2category
+             WHERE category = :id";
+            $stmt = $this->_dbh->prepare($sql);
+            $stmt->bindParam(':id', $category->getId(), PDO::PARAM_INT);
+            $stmt->execute();
+        } catch (PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+
+    }
+
+    public function removePhoto(
+        Toolkit_Photos_Models_Photo $photo
+    ) {
+        $photo2Categories = $this->fetchPhoto2CategoryByPhotoId(
+            $photo->getId()
+        );
+
+        foreach ($photo2Categories as $photoCat) {
+            $this->removePhoto2Category($photoCat);
+        }
+        try {
+            $sql = "
+            DELETE
+              FROM photos.photo
+             WHERE id = :id";
+            $stmt = $this->_dbh->prepare($sql);
+            $stmt->bindParam(':id', $photo->getId(), PDO::PARAM_INT);
+            $stmt->execute();
+        } catch (PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+
+    }
+
+    public function removePhoto2Category(
+        Toolkit_Photos_Models_Photo2Category $photo2Category
+    ) {
+        try {
+            $sql = "
+            DELETE
+              FROM " . Toolkit_Photos_Models_Photo2Category::TABLE_NAME . "
+             WHERE id = :id";
+            $stmt = $this->_dbh->prepare($sql);
+            $stmt->bindParam(':id', $photo2Category->getId(), PDO::PARAM_INT);
+            $stmt->execute();
+            $sql = "
+            UPDATE " . Toolkit_Photos_Models_Photo2Category::TABLE_NAME . "
+               SET pos = pos - 1
+             WHERE pos >= :pos
+               AND category = :category";
+            $stmt = $this->_dbh->prepare($sql);
+            $stmt->bindParam(':pos', $photo2Category->getPos(), PDO::PARAM_INT);
+            $stmt->bindParam(':category', $photo2Category->getCategory(), PDO::PARAM_INT);
+            $stmt->execute();
+        } catch (PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+
+    }
+
+    public function savePhoto(
+        Toolkit_Photos_Models_Photo $photo
+    ) {
+        try {
+            if ($photo->getId()) {
+                $sql = "
+                UPDATE " . Toolkit_Photos_Models_Photo::TABLE_NAME . "
+                   SET title = :title,
+                       catid = :catid,
+                       description = :description,
+                       image = :image,
+                       pos = :pos,
+                       download = :download,
+                       exclusive = :exclusive
+                 WHERE " . Toolkit_Photos_Models_Photo::PRI_KEY . " = :id";
+
+            } else {
+                $sql = "
+                INSERT INTO " . Toolkit_Photos_Models_Photo::TABLE_NAME . "
+                (title, catid, description, image, pos, download, exclusive)
+                VALUES
+                (:title, :catid, :description, :image, :pos, :download, :exclusive)
+                RETURNING " . Toolkit_Photos_Models_Photo::PRI_KEY;
+            }
+            $stmt = $this->_dbh->prepare($sql);
+            $stmt->bindParam(':title', $photo->getTitle());
+            $stmt->bindParam(':catid', $photo->getCatid(), PDO::PARAM_INT);
+            $stmt->bindParam(':description', $photo->getDescription());
+            $stmt->bindParam(':image', $photo->getImage());
+            $stmt->bindParam(':pos', $photo->getPos(), PDO::PARAM_INT);
+            $stmt->bindParam(':download', $photo->getDownload(), PDO::PARAM_BOOL);
+            $stmt->bindParam(':exclusive', $photo->getExclusive(), PDO::PARAM_BOOL);
+            if ($photo->getId()) {
+                $stmt->bindParam(':id', $photo->getId(), PDO::PARAM_INT);
+            }
+            $stmt->execute();
+        } catch (PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+
+        return ($photo->getId())
+            ? $photo->getId()
+            : $stmt->fetchColumn();
+    }
+
+    public function saveUserWithPost()
+    {
+        $postedData = filter_var_array(
+            $_POST,
+            array(
+                'id'     => FILTER_VALIDATE_INT,
+                'status' => FILTER_SANITIZE_STRING,
+                'expire' => array(
+                    'filter'  => FILTER_VALIDATE_REGEXP,
+                    'options' => array(
+                        'regexp' => '%[0-9]{2}/[0-9]{2}/[0-9]{4}%'
+                    )
+                ),
+                'phone'      => FILTER_SANITIZE_STRING,
+                'email'      => array(
+                    'filter' => FILTER_SANITIZE_STRING,
+                    'flags'  => FILTER_FLAG_NO_ENCODE_QUOTES
+                ),
+                'media_pass' => array(
+                    'filter' => FILTER_SANITIZE_STRING,
+                    'flags'  => FILTER_FLAG_NO_ENCODE_QUOTES
+                ),
+                'company'  => array(
+                    'filter' => FILTER_SANITIZE_STRING,
+                    'flags'  => FILTER_FLAG_NO_ENCODE_QUOTES
+                ),
+                'title'  => array(
+                    'filter' => FILTER_SANITIZE_STRING,
+                    'flags'  => FILTER_FLAG_NO_ENCODE_QUOTES
+                ),
+                'fname'  => array(
+                    'filter' => FILTER_SANITIZE_STRING,
+                    'flags'  => FILTER_FLAG_NO_ENCODE_QUOTES
+                ),
+                'lname' => array(
+                    'filter' => FILTER_SANITIZE_STRING,
+                    'flags'  => FILTER_FLAG_NO_ENCODE_QUOTES
+                ),
+                'address' => array(
+                    'filter' => FILTER_SANITIZE_STRING,
+                    'flags'  => FILTER_FLAG_NO_ENCODE_QUOTES
+                ),
+                'address2' => array(
+                    'filter' => FILTER_SANITIZE_STRING,
+                    'flags'  => FILTER_FLAG_NO_ENCODE_QUOTES
+                ),
+                'city' => array(
+                    'filter' => FILTER_SANITIZE_STRING,
+                    'flags'  => FILTER_FLAG_NO_ENCODE_QUOTES
+                ),
+                'state' => array(
+                    'filter' => FILTER_SANITIZE_STRING,
+                    'flags'  => FILTER_FLAG_NO_ENCODE_QUOTES
+                ),
+                'zip' => array(
+                    'filter' => FILTER_SANITIZE_STRING,
+                    'flags'  => FILTER_FLAG_NO_ENCODE_QUOTES
+                )
+            )
+        );
+        if ($postedData['status'] == 'approved') {
+            $postedData['approved'] = true;
+            $postedData['denied']   = false;
+        } else {
+            $postedData['approved'] = false;
+            $postedData['denied']   = true;
+        }
+        if (!$postedData['expire']) {
+            $postedData['expire'] = null;
+        }
+
+        $user = Toolkit_Photos_Models_User::createByValues(
+            $postedData
+        );
+        return $this->saveUser($user);
+    }
+
+    public function saveUser(
+        Toolkit_Photos_Models_User $user
+    ) {
+        if ($user->getId()) {
+            $sql = "
+            UPDATE " . Toolkit_Photos_Models_User::TABLE_NAME . "
+               SET company = :company,
+                   title = :title,
+                   fname = :fname,
+                   lname = :lname,
+                   email = :email,
+                   phone = :phone,
+                   address = :address,
+                   address2 = :address2,
+                   city = :city,
+                   state = :state,
+                   zip = :zip,
+                   media = true,
+                   approved = :approved,
+                   denied = :denied,
+                   expire = :expire
+             WHERE " . Toolkit_Photos_Models_User::PRI_KEY . " = :id";
+
+        } else {
+            $sql = "
+            INSERT INTO " . Toolkit_Photos_Models_User::TABLE_NAME . "
+            (fname, lname, email, phone,
+            address, address2, city, state, zip,
+            approved, denied, expire, media)
+            VALUES
+            (:fname, :lname, :email, :phone,
+            :address, :address2, :city, :state, :zip,
+            :approved, :denied, :expire, true)
+            RETURNING " . Toolkit_Photos_Models_User::PRI_KEY;
+        }
+        $stmt = $this->_dbh->prepare($sql);
+        $stmt->bindParam(':expire', $user->getExpire());
+        $stmt->bindParam(':company', $user->getCompany());
+        $stmt->bindParam(':title', $user->getTitle());
+        $stmt->bindParam(':fname', $user->getFname());
+        $stmt->bindParam(':lname', $user->getLname());
+        $stmt->bindParam(':email', $user->getEmail());
+        $stmt->bindParam(':phone', $user->getPhone());
+        $stmt->bindParam(':address', $user->getAddress());
+        $stmt->bindParam(':address2', $user->getAddress2());
+        $stmt->bindParam(':city', $user->getCity());
+        $stmt->bindParam(':state', $user->getState());
+        $stmt->bindParam(':zip', $user->getZip());
+        $stmt->bindParam(':approved', $user->getApproved(), PDO::PARAM_BOOL);
+        $stmt->bindParam(':denied', $user->getDenied(), PDO::PARAM_BOOL);
+        if ($user->getId()) {
+            $stmt->bindParam(':id', $user->getId(), PDO::PARAM_INT);
+        }
+        $stmt->execute();
+        return ($user->getId())
+            ? $user->getId()
+            : $stmt->fetchColumn();
+    }
+
+    public function fetchUserById($userId)
+    {
+        try {
+            $sql = "
+            SELECT *
+              FROM " . Toolkit_Photos_Models_User::TABLE_NAME . "
+             WHERE " . Toolkit_Photos_Models_User::PRI_KEY . " = :id
+               AND " . Toolkit_Photos_Models_User::WHERE;
+            $stmt = $this->_dbh->prepare($sql);
+            $stmt->bindParam(':id', $userId, PDO::PARAM_INT);
+            $stmt->execute();
+            return Toolkit_Photos_Models_User::createByValues(
+                $stmt->fetch(PDO::FETCH_ASSOC)
+            );
+
+        } catch (PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+
+    }
+
+    public function fetchUserByEmail($email)
+    {
+        try {
+            $sql = "
+            SELECT *
+              FROM " . Toolkit_Photos_Models_User::TABLE_NAME . "
+             WHERE " . Toolkit_Photos_Models_User::EMAIL . " = :email
+               AND " . Toolkit_Photos_Models_User::WHERE;
+            $stmt = $this->_dbh->prepare($sql);
+            $stmt->bindParam(':email', $email, PDO::PARAM_INT);
+            $stmt->execute();
+            return Toolkit_Photos_Models_User::createByValues(
+                $stmt->fetch(PDO::FETCH_ASSOC)
+            );
+
+        } catch (PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+
+    }
+
+    public function fetchAllUsers()
+    {
+        $users = new ArrayObject();
+        try {
+            $sql = "
+              SELECT *
+                FROM " . Toolkit_Media_Models_User::TABLE_NAME . "
+               WHERE media = true
+            ORDER BY " . Toolkit_Media_Models_User::SORT;
+            $sql .= " LIMIT 10 OFFSET 0";
+            $stmt = $this->_dbh->prepare($sql);
+            $stmt->execute();
+            while ($photo = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                $users->append(
+                    Toolkit_Media_Models_User::createByValues($photo)
+                );
+            }
+        } catch (PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+        return $users;
+    }
+
+    public function fetchUsers(array $filters = null)
+    {
+        $users = new ArrayObject();
+        try{
+            $where = (is_array($filters) && !empty($filters))
+                ? "AND " . implode(" AND ", $filters)
+                : '';
+            $sql = "
+              SELECT *
+                FROM " . Toolkit_Photos_Models_User::TABLE_NAME . "
+               WHERE media = true
+              $where
+            ORDER BY " . Toolkit_Photos_Models_User::SORT;
+            $sql .= " LIMIT 10 OFFSET 0";
+            $stmt = $this->_dbh->prepare($sql);
+            $stmt->execute();
+            while ($photo = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                $users->append(
+                    Toolkit_Photos_Models_User::createByValues($photo)
+                );
+            }
+        } catch (PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+        return $users;
+    }
+
+}
diff --git a/Toolkit/Photos/Models/Page2Category.php b/Toolkit/Photos/Models/Page2Category.php
new file mode 100644 (file)
index 0000000..f79ec4d
--- /dev/null
@@ -0,0 +1,92 @@
+<?php
+
+/**
+ * Photo2Category.php
+ *
+ * PHP version 5.3
+ *
+ * @category  Toolkit
+ * @package   Photos
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2013 Gaslight Media
+ * @license   Gaslight Media
+ * @version   SVN: (0.1)
+ * @link      <>
+ */
+
+/**
+ * Toolkit_Package_Photo2Category
+ *
+ * Description of Photo2Category
+ *
+ * @category  Toolkit
+ * @package   Photos
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2013 Gaslight Media
+ * @license   Gaslight Media
+ * @release   Release: (0.1)
+ * @link      <>
+ */
+class Toolkit_Photos_Models_Page2Category
+{
+    private $_id;
+    private $_photocatId;
+    private $_buscatId;
+
+    const TABLE_NAME = 'photos.photo_category_bus';
+    const PRI_KEY    = 'id';
+    const SORT       = 'pos';
+
+    private function __construct($values)
+    {
+        extract($values);
+        $this->setBuscatId($buscat_id)
+            ->setPhotocatId($photocat_id);
+        if ($id) {
+            $this->setId($id);
+        }
+    }
+
+    static public function createByValues($values)
+    {
+        return new Toolkit_Photos_Models_Page2Category($values);
+    }
+
+    public function getId()
+    {
+        return (int)$this->_id;
+    }
+
+    public function setId($id)
+    {
+        $this->_id
+            = (filter_var($id, FILTER_VALIDATE_INT))
+            ? $id
+            : null;
+        return $this;
+    }
+
+    public function getPhotocatId()
+    {
+        return $this->_photocatId;
+    }
+
+    public function setPhotocatId($photocatId)
+    {
+        $this->_photocatId = $photocatId;
+        return $this;
+    }
+
+    public function getBuscatId()
+    {
+        return $this->_buscatId;
+    }
+
+    public function setBuscatId($buscatId)
+    {
+        $this->_buscatId = $buscatId;
+        return $this;
+    }
+
+
+}
diff --git a/Toolkit/Photos/Models/Photo.php b/Toolkit/Photos/Models/Photo.php
new file mode 100644 (file)
index 0000000..0d3d8be
--- /dev/null
@@ -0,0 +1,157 @@
+<?php
+
+/**
+ * Photo.php
+ *
+ * PHP version 5.3
+ *
+ * @category  Toolkit
+ * @package   Media
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2013 Gaslight Media
+ * @license   Gaslight Media
+ * @version   SVN: (0.1)
+ * @link      <>
+ */
+
+/**
+ * Toolkit_Photos_Models_Photo
+ *
+ * Represents the Photo for Media Gallery
+ *
+ * @category  Toolkit
+ * @package   Media
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2013 Gaslight Media
+ * @license   Gaslight Media
+ * @release   Release: (0.1)
+ * @link      <>
+ */
+class Toolkit_Photos_Models_Photo
+{
+    private $_id;
+    private $_title;
+    private $_description;
+    private $_image;
+    private $_catid;
+    private $_pos;
+    private $_download;
+    private $_exclusive;
+
+    const TABLE_NAME = 'photos.photo';
+    const PRI_KEY    = 'id';
+    const SORT       = 'pos';
+
+    private function __construct($values)
+    {
+        extract($values);
+        $this->setTitle($title)
+            ->setDescription($description)
+            ->setImage($image)
+            ->setCatid($catid)
+            ->setPos($pos)
+            ->setDownload($download)
+            ->setExclusive($exclusive);
+        if ($id) {
+            $this->setId($id);
+        }
+    }
+
+    static public function createByValues($values)
+    {
+        return new Toolkit_Photos_Models_Photo($values);
+    }
+
+    public function getId()
+    {
+        return (int)$this->_id;
+    }
+
+    public function setId($id)
+    {
+        $this->_id
+            = (filter_var($id, FILTER_VALIDATE_INT))
+            ? $id
+            : null;
+        return $this;
+    }
+
+    public function getTitle()
+    {
+        return $this->_title;
+    }
+
+    public function setTitle($title)
+    {
+        $this->_title = $title;
+        return $this;
+    }
+
+    public function getDescription()
+    {
+        return $this->_description;
+    }
+
+    public function setDescription($description)
+    {
+        $this->_description = $description;
+        return $this;
+    }
+
+    public function getImage()
+    {
+        return $this->_image;
+    }
+
+    public function setImage($image)
+    {
+        $this->_image = $image;
+        return $this;
+    }
+
+    public function getCatid()
+    {
+        return (int)$this->_catid;
+    }
+
+    public function setCatid($catid)
+    {
+        $this->_catid = (int)$catid;
+        return $this;
+    }
+
+    public function getPos()
+    {
+        return (int)$this->_pos;
+    }
+
+    public function setPos($pos)
+    {
+        $this->_pos = (int)$pos;
+        return $this;
+    }
+
+    public function getDownload()
+    {
+        return (bool)$this->_download;
+    }
+
+    public function setDownload($download)
+    {
+        $this->_download = (bool)$download;
+        return $this;
+    }
+
+    public function getExclusive()
+    {
+        return (bool)$this->_exclusive;
+    }
+
+    public function setExclusive($exclusive)
+    {
+        $this->_exclusive = (bool)$exclusive;
+        return $this;
+    }
+
+
+}
diff --git a/Toolkit/Photos/Models/Photo2Category.php b/Toolkit/Photos/Models/Photo2Category.php
new file mode 100644 (file)
index 0000000..76b5249
--- /dev/null
@@ -0,0 +1,105 @@
+<?php
+
+/**
+ * Photo2Category.php
+ *
+ * PHP version 5.3
+ *
+ * @category  Toolkit
+ * @package   Photos
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2013 Gaslight Media
+ * @license   Gaslight Media
+ * @version   SVN: (0.1)
+ * @link      <>
+ */
+
+/**
+ * Toolkit_Package_Photo2Category
+ *
+ * Description of Photo2Category
+ *
+ * @category  Toolkit
+ * @package   Photos
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2013 Gaslight Media
+ * @license   Gaslight Media
+ * @release   Release: (0.1)
+ * @link      <>
+ */
+class Toolkit_Photos_Models_Photo2Category
+{
+    private $_id;
+    private $_photo;
+    private $_category;
+    private $_pos;
+
+    const TABLE_NAME = 'photos.photo2category';
+    const PRI_KEY    = 'id';
+    const SORT       = 'pos';
+
+    private function __construct($values)
+    {
+        extract($values);
+        $this->setCategory($category)
+            ->setPhoto($photo)
+            ->setPos($pos);
+        if ($id) {
+            $this->setId($id);
+        }
+    }
+
+    static public function createByValues($values)
+    {
+        return new Toolkit_Photos_Models_Photo2Category($values);
+    }
+
+    public function getId()
+    {
+        return (int)$this->_id;
+    }
+
+    public function setId($id)
+    {
+        $this->_id
+            = (filter_var($id, FILTER_VALIDATE_INT))
+            ? $id
+            : null;
+        return $this;
+    }
+
+    public function getPhoto()
+    {
+        return (int)$this->_photo;
+    }
+
+    public function setPhoto($photo)
+    {
+        $this->_photo = (int)$photo;
+        return $this;
+    }
+
+    public function getCategory()
+    {
+        return (int)$this->_category;
+    }
+
+    public function setCategory($category)
+    {
+        $this->_category = (int)$category;
+        return $this;
+    }
+
+    public function getPos()
+    {
+        return (int)$this->_pos;
+    }
+
+    public function setPos($pos)
+    {
+        $this->_pos = (int)$pos;
+        return $this;
+    }
+
+
+}
diff --git a/Toolkit/Photos/Models/User.php b/Toolkit/Photos/Models/User.php
new file mode 100644 (file)
index 0000000..9345b34
--- /dev/null
@@ -0,0 +1,279 @@
+<?php
+
+/**
+ * User.php
+ *
+ * PHP version 5.3
+ *
+ * @category  Toolkit
+ * @package   Media
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2013 Gaslight Media
+ * @license   Gaslight Media
+ * @version   SVN: (0.1)
+ * @link      <>
+ */
+
+/**
+ * Toolkit_Photos_Model_User
+ *
+ * Class representation of the User
+ *
+ * @category  Toolkit
+ * @package   Media
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2013 Gaslight Media
+ * @license   Gaslight Media
+ * @release   Release: (0.1)
+ * @link      <>
+ */
+class Toolkit_Photos_Models_User
+{
+    private $_id;
+    private $_company;
+    private $_title;
+    private $_fname;
+    private $_lname;
+    private $_email;
+    private $_address;
+    private $_address2;
+    private $_city;
+    private $_state;
+    private $_zip;
+    private $_phone;
+    private $_media;
+    private $_expire;
+    private $_mediaPass;
+    private $_approved;
+    private $_denied;
+
+    const TABLE_NAME = 'contacts.contact';
+    const PRI_KEY    = 'id';
+    const SORT       = 'lname,fname';
+    const EMAIL      = 'email';
+    const WHERE      = 'media = true';
+
+    private function __construct($values)
+    {
+        extract($values);
+        $this->setCompany($company)
+            ->setTitle($title)
+            ->setAddress($address)
+            ->setAddress2($address2)
+            ->setApproved($approved)
+            ->setMediaPass($media_pass)
+            ->setCity($city)
+            ->setDenied($denied)
+            ->setEmail($email)
+            ->setExpire($expire)
+            ->setFname($fname)
+            ->setLname($lname)
+            ->setMedia($media)
+            ->setPhone($phone)
+            ->setState($state)
+            ->setZip($zip);
+        if ($id) {
+            $this->setId($id);
+        }
+    }
+
+    public function createByValues($values)
+    {
+        if (is_array($values) && !empty($values)) {
+            return new Toolkit_Photos_Models_User($values);
+        } else {
+            return false;
+        }
+    }
+
+    public function getMediaPass()
+    {
+        return $this->_mediaPass;
+    }
+
+    public function setMediaPass($mediaPass)
+    {
+        $this->_mediaPass = $mediaPass;
+        return $this;
+    }
+
+
+    public function getId()
+    {
+        return (int)$this->_id;
+    }
+
+    public function setId($id)
+    {
+        $this->_id = (int)$id;
+        return $this;
+    }
+
+    public function getCompany()
+    {
+        return $this->_company;
+    }
+
+    public function setCompany($company)
+    {
+        $this->_company = $company;
+        return $this;
+    }
+
+    public function getTitle()
+    {
+        return $this->_title;
+    }
+
+    public function setTitle($title)
+    {
+        $this->_title = $title;
+        return $this;
+    }
+
+
+    public function getFname()
+    {
+        return $this->_fname;
+    }
+
+    public function setFname($fname)
+    {
+        $this->_fname = $fname;
+        return $this;
+    }
+
+    public function getLname()
+    {
+        return $this->_lname;
+    }
+
+    public function setLname($lname)
+    {
+        $this->_lname = $lname;
+        return $this;
+    }
+
+    public function getEmail()
+    {
+        return $this->_email;
+    }
+
+    public function setEmail($email)
+    {
+        $this->_email = $email;
+        return $this;
+    }
+
+    public function getAddress()
+    {
+        return $this->_address;
+    }
+
+    public function setAddress($address)
+    {
+        $this->_address = $address;
+        return $this;
+    }
+
+    public function getAddress2()
+    {
+        return $this->_address2;
+    }
+
+    public function setAddress2($address2)
+    {
+        $this->_address2 = $address2;
+        return $this;
+    }
+
+    public function getCity()
+    {
+        return $this->_city;
+    }
+
+    public function setCity($city)
+    {
+        $this->_city = $city;
+        return $this;
+    }
+
+    public function getState()
+    {
+        return $this->_state;
+    }
+
+    public function setState($state)
+    {
+        $this->_state = $state;
+        return $this;
+    }
+
+    public function getZip()
+    {
+        return $this->_zip;
+    }
+
+    public function setZip($zip)
+    {
+        $this->_zip = $zip;
+        return $this;
+    }
+
+    public function getPhone()
+    {
+        return $this->_phone;
+    }
+
+    public function setPhone($phone)
+    {
+        $this->_phone = $phone;
+        return $this;
+    }
+
+    public function getMedia()
+    {
+        return $this->_media;
+    }
+
+    public function setMedia($media)
+    {
+        $this->_media = (bool)$media;
+        return $this;
+    }
+
+    public function getExpire()
+    {
+        return $this->_expire;
+    }
+
+    public function setExpire($expire)
+    {
+        $this->_expire = $expire;
+        return $this;
+    }
+
+    public function getApproved()
+    {
+        return (bool)$this->_approved;
+    }
+
+    public function setApproved($approved)
+    {
+        $this->_approved = (bool)$approved;
+        return $this;
+    }
+
+    public function getDenied()
+    {
+        return (bool)$this->_denied;
+    }
+
+    public function setDenied($denied)
+    {
+        $this->_denied = (bool)$denied;
+        return $this;
+    }
+
+
+}
diff --git a/Toolkit/Photos/Photo.php b/Toolkit/Photos/Photo.php
new file mode 100644 (file)
index 0000000..12c9b08
--- /dev/null
@@ -0,0 +1,23 @@
+<?php
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+/**
+ * Description of Photo
+ *
+ * @author steve
+ */
+class Toolkit_Photos_Photo
+    extends Toolkit_Table
+{
+    public $tableName = 'photo';
+    protected $id;
+    protected $title;
+    protected $description;
+    protected $image;
+    protected $catid;
+    protected $pos;
+}
+
diff --git a/Toolkit/Photos/SlideShowCategoryFilter.php b/Toolkit/Photos/SlideShowCategoryFilter.php
new file mode 100644 (file)
index 0000000..df6d871
--- /dev/null
@@ -0,0 +1,46 @@
+<?php
+
+/**
+ * SlideShowCategoryFilter.php
+ *
+ * PHP version 5.3
+ *
+ * @category  Toolkit
+ * @package   Photos
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2013 Gaslight Media
+ * @license   Gaslight Media
+ * @version   SVN: (0.1)
+ * @link      <>
+ */
+
+/**
+ * Toolkit_Photos_SlideShowCategoryFilter
+ *
+ * Description of SlideShowFilter
+ *
+ * @category  Toolkit
+ * @package   Photos
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2013 Gaslight Media
+ * @license   Gaslight Media
+ * @release   Release: (0.1)
+ * @link      <>
+ */
+class Toolkit_Photos_SlideShowCategoryFilter
+    extends FilterIterator
+{
+    public function __construct(Iterator $iterator)
+    {
+        parent::__construct($iterator);
+    }
+
+    public function accept()
+    {
+        $category = $this->getInnerIterator()->current();
+        if ($category['slideshow']) {
+            return true;
+        }
+        return false;
+    }
+}
diff --git a/Toolkit/Photos/application.ini b/Toolkit/Photos/application.ini
new file mode 100644 (file)
index 0000000..d576e1c
--- /dev/null
@@ -0,0 +1,45 @@
+; Production server configuration data
+[production]
+; Application settings
+application.name = "Image Library"
+application.path = BASE "Toolkit/Photos/Controllers"
+application.application = "Photos/Controllers"
+
+; turn on media exclusive flag for categories and photos
+; This option also may need other new fields not added yet to the contact table
+settings.mediaExclusive  = Off
+settings.photoNameSearch = Off
+settings.slideShowOption = Off
+
+; default Flexy Options for this application
+flexyOptions.templateDir     = BASE "Toolkit/Photos/templates"
+flexyOptions.compileDir      = BASE "Toolkit/Photos/templates/compiled"
+flexyOptions.url_rewrite     = "baseurl/::" BASE_URL ",basesecureurl/::" BASE_SECURE_URL ",glmappbaseurl/::" GLM_APP_BASE_URL
+flexyOptions.forceCompile    = Off
+flexyOptions.locale          = "en"
+flexyOptions.debug           = Off
+flexyOptions.allowPHP        = On
+flexyOptions.flexyIgnore     = On
+flexyOptions.globals         = On
+flexyOptions.globalfunctions = On
+flexyOptions.privates        = On
+flexyOptions.compiler        = "Flexy"
+
+; development server configuration data inherits from production and
+; overrides values as necessary
+[development : production]
+
+; chuck's server configuration data inherits from development
+; and overrides values as necessary
+[chuck : development]
+
+; john's server configuration data inherits from development
+; and overrides values as necessary
+[john : development]
+
+; steve's server configuration data inherits from development
+; and overrides values as necessary
+[steve : development]
+; Vagrant server configuration inherits from development and
+; overrides values as needed
+[vagrant : development]
diff --git a/Toolkit/Photos/assets/.keepme b/Toolkit/Photos/assets/.keepme
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/Toolkit/Photos/css/gallery.css b/Toolkit/Photos/css/gallery.css
new file mode 100755 (executable)
index 0000000..545a01c
--- /dev/null
@@ -0,0 +1,28 @@
+.header {
+    margin: 10px 20px;
+    }
+#photo-gallery {
+    margin: 10px 20px;
+}
+#glmMediaGallery {
+    background: #F5F3E5;
+    border-width: 0 1px 1px;
+    border: 1px solid #A6C9E2;
+    padding: 15px;
+}
+div.thumb {
+    margin: 3px 9px;
+    border: 0px solid #A0ACC0;
+    height: auto;
+    float: left;
+    text-align: center;
+}
+.thumb img{
+    display: inline;
+    margin: 3px;
+    border: 1px solid #A0ACC0;
+    }
+.thumb a:hover img {border: 1px solid black;}
+.photocattitle {text-align: center; font-weight: bold;}
+.phototitle {text-align: center; font-weight: normal; width: 110px;}
+.galleryRow {clear: left;}
diff --git a/Toolkit/Photos/css/style.css b/Toolkit/Photos/css/style.css
new file mode 100644 (file)
index 0000000..f26d4c0
--- /dev/null
@@ -0,0 +1,344 @@
+.containerobj .widget {
+    background-color: white;
+}
+.containerobj .active .widget {
+    color: white;
+    background-color: #3671cf;
+}
+.containerobj .inpath .widget {
+    background-color: #d0d0d0;
+}
+#demo1 {
+    height: 200px;
+}
+#demo1 a {
+    padding: 0 3px;
+}
+.ui-dialog-buttonpane {
+    padding: 0 10px 0 20px !important;
+}
+#dialog-modal {
+    height: auto !important;
+}
+
+#addPageTo {
+    /*display: inline;
+    width: 82px;
+    height: 28px;
+    border: none;
+    text-align: left;
+    padding: 0 0 0 24px;*/
+    margin: 6px 0 0 0;
+    float: right;
+}
+#deleteBtn2 {
+    display: block;
+    position:absolute;
+    bottom: 36px;
+    right: 5px;
+    width: 55px;
+    height: 28px;
+    background: url(../assets/btn_delete.gif) no-repeat;
+    border: none;
+    padding: 0 0 0 27px;
+    font-size: 14px;
+    font-size: 1.4rem;
+    line-height: 28px;
+    text-decoration: none;
+    margin: 0px;
+}
+.ui-state-highlight {
+    color: #09F;
+    border-color: #09F;
+    background: #09F;
+}
+.ui-widget-content {
+    margin: 0;
+    padding: 0;
+}
+#photoGalleryFooter {
+    display: none;
+    padding: 10px;
+    text-align: center;
+    width: 100%;
+    float: bottom;
+}
+
+/* Search Form styles */
+#photoAdminSearch input[type="search"],
+    #categoryAdminSearch select {
+        height: 26px;
+        font-size: 18px;
+        width: 300px;
+        border: 1px solid #CCCCCC;
+        background-color: #EFEFEF !important;
+    }
+    #mediaGallerySearchForm {
+        padding: 0;
+        margin: 20px 0 20px;
+    }
+    #categorySearchWrapper {
+        width: 800px;
+        height: 34px;
+        position: relative;
+        border-bottom: 1px solid #CCC;
+    }
+    #categoryAdminSearch select {
+        display: block;
+        position: relative;
+        float: left;
+        z-index: 99;
+        left: 0;
+        top: 0;
+        width: 37.75%;
+        margin: 0 .5% 0 0;
+        font-size: 14px;
+        font-weight: normal;
+        color: #666;
+        height: 27px;
+        -webkit-box-sizing: border-box; /* Safari/Chrome, other WebKit */
+        -moz-box-sizing: border-box;    /* Firefox, other Gecko */
+        box-sizing: border-box;         /* Opera/IE 8+ */
+        padding:.2em;/.3em;
+    }
+    #categoryAdminSearch input[type="submit"], #photoAdminSearch input[type="submit"] {
+        display: block;
+        position: relative;
+        float: left;
+        z-index: 99;
+        right: 0;
+        top: 0;
+        width: 10%;
+        margin: 0;
+        font-size: 14px;
+        font-weight: normal;
+        color: #666;
+        margin: 0;
+        height: 28px;
+        -webkit-box-sizing: border-box; /* Safari/Chrome, other WebKit */
+        -moz-box-sizing: border-box;    /* Firefox, other Gecko */
+        box-sizing: border-box;         /* Opera/IE 8+ */
+    }
+    #photoAdminSearch input[type="search"] {
+        display: block;
+        position: relative;
+        float: left;
+        z-index: 99;
+        left: 0;
+        top: 0;
+        width: 37.75%;
+        margin: 0 .5% 0 3%;
+        font-size: 14px;
+        font-weight: normal;
+        color: #333;
+        height: 27px;
+        -webkit-box-sizing: border-box; /* Safari/Chrome, other WebKit */
+        -moz-box-sizing: border-box;    /* Firefox, other Gecko */
+        box-sizing: border-box;         /* Opera/IE 8+ */
+        padding:.2em;/.3em;
+        padding-left: 6px;
+    }
+    div.photo-category-list {
+        background: #EFEFEF;
+        border-radius: 5px;
+        display: block;
+        float: left;
+        height: 148px;
+        margin: 0 20px 20px 0;
+        padding: 10px;
+        position: relative;
+        width: 120px;
+        border: 1px solid #CCC;
+        -webkit-box-shadow: 0px 0px 5px rgba(0, 0, 0, 0.2);
+        -moz-box-shadow:    0px 0px 5px rgba(0, 0, 0, 0.2);
+        box-shadow:         0px 0px 5px rgba(0, 0, 0, 0.2);
+    }
+    div.pCount {
+        background-color: #333;
+        border-radius: 30px 30px 30px 30px;
+        -webkit-box-shadow: 0px 0px 5px rgba(0, 0, 0, 0.2);
+        -moz-box-shadow:    0px 0px 5px rgba(0, 0, 0, 0.2);
+        box-shadow:         0px 0px 5px rgba(0, 0, 0, 0.2);
+        color: white;
+        display: inline;
+        float: right;
+        font-weight: bold;
+        padding: 1px 5px;
+        position: absolute;
+        right: -8px;
+        text-shadow: 1px 1px grey;
+        top: 0;
+    }
+    div.photo-category-list h1 {
+        bottom: 10px;
+        left: 10px;
+        margin: 0;
+        overflow: hidden;
+        padding: 0;
+        position: absolute;
+        width: 123px;
+    }
+    div.photo-category-list img {
+        border: medium none;
+        border-radius: 5px;
+        -webkit-box-shadow: inset 0px 0px 5px rgba(0, 0, 0, 0.2);
+        -moz-box-shadow:    inset 0px 0px 5px rgba(0, 0, 0, 0.2);
+        box-shadow:         inset 0px 0px 5px rgba(0, 0, 0, 0.2);
+        border: 1px solid #CCC;
+    }
+
+/* edit category form styles */
+#editCategoryForm .webform {
+        margin: 10px 0;
+        width: 400px;
+        border: 1px solid #CCC;
+        border-radius: 5px;
+        padding: 10px 2%;
+    }
+    #editCategoryForm .webform td {
+        width: 100%;
+        float: left;
+        border: 0;
+        text-align: left;
+    }
+    #editCategoryForm .webform td input[type="text"] {
+        display: block;
+        position: relative;
+        z-index: 99;
+        left: 0;
+        top: 0;
+        width: 100%;
+        margin: 0;
+        font-size: 14px;
+        font-weight: normal;
+        color: #333;
+        height: 27px;
+        -webkit-box-sizing: border-box; /* Safari/Chrome, other WebKit */
+        -moz-box-sizing: border-box;    /* Firefox, other Gecko */
+        box-sizing: border-box;         /* Opera/IE 8+ */
+        padding:.2em;/.3em;
+        padding-left: 6px;
+        background: #EFEFEF !important;
+        border: 1px solid #CCC;
+    }
+    .category-category-list {
+        position: relative;
+        box-shadow: 1px 2px 2px grey;
+        border-radius: 10px;
+        -moz-border-radius: 10px;
+        -webkit-border-radius: 10px;
+        display:block;
+        width: 120px;
+        float: left;
+        background: #D6DFC3;
+        margin: 5px;
+        padding: 10px;
+        height: 130px;
+    }
+    .category-category-list h1 {
+        position: absolute;
+        bottom: 0;
+        left: 5px;
+        margin: 0;
+        padding: 0;
+        overflow: hidden;
+        width: 123px;;
+    }
+    .category-category-list h1 a {
+        color: #006BB4;
+        font-size: 12px;
+        text-decoration: none;
+        padding: 0;
+        margin: 0;
+        overflow: hidden;
+        white-space: nowrap;
+    }
+    .category-category-list img {
+        box-shadow: 1px 2px 2px grey;
+        border-radius: 10px;
+        -moz-border-radius: 10px;
+        -webkit-border-radius: 10px;
+        border: none;
+    }
+    #addPageTo, form#editCategoryForm input[type="submit"] {
+        /*box-shadow: 1px 2px 2px grey;
+        background-color: #D6DFC3;
+        font-size: 16px;
+        font-weight: normal;
+        text-align: center;
+        margin: 0;
+        padding: 1px 5px 1px 5px;
+        width: auto;*/
+    }
+    .pageLink {
+        /*border-radius: 10px;*/
+        /*box-shadow: 1px 2px 2px grey;
+        width: 280px;
+        background-color: #D6DFC3;
+        padding: 5px;
+        margin: 5px 0;
+        float: left;*/
+        width: 96%;
+        padding: 5px 2%;
+        border-bottom: 1px solid #CCC;
+    }
+    .btnDelete {
+        float: right;
+    }
+    #displayWrapper {
+        margin: 3px 0;
+        border-left: 1px solid #CCC;
+        border-right: 1px solid #CCC;
+        border-top: 1px solid #CCC;
+        background: #EFEFEF;
+    }
+    #categorySave {
+        width: 31.333333%;
+        margin: 0 2% 0 0;
+    }
+    #categoryCancel {
+        width: 31.333333%;
+        margin: 0 2% 0 0;
+    }
+    #categoryDelete {
+        width: 31.333333%;
+        margin: 0;
+    }
+    #pagesLabel {
+        padding-top: 5px;
+    }
+    div.category-category-list {
+        background: #EFEFEF;
+        border-radius: 5px;
+        display: block;
+        float: left;
+        height: 148px;
+        margin: 0 20px 20px 0;
+        padding: 10px;
+        position: relative;
+        width: 120px;
+        border: 1px solid #CCC;
+        -webkit-box-shadow: 0px 0px 5px rgba(0, 0, 0, 0.2);
+        -moz-box-shadow:    0px 0px 5px rgba(0, 0, 0, 0.2);
+        box-shadow:         0px 0px 5px rgba(0, 0, 0, 0.2);
+    }
+    div.category-category-list h1 {
+        bottom: 10px;
+        left: 10px;
+        margin: 0;
+        overflow: hidden;
+        padding: 0;
+        position: absolute;
+        width: 123px;
+    }
+    div.category-category-list img {
+        border: medium none;
+        border-radius: 5px;
+        -webkit-box-shadow: inset 0px 0px 5px rgba(0, 0, 0, 0.2);
+        -moz-box-shadow:    inset 0px 0px 5px rgba(0, 0, 0, 0.2);
+        box-shadow:         inset 0px 0px 5px rgba(0, 0, 0, 0.2);
+        border: 1px solid #CCC;
+    }
+    div#categoryPhotos {
+        margin-top: 20px;
+    }
diff --git a/Toolkit/Photos/css/thickbox.css b/Toolkit/Photos/css/thickbox.css
new file mode 100755 (executable)
index 0000000..f69f4e4
--- /dev/null
@@ -0,0 +1,168 @@
+/* ----------------------------------------------------------------------------------------------------------------*/
+/* ---------->>> global settings needed for thickbox <<<-----------------------------------------------------------*/
+/* ----------------------------------------------------------------------------------------------------------------*/
+/* *{padding: 0; margin: 0;} */
+
+/* ----------------------------------------------------------------------------------------------------------------*/
+/* ---------->>> thickbox specific link and font settings <<<------------------------------------------------------*/
+/* ----------------------------------------------------------------------------------------------------------------*/
+#TB_window {
+    font: 12px Arial, Helvetica, sans-serif;
+    color: #333333;
+}
+
+#TB_secondLine {
+    font: 10px Arial, Helvetica, sans-serif;
+    color:#666666;
+}
+
+#TB_window a:link {color: #666666;}
+#TB_window a:visited {color: #666666;}
+#TB_window a:hover {color: #000;}
+#TB_window a:active {color: #666666;}
+#TB_window a:focus{color: #666666;}
+
+/* ----------------------------------------------------------------------------------------------------------------*/
+/* ---------->>> thickbox settings <<<-----------------------------------------------------------------------------*/
+/* ----------------------------------------------------------------------------------------------------------------*/
+#TB_overlay {
+    position: fixed;
+    z-index:100;
+    top: 0px;
+    left: 0px;
+    height:100%;
+    width:100%;
+}
+
+.TB_overlayMacFFBGHack {background: url(macFFBgHack.png) repeat;}
+.TB_overlayBG {
+    background-color:#000;
+    filter:alpha(opacity=75);
+    -moz-opacity: 0.75;
+    opacity: 0.75;
+}
+
+* html #TB_overlay { /* ie6 hack */
+     position: absolute;
+     height: expression(document.body.scrollHeight > document.body.offsetHeight ? document.body.scrollHeight : document.body.offsetHeight + 'px');
+}
+
+#TB_window {
+    position: fixed;
+    background: #ffffff;
+    z-index: 102;
+    color:#000000;
+    display:none;
+    border: 4px solid #525252;
+    text-align:left;
+    top:50%;
+    left:50%;
+}
+
+* html #TB_window { /* ie6 hack */
+position: absolute;
+margin-top: expression(0 - parseInt(this.offsetHeight / 2) + (TBWindowMargin = document.documentElement && document.documentElement.scrollTop || document.body.scrollTop) + 'px');
+}
+
+#TB_window img#TB_Image {
+    display:block;
+    margin: 15px 0 0 15px;
+    border-right: 1px solid #ccc;
+    border-bottom: 1px solid #ccc;
+    border-top: 1px solid #666;
+    border-left: 1px solid #666;
+}
+
+#TB_caption{
+/*    height:25px; */
+    padding:7px 30px 10px 25px;
+    float:left;
+}
+
+#TB_DownloadLinks {
+    padding:7px 30px 10px 25px;
+    float:left;
+}
+
+#TB_closeWindow{
+    height:25px;
+    padding:11px 25px 10px 0;
+    float:right;
+}
+
+#TB_closeAjaxWindow{
+    padding:7px 10px 5px 0;
+    margin-bottom:1px;
+    text-align:right;
+    float:right;
+}
+
+#TB_ajaxWindowTitle{
+    float:left;
+    padding:7px 0 5px 10px;
+    margin-bottom:1px;
+}
+
+#TB_title{
+    background-color:#e8e8e8;
+    height:27px;
+}
+
+#TB_ajaxContent{
+    clear:both;
+    padding:2px 15px 15px 15px;
+    overflow:auto;
+    text-align:left;
+    line-height:1.4em;
+}
+
+#TB_ajaxContent.TB_modal{
+    padding:15px;
+}
+
+#TB_ajaxContent p{
+    padding:5px 0px 5px 0px;
+}
+
+#TB_load{
+    position: fixed;
+    display:none;
+    height:13px;
+    width:208px;
+    z-index:103;
+    top: 50%;
+    left: 50%;
+    margin: -6px 0 0 -104px; /* -height/2 0 0 -width/2 */
+}
+
+* html #TB_load { /* ie6 hack */
+position: absolute;
+margin-top: expression(0 - parseInt(this.offsetHeight / 2) + (TBWindowMargin = document.documentElement && document.documentElement.scrollTop || document.body.scrollTop) + 'px');
+}
+
+#TB_HideSelect{
+    z-index:99;
+    position:fixed;
+    top: 0;
+    left: 0;
+    background-color:#fff;
+    border:none;
+    filter:alpha(opacity=0);
+    -moz-opacity: 0;
+    opacity: 0;
+    height:100%;
+    width:100%;
+}
+
+* html #TB_HideSelect { /* ie6 hack */
+     position: absolute;
+     height: expression(document.body.scrollHeight > document.body.offsetHeight ? document.body.scrollHeight : document.body.offsetHeight + 'px');
+}
+
+#TB_iframeContent{
+    clear:both;
+    border:none;
+    margin-bottom:-1px;
+    margin-top:1px;
+    _margin-bottom:1px;
+}
diff --git a/Toolkit/Photos/js/jquery.columnview.js b/Toolkit/Photos/js/jquery.columnview.js
new file mode 100755 (executable)
index 0000000..53a3834
--- /dev/null
@@ -0,0 +1,267 @@
+/**
+ * jquery.columnview-1.2.js
+ *
+ * Created by Chris Yates on 2009-02-26.
+ * http://christianyates.com
+ * Copyright 2009 Christian Yates and ASU Mars Space Flight Facility. All rights reserved.
+ *
+ * Supported under jQuery 1.2.x or later
+ * Keyboard navigation supported under 1.3.x or later
+ * 
+ * Dual licensed under MIT and GPL.
+ */
+
+(function($){
+  $.fn.columnview = function(options){
+
+    var settings = $.extend({}, $.fn.columnview.defaults, options);
+        
+    // Add stylesheet, but only once
+    if(!$('.containerobj').get(0)){
+      $('head').prepend('\
+      <style type="text/css" media="screen">\
+        .containerobj {\
+          border: 1px solid #ccc;\
+          height:5em;\
+          overflow-x:auto;\
+          overflow-y:hidden;\
+          white-space:nowrap;\
+          position:relative;\
+        }\
+        .containerobj div {\
+          height:100%;\
+          overflow-y:scroll;\
+          overflow-x:hidden;\
+          position:absolute;\
+        }\
+        .containerobj a {\
+          display:block;\
+          white-space:nowrap;\
+          clear:both;\
+          padding-right:15px;\
+          overflow:hidden;\
+          text-decoration:none;\
+        }\
+        .containerobj a:focus {\
+          outline:none;\
+        }\
+        .containerobj a canvas {\
+        }\
+        .containerobj .feature {\
+          min-width:200px;\
+          overflow-y:auto;\
+        }\
+        .containerobj .feature a {\
+          white-space:normal;\
+        }\
+        .containerobj .hasChildMenu {\
+        }\
+        .containerobj .active {\
+          background-color:#3671cf;\
+          color:#fff;\
+        }\
+        .containerobj .inpath {\
+          background-color:#d0d0d0;\
+          color:#000;\
+        }\
+        .containerobj .hasChildMenu .widget {\
+          color:black;\
+          position:absolute;\
+          right:0;\
+          text-decoration:none;\
+          font-size:0.7em;\
+        }\
+      </style>');
+    }
+
+    // Hide original list
+    $(this).hide();
+    // Reset the original list's id
+    var origid = $(this).attr('id');
+    if (origid) {
+      $(this).attr('id', origid + "-processed");
+    }
+
+    // Create new top container from top-level LI tags
+    var top = $(this).children('li');
+    var container = $('<div/>').addClass('containerobj').attr('id', origid).insertAfter(this);
+    var topdiv = $('<div class="top"></div>').appendTo(container);
+    // Set column width
+    if (settings.fixedwidth || $.browser.msie) { // MSIE doesn't support auto-width
+      var width = typeof settings.fixedwidth == "string" ? settings.fixedwidth : '200px';
+      $('.top').width(width);
+    }
+    $.each(top,function(i,item){
+      var topitem = $(':eq(0)',item).clone(true).wrapInner("<span/>").data('sub',$(item).children('ul')).appendTo(topdiv);
+      if (settings.fixedwidth || $.browser.msie)
+      $(topitem).css({'text-overflow':'ellipsis', '-o-text-overflow':'ellipsis','-ms-text-overflow':'ellipsis'});
+      if($(topitem).data('sub').length) {
+        $(topitem).addClass('hasChildMenu');
+        addWidget(container, topitem);
+      }
+    });
+
+    // Firefox doesn't repeat keydown events when the key is held, so we use
+    // keypress with FF/Gecko/Mozilla to enable continuous keyboard scrolling.
+    var key_event = $.browser.mozilla ? 'keypress' : 'keydown';
+    
+    // Event handling functions
+    $(container).bind("click " + key_event, function(event){
+      if ($(event.target).is("a,span")) {
+        if ($(event.target).is("span")){
+          var self = $(event.target).parent();
+        }
+        else {
+          var self = event.target;          
+        }
+        if (!settings.multi) {
+          delete event.shiftKey;
+          delete event.metaKey;
+        }
+        self.focus();
+        var container = $(self).parents('.containerobj');
+        // Handle clicks
+        if (event.type == "click"){
+          var level = $('div',container).index($(self).parents('div'));
+          var isleafnode = false;
+          // Remove blocks to the right in the tree, and 'deactivate' other
+          // links within the same level, if metakey is not being used
+          $('div:gt('+level+')',container).remove();
+          if (!event.metaKey && !event.shiftKey) {
+            $('div:eq('+level+') a',container).removeClass('active').removeClass('inpath');
+            $('.active',container).addClass('inpath');
+            $('div:lt('+level+') a',container).removeClass('active');
+          }
+          // Select intermediate items when shift clicking
+          // Sorry, only works with jQuery 1.4 due to changes in the .index() function
+          if (event.shiftKey) {
+            var first = $('a.active:first', $(self).parent()).index();
+            var cur = $(self).index();
+            var range = [first,cur].sort(function(a,b){return a - b;});
+            $('div:eq('+level+') a', container).slice(range[0], range[1]).addClass('active');
+          }
+          $(self).addClass('active');
+          if ($(self).data('sub').children('li').length && !event.metaKey) {
+            // Menu has children, so add another submenu
+            var w = false;
+            if (settings.fixedwidth || $.browser.msie)
+            w = typeof settings.fixedwidth == "string" ? settings.fixedwidth : '200px';
+            submenu(container,self,w);
+          }
+          else if (!event.metaKey && !event.shiftKey) {
+            // No children, show title instead (if it exists, or a link)
+            isleafnode = true;
+            var previewcontainer = $('<div/>').addClass('feature').appendTo(container);
+            // Fire preview handler function
+            if ($.isFunction(settings.preview)) {
+              // We're passing the element back to the callback
+              var preview = settings.preview($(self));
+            }
+            // If preview is specifically disabled, do nothing with the previewbox
+            else if (!settings.preview) {
+            }
+            // If no preview function is specificied, use a default behavior
+            else {
+              var title = $('<a/>').attr({href:$(self).attr('href')}).text($(self).attr('title') ? $(self).attr('title') : $(self).text());
+              $(previewcontainer).html(title);
+            }
+            // Set the width
+            var remainingspace = 0; 
+            $.each($(container).children('div').slice(0,-1),function(i,item){
+              remainingspace += $(item).width();
+            });
+            var fillwidth = $(container).width() - remainingspace;
+            $(previewcontainer).css({'top':0,'left':remainingspace}).width(fillwidth).show();  
+          }
+          // Fire onchange handler function, but only if multi-select is off.
+          // FIXME Need to deal multiple selections.
+          if ($.isFunction(settings.onchange) && !settings.multi) {
+            // We're passing the element back to the callback
+            var onchange = settings.onchange($(self), isleafnode);
+          }
+        }
+        // Handle Keyboard navigation
+        if(event.type == key_event){
+          switch(event.keyCode){
+            case(37): //left
+              $(self).parent().prev().children('.inpath').focus().trigger("click");
+              break;
+            case(38): //up
+              $(self).prev().focus().trigger("click");
+              break;
+            case(39): //right
+              if($(self).hasClass('hasChildMenu')){
+                $(self).parent().next().children('a:first').focus().trigger("click");
+              }
+              break;
+            case(40): //down
+              $(self).next().focus().trigger("click");
+              break;
+            case(13): //enter
+              $(self).trigger("dblclick");
+              break;
+          }
+        }
+        event.preventDefault();
+      }
+    });
+
+  };
+  
+  $.fn.columnview.defaults = {
+    multi: false,     // Allow multiple selections
+    preview: true,    // Handler for preview pane
+    fixedwidth: false,// Use fixed width columns
+    onchange: false   // Handler for selection change
+  };
+
+  // Generate deeper level menus
+  function submenu(container,item,width){
+    var leftPos = 0;
+    $.each($(container).children('div'),function(i,mydiv){
+      leftPos += $(mydiv).width();
+    });
+    var submenu = $('<div/>').css({'top':0,'left':leftPos}).appendTo(container);
+    // Set column width
+    if (width)
+    $(submenu).width(width);
+    var subitems = $(item).data('sub').children('li');
+    $.each(subitems,function(i,subitem){
+      var subsubitem = $(':eq(0)',subitem).clone(true).wrapInner("<span/>").data('sub',$(subitem).children('ul')).appendTo(submenu);
+      if (width)
+      $(subsubitem).css({'text-overflow':'ellipsis', '-o-text-overflow':'ellipsis','-ms-text-overflow':'ellipsis'});
+      if($(subsubitem).data('sub').length) {
+        $(subsubitem).addClass('hasChildMenu');
+        addWidget(container, subsubitem);
+      }
+    });
+  }
+
+  // Uses canvas, if available, to draw a triangle to denote that item is a parent
+  function addWidget(container, item, color){
+    var triheight = $(item).height();
+    var canvas = $("<canvas></canvas>").attr({height:triheight,width:10}).addClass('widget').appendTo(item);    if(!color){ color = $(canvas).css('color'); }
+    canvas = $(canvas).get(0);
+    if(canvas.getContext){
+      var context = canvas.getContext('2d');
+      context.fillStyle = color;
+      context.beginPath();
+      context.moveTo(3,(triheight/2 - 3));
+      context.lineTo(10,(triheight/2));
+      context.lineTo(3,(triheight/2 + 3));
+      context.fill();
+    } else {
+      /**
+       * Canvas not supported - put something in there anyway that can be
+       * suppressed later if desired. We're using a decimal character here
+       * representing a "black right-pointing pointer" in Windows since IE
+       * is the likely case that doesn't support canvas.
+       */
+      $("<span>&#9658;</span>").addClass('widget').css({'height':triheight,'width':10}).prependTo(item);
+    }
+    $(container).find('.widget').bind('click', function(event){
+      event.preventDefault();
+    });
+
+  }
+})(jQuery);
\ No newline at end of file
diff --git a/Toolkit/Photos/js/photoGallery.js b/Toolkit/Photos/js/photoGallery.js
new file mode 100644 (file)
index 0000000..d05c97f
--- /dev/null
@@ -0,0 +1,173 @@
+$(function(){
+   $("#photoCategoryId").change(function(){
+       $(this).parent().submit();
+   });
+});
+
+(function(window, PhotoSwipe){
+   //if ($(".photoimg").length > 0) {
+         if(document.addEventListener) {
+            document.addEventListener('DOMContentLoaded', function(){
+            var
+                options = {
+                    getImageCaption: function(el){
+                        var captionText, captionDesc, captionId, captionEl;
+
+                        if (el.nodeName === "A") {
+                            captionText = el.getAttribute('title');
+                            captionDesc = el.getAttribute('rel');
+                            captionId = el.getAttribute('data-photoid');
+                            captionDownload = el.getAttribute('data-download');
+                        }
+
+                        // Return a DOM element with custom styling
+                        if((captionText != null && captionText != "") || (captionDesc != null && captionDesc != "")) {
+                            captionEl = document.createElement('div');
+                            captionEl.className = 'captionWrapper';
+                            // Add Caption Title.
+                            if(captionText != null && captionText != "") {
+                                captionTitleOutput = document.createElement('h2');
+                                captionTitleOutput.className = "captionTitleWrapper";
+                                captionTitleOutput.appendChild(document.createTextNode(captionText));
+                                captionEl.appendChild(captionTitleOutput);
+                            }
+                            // Add Caption Description.
+                            if(captionDesc != null && captionDesc != "") {
+                                captionDescOutput = document.createElement('p');
+                                captionDescOutput.className = "captionDescWrapper";
+                                captionDescOutput.appendChild(document.createTextNode(captionDesc));
+                                captionEl.appendChild(captionDescOutput);
+                            }
+                        } else {
+                            //captionEl = document.createElement('div');
+                            //captionEl.style.cssText = 'display:none; width:0; height:0;';
+                            captionEl = "";
+                        }
+                        // Add Download links.
+                        if(captionId != null && captionId != "" && captionDownload == "1")  {
+                            captionIdOutput = document.createElement('div');
+                            captionIdOutput.className = 'download_wrapper';
+                            captionEl.appendChild(captionIdOutput);
+                            // Add Link Text.
+                            captionLinkText = document.createElement('span');
+                            captionText = document.createTextNode("Download for ");
+                            captionLinkText.appendChild(captionText);
+                            captionIdOutput.appendChild(captionLinkText);
+                            // Add Web Link.
+                            captionWebLink = base_url + "download-photo-web/" + captionId + "/";
+                            downloadWebLink = document.createElement('a');
+                            linkText = document.createTextNode("Web");
+                            downloadWebLink.appendChild(linkText);
+                            downloadWebLink.title = "Download for Web";
+                            downloadWebLink.href = captionWebLink;
+                            captionIdOutput.appendChild(downloadWebLink);
+                            // Add Spacer Text.
+                            captionSpacerText = document.createElement('span');
+                            captionText = document.createTextNode(" - ");
+                            captionSpacerText.appendChild(captionText);
+                            captionIdOutput.appendChild(captionSpacerText);
+                            // Add Print Link.
+                            captionPrintLink = base_url + "download-photo-print/" + captionId + "/";
+                            downloadPrintLink = document.createElement('a');
+                            linkPrintText = document.createTextNode("Print");
+                            downloadPrintLink.appendChild(linkPrintText);
+                            downloadPrintLink.title = "Download for Print";
+                            downloadPrintLink.href = captionPrintLink;
+                            captionIdOutput.appendChild(downloadPrintLink);
+                        }
+
+                        return captionEl;
+
+                        // Alternatively you can just pass back a string. However, any HTML
+                        // markup will be escaped
+
+                    }, captionAndToolbarAutoHideDelay:0, imageScaleMethod: "fitNoUpscale", captionAndToolbarFlipPosition: true, backButtonHideEnabled: true
+                },
+                instance = PhotoSwipe.attach( window.document.querySelectorAll('a.photoimg'), options );
+            }, false);
+        } else { // You are using IE8- and you should feel bad.
+            document.attachEvent('onreadystatechange', function(){
+            var
+                options = {
+                    getImageCaption: function(el){
+                        var captionText, captionDesc, captionId, captionEl;
+
+                        if (el.nodeName === "A") {
+                            captionText = el.getAttribute('title');
+                            captionDesc = el.getAttribute('rel');
+                            captionId = el.getAttribute('data-photoid');
+                            captionDownload = el.getAttribute('data-download');
+                        }
+
+                        // Return a DOM element with custom styling
+                        if((captionText != null && captionText != "") || (captionDesc != null && captionDesc != "")) {
+                            //captionEl = document.createElement('div');
+                            captionEl = $('<div style="display: block;width: 100%;overflow: hidden;"></div>');
+                            captionEl.addClass = 'captionWrapper';
+                            // Add Caption Title.
+                            if(captionText != null && captionText != "") {
+                                //captionTitleOutput = document.createElement('h2');
+                                captionTitleOutput = $('<h2 style="display: block;width: 50%;float: left;font-size: 18px;font-weight: bold;color: #FFF;text-align: left;padding: 10px 20px;margin: 0;"></h2>');
+                                captionTitleOutput.addClass = "captionTitleWrapper";
+                                captionTitleOutput.append(captionText);
+                                captionEl.append(captionTitleOutput);
+                            }
+                            // Add Caption Description.
+                            if(captionDesc != null && captionDesc != "") {
+                                //captionDescOutput = document.createElement('p');
+                                captionDescOutput = $('<p style="display: block;width: 50%;float: left;clear: left;font-size: 14px;color: #FFF;text-align: left;padding: 0 20px 10px 20px;margin: 0;"></p>');
+                                captionDescOutput.addClass = "captionDescWrapper";
+                                captionDescOutput.append(document.createTextNode(captionDesc));
+                                captionEl.append(captionDescOutput);
+                            }
+                        } else {
+                            //captionEl = document.createElement('div');
+                            //captionEl.style.cssText = 'display:none; width:0; height:0;';
+                            captionEl = "";
+                        }
+                        // Add Download links.
+                        if(captionId != null && captionId != "" && captionDownload == "1") {
+                            captionIdOutput = $('<div style="display: block;position: absolute;z-index: 99;top: 10px;right: 20px;width: 30%;font-size: 14px;color: #FFF;text-align: left;padding: 6px 0 10px 20px;margin: 0;text-align: right;"></div>');
+                            captionIdOutput.addClass = 'download_wrapper';
+                            captionEl.append(captionIdOutput);
+                            // Add Link Text.
+                            captionLinkText = $('<span></span>');
+                            captionText = document.createTextNode("Download for ");
+                            captionLinkText.append(captionText);
+                            captionIdOutput.append(captionLinkText);
+                            // Add Web Link.
+                            captionWebLink = base_url + "download-photo-web/" + captionId + "/";
+                            downloadWebLink = $('<a style="color: #CCC;"></a>');
+                            linkText = document.createTextNode("Web");
+                            downloadWebLink.append(linkText);
+                            downloadWebLink.title = "Download for Web";
+                            downloadWebLink.attr('href', captionWebLink);
+                            captionIdOutput.append(downloadWebLink);
+                            // Add Spacer Text.
+                            captionSpacerText = $('<span></span>');
+                            captionText = document.createTextNode(" - ");
+                            captionSpacerText.append(captionText);
+                            captionIdOutput.append(captionSpacerText);
+                            // Add Print Link.
+                            captionPrintLink = base_url + "download-photo-print/" + captionId + "/";
+                            downloadPrintLink = $('<a style="color: #CCC;"></a>');
+                            linkPrintText = document.createTextNode("Print");
+                            downloadPrintLink.append(linkPrintText);
+                            downloadPrintLink.title = "Download for Print";
+                            downloadPrintLink.attr('href', captionPrintLink);
+                            captionIdOutput.append(downloadPrintLink);
+                        }
+
+                        return captionEl;
+
+                        // Alternatively you can just pass back a string. However, any HTML
+                        // markup will be escaped
+
+                    }, captionAndToolbarAutoHideDelay:0, imageScaleMethod: "fitNoUpscale", captionAndToolbarFlipPosition: true, backButtonHideEnabled: true
+                },
+                instance = PhotoSwipe.attach( window.document.querySelectorAll('a.photoimg'), options );
+            }, false);
+        }
+    //}
+}(window, window.Code.PhotoSwipe));
+
diff --git a/Toolkit/Photos/login.php b/Toolkit/Photos/login.php
new file mode 100644 (file)
index 0000000..b30a681
--- /dev/null
@@ -0,0 +1,59 @@
+<?php
+require_once '../../setup.phtml';
+
+HTTP_Session2::useCookies(false);
+HTTP_Session2::start();
+
+$catid = filter_var($_REQUEST['catid'], FILTER_VALIDATE_INT);
+$dbh = Toolkit_Database::getInstance();
+$authContainer = new Toolkit_Photos_AuthContainer($dbh);
+$photoAuth = new Toolkit_Photos_Auth(
+    $authContainer,
+    '',
+    false
+);
+$photoAuth->start();
+
+if (isset($_REQUEST['logout'])) {
+    $photoAuth->logout();
+    header('Location: ' . BASE_URL . 'index.php?catid=' . $catid);
+    exit;
+}
+
+if (!$photoAuth->checkAuth()) {
+    // see if the user exists but has expired
+    $userName = filter_var($_REQUEST['username'], FILTER_SANITIZE_STRING);
+    $passWord = filter_var($_REQUEST['password'], FILTER_SANITIZE_STRING);
+    $status = $photoAuth->getStatus();
+    if ($passWord && $userName) {
+        try {
+            $sql = "
+            SELECT id
+              FROM contacts.contact
+             WHERE email = :uname
+               AND media_pass = :pword
+               AND media = true
+               AND approved = true
+               AND expire < current_date";
+            $stmt = $dbh->prepare($sql);
+            $stmt->bindParam(':uname', $userName, PDO::PARAM_STR);
+            $stmt->bindParam(':pword', $passWord, PDO::PARAM_STR);
+            $stmt->execute();
+            $row = $stmt->fetch(PDO::FETCH_ASSOC);
+            if ($row !== FALSE) {
+                $status = -4;
+            }
+        } catch (PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+    }
+    //    Manually adjust the authentication status for empty credentials
+    if (empty($_POST['username']) || empty($_POST['password'])) {
+        $status = -3;
+    }
+    header('Location: '
+        . BASE_URL . 'index.php?catid=' . $catid . '&status=' . $status);
+    exit;
+} else {
+    header('Location: ' . BASE_URL . 'index.php?catid=' . $catid);
+}
diff --git a/Toolkit/Photos/photoProxy.php b/Toolkit/Photos/photoProxy.php
new file mode 100644 (file)
index 0000000..560bf58
--- /dev/null
@@ -0,0 +1,97 @@
+<?php
+require_once '../../setup.phtml';
+
+HTTP_Session2::useCookies(false);
+HTTP_Session2::start();
+$dbh = Toolkit_Database::getInstance();
+$catid = filter_var($_REQUEST['catid'], FILTER_VALIDATE_INT);
+$authContainer = new Toolkit_Photos_AuthContainer($dbh);
+
+$photoAuth = new Auth(
+    $authContainer,
+    array(
+        'sessionName' => 'glmMedia',
+        'allowLogin' => true,
+        'dbFields' => array('id', 'fname', 'lname')
+    ),
+    '',
+    false
+);
+$photoAuth->setIdle(86400, true);
+$photoAuth->start();
+//var_dump($_REQUEST);
+
+// get image name from db
+$photoId = filter_var($_REQUEST['photo_id'], FILTER_VALIDATE_INT);
+if (!$photoId) {
+    die('Sorry no image found');
+}
+$isAuthorized = $photoAuth->checkAuth();
+try {
+    // here we'll need to also check to make sure that if they're not
+    // logged in they cannot get the exclusive photos.
+    $where
+        = (!$isAuthorized)
+        ? "AND exclusive <> true"
+        : '';
+    $sql = "
+    SELECT image
+      FROM photo
+     WHERE id = :id
+    $where";
+    $stmt = $dbh->prepare($sql);
+    $stmt->bindParam(':id', $photoId, PDO::PARAM_INT);
+    $stmt->execute();
+    $imageName = $stmt->fetchColumn();
+    if (!$imageName) {
+        return false;
+    }
+} catch (PDOException $e) {
+    Toolkit_Common::handleError($e);
+}
+//var_dump($imageName);
+//exit;
+if (!$isAuthorized) {
+    // use watermarked image style
+    $style = ($_REQUEST['type'] == 'print') ? 'downPrint': 'downWeb';
+    $fileUrlPath = FILE_SERVER_URL . IS_OWNER_ID .  "/{$style}/{$imageName}";
+
+    $ch = curl_init($fileUrlPath);
+    curl_setopt($ch, CURLOPT_TIMEOUT, 50);
+    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
+    $imageResponse = curl_exec($ch);
+    if (!curl_errno($ch)) {
+        $bytes = curl_getinfo($ch, CURLINFO_CONTENT_LENGTH_DOWNLOAD);
+        header("Pragma: public");
+        header("Expires: 0");
+        header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
+        header("Content-Type: application/force-download");
+        header("Content-Type: application/octet-stream");
+        header("Content-Type: application/download");
+        header("Content-Disposition: attachment;filename=photo-" . $photoId.".jpeg");
+        header("Content-Transfer-Encoding: binary ");
+        curl_close($ch);
+        echo $imageResponse;
+    }
+
+    exit;
+} else {
+    // use style without watermarks
+    $style = ($_REQUEST['type'] == 'print') ? 'downPrintMedia': 'downWebMedia';
+    $fileUrlPath = FILE_SERVER_URL . IS_OWNER_ID .  "/{$style}/{$imageName}";
+    $ch = curl_init($fileUrlPath);
+    curl_setopt($ch, CURLOPT_TIMEOUT, 50);
+    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
+    $imageResponse = curl_exec($ch);
+    header("Pragma: public");
+    header("Expires: 0");
+    header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
+    header("Content-Type: application/force-download");
+    header("Content-Type: application/octet-stream");
+    header("Content-Type: application/download");
+    header("Content-Disposition: attachment;filename=photo-" . $photoId.".jpeg");
+    header("Content-Transfer-Encoding: binary ");
+    curl_close($ch);
+    echo $imageResponse;
+    exit;
+}
diff --git a/Toolkit/Photos/photoSearch.php b/Toolkit/Photos/photoSearch.php
new file mode 100644 (file)
index 0000000..8d51338
--- /dev/null
@@ -0,0 +1,27 @@
+<?php
+require_once '../../setup.phtml';
+$jsonData = array();
+$mediaMapper = new Toolkit_Photos_Models_MediaMapper(
+    Toolkit_Database::getInstance()
+);
+$photoSearch = filter_var($_REQUEST['photoName'], FILTER_SANITIZE_STRING);
+$pageId = filter_var($_REQUEST['pageId'], FILTER_VALIDATE_INT);
+$gallery = new Toolkit_Photos_Gallery(
+    Toolkit_Database::getInstance(),
+    new Toolkit_Photos_Display(
+         Toolkit_Database::getInstance()
+    )
+);
+$galleries = $gallery->getPageGalleries($pageId);
+
+$photos      = $mediaMapper->fetchPhotosByNameAndCats(
+    $photoSearch,
+    $galleries,
+    true
+);
+if ($photos) {
+    foreach ($photos as $photo) {
+        $jsonData[] = $photo->getTitle();
+    }
+}
+echo json_encode($jsonData);
diff --git a/Toolkit/Photos/setupNewPhotos.php b/Toolkit/Photos/setupNewPhotos.php
new file mode 100644 (file)
index 0000000..7e492a1
--- /dev/null
@@ -0,0 +1,9 @@
+<?php
+
+require_once '../../setup.phtml';
+
+$mediaMapper = new Toolkit_Photos_Models_MediaMapper(
+    Toolkit_Database::getInstance()
+);
+
+$mediaMapper->fixAllPhoto2Cats();
diff --git a/Toolkit/Photos/templates/Admin/editCategory.html b/Toolkit/Photos/templates/Admin/editCategory.html
new file mode 100644 (file)
index 0000000..47219b2
--- /dev/null
@@ -0,0 +1,187 @@
+<div id="editCategorySection">
+    <header>
+        <hgroup>
+            <h1>
+               Edit Category
+            </h1>
+        </hgroup>
+    </header>
+    <form
+        id="editCategoryForm"
+        name="editCategoryForm"
+        action="{formAction:h}"
+        flexy:ignore="yes"
+        method="post">
+        {if:category}
+            <input type="hidden" name="id" value="{category.getId()}">
+            <input type="hidden" name="pos" value="{category.getPos()}">
+            {if:!mediaExclusive}
+                <input type="hidden" name="exclusive" value="0">
+            {end:}
+        {end:}
+        <table class="webform">
+            <tr>
+                <td class="labelcell">
+                    Name
+                </td>
+                <td class="fieldcell">
+                    {if:category}
+                    <input type="text" name="category" value="{category.getCategory()}">
+                    {else:}
+                    <input type="text" name="category">
+                    {end:}
+                </td>
+            </tr>
+            <tr>
+                <td class="labelcell" id="pagesLabel">
+                    Pages for Display
+                </td>
+                <td class="fieldcell">
+                    <div id="displayWrapper">
+                    <div id="pages2PhotosDiv">
+                        {if:pages2Categories}
+                            <div class="pageLink" flexy:foreach="pages2Categories,page">
+                                <input type="hidden" name="pages[]" value="{page[page]}">
+                                {page[page_name]}
+                                <a href="#" class="ui-icon ui-icon-trash btnDelete">Delete</a>
+                            </div>
+                        {end:}
+                    </div>
+                    <div id="dialog-modal" title="Page Selector" style="display:none;">
+                        <div id="pages"></div>
+                    </div>
+                    <input type="submit" id="addPageTo" value="Add Pages">
+                    </div><!--/#displayWrapper-->
+                </td>
+            </tr>
+            <tr flexy:if="slideShow">
+                <td class="labelcell">
+                    SlideShow Option?
+                </td>
+                <td class="fieldcell">
+                    <input type="hidden" name="slideshow" value="0"><label>
+                    {if:category}
+                      {if:category.getSlideShow()}
+                      <input type="checkbox" name="slideshow" value="1" checked>
+                      {else:}
+                      <input type="checkbox" name="slideshow" value="1">
+                      {end:}
+                    {else:}
+                    <input type="checkbox" name="slideshow" value="1">
+                    {end:}Yes</label>
+                </td>
+            </tr>
+            <tr flexy:if="mediaExclusive">
+                <td class="labelcell">
+                    Media Exclusive?
+                </td>
+                <td class="fieldcell">
+                    <input type="hidden" name="exclusive" value="0"><label>
+                    {if:category}
+                      {if:category.getExclusive()}
+                      <input type="checkbox" name="exclusive" value="1" checked>
+                      {else:}
+                      <input type="checkbox" name="exclusive" value="1">
+                      {end:}
+                    {else:}
+                    <input type="checkbox" name="exclusive" value="1">
+                    {end:}Yes</label>
+                </td>
+            </tr>
+            <tr>
+                <td colspan="2" align="center">
+                    <input id="categorySave" type="submit" value="Save">
+                    {if:category}
+                    <input id="categoryCancel" type="submit" value="Cancel" name="Cancel">
+                    <input id="categoryDelete" type="submit" value="Delete Category" name="Delete">
+                    {end:}
+                </td>
+            </tr>
+        </table>
+    </form>
+</div>
+<div id="categoryPhotos">
+    <form id="sortable">
+        <input type="hidden" name="ac" value="movePhotos">
+        <input type="hidden" name="categoryId" value="{category.getId()}">
+        <div
+            class="category-category-list"
+            flexy:foreach="photos,photo">
+            <input type="hidden" name="photos[]" value="{photo.getId()}">
+            {if:photo.getImage()}
+            <a href="{editUrl:h}{photo.getId()}">
+                <img width="120" height="120" src="{imgPath:h}{photo.getImage():h}">
+            </a>
+            {end:}
+            <header>
+                <hgroup>
+                    <h1>
+                        <a href="{editUrl:h}{photo.getId()}">
+                            {photo.getTitle()}
+                        </a>
+                    </h1>
+                </hgroup>
+            </header>
+        </div>
+    </form>
+</div>
+<script>
+    $(function(){
+        if ($("#categoryDelete").length) {
+            $("#categoryDelete").click(function(){
+                return confirm("This will Delete this Category.\nAre you sure?");
+            });
+        }
+        $('body').delegate('.btnDelete', 'click', function(){
+            $(this).parent().remove();
+        });
+        $("#sortable").sortable({
+            update: function() {
+                var inputs = $("#sortable").serialize();
+                $.get('./photos.php', inputs, console.log('positions saved'));
+            }
+        });
+        $("#addPageTo").click(function(){
+            var selectedPageId   = null;
+            var selectedPageName = null;
+            $("#dialog-modal").dialog({
+                height: 240,
+                width: 800,
+                modal: true,
+                buttons: {
+                    "Select Page": function() {
+                        if (selectedPageId != '') {
+                            // here is where it adds the page
+
+                            console.log('Selected PageId: ' + selectedPageId);
+                            console.log('Selected Pagename: ' + selectedPageName);
+                            $("#pages2PhotosDiv").append('<div class="pageLink">'
+                                    + '<input type="hidden" name="pages[]" value="'
+                                    + selectedPageId + '">'
+                                    + selectedPageName
+                                    + '<a href="#" class="ui-icon ui-icon-trash btnDelete">Delete</a>'
+                                    + '</div>');
+                            $(this).dialog("close");
+                        } else {
+                            alert("Select a Page");
+                        }
+                    },
+                    Cancel: function() {
+                        $(this).dialog("close");
+                    }
+                }
+            });
+            $("#pages").load('photos.php?ac=showPages', function(){
+                $('#demo1').columnview({
+                    preview:false,
+                    onchange: function(element) {
+                        selectedPageId = $(element).attr('data-page');
+                        selectedPageName = $(element).attr('data-name');
+                    }
+                });
+            });
+            return false;
+        });
+    });
+
+</script>
diff --git a/Toolkit/Photos/templates/Admin/editPhoto.html b/Toolkit/Photos/templates/Admin/editPhoto.html
new file mode 100644 (file)
index 0000000..77d6c69
--- /dev/null
@@ -0,0 +1,264 @@
+<style>
+    #editPhotoForm .webform {
+        margin: 10px 0;
+        width: 400px;
+        border: 1px solid #CCC;
+        border-radius: 5px;
+        padding: 10px 2%;
+    }
+    #editPhotoForm .webform td {
+        width: 100%;
+        float: left;
+        border: 0;
+        text-align: left;
+    }
+    #editPhotoForm .webform td input[type="text"], #editPhotoForm .webform td select {
+        display: block;
+        position: relative;
+        z-index: 99;
+        left: 0;
+        top: 0;
+        width: 100%;
+        margin: 0;
+        font-size: 14px;
+        font-weight: normal;
+        color: #333;
+        height: 27px;
+        -webkit-box-sizing: border-box; /* Safari/Chrome, other WebKit */
+        -moz-box-sizing: border-box;    /* Firefox, other Gecko */
+        box-sizing: border-box;         /* Opera/IE 8+ */
+        padding:.2em;/.3em;
+        padding-left: 6px;
+        background: #EFEFEF !important;
+        border: 1px solid #CCC;
+    }
+    ol.asmList {
+        -webkit-box-sizing: border-box; /* Safari/Chrome, other WebKit */
+        -moz-box-sizing: border-box;    /* Firefox, other Gecko */
+        box-sizing: border-box;         /* Opera/IE 8+ */
+        width: 100%;
+        border: 0;
+        background: #EFEFEF;
+        margin: 0;
+        padding: 0;
+    }
+    ol.asmList .asmListItem {
+        border: none;
+        -webkit-box-sizing: border-box; /* Safari/Chrome, other WebKit */
+        -moz-box-sizing: border-box;    /* Firefox, other Gecko */
+        box-sizing: border-box;         /* Opera/IE 8+ */
+        border-bottom: 1px solid #CCC;
+        border-right: 1px solid #CCC;
+        border-left: 1px solid #CCC;
+        padding-bottom: 5px;
+        padding-left: 6px;
+        display: block;
+        min-height: 26px;
+        background: #FFFFFF;
+    }
+    #editPhotoForm .webform textarea {
+        resize: vertical;
+        width: 98.2%;
+        max-width: 98.2%;
+        min-width: 98.2%;
+    }
+    #photoSave {
+        width: 31.333333%;
+        margin: 0 2% 0 0;
+    }
+    #photoCancel {
+        width: 31.333333%;
+        margin: 0 2% 0 0;
+    }
+    #photoDelete {
+        width: 31.333333%;
+        margin: 0;
+    }
+</style>
+<div id="editPhotoSection">
+    <header>
+        <hgroup>
+            <h1>
+                Edit Photo in {categoryName}
+            </h1>
+        </hgroup>
+    </header>
+    <form
+        id="editPhotoForm"
+        name="editPhotoForm"
+        action="{formAction:h}"
+        flexy:ignore="yes"
+        method="post"
+        enctype="multipart/form-data">
+        {if:photo}
+            <input type="hidden" name="id" value="{photo.getId()}">
+            {if:!mediaExclusive}
+                <input type="hidden" name="exclusive" value="0">
+            {end:}
+        {end:}
+        <input type="hidden" name="catid" value="{_REQUEST[catid]}">
+        <table class="webform">
+            <tr>
+                <td class="labelcell">
+                    Name
+                </td>
+                <td class="fieldcell">
+                    {if:photo}
+                    <input type="text" name="title" value="{photo.getTitle()}">
+                    {else:}
+                    <input type="text" name="title">
+                    {end:}
+                </td>
+            </tr>
+            <tr flexy:if="mediaExclusive">
+                <td class="labelcell">
+                    Media Exclusive?
+                </td>
+                <td class="fieldcell">
+                    <input type="hidden" name="exclusive" value="0"><label>
+                        {if:photo}
+                        {if:photo.getExclusive()}
+                        <input type="checkbox" name="exclusive" value="1" checked>
+                        {else:}
+                        <input type="checkbox" name="exclusive" value="1">
+                        {end:}
+                        {else:}
+                        <input type="checkbox" name="exclusive" value="1">
+                        {end:}Yes</label>
+                </td>
+            </tr>
+            <tr>
+                <td class="labelcell">
+                    Download?
+                </td>
+                <td class="fieldcell">
+                    <input type="hidden" name="download" value="0"><label>
+                        {if:photo}
+                        {if:photo.getDownload()}
+                        <input type="checkbox" name="download" value="1" checked>
+                        {else:}
+                        <input type="checkbox" name="download" value="1">
+                        {end:}
+                        {else:}
+                        <input type="checkbox" name="download" value="1">
+                        {end:}Yes</label>
+                </td>
+            </tr>
+            <tr>
+                <td class="labelcell">
+                    Category
+                </td>
+                <td class="fieldcell">
+                    <select
+                        id="P2CSelect"
+                        name="photo2Categories[]"
+                        title="Select Categories"
+                        multiple>
+                        <?php
+                        foreach ($t->categories as $category) {
+                        echo '<option value="'.$category->getId().'"';
+                        if ($t->photo2Categories->offsetExists($category->getId())
+                        || $category->getId() == $_REQUEST['catid']
+                        ) {
+                        echo ' selected';
+                        }
+                        echo '>'.$category->getCategory().'</option>';
+                        }
+                        ?>
+                    </select>
+                </td>
+            </tr>
+            <tr>
+                <td class="labelcell">
+                    Description
+                </td>
+                <td class="fieldcell">
+                    {if:photo}
+                    <textarea name="description">{photo.getDescription()}</textarea>
+                    {else:}
+                    <textarea name="description"></textarea>
+                    {end:}
+                </td>
+            </tr>
+            {if:photo}
+            <tr>
+                <td class="labelcell">
+                    Current Photo
+                </td>
+                <td class="fieldcell">
+                    <input type="hidden" name="image" value="{photo.getImage()}">
+                    <img src="{imgPath:h}{photo.getImage()}">
+                </td>
+            </tr>
+            {end:}
+            <tr>
+                <td class="labelcell">
+                    New Photo
+                </td>
+                <td class="fieldcell">
+                    {if:photo}
+                    <input
+                        id="imageUpload"
+                        accept="image/*"
+                        type="file"
+                        name="image">
+                    {else:}
+                    <input
+                        id="imageUpload"
+                        accept="image/*"
+                        type="file"
+                        name="image"
+                        required>
+                    {end:}
+                </td>
+            </tr>
+            <tr>
+                <td colspan="2">
+                    <input id="photoSave" type="submit" value="Save Photo">
+                    {if:photo}
+                    <input id="photoCancel" type="submit" value="Cancel" name="Cancel">
+                    <input id="photoDelete" type="submit" value="Delete Photo" name="Delete">
+                    {end:}
+                </td>
+            </tr>
+        </table>
+    </form>
+</div>
+<script>
+    $(function() {
+        $('INPUT[type="file"]').change(function() {
+            var ext = this.value.match(/(?:\.([^.]+))?$/)[1];
+            switch (ext.toLowerCase()) {
+                case 'jpg':
+                case 'jpeg':
+                case 'png':
+                case 'gif':
+                    $('#uploadButton').attr('disabled', false);
+                    break;
+                default:
+                    alert('This is not an allowed file type.');
+                    this.value = '';
+            }
+        });
+        $("#editPhotoForm").submit(function() {
+            var atLeastOneSeleted = false;
+            $("#P2CSelect option:selected").each(function() {
+                atLeastOneSeleted = true;
+            });
+            if (!atLeastOneSeleted) {
+                alert('You must select at least one Category');
+            }
+            return atLeastOneSeleted;
+        });
+        $("select[multiple]").asmSelect({
+            animate: true,
+            sortable: false,
+            highlight: true
+        });
+        if ($("#photoDelete").length) {
+            $("#photoDelete").click(function() {
+                return confirm("This will Delete this photo.\nAre you sure?");
+            });
+        }
+    });
+</script>
diff --git a/Toolkit/Photos/templates/Admin/editUser.html b/Toolkit/Photos/templates/Admin/editUser.html
new file mode 100644 (file)
index 0000000..128b30e
--- /dev/null
@@ -0,0 +1,233 @@
+<style>
+    form#editUserForm input[type="submit"] {
+        box-shadow: 1px 2px 2px grey;
+        border-radius: 10px;
+        background-color: #D6DFC3;
+        font-size: 16px;
+        font-weight: normal;
+        text-align: center;
+        margin: 0;
+        padding: 1px 5px 1px 5px;
+        width: auto;
+    }
+</style>
+<div>
+    <form
+        id="editUserForm"
+        name="editUserForm"
+        action="{formAction:h}"
+        flexy:ignore="yes"
+        method="post"
+        enctype="multipart/form-data">
+        <input type="hidden" name="userStatus" value="{_REQUEST[userStatus]}">
+        {if:user}
+        <input type="hidden" name="id" value="{user.getId()}">
+        {end:}
+        <table class="webform">
+            <tr>
+                <td class="labelcell">
+                    Status
+                </td>
+                <td class="fieldcell">
+
+                    {if:user}
+                    <label>
+                        {if:user.getApproved()}
+                        <input type="radio" name="status" value="approved" checked>
+                        {else:}
+                        <input type="radio" name="status" value="approved">
+                        {end:}
+                         Approved
+                    </label>
+                    <label>
+                        {if:user.getDenied()}
+                        <input type="radio" name="status" value="denied" checked>
+                        {else:}
+                        <input type="radio" name="status" value="denied">
+                        {end:}
+                        Denied
+                    </label>
+                    {else:}
+                    <label>
+                        <input type="radio" name="status" value="approved">
+                        Approved
+                    </label>
+                    <label>
+                        <input type="radio" name="status" value="denied">
+                        Denied
+                    </label>
+
+                    {end:}
+
+                </td>
+            </tr>
+            <tr>
+                <td class="labelcell">
+                    Company Name
+                </td>
+                <td class="fieldcell">
+                    {if:user}
+                    <input name="company" value="{user.getCompany()}">
+                    {else:}
+                    <input name="company">
+                    {end:}
+                </td>
+            </tr>
+            <tr>
+                <td class="labelcell">
+                    Title
+                </td>
+                <td class="fieldcell">
+                    {if:user}
+                    <input name="title" value="{user.gettitle()}">
+                    {else:}
+                    <input name="title">
+                    {end:}
+                </td>
+            </tr>
+            <tr>
+                <td class="labelcell">
+                    Expire Date
+                </td>
+                <td class="fieldcell">
+                    {if:user}
+                    <input id="expire" name="expire" value="{user.getExpire()}">
+                    {else:}
+                    <input id="expire" name="expire">
+                    {end:}
+                </td>
+            </tr>
+            <tr>
+                <td class="labelcell">
+                    Email Address
+                </td>
+                <td class="fieldcell">
+                    {if:user}
+                    <input name="email" value="{user.getEmail()}" type="email" required>
+                    {else:}
+                    <input name="email" type="email" required>
+                    {end:}
+                </td>
+            </tr>
+            <tr>
+                <td class="labelcell">
+                    Password
+                </td>
+                <td class="fieldcell">
+                    {if:user}
+                    <input name="media_pass" value="{user.getMediaPass()}" required>
+                    {else:}
+                    <input name="media_pass" required>
+                    {end:}
+                </td>
+            </tr>
+            <tr>
+                <td class="labelcell">
+                    First Name
+                </td>
+                <td class="fieldcell">
+                    {if:user}
+                    <input name="fname" value="{user.getFname()}" required>
+                    {else:}
+                    <input name="fname">
+                    {end:}
+                </td>
+            </tr>
+            <tr>
+                <td class="labelcell">
+                    Last Name
+                </td>
+                <td class="fieldcell">
+                    {if:user}
+                    <input name="lname" value="{user.getLname()}" required>
+                    {else:}
+                    <input name="lname" required>
+                    {end:}
+                </td>
+            </tr>
+
+            <tr>
+                <td class="labelcell">
+                    Phone
+                </td>
+                <td class="fieldcell">
+                    {if:user}
+                    <input name="phone" value="{user.getPhone()}">
+                    {else:}
+                    <input name="phone">
+                    {end:}
+                </td>
+            </tr>
+            <tr>
+                <td class="labelcell">
+                    Address 1
+                </td>
+                <td class="fieldcell">
+                    {if:user}
+                    <input name="address" value="{user.getAddress()}">
+                    {else:}
+                    <input name="address">
+                    {end:}
+                </td>
+            </tr>
+            <tr>
+                <td class="labelcell">
+                    Address2
+                </td>
+                <td class="fieldcell">
+                    {if:user}
+                    <input name="address2" value="{user.getAddress2()}">
+                    {else:}
+                    <input name="address2">
+                    {end:}
+                </td>
+            </tr>
+            <tr>
+                <td class="labelcell">
+                    City
+                </td>
+                <td class="fieldcell">
+                    {if:user}
+                    <input name="city" value="{user.getCity()}">
+                    {else:}
+                    <input name="city">
+                    {end:}
+                </td>
+            </tr>
+            <tr>
+                <td class="labelcell">
+                    State
+                </td>
+                <td class="fieldcell">
+                    {if:user}
+                    <input name="state" value="{user.getState()}">
+                    {else:}
+                    <input name="state">
+                    {end:}
+                </td>
+            </tr>
+            <tr>
+                <td class="labelcell">
+                    Zip
+                </td>
+                <td class="fieldcell">
+                    {if:user}
+                    <input name="zip" value="{user.getZip()}">
+                    {else:}
+                    <input name="zip">
+                    {end:}
+                </td>
+            </tr>
+            <tr>
+                <td colspan="2" align="center">
+                    <input type="submit" value="Save">
+                </td>
+            </tr>
+        </table>
+    </form>
+</div>
+<script>
+    $(function(){
+        $('#expire').datepicker();
+    });
+</script>
diff --git a/Toolkit/Photos/templates/Admin/emailUser.tpl b/Toolkit/Photos/templates/Admin/emailUser.tpl
new file mode 100644 (file)
index 0000000..ac8da03
--- /dev/null
@@ -0,0 +1,68 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+    <head>
+        <meta http-equiv="content-type" content="text/html;charset=utf-8">
+        <title>{title:h}</title>
+    </head>
+    <body>
+    <p>
+        <font size="4" face="arial, sans-serif">
+            <b>{subject:h}</b>
+        </font>
+    </p>
+    <table
+        cellspacing="0"
+        cellpadding="0"
+        bgcolor="#ffffff"
+        border="0"
+        width="400">
+        <tr>
+            <td>
+        {if:approved}
+            <p>Thank you for submitting your request for photos to the Traverse
+            City Convention & Visitors Bureau. Your request has been approved
+            and your login information is below. Please note that your access
+            to this site will be active for 2 weeks, beginning today. Should
+            you require additional time once your access period has expired, we
+            ask that you fill out a new request form.</p>
+            <br>
+            <p>Login: {user.getEmail()}</p>
+            <p>Password: {user.getMediaPass()}</p>
+            <br>
+            <p>Sincerely,</p>
+            <br>
+            <p>The Traverse City Tourism Marketing Team</p>
+        {end:}
+        {if:denied}
+            <p>We're sorry, but your request for media-quality photos did not meet
+            our criteria. However, we encourage you to choose from the wide range
+            of photos available on our general photo library. You do not require
+            a special login to view and download those photos.</p>
+            <br>
+            <p>Thanks for your interest in Traverse City!</p>
+            <p>The Traverse City Tourism Marketing Team</p>
+        {end:}
+            </td>
+        </tr>
+        <tr>
+            <td>
+                <table
+                    cellspacing="0"
+                    cellpadding="10"
+                    border="0"
+                    width="400">
+                    <tr>
+                        <td bgcolor="#eeeeee">
+                            <font size="1" face="arial, sans-serif">
+                    To ensure the delivery of these e-mails to your inbox, please
+                     add {email_from:h} to your e-mail Address Book or Safe List.
+                            </font>
+                        </td>
+                    </tr>
+                </table>
+            </td>
+        </tr>
+
+    </table>
+        </body>
+    </html>
diff --git a/Toolkit/Photos/templates/Admin/index.html b/Toolkit/Photos/templates/Admin/index.html
new file mode 100644 (file)
index 0000000..4ce6dfe
--- /dev/null
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html lang="en">
+    <head>
+        <title>{AppName}</title>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+        <link rel="stylesheet" href="main.css">
+        {styles:h}
+        <script>
+            function showMyError(e) {
+                console.log('Error: ' + e.message);
+                console.log('Line: ' + e.lineno);
+                console.log('URL: ' + e.filename);
+            }
+            addEventListener('error', showMyError);
+        </script>
+        {topScripts:h}
+    </head>
+    <body>
+        <header>
+            <hgroup>
+                <h1>{AppName}</h1>
+            </hgroup>
+            <flexy:include src="Admin/nav.html">
+                <flexy:include src="Admin/searchForm.html">
+        </header>
+        {content:h}
+    {bottomScripts:h}
+    </body>
+</html>
diff --git a/Toolkit/Photos/templates/Admin/listCategories.html b/Toolkit/Photos/templates/Admin/listCategories.html
new file mode 100644 (file)
index 0000000..d0e9508
--- /dev/null
@@ -0,0 +1,93 @@
+<style>
+    .photo-category-list {
+        position: relative;
+        box-shadow: 1px 2px 2px grey;
+        border-radius: 10px;
+        -moz-border-radius: 10px;
+        -webkit-border-radius: 10px;
+        display:block;
+        width: 120px;
+        float: left;
+        background: #D6DFC3;
+        margin: 5px;
+        padding: 10px;
+        height: 140px;
+    }
+    .photo-category-list h1 {
+        position: absolute;
+        bottom: 0;
+        left: 5px;
+        margin: 0;
+        padding: 0;
+        overflow: hidden;
+        width: 123px;
+    }
+    .photo-category-list h1 a {
+        color: #006BB4;
+        font-size: 12px;
+        text-decoration: none;
+        padding: 0;
+        margin: 0;
+        overflow: hidden;
+        white-space: nowrap;
+    }
+    .pCount {
+        text-shadow: 1px 1px grey;
+        position: absolute;
+        top: 0;
+        right: -10px;
+        padding: 1px 5px 1px 5px;
+        background-color: #250;
+        border-radius: 30px;
+        -moz-border-radius: 30px;
+        -webkit-border-radius: 30px;
+        box-shadow: 1px 2px 2px grey;
+        display:inline;
+        float: right;
+        color: white;
+        text-size: 18px;
+        font-weight: bold;
+    }
+    .photo-category-list img {
+        box-shadow: 1px 2px 2px grey;
+        border-radius: 10px;
+        -moz-border-radius: 10px;
+        -webkit-border-radius: 10px;
+        border: none;
+    }
+</style>
+<section>
+    <form id="sortable" method="get">
+        <input type="hidden" name="ac" value="moveCategories">
+        <div
+            class="photo-category-list"
+            flexy:foreach="categories,category">
+            <input type="hidden" name="catPhotos[]" value="{category.getId()}">
+            {if:category.getFirstPhoto()}
+            <a href="{editUrl:h}{category.getId()}">
+                <img width="120" height="120" src="{imgPath:h}{category.getFirstPhoto():h}">
+            </a>
+            {end:}
+            <div class="pCount">
+                {category.getTotalNumberOfPhotos():h}
+            </div>
+            <header>
+                <hgroup>
+                    <h1>
+                        <a href="{editUrl:h}{category.getId()}">
+                            {category.getCategory()}
+                        </a>
+                    </h1>
+                </hgroup>
+            </header>
+        </div>
+    </form>
+</section>
+<script>
+    $("#sortable").sortable({
+        update: function() {
+            var inputs = $("#sortable").serialize();
+            $.get('./photos.php', inputs, console.log('positions saved'));
+        }
+    });
+</script>
diff --git a/Toolkit/Photos/templates/Admin/listPhotos.html b/Toolkit/Photos/templates/Admin/listPhotos.html
new file mode 100644 (file)
index 0000000..7711ee8
--- /dev/null
@@ -0,0 +1,63 @@
+<style>
+    .category-category-list {
+        background: #EFEFEF;
+        border-radius: 5px;
+        display: block;
+        float: left;
+        height: 148px;
+        margin: 0 20px 20px 0;
+        padding: 10px;
+        position: relative;
+        width: 120px;
+        border: 1px solid #CCC;
+        -webkit-box-shadow: 0px 0px 5px rgba(0, 0, 0, 0.2);
+        -moz-box-shadow:    0px 0px 5px rgba(0, 0, 0, 0.2);
+        box-shadow:         0px 0px 5px rgba(0, 0, 0, 0.2);
+    }
+    .category-category-list h1 {
+        bottom: 10px;
+        left: 10px;
+        margin: 0;
+        overflow: hidden;
+        padding: 0;
+        position: absolute;
+        width: 123px;
+    }
+    .category-category-list h1 a {
+        color: #006BB4;
+        font-size: 12px;
+        text-decoration: none;
+        padding: 0;
+        margin: 0;
+        overflow: hidden;
+        white-space: nowrap;
+    }
+    .category-category-list img {
+        border: medium none;
+        border-radius: 5px;
+        -webkit-box-shadow: inset 0px 0px 5px rgba(0, 0, 0, 0.2);
+        -moz-box-shadow:    inset 0px 0px 5px rgba(0, 0, 0, 0.2);
+        box-shadow:         inset 0px 0px 5px rgba(0, 0, 0, 0.2);
+        border: 1px solid #CCC;
+    }
+</style>
+<div id="categoryPhotos">
+    <div
+        class="category-category-list"
+        flexy:foreach="photos,photo">
+        {if:photo.getImage()}
+        <a href="{editUrl:h}&amp;catid={photo.getCatid()}&amp;id={photo.getId()}">
+            <img width="120" height="120" src="{imgPath:h}{photo.getImage():h}">
+        </a>
+        {end:}
+        <header>
+            <hgroup>
+                <h1>
+                    <a href="{editUrl:h}&amp;catid={photo.getCatid()}&amp;id={photo.getId()}">
+                        {photo.getTitle()}
+                    </a>
+                </h1>
+            </hgroup>
+        </header>
+    </div>
+</div>
diff --git a/Toolkit/Photos/templates/Admin/listUsers.html b/Toolkit/Photos/templates/Admin/listUsers.html
new file mode 100644 (file)
index 0000000..1d0d90c
--- /dev/null
@@ -0,0 +1,26 @@
+<flexy:include src="Admin/userNav.html">
+<div>
+    <h2>{statusText}</h2>
+    <table id="admin-list-table">
+        <tr>
+            <th width="15">Function</th>
+            <th>Name</th>
+            <th width="50">Expires</th>
+            <th width="10">Approved</th>
+        </tr>
+        <tr flexy:foreach="users,user">
+            <td nowrap="nowrap" align="left">
+                <a href="{editUrl:h}{user.getId()}&userStatus={_REQUEST[userStatus]}">[Edit]</a>
+            </td>
+            <td align="left">{user.getFname()} {user.getLname()}</td>
+            <td align="right">{user.getExpire()}</td>
+            <td align="right">
+                {if:user.getApproved()}
+                Yes
+                {else:}
+                No
+                {end:}
+            </td>
+        </tr>
+    </table>
+</div>
diff --git a/Toolkit/Photos/templates/Admin/nav.html b/Toolkit/Photos/templates/Admin/nav.html
new file mode 100644 (file)
index 0000000..953174b
--- /dev/null
@@ -0,0 +1,17 @@
+<ul class="admin_nav">
+    <li flexy:if="addCategoryUrl">
+        <a href="{addCategoryUrl}">Add New Category</a>
+    </li>
+    <li flexy:if="addPhotoUrl">
+        <a href="{addPhotoUrl:h}">Add Photo</a>
+    </li>
+    <li flexy:if="addMultipleUrl" id="multiple-link">
+        <a href="{addMultipleUrl:h}">Add Multiple Photos</a>
+    </li>
+    <li flexy:if="listCategoryUrl">
+        <a href="{listCategoryUrl:h}">List Categories</a>
+    </li>
+    <li flexy:if="listUsersUrl">
+        <a href="{listUsersUrl:h}">List Users</a>
+    </li>
+</ul>
diff --git a/Toolkit/Photos/templates/Admin/searchForm.html b/Toolkit/Photos/templates/Admin/searchForm.html
new file mode 100644 (file)
index 0000000..6b5ac51
--- /dev/null
@@ -0,0 +1,76 @@
+<script>
+    $(function() {
+        $("#photoSearch").autocomplete({
+            source: function(request, response) {
+                $.ajax({
+                    url: "photos.php?ac=ajaxPhotoSearch",
+                    dataType: "json",
+                    data: {
+                        photoName: request.term
+                    },
+                    success: function(data) {
+                        response(data);
+                    }
+                });
+            },
+            minLength: 2
+        });
+    });
+</script>
+<section id="mediaGallerySearchForm">
+    <div id="categorySearchWrapper">
+    <form
+        method="get"
+        action="{categorySearchUrl:h}"
+        id="categoryAdminSearch"
+        name="photoAdminSearch"
+        flexy:ignore="yes">
+        <input type="hidden" name="ac" value="editCategory">
+        <select id="categorySearch" name="id">
+            <option value="">Search By Category</option>
+            <?php
+            if (!empty($t->categories)) {
+            foreach ($t->categories as $category) {
+            echo '<option value="' . $category->getId() . '"';
+            if ($category->getId() == $t->categoryId) {
+            echo ' selected';
+            }
+            echo '>'.$category->getCategory().'</option>';
+            }
+            }
+            ?>
+        </select>
+        <input
+            type="submit"
+            value="Search">
+    </form>
+    <form
+        method="get"
+        action="{photoSearchUrl:h}"
+        id="photoAdminSearch"
+        name="photoAdminSearch"
+        flexy:ignore="yes">
+        <input
+            type="hidden"
+            name="ac"
+            value="searchPhotos">
+        {if:_REQUEST[photoName]}
+        <input
+            type="search"
+            id="photoSearch"
+            name="photoName"
+            value="{_REQUEST[photoName]}"
+            placeholder="Find Photo By Title">
+        {else:}
+        <input
+            type="search"
+            id="photoSearch"
+            name="photoName"
+            placeholder="Find Photo By Title">
+        {end:}
+        <input
+            type="submit"
+            value="Search">
+    </form>
+    </div><!--/#categorySearchWrapper-->
+</section>
diff --git a/Toolkit/Photos/templates/Admin/userNav.html b/Toolkit/Photos/templates/Admin/userNav.html
new file mode 100644 (file)
index 0000000..721ba30
--- /dev/null
@@ -0,0 +1,14 @@
+<ul class="admin_nav">
+    <li>
+        <a href="{userAdminUrl:h}?ac=listUsers">Pending</a>
+    </li>
+    <li>
+        <a href="{userAdminUrl:h}?ac=listUsers&amp;userStatus=approved">Approved</a>
+    </li>
+    <li>
+        <a href="{userAdminUrl:h}?ac=listUsers&amp;userStatus=denied">Denied</a>
+    </li>
+    <li>
+        <a href="{userAdminUrl:h}?ac=listUsers&amp;userStatus=expired">Expired</a>
+    </li>
+</ul>
diff --git a/Toolkit/Photos/templates/photoCats.html b/Toolkit/Photos/templates/photoCats.html
new file mode 100755 (executable)
index 0000000..a01d715
--- /dev/null
@@ -0,0 +1,17 @@
+<?php
+if ($t->catPhotos) {
+    foreach ($t->catPhotos as $category) {
+        ?>
+        <li>
+            <a
+                href="<?php echo $t->photoUrl;?><?php echo $category->getId();?>">
+                <img
+                    title="<?php echo $category->getCategory()?>"
+                    src="<?php echo $t->photoUrlSmall.$category->getFirstPhoto()?>">
+            </a>
+            <span class="photoHead"><?php echo $category->getCategory();?></span>
+        </li>
+        <?php
+    }
+}
+?>
\ No newline at end of file
diff --git a/Toolkit/Photos/templates/photoGalleryWrapper.html b/Toolkit/Photos/templates/photoGalleryWrapper.html
new file mode 100644 (file)
index 0000000..e48bb57
--- /dev/null
@@ -0,0 +1,336 @@
+<style>
+#media-photo-gallery {
+    font-size:12px;
+}
+#gallery {
+    clear:both;
+}
+.gallery {
+    text-align: center;
+    list-style: none outside none;
+    padding: 10px 0 0 0;
+}
+.gallery:after {
+    clear: both;
+    content: ".";
+    display: block;
+    height: 0;
+    visibility: hidden;
+}
+.gallery li {
+    background: none repeat scroll 0 0 #F5F3E5;
+    float: left;
+    margin: 0 2% 26px 0;
+    overflow: hidden;
+    position: relative;
+    width: 23%
+}
+/*.gallery li:nth-child(4n+1) {
+    clear: both;
+    margin: 0 16px 26px 0;
+}
+.gallery li:nth-child(4n+2) {
+    margin: 0 16px 26px 0;
+}
+.gallery li:nth-child(4n+3) {
+    margin: 0 16px 26px 0;
+}
+.gallery li:nth-child(4n+4) {
+    margin: 0 0 26px 0;
+}*/
+.gallery li img {
+    width: 100%;
+    border: 1px solid #2797AB;
+    display: block;
+    height: auto;/*width: 143px;*/
+    -webkit-box-sizing: border-box;
+    -moz-box-sizing: border-box;
+    box-sizing: border-box;
+}
+.gallery li:hover img {
+    border: 1px solid #B8C728;
+}
+.gallery .ui-content {
+    overflow: hidden;
+}
+/*#inside #main {
+    width: 720px;
+}*/
+#staticBanners {
+    /*display:none;*/
+}
+.media-gallery-download {
+    font-size: 10px;
+    min-height: 22px;
+}
+#media-photo-gallery div {
+    clear:none;
+    font-size: 1em;
+    height: auto;
+    margin: 0;
+    padding: 0;
+}
+#media-photo-gallery div p {
+    display: block;
+    color: black;
+    padding: 5px;
+    text-shadow: none;
+    font-weight: normal;
+}
+#media-photo-gallery div a {
+}
+#filter_wrapper {
+    overflow: hidden;
+}
+#cat_filter_wrapper {
+    float: left;
+    height: 56px;
+}
+#photoCategoryLabel {
+    float: left;
+}
+#photoCategoryId {
+    float: left;
+    clear: left;
+    width: 180px;
+    height: 24px;
+    border: 1px solid #ABADB3;
+    box-sizing: border-box;
+    padding:.2em;/.3em;
+}
+#photoCategoryId option {
+    padding: 3px;
+}
+#media-login #username {
+    float: left;
+    width: 100px;
+    height: 10px;
+    padding: 5px;
+    margin: 0 5px 0 0;
+    border: 1px solid #ABADB3;
+}
+#media-login #password {
+    float: left;
+    width: 100px;
+    height: 10px;
+    padding: 5px;
+    margin: 0 5px 0 0;
+    border: 1px solid #ABADB3;
+}
+#media-login #submit {
+    display: block;
+    float: left;
+    width: 24px;
+    height: 22px;
+    cursor: pointer;
+    background: url(<?php echo $t->base_url;?>assets/play.gif) 50% 50% no-repeat;
+    border: 1px solid #ABADB3;
+    -bracket-:hack(;
+        height: 22px;
+    );
+}
+.photoHead {
+    display: block;
+    text-align: center;
+    min-height: 38px;
+    margin: 0 !important;
+    padding: 5px;
+    background: none;
+}
+.captionWrapper {
+    display: block;
+    width: 100%;
+    overflow: hidden;
+}
+.captionTitleWrapper {
+    display: block;
+    width: 50%;
+    float: left;
+    font-size: 18px;
+    font-size: 1.8rem;
+    font-weight: bold;
+    color: #FFF;
+    text-align: left;
+    padding: 10px 20px;
+    margin: 0;
+}
+.captionDescWrapper {
+    display: block;
+    width: 50%;
+    float: left;
+    clear: left;
+    font-size: 14px;
+    font-size: 1.4rem;
+    color: #FFF;
+    text-align: left;
+    padding: 0 20px 10px 20px;
+    margin: 0;
+}
+.download_wrapper {
+    display: block;
+    position: absolute;
+    z-index: 99;
+    top: 10px;
+    right: 20px;
+    width: 30%;
+    font-size: 14px;
+    font-size: 1.4rem;
+    color: #FFF;
+    text-align: left;
+    padding: 6px 0 10px 20px;
+    margin: 0;
+    text-align: right;
+}
+.download_wrapper a {
+    color: #CCC;
+}
+.download_wrapper a:hover {
+    color: #FFF;
+}
+.ps-caption-content {
+    width: 100%;
+    overflow: hidden;
+    position: relative;
+}
+</style>
+<flexy:toJavascript flexy:prefix="" base_url="base_url" ></flexy:toJavascript>
+<flexy:toJavascript flexy:prefix="" pageId="pageId" ></flexy:toJavascript>
+<div id="media-photo-gallery">
+    <div id="filter_wrapper">
+        <div id="cat_filter_wrapper">
+            <div class="name_wrapper" flexy:if="photoNameSearch">
+            <h2>Filter By Name</h2>
+            <form
+                method="get"
+                id="photoGallery"
+                action="{photoSearchFormUrl:h}"
+                flexy:ignore="yes">
+                <input type="hidden" name="catid" value="{_REQUEST[catid]}">
+                <label id="photoNameLabel"></label>
+                <input
+                    type="search"
+                    name="photo_name"
+                    id="photoSearch"
+                    value="{_REQUEST[photo_name]}"
+                    placeholder="Name">
+                <input type="submit" value="Search">
+            </form>
+            </div>
+            <?php if (count($t->categories) > 1) {?>
+            <div class="cat_wrapper">
+            <h2>Filter By Category</h2>
+            <form
+                method="get"
+                id="photoGallery"
+                action="{photoSearchFormUrl:h}"
+                flexy:ignore="yes">
+                <input type="hidden" name="catid" value="{_REQUEST[catid]}">
+                <label id="photoCategoryLabel"></label><!--/Filter By Category-->
+                <select id="photoCategoryId" name="photoCategoryId">
+                    <option value="">Show Categories</option>
+                    <?php foreach ($t->categories as $category) {
+                        echo '<option value="'.$category['id'].'"';
+                        if ($_REQUEST['photoCategoryId'] == $category['id']) {
+                            echo ' selected';
+                        }
+                        echo '>'.$category['category'].'</option>';
+                    }?>
+                </select>
+            </form>
+            </div>
+            <?php }?>
+
+        </div><!--/#cat_filter_wrapper-->
+        <div style="float:right;" flexy:if="mediaExclusive">
+            {if:isLoggedIn}
+            You're logged in! <a href="{logoutUrl:h}">Logout</a>
+            {else:}
+            <label>Media Login</label>
+            <form
+                id="media-login"
+                method="post"
+                action="{loginUrl:h}"
+                flexy:ignore="yes">
+                <input
+                    type="hidden"
+                    name="catid"
+                    value="{_REQUEST[catid]}">
+                <input
+                    id="username"
+                    name="username"
+                    placeholder="Username"
+                    size="10"
+                    required>
+                <input
+                    id="password"
+                    type="password"
+                    name="password"
+                    placeholder="Password"
+                    size="10"
+                    required>
+                <input
+                    id="submit"
+                    type="submit"
+                    value="">
+            </form>
+            {if:failedStatus}
+            <div style="clear:both;width:250px;color:red;text-wrap: normal;">
+                {failedStatus:h}
+            </div>
+            {end:}
+        {end:}
+        </div>
+    </div>
+    <div class="gallery_wrapper">
+        <div flexy:if="expired">
+            <p>We’re sorry, but your access to this page has expired. If you
+            require additional time to select photos, please fill out and
+            submit a new
+            <a href="{mediaGalleryRequestFormUrl:h}">photo request form</a>.
+            If approved, you will receive your new login information by email
+            within 1 to 2 days.</p>
+
+            <p>Sincerely,</p>
+
+            <p>The Traverse City Tourism Marketing Team</p>
+        </div>
+        <h2 flexy:if="photoCategoryName">{photoCategoryName}</h2>
+        <ul id="Gallery" class="gallery">
+            {if:photos}
+            <flexy:include src="photos.html">
+            {end:}
+            {if:catPhotos}
+            <flexy:include src="photoCats.html">
+            {end:}
+        </ul>
+        {if:mediaExclusive}
+        <div flexy:if="!isLoggedIn">
+            <p>Are you a member of the media and looking for additional or
+                specific photos?
+        <a href="{mediaGalleryRequestFormUrl:h}">Click Here</a>.</p>
+        </div>
+        {end:}
+    </div>
+</div>
+{if:photoNameSearch}
+<script>
+    $(function() {
+        $("#photoSearch").autocomplete({
+            source: function(request, response) {
+                $.ajax({
+                    url: "photoSearch",
+                    dataType: "json",
+                    data: {
+                        photoName: request.term,
+                        pageId: pageId
+                    },
+                    success: function(data) {
+                        response(data);
+                    }
+                });
+            },
+            minLength: 2
+        });
+    });
+</script>
+{end:}
\ No newline at end of file
diff --git a/Toolkit/Photos/templates/photos.html b/Toolkit/Photos/templates/photos.html
new file mode 100755 (executable)
index 0000000..90e3171
--- /dev/null
@@ -0,0 +1,31 @@
+<?php
+if ($t->photos) {
+    foreach ($t->photos as $photo) {
+        ?>
+        <li>
+            <a data-download="<?php if($photo->getDownload()){echo '1';}else{echo '0';}?>" data-photoid="<?php echo $photo->getId()?>" data-download="<?php echo $photo->getDownload()?>" rel="<?php echo htmlspecialchars($photo->getDescription())?>" title="<?php echo htmlspecialchars($photo->getTitle())?>" class="photoimg" href="<?php echo $t->photoUrlLarge.$photo->getImage()?>">
+                <img title="<?php echo htmlspecialchars($photo->getTitle())?>" alt="<?php echo htmlspecialchars($photo->getDescription())?>" src="<?php echo $t->photoUrlSmall.$photo->getImage()?>">
+                <?php if ($photo->getTitle()) {?>
+                <span class="photoHead">
+                <?php
+                    $tempTitle = $photo->getTitle();
+                    if(strlen($tempTitle) > 50) {
+                        $tempTitle = substr($tempTitle, 0, 47);
+                        $tempTitle .= "...";
+                    }
+                    echo $tempTitle;
+                    //echo $photo->getTitle();
+                ?>
+                </span>
+                <?php } ?>
+            </a>
+            <?php if ($photo->getDownload()) {?>
+                <span class="media-gallery-download">
+                    Download for
+                    <a href="<?php echo $t->photoDownWeb.$photo->getId()?>/">Web</a>&nbsp;-&nbsp;<a href="<?php echo $t->photoDownPrint.$photo->getId()?>/">Print</a>
+                </span>
+            <?php } ?>
+        </li>
+    <?php }
+}
+?>
diff --git a/Toolkit/Registry.php b/Toolkit/Registry.php
new file mode 100644 (file)
index 0000000..3003cd1
--- /dev/null
@@ -0,0 +1,82 @@
+<?php
+
+/**
+ * Variable Registry
+ *
+ * PHP version 5
+ *
+ * The license text...
+ *
+ * @category  Toolkit
+ * @package   Registry
+ * @author    Jamie Kahgee <jamie@gaslightmedia.com>
+ * @copyright 2010 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com/ Gaslightmedia
+ * @version   CVS: $Id: Registry.php,v 1.2 2010/05/25 14:07:22 jamie Exp $
+ * @link      <>
+ * @see       References to other sections (if any)...
+ */
+
+/**
+ * Variable Registry
+ *
+ * The registry is an object where site wide variables can be stored
+ * without the use of globals.  By passing the registry object to
+ * the controllers that need them, we avoid pollution of the global
+ * namespace and render our variables safe.
+ *
+ * @category  Toolkit
+ * @package   Registry
+ * @author    Jamie Kahgee <jamie@gaslightmedia.com>
+ * @copyright 2010 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com/ Gaslightmedia
+ * @version   Release: @package_version@
+ * @link      <>
+ * @see       References to other sections (if any)...
+ */
+class Toolkit_Registry
+{
+       //      {{{     properties
+
+       /**
+        * variables
+        * @access private
+        */
+       private $_vars = array();
+
+       //      }}}
+
+       //      {{{     __set()
+
+       /**
+        * Sets undefined variables
+        *
+        * @param string $index key index in variables array
+        * @param mixed  $value variable value
+        *
+        * @return void
+        * @access public
+        */
+       public function __set($index, $value)
+       {
+               $this->_vars[$index] = $value;
+       }
+
+       //      }}}
+       //      {{{     __get()
+
+       /**
+        * Gets defined variables
+        *
+        * @param mixed $index variable to retrieve
+        *
+        * @return mixed
+        */
+       public function __get($index)
+       {
+               return $this->_vars[$index];
+       }
+
+       //      }}}
+}
+?>
diff --git a/Toolkit/Router.php b/Toolkit/Router.php
new file mode 100644 (file)
index 0000000..eaca26d
--- /dev/null
@@ -0,0 +1,195 @@
+<?php
+
+/**
+ * Router.php
+ *
+ * PHP version 5
+ *
+ * The license text...
+ *
+ * @category  Toolkit
+ * @package   Router
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2010 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com/ Gaslightmedia
+ * @version   CVS: $Id: Registry.php,v 1.2 2010/05/25 14:07:22 jamie Exp $
+ * @link      <>
+ * @see       References to other sections (if any)...
+ */
+
+/**
+ * Toolkit_Router
+ *
+ * The registry is an object where site wide variables can be stored
+ * without the use of globals.  By passing the registry object to
+ * the controllers that need them, we avoid pollution of the global
+ * namespace and render our variables safe.
+ *
+ * @category  Toolkit
+ * @package   Router
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2010 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com/ Gaslightmedia
+ * @version   Release: @package_version@
+ * @link      <>
+ * @see       References to other sections (if any)...
+ */
+class Toolkit_Router
+{
+    //    {{{    properties
+
+
+    /**
+     * Description for $_registry
+     * @var string
+     * @access protected
+     */
+    private $_registry;
+
+    /**
+     * Description for $_path
+     * @var string
+     * @access protected
+     */
+    private $_path;
+
+    /**
+     * Description for $_application
+     * @var string
+     * @access protected
+     */
+    private $_application;
+
+    /**
+     * Will hold location of controller
+     * @var string
+     * @access protected
+     */
+    public $file;
+
+    /**
+     * Holds intended controller
+     * @var string
+     * @access protected
+     */
+    public $controller;
+
+    /**
+     * Holds intended action
+     * @var string
+     * @access protected
+     */
+    public $action;
+
+    //    }}}
+    //    {{{    __construct()
+
+    /**
+     * class constructor
+     *
+     * @param Toolkit_Registry $registry Registry
+     *
+     * @access public
+     */
+    public function __construct(Toolkit_Registry $registry)
+    {
+        $this->_registry = $registry;
+    }
+
+    //    }}}
+
+    //    {{{    _getController()
+
+    /**
+     * Gets controller and action.
+     *
+     * @access private
+     * @return void
+     */
+    private function _getController()
+    {
+        $this->controller = empty($_GET['rt']) ? 'Index' : $_GET['rt'];
+        $this->action     = empty($_GET['ac']) ? 'index' : $_GET['ac'];
+
+        // all action methods must end in the word [Action] so we
+        // can easily spot them.
+        $this->action .= 'Action';
+
+        $this->file = $this->_path . '/' . $this->controller . 'Controller.php';
+    }
+
+    //    }}}
+
+    //    {{{    loader()
+
+    /**
+     * Loads toolkit application
+     *
+     * @return string
+     */
+    public function loader()
+    {
+        //    Check the route
+        $this->_getController();
+
+        //    If the file is not there, log error
+        if (is_readable($this->file) == false) {
+            $this->_registry
+                ->logger
+                ->emerg("File is not readable: `{$this->file}`");
+        }
+
+        $class = "Toolkit_{$this->_application}_{$this->controller}Controller";
+        $controller = new $class($this->_registry);
+
+        if (is_callable(array($controller, $this->action)) == false) {
+            $action = 'indexAction';
+        } else {
+            $action = $this->action;
+        }
+
+        return $controller->$action();
+    }
+
+    //    }}}
+
+    //    {{{    setApplication()
+
+    /**
+     * Set application
+     *
+     * @param string $application Application
+     *
+     * @return void
+     */
+    public function setApplication($application)
+    {
+        $this->_application = str_replace('/', '_', $application);
+    }
+
+    //    }}}
+    //    {{{    setPath()
+
+    /**
+     * Sets path
+     *
+     * @param string $path Path
+     *
+     * @throws RuntimeException
+     *
+     * @return void
+     */
+    public function setPath($path)
+    {
+        if (is_dir($path) == false) {
+            throw new RuntimeException(
+                "Invalid controller path: `$path`"
+            );
+        }
+
+        $this->_path = $path;
+    }
+
+    //    }}}
+}
+?>
diff --git a/Toolkit/ShortURL.php b/Toolkit/ShortURL.php
new file mode 100755 (executable)
index 0000000..208b885
--- /dev/null
@@ -0,0 +1,163 @@
+<?php
+
+/**
+ * ShortURL.php
+ *
+ * Integration of Toolbox with creation of ShortURL
+ *
+ * PHP version 5
+ *
+ * @category  Toolkit
+ * @package   Toolkit_ShortURL
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2009 Gaslight Media
+ * @license   Gaslight Media
+ * @version   CVS: $Id: ShortURL.php,v 1.5 2010/05/25 14:07:22 jamie Exp $
+ * @link      <>
+ */
+
+/**
+ * Toolkit_ShortURL
+ *
+ * Integration of Toolbox with creation of ShortURL
+ * the url rewrite happens with one line of code in .htaccess
+ * RewriteRule ^([A-Za-z0-9_-]*)$ index.php?page=$1 [L]
+ * index page checks for $_GET['page'] and then calls
+ * getShortUrlId to get the page's catid. If not found it
+ * defaults to HOME_ID
+ *
+ * @category  Toolkit
+ * @package   Toolkit_ShortURL
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2009 Gaslight Media
+ * @license   Gaslight Media
+ * @version   Release: @package_version@
+ * @link      <>
+ */
+class Toolkit_ShortURL
+{
+    // {{{ Class Properties
+
+    /**
+     * Database handler
+     * @var    PDO
+     * @access private
+     */
+    private $_dbh;
+
+    // }}}
+
+    // {{{ __construct()
+
+    /**
+     * __construct()
+     *
+     * @param PDO $dbh Database handler
+     *
+     * @return void
+     * @access public
+     */
+    function __construct(PDO $dbh)
+    {
+        $this->_dbh = $dbh;
+    }
+
+    // }}}
+    // {{{ getShortUrl()
+
+    /**
+     * getShortUrl()
+     *
+     * grab the shorturl from bus_category table
+     * else return false
+     *
+     * @param string $id bus_category id
+     *
+     * @return mixed  ShortURL from bus_category
+     * @access public
+     */
+    function getShortUrl($id)
+    {
+        $sql = "
+            SELECT short_url
+              FROM pages
+             WHERE id = :id";
+        try {
+            $stmt = $this->_dbh->prepare($sql);
+            $stmt->bindParam(":id", $id, PDO::PARAM_INT);
+            $stmt->execute();
+            return $stmt->fetchColumn();
+        } catch(PDOException $e) {
+            return Toolkit_Common::handleError($e);
+        }
+    }
+
+    // }}}
+    // {{{ getShortUrlId()
+
+    /**
+     * getShortUrlId()
+     *
+     * grab the id from bus_category table
+     * else return false
+     *
+     * @param string $short_url bus_category short_url
+     *
+     * @return mixed  catid for page or HOME_ID if not found
+     * @access public
+     */
+    function getShortUrlId($short_url)
+    {
+        $sql = "
+            SELECT id
+              FROM pages
+             WHERE short_url = :short_url";
+        try {
+            $stmt = $this->_dbh->prepare($sql);
+            $stmt->bindParam(":short_url", $short_url, PDO::PARAM_STR);
+            $stmt->execute();
+            return $stmt->fetchColumn();
+        } catch(PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+        return HOME_ID;
+    }
+
+    // }}}
+    // {{{ isValidUrlName()
+
+    /**
+     * check the short url nam eto make sure it doesn't match any file names in the root
+     *
+     * @param mixed $url requested short url name
+     *
+     * @access public
+     * @return string
+     */
+    public function isValidUrlName($url)
+    {
+        if (is_dir(BASE)) {
+            if (file_exists(BASE . $url)) {
+                return false;
+            }
+            if (file_exists(BASE . $url.'.php')) {
+                return false;
+            }
+            if (file_exists(BASE . $url.'.css')) {
+                return false;
+            }
+            if (file_exists(BASE . $url.'.html')) {
+                return false;
+            }
+            if (file_exists(BASE . $url.'.js')) {
+                return false;
+            }
+            return true;
+        } else {
+            throw new Exception('Main Web root dir define BASE not set');
+        }
+    }
+
+    // }}}
+}
+?>
diff --git a/Toolkit/SiteMap.php b/Toolkit/SiteMap.php
new file mode 100755 (executable)
index 0000000..c527645
--- /dev/null
@@ -0,0 +1,158 @@
+<?php
+
+/**
+ * SiteMap.php
+ *
+ * PHP versions 4 and 5
+ *
+ * @category  Toolkit
+ * @package   Toolkit_SiteMap
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2009 Gaslight Media
+ * @license   Gaslight Media
+ * @version   CVS: $Id: SiteMap.php,v 1.10 2010/06/08 20:39:20 jamie Exp $
+ * @link      <>
+ */
+
+/**
+ * Toolkit_SiteMap
+ *
+ * Generate a sitemap for the site
+ * need to remove members only pages and other pages you
+ * don't want viewed by public
+ *
+ * @category  Toolkit
+ * @package   Toolkit_SiteMap
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2009 Gaslight Media
+ * @license   Gaslight Media
+ * @link      <>
+ */
+class Toolkit_SiteMap
+{
+    // {{{ Properties
+    /**
+     * Description of $beginLevel
+     * @var    string
+     * @access public
+     */
+    var $beginLevel = "<ul>";
+
+    /**
+     * Description of $endLevel
+     * @var    string
+     * @access public
+     */
+    var $endLevel = "</ul>";
+
+    /** 
+     * Description of $beginItem
+     * @var    string
+     * @access public
+     */
+    var $beginItem = "<li>";
+
+    /**
+     * Description of $endItem
+     * @var    string
+     * @access public
+     */
+    var $endItem = "</li>";
+
+    /**
+     * Description of $wholeThread
+     * @var    string
+     * @access public
+     */
+    var $wholeThread;
+
+    /**
+     * Description of $search
+     * @var    string
+     * @access public
+     */
+    var $search = "";
+    
+    /**
+     * Description of $pageGateway
+     * @var    string
+     * @access public
+     */
+    var $pageGateway;
+
+    // }}}
+    
+    /**
+     * Class constructor
+     * 
+     * Creates object of siteMap
+     * 
+     * @param string $pageGateway page gateway
+     */
+    public function __construct($pageGateway)
+    {
+        $this->pageGateway = $pageGateway;
+    }
+    // {{{ sortChilds()
+
+    /**
+     * sortChilds
+     *
+     * Sort by Parent
+     *
+     * @param array $threads Array
+     *
+     * @return array   Return array
+     * @access public
+     */
+    function sortChilds($threads)
+    {
+        $childs = array();
+        while (list($var, $value) = each($threads)) {
+            $childs[$value['parent']][$value['id']] = $value;
+        }
+        return $childs;
+    }
+
+    // }}}
+
+    // {{{ convertToThread()
+
+    /**
+     * convertToThread
+     *
+     * outputs the array with the correct styles and code applied
+     *
+     * @param array $threads Thread array
+     * @param array $thread  Start with thread[0] will work it way down
+     *
+     * @return wholeThread
+     * @access public
+     */
+    function convertToThread($threads, $thread)
+    {
+        static $level_counter = 1;
+
+        $this->wholeThread .= $this->beginLevel;
+        while (list($parent, $value) = each($thread)) {
+            $this->wholeThread .= $this->beginItem;
+            $page = Toolkit_Template_Page::getSeoUrl(
+                $this->pageGateway,
+                $value['id']
+            );
+            $this->wholeThread .= '<a href="'.$page.'">';
+            $this->wholeThread .= $value["category"]
+                . "</a>";
+            if ($threads[$parent]) {
+                ++$level_counter;
+                $this->convertToThread($threads, $threads[$parent]);
+                --$level_counter;
+            }
+            $this->wholeThread .= $this->endItem ."\n";
+        }
+        $this->wholeThread .= $this->endLevel;
+        return $this->wholeThread;
+    }
+    // }}}
+}
+?>
diff --git a/Toolkit/SiteMapXml.php b/Toolkit/SiteMapXml.php
new file mode 100644 (file)
index 0000000..7fb394f
--- /dev/null
@@ -0,0 +1,186 @@
+<?php
+/**
+ * SiteMapXml.php
+ *
+ * PHP Version 5.2
+ *
+ * @category  Toolkit
+ * @package   Toolkit
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @version   SVN: $id$
+ * @link      <>
+ */
+
+/**
+ * Toolkit_SiteMapXml
+ *
+ * Description of Toolkit_SiteMapXml
+ *
+ * @category  Toolkit
+ * @package   Toolkit
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @release   Release: $id$
+ * @link      <>
+ */
+
+class Toolkit_SiteMapXml
+{
+    /**
+     * Database Connection
+     *
+     * @var PDO
+     * @access private
+     */
+    private $_dbh;
+
+    /**
+     * Class Constructor
+     *
+     * @param PDO $pdo Database Connection
+     *
+     * @return void
+     * @access public
+     */
+    public function __construct(PDO $pdo)
+    {
+        $this->_dbh = $pdo;
+    }
+
+    /**
+     * getMobileSitemap
+     *
+     * Call this function with no $sid.
+     *
+     * This function will recurse down the menu structure and
+     * return a full list of all menu data at all levels
+     *
+     * This is helpful for generating a sitemap or index
+     *
+     * @static var array $mids Member array
+     * @static var string $xml XML string
+     *
+     * @param int $sid Catid of the page
+     *
+     * @return string xml
+     * @access public
+     */
+    public function getXmlSitemap($sid = 0)
+    {
+
+        // Characters to be replaced in link text
+        $transdata = array('/' => '', ' ' => '-', '&' => '');
+
+        // Array to keep track of member's for whom we produced links
+        // This is used to prevent duplicate links to the same member
+        // records from different catids. It's static so that it is
+        // common to all instances of recursion.
+        static $mids = array();
+
+        // This is where the XML site map goes. It's also static so
+        // we don't have to pass map segments back from recursive calls.
+        static $xml = '';
+
+        // If this is the top level, add the beginning of the xml file
+        if ($sid == 0) {
+            $xml = '<?xml version="1.0" encoding="UTF-8"?>' . "\r\n"
+                . '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"'
+                . "\r\n"
+                . '        >' . "\r\n";
+        }
+
+        $qs = "
+          SELECT id, navigation_name as category, include_members
+            FROM pages
+           WHERE parent = :id";
+        if (defined('MEMBERS_CATEGORY') && MEMBERS_CATEGORY) {
+            $qs .= " AND parent != ".MEMBERS_CATEGORY."
+             AND id != ".MEMBERS_CATEGORY;
+        }
+        $qs .= " AND active = 't'
+        ORDER BY pos";
+        $stmt = $this->_dbh->prepare($qs);
+        $stmt->bindParam(
+            ':id',
+            $sid,
+            PDO::PARAM_INT
+        );
+        $stmt->execute();
+
+        while ($v = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                $catid = $v['id'];
+                if ($catid > 0) {
+                    // Add this menu item to the site map
+                    $url
+                        = ($catid == HOME_ID)
+                        ? MEDIA_BASE_URL
+                        : MEDIA_BASE_URL . strtr($v['category'], $transdata) . '-'
+                        . $catid . '/';
+                    $xml .= '<url><loc>' . $url . "</loc></url>\r\n";
+                    // Get members for this catid
+                    if (MEMBERS_DB == 1 && $v['include_members']) {
+                            // now check to see if the page has member
+                            // categories assigned to it
+                            $qs = "
+                                SELECT *
+                                  FROM member_categories2toolbox_pages
+                                 WHERE page = {$catid}";
+                            $mc2tpData
+                                = $this->_dbh->query($qs)
+                                ->fetchAll(PDO::FETCH_ASSOC);
+                            if ($mc2tpData) {
+                                foreach ($mc2tpData as $mc2tp) {
+                                    $mc2tpCat[] = $mc2tp['category'];
+                                }
+                                if (is_array($mc2tpCat) && !empty($mc2tpCat)) {
+                                    $where = " WHERE member_id IN (
+                                            SELECT member_id
+                                            FROM member_category
+                                            WHERE category_id IN ("
+                                    . implode(",", $mc2tpCat) . ")) ";
+                                }
+                            }
+                            $qs = "
+                                SELECT member_id,member_name
+                                  FROM member
+                                $where";
+                        $members = $this->_dbh
+                            ->query($qs)
+                            ->fetchAll(PDO::FETCH_ASSOC);
+                    } //IF MEMBERS_DB
+                    // If there are any
+                    if ($members && count($members) > 0) {
+                        // Include an entry for each member on this page
+                        foreach ($members as $memb) {
+
+                            // Check to make sure we have a member # and
+                            // that we haven't already provided some
+                            // link to this member
+                            $member = ($memb['member_id'] - 0);
+                            if ($member > 0 && !isset($mids[$member])) {
+
+                                // Add this member to the site map
+                                $murl = MEDIA_BASE_URL . 'member-profile/'.$catid
+                                    . '/' . $member . '/';
+                                $xml .= '<url><loc>' . $murl
+                                    . "</loc></url>\r\n";
+                                // Add this member to the member id array so we
+                                // don't display them again.
+                                $mids[$member] = true;
+                            }
+                        }
+                    } // if members
+                    $this->getXmlSitemap($catid);
+                } // if catid
+        } // if menu results
+        // If this is the top level, add the end of the XML file and be done
+        if ($sid == 0) {
+            $xml .= "</urlset>\r\n";
+            return $xml;
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/Toolkit/SortForm.php b/Toolkit/SortForm.php
new file mode 100644 (file)
index 0000000..1c6e8da
--- /dev/null
@@ -0,0 +1,67 @@
+<?php
+//    vim:set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker syntax=php:
+
+/**
+ * Creates a sortform to be used w/ datagrid DataGrids
+ *
+ * PHP version 5
+ *
+ * @category HTML
+ * @package  Toolkit_SortForm
+ * @author   Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @license     http://www.gaslightmedia.com Gaslightmedia
+ * @version  CVS: $Id: SortForm.php,v 1.6 2010/01/18 02:09:05 jamie Exp $
+ * @link     http://demo.gaslightmedia.com
+ */
+
+/**
+ * Creates a sortform to be used w/ datagrid DataGrids
+ *
+ * @category  HTML
+ * @package   Toolkit_SortForm
+ * @author    Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2008 Gaslight Media
+ * @license      http://www.gaslightmedia.com Gaslightmedia
+ * @link      http://demo.gaslightmedia.com
+ */
+class Toolkit_SortForm extends Toolkit_FormBuilder
+{
+    //  {{{ properties
+
+    /**
+     * The default rules to register for validating
+     *
+     * @var string
+     * @access protected
+     */
+    protected $registeredRules = array();
+
+    //  }}}
+    //    {{{    toHTML()
+
+    /**
+     * Render the form to a string
+     *
+     * Handle the rendering and validation of the form when displayed
+     * and submitted.
+     *
+     * @return string html form
+     * @access public
+     */
+    public function toHTML()
+    {
+        $this->setupRenderers();
+        if ($this->validate()) {
+            $output = parent::toHTML();
+        } else if ($this->isSubmitted()) {
+            $output  = $this->errorMsg;
+            $output .= parent::toHTML();
+        } else {
+            $output = parent::toHTML();
+        }
+        return $output;
+    }
+
+    //    }}}
+}
+?>
diff --git a/Toolkit/Table.php b/Toolkit/Table.php
new file mode 100644 (file)
index 0000000..2b6120a
--- /dev/null
@@ -0,0 +1,300 @@
+<?php
+/**
+ * Table.php
+ *
+ * PHP version 5
+ *
+ * @category Toolkit
+ * @package  Table
+ * @author   Steve Sutton <steve@gaslightmedia.com>
+ * @license  http://www.gaslightmedia.com Gaslightmedia
+ * @link     <>
+ */
+
+/**
+ * Abstract class for aiding creation of class objects which relate to
+ * Database tables
+ *
+ * @category Toolkit
+ * @package  Table
+ * @author   Steve Sutton <steve@gaslightmedia.com>
+ * @license  http://www.gaslightmedia.com Gaslightmedia
+ * @link     <>
+ */
+abstract class Toolkit_Table
+    implements Toolkit_ITable, Toolkit_IMapper
+{
+    /**
+     * Description for $tableName
+     * @var string
+     * @access protected
+     */
+    protected $tableName;
+
+    /**
+     * Helps to create functions for any class variable if the variable does
+     * exists
+     *
+     * @param String $name Name of variable
+     * @param mixed  $args Argument for the varable (if set)
+     *
+     * @return Toolkit_Table
+     */
+    public function __call($name, $args)
+    {
+        if (   preg_match('/^(get|set)(\w+)/', strtolower($name), $match)
+            && $attribute = $this->validateAttribute($match[2])
+        ) {
+            if ('get' == $match[1]) {
+                return $this->$attribute;
+            } else {
+                $this->$attribute = trim($args[0]);
+                return $this;
+            }
+        } else {
+            throw new Exception(
+                'Call to undefined method '.  get_class($this).'::' . $name
+            );
+        }
+    }
+
+    /**
+     * Create a class object that has the given values from an array
+     *
+     * @param Array $values Array of values to set the class properties to
+     *
+     * @return Object
+     * @access public
+     */
+    public function  createByValues($values)
+    {
+        $classVars = get_object_vars($this);
+        unset($classVars['tableName']);
+        foreach ($classVars as $fieldName => $var) {
+            if ($fieldName == 'id' && $values[$fieldName] == '') {
+                continue;
+            }
+            $setter = 'set' . ucfirst($fieldName);
+            $this->$setter($values[$fieldName]);
+        }
+        return $this;
+    }
+
+    /**
+     * Removes a object from the database by the id
+     * if id is not set then it throws an error
+     *
+     * @param PDO $dbh Database Connection
+     *
+     * @return void
+     * @throws Exception
+     * @access public
+     */
+    public function delete(PDO $dbh)
+    {
+        if (!$this->id) {
+            throw new Exception('id is not set');
+        }
+        try {
+            $sql = "
+            DELETE
+              FROM {$this->tableName}
+             WHERE id = :id";
+            $stmt = $dbh->prepare($sql);
+            $stmt->bindParam(':id', $this->getId(), PDO::PARAM_INT);
+            $stmt->execute();
+        } catch(PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+    }
+
+    /**
+     * fetch from $this->tableName by id
+     *
+     * @param PDO     $dbh Database Connection
+     * @param integer $id  id to fetch with
+     *
+     * @return null|object
+     * @access public
+     */
+    public function fetchById(PDO $dbh, $id)
+    {
+        try {
+            $sql = "
+            SELECT *
+              FROM {$this->tableName}
+             WHERE id = :id";
+            $stmt = $dbh->prepare($sql);
+            $stmt->bindParam(':id', $id, PDO::PARAM_INT);
+            $stmt->execute();
+            $values = $stmt->fetch(PDO::FETCH_ASSOC);
+            if ($values) {
+                return $this->createByValues($values);
+            } else {
+                return null;
+            }
+        } catch (PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+    }
+
+    /**
+     * Return the object as an array
+     *
+     * @return Array
+     * @access public
+     */
+    public function  getPropertiesAsArray()
+    {
+        $classVars = get_object_vars($this);
+        unset($classVars['tableName']);
+        return $classVars;
+    }
+
+    /**
+     * Return the table metadata
+     *
+     * @param PDO    $dbh       Database Connection
+     * @param string $tableName Table name
+     *
+     * @return array
+     * @access public
+     */
+    public function getTableMetaData(PDO $dbh, $tableName = null)
+    {
+        try {
+            $sql  = "
+            SELECT column_name, data_type
+              FROM information_schema.columns
+             WHERE table_name = :tname";
+            $stmt = $dbh->prepare($sql);
+            $stmt->bindParam(':tname', $tableName, PDO::PARAM_STR);
+            $stmt->execute();
+
+            $metaData = array();
+            while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                $metaData[$row['column_name']] = $row['data_type'];
+            }
+
+            return $metaData;
+        } catch (PDOException $e) {
+            return Toolkit_Common::handleError($e);
+        }
+    }
+
+    /**
+     * insert the object
+     *
+     * @param PDO $dbh Database Connection
+     *
+     * @return Toolkit_Table
+     * @access public
+     */
+    public function insert(PDO $dbh)
+    {
+
+        try {
+            $values = get_object_vars($this);
+            unset($values['id'], $values['tableName']);
+            $sql = Toolkit_Common::createSQLInsert(
+                $this->tableName,
+                array_keys($values)
+            );
+            $sql .= ' RETURNING id';
+            $stmt = Toolkit_Common::prepareQuery(
+                $dbh,
+                $this->tableName,
+                $sql,
+                $values
+            );
+            $stmt->execute();
+            $this->setId($stmt->fetchColumn());
+            return $this;
+        } catch(PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+    }
+
+    /**
+     * Checks the id of the object if it is set then calls update othervise
+     * calls insert function
+     *
+     * @param PDO $dbh Database connection
+     *
+     * @return void
+     * @access public
+     */
+    public function save(PDO $dbh)
+    {
+        if ($this->id) {
+            $this->update($dbh);
+        } else {
+            $this->insert($dbh);
+        }
+    }
+
+    /**
+     * sets the id of object
+     *
+     * @param Int $id set the id of object if not numeric throw Exception
+     *
+     * @return Toolkit_Table
+     * @access public
+     */
+    public function setId($id)
+    {
+        if (!is_numeric($id)) {
+            throw new Exception('id must be an integer');
+        }
+        if (!$this->id) {
+            $this->id = $id;
+        }
+        return $this;
+    }
+
+    /**
+     * update the object
+     *
+     * @param PDO $dbh Database connection
+     *
+     * @return Toolkit_Table
+     * @access public
+     */
+    public function update(PDO $dbh)
+    {
+        try {
+            $values = get_object_vars($this);
+            unset($values['tableName']);
+            $sql = Toolkit_Common::createSQLUpdate(
+                $this->tableName,
+                array_keys($values),
+                array('id = :id')
+            );
+            $stmt = Toolkit_Common::prepareQuery(
+                $dbh,
+                $this->tableName,
+                $sql,
+                $values
+            );
+            $stmt->execute();
+            return $this;
+        } catch(PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+    }
+
+    /**
+     * Validates the property
+     *
+     * @param String $name property name
+     *
+     * @return String
+     * @access public
+     */
+    protected function validateAttribute($name)
+    {
+        if (property_exists(get_class($this), $name)) {
+            return strtolower($name);
+        }
+    }
+}
diff --git a/Toolkit/Template/BreadCrumbs.php b/Toolkit/Template/BreadCrumbs.php
new file mode 100644 (file)
index 0000000..3a2a20e
--- /dev/null
@@ -0,0 +1,143 @@
+<?php
+/**
+ * BreadCrumbs.php
+ *
+ * PHP version 5
+ *
+ * @category  Toolkit
+ * @package   Template
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2009 Gaslight Media
+ * @license   Gaslight Media
+ * @version   CVS: $Id: ShortURL.php,v 1.5 2010/05/25 14:07:22 jamie Exp $
+ * @link      <>
+ */
+
+
+/**
+ * Toolkit_Template_BreadCrumbs
+ *
+ * Description of Toolkit_Template_BreadCrumbs
+ *
+ * @category  Toolkit
+ * @package   Template
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @release   Release: $id$
+ * @link      <>
+ */
+class Toolkit_Template_BreadCrumbs
+{
+       //      {{{     properties
+
+    /**
+     * Description for $pageGateway
+     * @var    Toolkit_Toolbox_PageGatewayAbstract $gateway
+     * @access protected
+     */
+       protected $pageGateway;
+
+       //      }}}
+       //      {{{     __construct()
+
+    /**
+     * Class constructor
+     *
+     * @param Toolkit_Toolbox_PageGatewayAbstract $gateway Gateway
+     * @access public
+     */
+       public function __construct(Toolkit_Toolbox_PageGatewayAbstract $gateway)
+       {
+               $this->pageGateway = $gateway;
+       }
+
+       //      }}}
+       //      {{{     getBreadCrumbsArray()
+
+    /**
+     * Gets bread crumbs array
+     *
+     * @param int $id ID
+     *
+     * @return array
+     * @throws RuntimeException
+     * @access protected
+     */
+       protected function getBreadCrumbsArray($id)
+       {
+               $stack = array();
+
+               $hasPhotoGalleriesOnThisPage
+                       = (   defined('PHOTO_GALLERY')
+                          && PHOTO_GALLERY
+                          && filter_var($_GET['photo_catid'], FILTER_VALIDATE_INT)
+               );
+
+               if ($hasPhotoGalleriesOnThisPage) {
+                       if (!$catid = filter_var($_GET['catid'], FILTER_VALIDATE_INT)) {
+                               throw new RuntimeException('Invalid catid');
+                       }
+                       if (!$photoCatid = filter_var($_GET['photo_catid'], FILTER_VALIDATE_INT)) {
+                               throw new RuntimeException('Invalid photo_catid');
+                       }
+                       $page = $this->pageGateway->findNavItem($catid);
+                       array_unshift($stack, $page['photo_galleries'][$_GET['photo_catid']]);
+                       $seoUrl = Toolkit_Template_Page::getSeoUrl(
+                               $this->pageGateway,
+                               $_GET['catid']
+                       );
+                       $anchor = '<a href="'.$seoUrl.'">'.$page['navigation_name'].'</a>';
+                       array_unshift($stack, $anchor);
+               }
+
+               if ($id != HOME_ID) {
+                       $page  = $this->pageGateway->findNavItem($id);
+                       if ($hasPhotoGalleriesOnThisPage) {
+                               //      Do Nothing
+                       } else {
+                               array_unshift($stack, $page['navigation_name']);
+                       }
+                       $parent = $page['parent'];
+                       while ($parent != 0) {
+                               $page = $this->pageGateway->findNavItem($parent);
+                               $seoUrl = Toolkit_Template_Page::getSeoUrl(
+                                       $this->pageGateway,
+                                       $parent
+                               );
+                               $anchor = '<a href="'.$seoUrl.'">'.$page['navigation_name'].'</a>';
+                               array_unshift($stack, $anchor);
+                               $parent = $page['parent'];
+                       }
+
+                       $anchor = '<a href="' . BASE_URL . 'index.php">Home</a>';
+                       array_unshift($stack, $anchor);
+               }
+
+               return $stack;
+       }
+
+       //      }}}
+       //      {{{     toHtml()
+
+    /**
+     * to html
+     *
+     * @param int $id ID
+     *
+     * @return string
+     * @access public
+     */
+       public function toHtml($id)
+       {
+               $breadCrumbsArray = $this->getBreadCrumbsArray($id);
+               $breadCrumbs      = implode(' > ', $breadCrumbsArray);
+
+               return !empty($breadCrumbsArray)
+                       ? '<div id="breadcrumbs" class="opensearchserver.ignore">' . $breadCrumbs . '</div>'
+                       : '';
+       }
+
+       //      }}}
+}
+?>
diff --git a/Toolkit/Template/DraftBreadCrumbs.php b/Toolkit/Template/DraftBreadCrumbs.php
new file mode 100644 (file)
index 0000000..8bd85e4
--- /dev/null
@@ -0,0 +1,110 @@
+<?php
+/**
+ * Exception.php
+ *
+ * PHP version 5
+ *
+ * @category  Toolkit
+ * @package   Template
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2009 Gaslight Media
+ * @license   Gaslight Media
+ * @version   CVS: $Id: ShortURL.php,v 1.5 2010/05/25 14:07:22 jamie Exp $
+ * @link      <>
+ */
+
+
+/**
+ * Toolkit_Template_DraftBreadCrumbs
+ *
+ * Description of Toolkit_Template_DraftBreadCrumbs
+ *
+ * @category  Toolkit
+ * @package   Template
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @release   Release: $id$
+ * @link      <>
+ */
+class Toolkit_Template_DraftBreadCrumbs
+       extends Toolkit_Template_BreadCrumbs
+{
+       //      {{{     properties
+
+    /**
+     * Description for $pageGatewayPublish
+     * @var    Toolkit_Toolbox_PageGatewayAbstract
+     * @access protected
+     */
+       protected $pageGatewayPublish;
+
+    /**
+     * Description for $pageGatewayDraft
+     * @var    Toolkit_Toolbox_PageGatewayAbstract
+     * @access protected
+     */
+       protected $pageGatewayDraft;
+
+       //      }}}
+       //      {{{     __construct()
+
+    /**
+     * Class constructor
+     *
+     * @param Toolkit_Toolbox_PageGatewayAbstract $publishGateway
+     * @param Toolkit_Toolbox_PageGatewayAbstract $draftGateway
+     *
+     * @access public
+     */
+       public function __construct(
+               Toolkit_Toolbox_PageGatewayAbstract $publishGateway,
+               Toolkit_Toolbox_PageGatewayAbstract $draftGateway
+       ) {
+               $this->pageGatewayPublish = $publishGateway;
+               $this->pageGatewayDraft   = $draftGateway;
+       }
+
+       //      }}}
+       //      {{{     getBreadCrumbsArray()
+
+    /**
+     * Gets BreadCrumbs Array
+     *
+     * @param int $id
+     *
+     * @return array
+     * @access protected
+     */
+       protected function getBreadCrumbsArray($id)
+       {
+               if ($id == HOME_ID) {
+                       return array();
+               }
+
+               $stack = array();
+               $draftPage  = $this->pageGatewayDraft->find($id);
+               array_unshift($stack, $draftPage['navigation_name']);
+
+               $publishPage = $this->pageGatewayPublish->find($draftPage['published_page']);
+               $parent = $publishPage['parent'];
+               while ($parent != 0) {
+                       $page = $this->pageGatewayPublish->find($parent);
+                       $seoUrl = Toolkit_Template_Page::getSeoUrl(
+                               $this->pageGatewayPublish,
+                               $parent
+                       );
+                       $anchor = '<a href="'.$seoUrl.'">'.$page['navigation_name'].'</a>';
+                       array_unshift($stack, $anchor);
+                       $parent = $page['parent'];
+               }
+
+               $anchor = '<a href="' . BASE_URL . 'index.php">Home</a>';
+               array_unshift($stack, $anchor);
+
+               return $stack;
+       }
+
+       //      }}}
+}
+?>
diff --git a/Toolkit/Template/DraftController.php b/Toolkit/Template/DraftController.php
new file mode 100644 (file)
index 0000000..8d79bab
--- /dev/null
@@ -0,0 +1,82 @@
+<?php
+/**
+ * DraftController.php
+ *
+ * PHP version 5
+ *
+ * @category  Toolkit
+ * @package   Template
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2009 Gaslight Media
+ * @license   Gaslight Media
+ * @version   CVS: $Id: ShortURL.php,v 1.5 2010/05/25 14:07:22 jamie Exp $
+ * @link      <>
+ */
+
+/**
+ * Toolkit_Template_DraftController
+ * 
+ * Description of Toolkit_Template_DraftController
+ * 
+ * @category  Toolkit
+ * @package   Template
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @release   Release: $id$
+ * @link      <>
+ */
+class Toolkit_Template_DraftController
+       extends Toolkit_Template_PageControllerAbstract
+{
+    /**
+     * Description of $_pageGatewayFactory
+     * 
+     * @var Toolkit_Toolbox_PageGatewayDraftFactory
+     * @access private 
+     */
+       private $_pageGatewayFactory;
+    
+    /**
+     * Description of $_paragraphGatewayFactory
+     * 
+     * @var Toolkit_Toolbox_ParagraphGatewayDraftFactory
+     * @access private 
+     */
+       private $_paragraphGatewayFactory;
+
+    /**
+     * Description of getPageGatewayFactory
+     * 
+     * @return Toolkit_Toolbox_PageGatewayDraftFactory
+     * @access protected
+     */
+       protected function getPageGatewayFactory()
+       {
+               if (!($this->_pageGatewayFactory instanceof Toolkit_Toolbox_PageGatewayDraftFactory)) {
+                       $this->_pageGatewayFactory = new Toolkit_Toolbox_PageGatewayDraftFactory(
+                               $this->registry->dbh
+                       );
+               }
+
+               return $this->_pageGatewayFactory;
+       }
+
+    /**
+     * Description of getParagraphGatewayFactory
+     * 
+     * @return Toolkit_Toolbox_ParagraphGatewayDraftFactory
+     * @access protected 
+     */
+       protected function getParagraphGatewayFactory()
+       {
+               if (!($this->_paragraphGatewayFactory instanceof Toolkit_Toolbox_ParagraphGatewayDraftFactory)) {
+                       $this->_paragraphGatewayFactory = new Toolkit_Toolbox_ParagraphGatewayDraftFactory(
+                               $this->registry->dbh
+                       );
+               }
+
+               return $this->_paragraphGatewayFactory;
+       }
+}
+?>
diff --git a/Toolkit/Template/Exception.php b/Toolkit/Template/Exception.php
new file mode 100644 (file)
index 0000000..d623f22
--- /dev/null
@@ -0,0 +1,31 @@
+<?php
+/**
+ * Exception.php
+ *
+ * PHP version 5
+ *
+ * @category  Toolkit
+ * @package   Template
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2009 Gaslight Media
+ * @license   Gaslight Media
+ * @version   CVS: $Id: ShortURL.php,v 1.5 2010/05/25 14:07:22 jamie Exp $
+ * @link      <>
+ */
+
+
+/**
+ * Toolkit_Template_Exception
+ * 
+ * Description of Toolkit_Template_Exception
+ * 
+ * @category  Toolkit
+ * @package   Template
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @release   Release: $id$
+ * @link      <>
+ */
+class Toolkit_Template_Exception extends Exception {}
+?>
diff --git a/Toolkit/Template/Image/Factory.php b/Toolkit/Template/Image/Factory.php
new file mode 100644 (file)
index 0000000..13614db
--- /dev/null
@@ -0,0 +1,91 @@
+<?php
+/**
+ * Factory.php
+ *
+ * PHP version 5
+ *
+ * @category  Toolkit
+ * @package   Template/Image
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2009 Gaslight Media
+ * @license   Gaslight Media
+ * @version   CVS: $Id: ShortURL.php,v 1.5 2010/05/25 14:07:22 jamie Exp $
+ * @link      <>
+ */
+
+/**
+ * Toolkit_Template_Image_Factory
+ * 
+ * Description of Toolkit_Template_Image_Factory
+ * 
+ * @category  Toolkit
+ * @package   Template/Image
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @release   Release: $id$
+ * @link      <>
+ */
+class Toolkit_Template_Image_Factory
+{
+       //      {{{     properties
+
+    /**
+     * Description of $_imageServerAdapter
+     * 
+     * @var string 
+     * @access private
+     */
+       private $_imageServerAdapter;
+
+       //      }}}
+       //      {{{     __construct()
+
+    /**
+     * Class constructor
+     *  
+     * @param Toolkit_FileServer_ImageAdapter $adapter Adapter
+     * 
+     * @access public
+     */
+       public function __construct(Toolkit_FileServer_ImageAdapter $adapter)
+       {
+               $this->_imageServerAdapter = $adapter;
+       }
+
+       //      }}}
+       //      {{{     getImageBuilder()
+
+    /**
+     * Get image builder
+     * 
+     * @param array  $data data
+     * @param string $side Side image is on
+     * 
+     * @access public
+     * @return \Toolkit_Template_Image_Left|\Toolkit_Template_Image_Right|\Toolkit_Template_Image_Null 
+     */
+       public function getImageBuilder(array $data, $side)
+       {
+               if (isset($data['image']) && !empty($data['image'])) {
+                       if ($side == 'left') {
+                               return new Toolkit_Template_Image_Left(
+                                       $this->_imageServerAdapter,
+                                       $data['image'],
+                                       $data['caption']
+                               );
+                       } else {
+                               return new Toolkit_Template_Image_Right(
+                                       $this->_imageServerAdapter,
+                                       $data['image'],
+                                       $data['caption']
+                               );
+                       }
+               } else {
+                       return new Toolkit_Template_Image_Null();
+               }
+       }
+
+       //      }}}
+}
+?>
diff --git a/Toolkit/Template/Image/ImageAbstract.php b/Toolkit/Template/Image/ImageAbstract.php
new file mode 100644 (file)
index 0000000..bb212ca
--- /dev/null
@@ -0,0 +1,114 @@
+<?php
+
+/**
+ * Page Image Element
+ *
+ * PHP version 5
+ *
+ * @category  Toolkit_Template
+ * @package   Template
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2010 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com/ Gaslightmedia
+ * @version   CVS: $Id: ImageAbstract.php,v 1.1 2010/07/22 19:59:01 jamie Exp $
+ * @link      <>
+ * @see       References to other sections (if any)...
+ */
+
+/**
+ * Page Image Element
+ *
+ * @category  Toolkit_Template
+ * @package   Template
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2010 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com/ Gaslightmedia
+ * @version   Release: @package_version@
+ * @link      <>
+ * @see       References to other sections (if any)...
+ */
+abstract class Toolkit_Template_Image_ImageAbstract
+{
+       //      {{{     properties
+
+    /**
+     * Description for $_adapter
+     * 
+     * @var string 
+     * @access protected
+     */
+       protected $_adapter;
+
+    /**
+     * Description for $_caption
+     * 
+     * @var string 
+     * @access protected
+     */
+       protected $_caption;
+
+    /**
+     * Description for $_src
+     * 
+     * @var string 
+     * @access protected
+     */
+       protected $_src;
+
+       //      }}}
+       //  {{{ __construct()
+
+    /**
+     * Constructor
+     *
+     * Sets elements attributes
+     *
+        * @param Toolkit_FileServer_ImageAdapter $adapter File server adapter
+     * @param string                          $src     src attribute value
+     * @param string                          $caption caption
+        *
+     * @return void
+     * @access public
+     */
+       public function __construct(
+               Toolkit_FileServer_ImageAdapter $adapter,
+               $src,
+               $caption
+       ) {
+               if (empty($src)) {
+                       throw new InvalidArgumentException('$src cannot be empty');
+               }
+
+               $this->_adapter = $adapter;
+               $this->_caption = $caption;
+               $this->_src     = $src;
+       }
+
+       //      }}}
+
+       //      {{{     getImage()
+
+    /**
+     * Get image
+     * 
+     * @param int $size Size
+     * 
+     * @return string
+     * @access public 
+     */
+       public function getImage($size)
+       {
+               $format = '<img src="%s" title="%s" alt="%s">';
+               $img = sprintf(
+                       $format,
+                       $size . $this->_src,
+                       $this->_caption,
+                       $this->_src
+               );
+
+               return $img;
+       }
+
+       //      }}}
+}
+?>
diff --git a/Toolkit/Template/Image/Left.php b/Toolkit/Template/Image/Left.php
new file mode 100644 (file)
index 0000000..6fb37f1
--- /dev/null
@@ -0,0 +1,62 @@
+<?php
+
+/**
+ * Page Image
+ *
+ * PHP version 5
+ *
+ * @category  Toolkit_Template
+ * @package   Template
+ * @author    Jamie Kahgee <jamie@gaslightmedia.com>
+ * @copyright 2010 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com/ Gaslightmedia
+ * @version   CVS: $Id: Left.php,v 1.1 2010/07/22 19:59:02 jamie Exp $
+ * @link      <>
+ * @see       References to other sections (if any)...
+ */
+
+/**
+ * Page Image
+ *
+ * Returns a left side image
+ *
+ * @category  Toolkit_Template
+ * @package   Template
+ * @author    Jamie Kahgee <jamie@gaslightmedia.com>
+ * @copyright 2010 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com/ Gaslightmedia
+ * @version   Release: @package_version@
+ * @link      <>
+ * @see       References to other sections (if any)...
+ */
+class Toolkit_Template_Image_Left extends Toolkit_Template_Image_ImageAbstract
+{
+       //  {{{ getImage()
+
+    /**
+     * Gets the image markup
+     *
+        * @param string $size constant path to image size
+     *
+     * @return string Html image element
+     * @access public
+     */
+       public function getImage($size)
+       {
+               $imageData = $this->_adapter->getImageSize($size . $this->_src);
+
+               $html  = '<div class="imageleft" style="width: ' . $imageData[0] . 'px;">';
+               $html .=        parent::getImage($size);
+               if (!empty($this->_caption)) {
+                       $html .=        '<div class="imagecaption">';
+                       $html .=                $this->_caption;
+                       $html .=        '</div>';
+               }
+               $html .= '</div>';
+
+               return $html;
+       }
+
+       //      }}}
+}
+?>
diff --git a/Toolkit/Template/Image/Null.php b/Toolkit/Template/Image/Null.php
new file mode 100644 (file)
index 0000000..fe291cb
--- /dev/null
@@ -0,0 +1,56 @@
+<?php
+/**
+ * Coupon Image Element
+ *
+ * PHP version 5
+ *
+ * @category  Toolkit_Template
+ * @package   Template
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2010 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com/ Gaslightmedia
+ * @version   CVS: $Id: Null.php,v 1.1 2010/07/22 19:59:02 jamie Exp $
+ * @link      <>
+ * @see       References to other sections (if any)...
+ */
+
+/**
+ * Coupon Image Element
+ *
+ * @category  Toolkit_Template
+ * @package   Template
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2010 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com/ Gaslightmedia
+ * @version   Release: @package_version@
+ * @link      <>
+ * @see       References to other sections (if any)...
+ */
+class Toolkit_Template_Image_Null
+{
+    /**
+     * Class construct 
+     */
+       public function __construct() 
+    {
+        
+    }
+
+       //  {{{ getImage()
+
+    /**
+     * Get nothing for the image
+     *
+     * @param int $size Size of image
+     * 
+     * @return string empty html string;
+     * @access public
+     */
+       public function getImage($size)
+       {
+               return;
+       }
+
+       //      }}}
+}
+?>
diff --git a/Toolkit/Template/Image/Right.php b/Toolkit/Template/Image/Right.php
new file mode 100644 (file)
index 0000000..f1ef03b
--- /dev/null
@@ -0,0 +1,62 @@
+<?php
+
+/**
+ * Page Image
+ *
+ * PHP version 5
+ *
+ * @category  Toolkit_Template
+ * @package   Template
+ * @author    Jamie Kahgee <jamie@gaslightmedia.com>
+ * @copyright 2010 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com/ Gaslightmedia
+ * @version   CVS: $Id: Right.php,v 1.1 2010/07/22 19:59:02 jamie Exp $
+ * @link      <>
+ * @see       References to other sections (if any)...
+ */
+
+/**
+ * Page Image
+ *
+ * Returns a right side image
+ *
+ * @category  Toolkit_Template
+ * @package   Template
+ * @author    Jamie Kahgee <jamie@gaslightmedia.com>
+ * @copyright 2010 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com/ Gaslightmedia
+ * @version   Release: @package_version@
+ * @link      <>
+ * @see       References to other sections (if any)...
+ */
+class Toolkit_Template_Image_Right extends Toolkit_Template_Image_ImageAbstract
+{
+       //  {{{ getImage()
+
+    /**
+     * Gets the image markup
+     *
+        * @param string $size constant path to image size
+     *
+     * @return string Html image element
+     * @access public
+     */
+       public function getImage($size)
+       {
+               $imageData = $this->_adapter->getImageSize($size . $this->_src);
+
+               $html  = '<div class="imageright" style="width: ' . $imageData[0] . 'px;">';
+               $html .=        parent::getImage($size);
+               if (!empty($this->_caption)) {
+                       $html .=        '<div class="imagecaption">';
+                       $html .=                $this->_caption;
+                       $html .=        '</div>';
+               }
+               $html .= '</div>';
+
+               return $html;
+       }
+
+       //      }}}
+}
+?>
diff --git a/Toolkit/Template/IndexController.php b/Toolkit/Template/IndexController.php
new file mode 100644 (file)
index 0000000..442ebfb
--- /dev/null
@@ -0,0 +1,82 @@
+<?php
+/**
+ * IndexController.php
+ *
+ * PHP version 5
+ *
+ * @category  Toolkit
+ * @package   Template
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2009 Gaslight Media
+ * @license   Gaslight Media
+ * @version   CVS: $Id: ShortURL.php,v 1.5 2010/05/25 14:07:22 jamie Exp $
+ * @link      <>
+ */
+
+/**
+ * Toolkit_Template_IndexController
+ * 
+ * Description of Toolkit_Template_IndexController
+ * 
+ * @category  Toolkit
+ * @package   Template
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @release   Release: $id$
+ * @link      <>
+ */
+class Toolkit_Template_IndexController
+       extends Toolkit_Template_PageControllerAbstract
+{
+    /**
+     * Description for $_pageGatewayFactory
+     * 
+     * @var Toolkit_Toolbox_PageGatewayPublishFactory 
+     * @access private
+     */
+       private $_pageGatewayFactory;
+    
+    /**
+     * Description for $_paragraphGatewayFactory
+     * 
+     * @var Toolkit_Toolbox_PageGatewayPublishFactory 
+     * @access private
+     */
+       private $_paragraphGatewayFactory;
+
+    /**
+     * description of paragraphGatewayFactory
+     * 
+     * @return Toolkit_Toolbox_ParagraphGatewayPublishFactory
+     * @access protected 
+     */
+       protected function getPageGatewayFactory()
+       {
+               if (!($this->_pageGatewayFactory instanceof Toolkit_Toolbox_PageGatewayPublishFactory)) {
+                       $this->_pageGatewayFactory = new Toolkit_Toolbox_PageGatewayPublishFactory(
+                               $this->registry->dbh
+                       );
+               }
+
+               return $this->_pageGatewayFactory;
+       }
+
+    /**
+     * description of getParagraphGatewayFactory
+     * 
+     * @return Toolkit_Toolbox_ParagraphGatewayPublishFactory 
+     * @access protected
+     */
+       protected function getParagraphGatewayFactory()
+       {
+               if (!($this->_paragraphGatewayFactory instanceof Toolkit_Toolbox_ParagraphGatewayPublishFactory)) {
+                       $this->_paragraphGatewayFactory = new Toolkit_Toolbox_ParagraphGatewayPublishFactory(
+                               $this->registry->dbh
+                       );
+               }
+
+               return $this->_paragraphGatewayFactory;
+       }
+}
+?>
diff --git a/Toolkit/Template/KeywordReplacement.php b/Toolkit/Template/KeywordReplacement.php
new file mode 100644 (file)
index 0000000..55fa8c8
--- /dev/null
@@ -0,0 +1,90 @@
+<?php
+/**
+ * KeywordReplacement.php
+ *
+ * PHP version 5
+ *
+ * @category  Toolkit
+ * @package   Template
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2009 Gaslight Media
+ * @license   Gaslight Media
+ * @version   CVS: $Id: ShortURL.php,v 1.5 2010/05/25 14:07:22 jamie Exp $
+ * @link      <>
+ */
+
+/**
+ * Toolkit_Template_KeywordReplacement
+ *
+ * Description of Toolkit_Template_KeywordReplacement
+ *
+ * @category  Toolkit
+ * @package   Template
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @release   Release: $id$
+ * @link      <>
+ */
+class Toolkit_Template_KeywordReplacement
+{
+    /**
+     * Description for $_pageGateway
+     * @var Toolkit_Toolbox_GatewayAbstract
+     * @access private
+     */
+       private $_pageGateway;
+
+    /**
+     * Class constructor
+     *
+     * @param Toolkit_Toolbox_GatewayAbstract $gateway Gateway
+     *
+     * @access public
+     */
+       public function __construct(Toolkit_Toolbox_GatewayAbstract $gateway)
+       {
+               $this->_pageGateway = $gateway;
+       }
+
+    /**
+     * Description for findAndReplace()
+     *
+     * @param string $haystack Haystack
+     *
+     * @return string
+     * @access public
+     */
+       public function findAndReplace($haystack)
+       {
+               if (strstr($haystack, '{') !== false) {
+                       $pattern = '/\{([A-Z0-9\&\-\,\'\" ]*)\}/i';
+                       if (preg_match_all($pattern, $haystack, $needle) != 0) {
+                $total = count($needle[0]);
+                for ($iter = 0; $iter < $total; $iter++) {
+                    $page = $this->_pageGateway->findByKeyword($needle[1][$iter]);
+                    if ($page == false) {
+                        //return $haystack;
+                    } else {
+                        $seoUrl = Toolkit_Template_Page::getSeoUrl(
+                            $this->_pageGateway,
+                            $page['id']
+                        );
+                        $anchor = '<a href="'.$seoUrl.'">'.$page['navigation_name'].'</a>';
+                        $haystack = str_replace($needle[0][$iter], $anchor, $haystack);
+                    }
+                }
+                return $haystack;
+                       } else {
+                               return $haystack;
+                       }
+
+                       if (strstr($haystack, '{') !== false) {
+                               return $this->findAndReplace($haystack);
+                       }
+               }
+
+               return $haystack;
+       }
+}
+?>
diff --git a/Toolkit/Template/Navigation/AllInOneSideNav.php b/Toolkit/Template/Navigation/AllInOneSideNav.php
new file mode 100755 (executable)
index 0000000..2f2df53
--- /dev/null
@@ -0,0 +1,128 @@
+<?php
+
+/**
+ * AllInOneSideNav.php
+ *
+ * PHP version 5.3
+ *
+ * @category  Toolkit
+ * @package   Package
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2013 Gaslight Media
+ * @license   Gaslight Media
+ * @version   SVN: (0.1)
+ * @link      <>
+ */
+
+/**
+ * Toolkit_Package_AllInOneSideNav
+ *
+ * Description of AllInOneSideNav
+ *
+ * @category  Toolkit
+ * @package   Package
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2013 Gaslight Media
+ * @license   Gaslight Media
+ * @release   Release: (0.1)
+ * @link      <>
+ */
+class Toolkit_Template_Navigation_AllInOneSideNav
+{
+    private $_parents = array();
+    private $_navArray = array();
+    private $_gateway;
+    private $_pageId;
+
+    public function __construct(
+        Toolkit_Toolbox_GatewayAbstract $gateway
+    ) {
+        $this->_gateway = $gateway;
+        $id = filter_var($_REQUEST['catid'], FILTER_VALIDATE_INT);
+        $this->_pageId = $id;
+        if (!$id) {
+            $id = HOME_ID;
+        }
+        $this->_parents = $this->_getParentIds($id);
+    }
+
+    public function getNavigation()
+    {
+        return $this->setNavArray(0);
+    }
+
+    public function setNavArray($id)
+    {
+        try {
+            $navArray = array();
+            $dbh = Toolkit_Database::getInstance();
+            $pages = (defined('ALL_IN_ONE_INCLUDED')
+                && ALL_IN_ONE_INCLUDED)
+                ? unserialize(ALL_IN_ONE_INCLUDED)
+                : null;
+            $includedPages = (!empty($pages))
+                ? "AND (parent != 0 OR id IN (".implode(',', $pages)."))"
+                : '';
+            $memberPages = (defined('MEMBERS_DB') && MEMBERS_DB)
+                ? "AND id NOT IN (".MEMBERS_CATEGORY.")"
+                : '';
+            $sql = "
+            SELECT id,parent,navigation_name
+              FROM pages
+             WHERE parent = :id
+               AND active = true
+               $includedPages
+               $memberPages
+            ORDER BY parent,pos"; // AND id != ".MEMBERS_CATEGORY."
+            $stmt = $dbh->prepare($sql);
+            $stmt->bindParam(':id', $id, PDO::PARAM_INT);
+            $stmt->execute();
+            while($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                if ($row['id'] == $this->_pageId) {
+                    $class = 'current';
+                } else if (in_array($row['id'], $this->_parents)) {
+                    $class = 'parent';
+                } else {
+                    $class = '';
+                }
+                $navArray[] = array(
+                    'id'     => $row['id'],
+                    'parent' => $row['parent'],
+                    'label'  => $row['navigation_name'],
+                    'uri'    => Toolkit_Template_Page::getSeoUrl(
+                        $this->_gateway, $row['id']
+                    ),
+                    'class'  => $class,
+                    'pages'  => $this->setNavArray($row['id'])
+                );
+            }
+        } catch (PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+        return $navArray;
+    }
+
+    private function _getParentIds($id)
+    {
+        try {
+            $dbh = Toolkit_Database::getInstance();
+            $sql = "
+            SELECT parent
+              FROM pages
+             WHERE id = :id";
+            $stmt = $dbh->prepare($sql);
+            $stmt->bindParam(':id', $id, PDO::PARAM_INT);
+            $stmt->execute();
+            $parent = $stmt->fetchColumn();
+            $this->_parents[] = $parent;
+            if ($parent == 0) {
+                return $this->_parents;
+            } else {
+                return $this->_getParentIds($parent);
+            }
+        } catch (PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+    }
+}
+
diff --git a/Toolkit/Template/Navigation/Factory.php b/Toolkit/Template/Navigation/Factory.php
new file mode 100644 (file)
index 0000000..52c76e5
--- /dev/null
@@ -0,0 +1,116 @@
+<?php
+/**
+ * Factory.php
+ *
+ * PHP version 5
+ *
+ * @category  Toolkit
+ * @package   Template/Navigation
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2009 Gaslight Media
+ * @license   Gaslight Media
+ * @version   CVS: $Id: ShortURL.php,v 1.5 2010/05/25 14:07:22 jamie Exp $
+ * @link      <>
+ */
+
+/**
+ * Toolkit_Template_Navigation_Factory
+ *
+ * Description of Toolkit_Template_Navigation_Factory
+ *
+ * @category  Toolkit
+ * @package   Template/Navigation
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @release   Release: $id$
+ * @link      <>
+ */
+class Toolkit_Template_Navigation_Factory
+       extends Toolkit_NavigationFactoryAbstract
+{
+    /**
+     * Description for $_gateway
+     *
+     * @var Toolkit_Toolbox_PageGatewayAbstract
+     * @access private
+     */
+       private $_gateway;
+
+    /**
+     * Sets gateway
+     *
+     * @param Toolkit_Toolbox_PageGatewayAbstract $gateway Gateway
+     *
+     * @access public
+     * @return void
+     */
+       public function setGateway(Toolkit_Toolbox_PageGatewayAbstract $gateway)
+       {
+               $this->_gateway = $gateway;
+       }
+
+    /**
+     * NOTE: You have different renderers that you can choose for the main
+     * navigation.
+     * Toolkit/Template/Navigation/SideNavigation.php
+     *
+     * Renderers: all renderers use HTML_Menu which renders ul li
+     * DirectTreeLastLi       = with id in the last li element
+     * DirectTreeUlId         = with id in the main ul element
+     * DirectTreeLiId         = with id on the current li element
+     * DirectTreeSideNavTitle = adds the page title above sidenav
+     *
+     * @param Toolkit_Toolbox_PageGatewayAbstract $gateway Gateway
+     *
+     * @access public
+     * @return Toolkit_Template_Navigation_SideNavigation
+     */
+       public function createSideNav(
+               Toolkit_Toolbox_PageGatewayAbstract $gateway = null
+       ) {
+               return new Toolkit_Template_Navigation_SideNavigation(
+                       new HTML_Menu(),
+                       new Toolkit_Template_Navigation_Renderer_DirectTreeSideNavTitle($this->_gateway)
+               );
+       }
+
+    /**
+     * New main nav added can use
+     * Toolkit_Template_Navigation_MainNavigationAllInOne
+     * Which will create a side nav that includes all main level pages
+     *
+     * NOTE: You have different renderers that you can choose for the main
+     * navigation. And you can choose from a static or dynamic setup.
+     * 1) for Dynamic setup use
+     * Toolkit_Template_Navigation_MainNavigationDynamic
+     * 2) to edit the static one edit the file
+     * Toolkit/Template/Navigation/MainNavigationStatic.php
+     * 3) with either of these navigation objects you can use a different
+     * renderer.
+     *
+     * Renderers: all renderers use HTML_Menu which renders ul li
+     * DirectTreeLastLi = with id in the last li element
+     * DirectTreeUlId   = with id in the main ul element
+     * DirectTreeLiId   = with id on the current li element
+     *
+     * @access public
+     * @return Toolkit_Template_Navigation_MainNavigationDynamic
+     */
+       public function createMainNav()
+       {
+               return new Toolkit_Template_Navigation_MainNavigationDynamic(
+                       new HTML_Menu(),
+            /**
+             * new Toolkit_Template_Navigation_Renderer_DirectTreeLiId(
+             * $this->_gateway,
+             * 'current'
+             * )
+             */
+                       new Toolkit_Template_Navigation_Renderer_DirectTreeLastLi(
+                               'lastli'
+                       )
+               );
+       }
+}
+?>
diff --git a/Toolkit/Template/Navigation/MainNavigationAbstract.php b/Toolkit/Template/Navigation/MainNavigationAbstract.php
new file mode 100644 (file)
index 0000000..69accb3
--- /dev/null
@@ -0,0 +1,103 @@
+<?php
+/**
+ * MainNavigationAbstract.php
+ *
+ * PHP version 5
+ *
+ * @category  Toolkit
+ * @package   Template/Navigation
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2009 Gaslight Media
+ * @license   Gaslight Media
+ * @version   CVS: $Id: ShortURL.php,v 1.5 2010/05/25 14:07:22 jamie Exp $
+ * @link      <>
+ */
+
+/**
+ * Toolkit_Template_Navigation_MainNavigationAbstract
+ * 
+ * Description of Toolkit_Template_Navigation_MainNavigationAbstract
+ * 
+ * @category  Toolkit
+ * @package   Template/Navigation
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @release   Release: $id$
+ * @link      <>
+ */
+abstract class Toolkit_Template_Navigation_MainNavigationAbstract
+       extends Toolkit_NavigationAbstract implements Toolkit_INavigation
+{
+       //      {{{     __construct()
+
+    /**
+     * Class constructor
+     * 
+     * @param HTML_Menu $menu
+     * @param HTML_Menu_Renderer $rEngine 
+     * 
+     * @access public
+     */
+       public function __construct(
+               HTML_Menu $menu,
+               HTML_Menu_Renderer $rEngine
+       ) {
+               $this->menu      = $menu;
+               $this->rEngine   = $rEngine;
+       }
+
+       //      }}}
+       //      {{{     setNavTemplates()
+
+    /**
+     * Set nav templates
+     * 
+     * @return void
+     * @access protected 
+     */
+       protected function setNavTemplates()
+       {
+               $tpl = '<a href="%s" %s>{Title}</a>';
+               $this->rEngine->setEntryTemplate(
+                       HTML_MENU_ENTRY_INACTIVE,
+                       sprintf($tpl, '{url}', '', '{Title}')
+               );
+               $this->rEngine->setEntryTemplate(
+                       HTML_MENU_ENTRY_ACTIVE,
+                       sprintf($tpl, '{url}', 'id="current"', '{Title}')
+               );
+               $this->rEngine->setEntryTemplate(
+                       HTML_MENU_ENTRY_ACTIVEPATH,
+                       sprintf($tpl, '{url}', '', '{Title}')
+               );
+       }
+
+       //      }}}
+       //      {{{     setCurrentIndex()
+
+    /**
+     * Set current index
+     * 
+     * @access protected
+     * @return void 
+     */
+       protected function setCurrentIndex()
+       {
+               $this->menu->forceCurrentIndex($_GET['catid']);
+       }
+
+       //      }}}
+       //      {{{     getNavSructure()
+
+    /**
+     * Create a static array of nav items to use
+        *
+     * @return array navigational array hash
+     * @access public
+     */
+       public function getNavStructure() {}
+
+       //      }}}
+}
+?>
diff --git a/Toolkit/Template/Navigation/MainNavigationAllInOne.php b/Toolkit/Template/Navigation/MainNavigationAllInOne.php
new file mode 100644 (file)
index 0000000..36e45d9
--- /dev/null
@@ -0,0 +1,123 @@
+<?php
+/**
+ * MainNavigationAllInOne.php
+ *
+ * PHP version 5
+ *
+ * @category  Toolkit
+ * @package   Template/Navigation
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2009 Gaslight Media
+ * @license   Gaslight Media
+ * @version   CVS: $Id: ShortURL.php,v 1.5 2010/05/25 14:07:22 jamie Exp $
+ * @link      <>
+ */
+
+/**
+ * Toolkit_Template_Navigation_MainNavigationAllInOne
+ *
+ * Description of Toolkit_Template_Navigation_MainNavigationDynamic
+ *
+ * @category  Toolkit
+ * @package   Template/Navigation
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @release   Release: $id$
+ * @link      <>
+ */
+class Toolkit_Template_Navigation_MainNavigationAllInOne
+       extends Toolkit_Template_Navigation_MainNavigationAbstract
+{
+
+    /**
+     * Recursive function to create a multi dimensional array for sub nav
+        *
+        * create tree (A) at the starting page level, pass the tree up to
+        * parent page, where tree (B) is created for that level. append
+        * tree A to tree B as a sub under the correct parent
+        *
+        * @param Toolkit_Toolbox_GatewayAbstract $gateway  Toolbox gateway
+        * @param integer                         $id       id to get subtree for
+        * @param array                           $tree     subtree created to be
+        *                                                  passed up to the parent
+        *                                                  level
+        * @param integer                         $appendTo pageId to append the
+        *                                                  passed up subtree to
+        *
+     * @return array navigational array hash
+     * @access public
+     */
+       public function getNavStructure(
+               Toolkit_Toolbox_GatewayAbstract $gateway,
+               $id
+       ) {
+        static $currentPage;
+        if (!$currentPage) {
+               $currentPage = $gateway->findNavItem($id);
+            $this->_currentPage = $currentPage['navigation_name'];
+            $subPages = $gateway->findAllByParent(0);
+        } else {
+            $subPages = $gateway->findAllByParent($id);
+        }
+               $nav = array();
+               foreach ($subPages as $subPage) {
+                       // no home page in sub nav
+                       if (defined('MEMBERS_CATEGORY') && MEMBERS_CATEGORY) {
+                               if (  ($subPage['parent'] != MEMBERS_CATEGORY)
+                                       && $subPage['active']
+                               ) {
+                                       $nav[$subPage['id']] = array(
+                                               'Title' => htmlspecialchars($subPage['navigation_name']),
+                                               'url' => Toolkit_Template_Page::getSeoUrl($gateway, $subPage['id'])
+                                       );
+                               }
+                       } else {
+                               if ($subPage['active']) {
+                                       $nav[$subPage['id']] = array(
+                                               'Title' => htmlspecialchars($subPage['navigation_name']),
+                                               'url' => Toolkit_Template_Page::getSeoUrl($gateway, $subPage['id'])
+                                       );
+                               }
+                       }
+            $isSubPage = $this->isSubOfPage($currentPage['id'], $subPage['id']);
+            if ($isSubPage) {
+                $nav[$subPage['id']]['sub']
+                    = $this->getNavStructure($gateway, $subPage['id']);
+            }
+               }
+        return $nav;
+       }
+
+    /**
+     * Checks to see if the given $id is a sub page of the $parent
+     *
+     * @param type $id     id of page to check
+     * @param type $parent Parent id to check
+     *
+     * @return boolean
+     */
+    private function isSubOfPage($id, $parent)
+    {
+        if ($id == $parent) {
+            return true;
+        }
+        if ($id == 0 || $parent == 0) {
+            return false;
+        }
+        try {
+            $dbh = Toolkit_Database::getInstance();
+            $sql = "
+            SELECT parent
+              FROM pages
+             WHERE id = :id";
+            $stmt = $dbh->prepare($sql);
+            $stmt->bindParam(':id', $id, PDO::PARAM_INT);
+            $stmt->execute();
+            $id = $stmt->fetchColumn();
+            return $this->isSubOfPage($id, $parent);
+        } catch(PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+    }
+}
diff --git a/Toolkit/Template/Navigation/MainNavigationDynamic.php b/Toolkit/Template/Navigation/MainNavigationDynamic.php
new file mode 100644 (file)
index 0000000..8e3c079
--- /dev/null
@@ -0,0 +1,104 @@
+<?php
+/**
+ * MainNavigationDynamic.php
+ *
+ * PHP version 5
+ *
+ * @category  Toolkit
+ * @package   Template/Navigation
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2009 Gaslight Media
+ * @license   Gaslight Media
+ * @version   CVS: $Id: ShortURL.php,v 1.5 2010/05/25 14:07:22 jamie Exp $
+ * @link      <>
+ */
+
+/**
+ * Toolkit_Template_Navigation_MainNavigationDynamic
+ * 
+ * Description of Toolkit_Template_Navigation_MainNavigationDynamic
+ * 
+ * @category  Toolkit
+ * @package   Template/Navigation
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @release   Release: $id$
+ * @link      <>
+ */
+class Toolkit_Template_Navigation_MainNavigationDynamic
+       extends Toolkit_Template_Navigation_MainNavigationAbstract
+{
+       //      {{{     getNavSructure()
+
+    /**
+     * Recursive function to create a multi dimensional array for sub nav
+        *
+        * create tree (A) at the starting page level, pass the tree up to
+        * parent page, where tree (B) is created for that level. append
+        * tree A to tree B as a sub under the correct parent
+        *
+        * @param Toolkit_Toolbox_GatewayAbstract $gateway  Toolbox gateway
+        * @param integer                         $id       id to get subtree for
+        * @param array                           $tree     subtree created to be
+        *                                                  passed up to the parent
+        *                                                  level
+        * @param integer                         $appendTo pageId to append the
+        *                                                  passed up subtree to
+        *
+     * @return array navigational array hash
+     * @access public
+     */
+       public function getNavStructure(
+               Toolkit_Toolbox_GatewayAbstract $gateway,
+               $id,
+               array $tree = null,
+               $appendTo = null
+       ) {
+        $id                 = 0;
+               $currentPage        = $gateway->findNavItem($id);
+               $this->_currentPage = $currentPage['navigation_name'];
+
+               $subPages = $gateway->findAllByParent($id);
+
+               $nav = array();
+               foreach ($subPages as $subPage) {
+                       // no home page in sub nav
+                       if (defined('MEMBERS_CATEGORY') && MEMBERS_CATEGORY) {
+                               if (  ($subPage['parent'] != MEMBERS_CATEGORY)
+                                       && $subPage['active']
+                               ) {
+                                       $nav[$subPage['id']] = array(
+                                               'Title' => htmlspecialchars($subPage['navigation_name']),
+                                               'url' => Toolkit_Template_Page::getSeoUrl($gateway, $subPage['id'])
+                                       );
+                               }
+                       } else {
+                               if ($subPage['active']) {
+                                       $nav[$subPage['id']] = array(
+                                               'Title' => htmlspecialchars($subPage['navigation_name']),
+                                               'url' => Toolkit_Template_Page::getSeoUrl($gateway, $subPage['id'])
+                                       );
+                               }
+                       }
+               }
+
+               if (is_array($tree) && array_key_exists($appendTo, $nav)) {
+                       $nav[$appendTo]['sub'] = $tree;
+               }
+
+               if ($currentPage['parent'] != 0) {
+                       return $this->getNavStructure(
+                               $gateway,
+                               $currentPage['parent'],
+                               $nav,
+                               $id
+                       );
+               } else {
+                       return $nav;
+               }
+       }
+
+       //      }}}
+}
+?>
diff --git a/Toolkit/Template/Navigation/MainNavigationStatic.php b/Toolkit/Template/Navigation/MainNavigationStatic.php
new file mode 100644 (file)
index 0000000..9efce29
--- /dev/null
@@ -0,0 +1,71 @@
+<?php
+/**
+ * MainNavigationStatic.php
+ *
+ * PHP version 5
+ *
+ * @category  Toolkit
+ * @package   Template/Navigation
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2009 Gaslight Media
+ * @license   Gaslight Media
+ * @version   CVS: $Id: ShortURL.php,v 1.5 2010/05/25 14:07:22 jamie Exp $
+ * @link      <>
+ */
+
+
+/**
+ * Toolkit_Template_Navigation_MainNavigationStatic
+ * 
+ * Description of Toolkit_Template_Navigation_MainNavigationStatic
+ * 
+ * @category  Toolkit
+ * @package   Template/Navigation
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @release   Release: $id$
+ * @link      <>
+ */
+class Toolkit_Template_Navigation_MainNavigationStatic
+       extends Toolkit_Template_Navigation_MainNavigationAbstract
+{
+       //      {{{     getNavSructure()
+
+    /**
+     * Create a static array of nav items to use
+        *
+        * @param Toolkit_Toolbox_GatewayAbstract $gateway Toolbox gateway
+        * @param integer                         $id      Not used for static nav
+        *
+     * @return array navigational array hash
+     * @access public
+     */
+       public function getNavStructure(
+               Toolkit_Toolbox_GatewayAbstract $gateway,
+               $id = null
+       ) {
+               $staticPages = array(
+                       HOME_ID, // Home Page
+                       2,               // The GLM Associate
+                       10,      // Contact Gaslight Media
+                       81,      // Services Offered
+                       86,      // About Gaslight Media
+               );
+
+               $nav = array();
+
+               foreach ($staticPages as $pageId) {
+                       //      Home Page
+                       $page = $gateway->findNavItem($pageId);
+                       $nav[$pageId] = array(
+                               'Title' => htmlspecialchars($page['navigation_name']),
+                               'url' => Toolkit_Template_Page::getSeoUrl($gateway, $pageId)
+                       );
+               }
+
+               return $nav;
+       }
+
+       //      }}}
+}
diff --git a/Toolkit/Template/Navigation/Renderer/DirectTreeLastLi.php b/Toolkit/Template/Navigation/Renderer/DirectTreeLastLi.php
new file mode 100644 (file)
index 0000000..28f43db
--- /dev/null
@@ -0,0 +1,97 @@
+<?php
+
+/**
+ * Renderer to create a LI list that has an id on the last element
+ *
+ * PHP version 5
+ *
+ * The license text...
+ *
+ * @category  Toolkit_Template
+ * @package   Template
+ * @author    Jamie Kahgee <jamie@gaslightmedia.com>
+ * @copyright 2010 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com/ Gaslightmedia
+ * @version   CVS: $Id: DirectTreeRendererLastLi.php,v 1.2 2010/07/27 19:14:02 jamie Exp $
+ * @link      <>
+ * @see       References to other sections (if any)...
+ */
+
+/**
+ * Renderer to create a LI list that has an id on the last element
+ *
+ * @category  Toolkit_Template
+ * @package   Template
+ * @author    Jamie Kahgee <jamie@gaslightmedia.com>
+ * @copyright 2010 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com/ Gaslightmedia
+ * @version   Release: @package_version@
+ * @link      <>
+ * @see       References to other sections (if any)...
+ */
+class Toolkit_Template_Navigation_Renderer_DirectTreeLastLi
+       extends HTML_Menu_DirectTreeRenderer
+{
+       //      {{{     properties
+
+    /**
+     * Id to be assigned to last LI
+     * @var    string
+     * @access private
+     */
+       private $_id = null;
+
+       //      }}}
+       //      {{{     __construct()
+
+    /**
+     * constructor
+     *
+     * @param string $id id to be assigned to last li
+        *
+     * @return void
+     * @access public
+     */
+       public function __construct($id = null)
+       {
+               if (!is_null($id)) {
+                       $this->_id = $id;
+               }
+       }
+
+       //      }}}
+       //      {{{     finishMenu()
+
+    /**
+     * Rendering before finishing
+     *
+        * if an id must be assigned to the last li element, then extract that
+        * element from the string and adjust the opening template
+     *
+     * @param integer $level Parameter description (if any) ...
+        *
+     * @return void
+     * @access public
+     */
+    function finishMenu($level)
+    {
+               parent::finishMenu($level);
+               if (!is_null($this->_id)) {
+                       $dom = new DOMDocument();
+                       $htmlUTF8 = mb_convert_encoding($this->_html, 'HTML-ENTITIES', 'UTF-8');
+                       @$dom->loadHTML($htmlUTF8);
+
+                       $uls = $dom->getElementsByTagName('ul');
+                       $ul = $uls->item(0);
+                       $ul->lastChild->setAttribute('id', $this->_id);
+
+                       //      Prevent auto tags and doctype from being added to the HTML
+                       $this->_html = substr(
+                               $dom->saveXML($dom->getElementsByTagName('ul')->item(0)),
+                               0
+                       );
+               }
+    }
+
+       //      }}}
+}
diff --git a/Toolkit/Template/Navigation/Renderer/DirectTreeLiId.php b/Toolkit/Template/Navigation/Renderer/DirectTreeLiId.php
new file mode 100644 (file)
index 0000000..1b81bc8
--- /dev/null
@@ -0,0 +1,121 @@
+<?php
+
+/**
+ * Renderer to create a LI list that has an id on the last element
+ *
+ * PHP version 5
+ *
+ * The license text...
+ *
+ * @category  Toolkit_Template
+ * @package   Template
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2010 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com/ Gaslightmedia
+ * @version   CVS: $Id: DirectTreeRendererLastLi.php,v 1.2 2010/07/27 19:14:02 jamie Exp $
+ * @link      <>
+ * @see       References to other sections (if any)...
+ */
+
+/**
+ * Renderer to create a LI list that has an id on the last element
+ *
+ * @category  Toolkit_Template
+ * @package   Template
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2010 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com/ Gaslightmedia
+ * @version   Release: @package_version@
+ * @link      <>
+ * @see       References to other sections (if any)...
+ */
+class Toolkit_Template_Navigation_Renderer_DirectTreeLiId
+       extends HTML_Menu_DirectTreeRenderer
+{
+       //      {{{     properties
+
+    /**
+     * Id to be assigned to last LI
+     * @var    string
+     * @access private
+     */
+       private $_id = null;
+
+    /**
+        * Database gateway for toolbox
+     * @var    string
+     * @access private
+     */
+       private $_gateway;
+
+       //      }}}
+       //      {{{     __construct()
+
+    /**
+     * constructor
+     *
+     * @param Toolkit_Toolbox_PageGatewayAbstract $gateway Gateway
+     * @param string                              $id      id to be assigned to last li
+        *
+     * @return void
+     * @access public
+     */
+       public function __construct(
+               Toolkit_Toolbox_PageGatewayAbstract $gateway,
+               $id = null
+       ) {
+               $this->_gateway = $gateway;
+
+               if (!is_null($id)) {
+                       $this->_id = $id;
+               }
+       }
+
+       //      }}}
+       //      {{{     finishMenu()
+
+    /**
+     * Rendering before finishing
+     *
+        * if an id must be assigned to the last li element, then extract that
+        * element from the string and adjust the opening template
+     *
+     * @param integer $level Parameter description (if any) ...
+        *
+     * @return void
+     * @access public
+     */
+    function finishMenu($level)
+    {
+               parent::finishMenu($level);
+               if (!is_null($this->_id)) {
+                       $dom = new DOMDocument();
+                       $htmlUTF8 = mb_convert_encoding($this->_html, 'HTML-ENTITIES', 'UTF-8');
+                       @$dom->loadHTML($htmlUTF8);
+
+                       $xpath = new DOMXPath($dom);
+                       $element = $xpath->query("//*[@id='{$this->_id}']")->item(0);
+
+                       if ($pageId = filter_input(INPUT_GET, 'catid', FILTER_VALIDATE_INT)) {
+                               $topParent = $this->_gateway->findTopParent($pageId);
+                               $page = $this->_gateway->find($topParent);
+                               $nodes = $dom->getElementsByTagName('a');
+                               foreach ($nodes as $node) {
+                                       if ($node->nodeValue == $page['navigation_name']) {
+                                               //      uncomment to put id on parent node
+                                               // $node = $node->parentNode;
+                                               $node->setAttribute('id', $this->_id);
+                                       }
+                               }
+                       }
+
+                       //      Prevent auto tags and doctype from being added to the HTML
+                       $this->_html = substr(
+                               $dom->saveXML($dom->getElementsByTagName('ul')->item(0)),
+                               0
+                       );
+               }
+    }
+
+       //      }}}
+}
diff --git a/Toolkit/Template/Navigation/Renderer/DirectTreeSideNavTitle.php b/Toolkit/Template/Navigation/Renderer/DirectTreeSideNavTitle.php
new file mode 100644 (file)
index 0000000..87e0da6
--- /dev/null
@@ -0,0 +1,97 @@
+<?php
+
+/**
+ * Render the side nav w/ the top parent as a header above the nav
+ *
+ * PHP version 5
+ *
+ * The license text...
+ *
+ * @category  Toolkit
+ * @package   Toolkit_Template
+ * @author    Jamie Kahgee <jamie@gaslightmedia.com>
+ * @copyright 2010 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com/ Gaslightmedia
+ * @version   CVS: $Id: DirectTreeRendererSideNavTitle.php,v 1.1 2010/07/22 19:59:02 jamie Exp $
+ * @link      <>
+ * @see       References to other sections (if any)...
+ */
+
+/**
+ * Render the side nav w/ the top parent as a header above the nav
+ *
+ * @category  Toolkit
+ * @package   Toolkit_Template
+ * @author    Jamie Kahgee <jamie@gaslightmedia.com>
+ * @copyright 2010 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com/ Gaslightmedia
+ * @version   Release: @package_version@
+ * @link      <>
+ * @see       References to other sections (if any)...
+ */
+class Toolkit_Template_Navigation_Renderer_DirectTreeSideNavTitle
+       extends HTML_Menu_DirectTreeRenderer
+{
+       //      {{{     properties
+
+       /**
+        * Page gateway to use
+        * @var Toolkit_Toolbox_PageGatewayAbstract
+        * @access private
+        */
+       private $_gateway;
+    private $_useTitleAsLink = false;
+
+       //      }}}
+       //      {{{     __construct()
+
+       /**
+        * Constructor
+        *
+        * @param Toolkit_Toolbox_PageGatewayAbstract $gateway Page gateway to use
+        *
+        * @return void
+        * @access public
+        */
+       public function __construct(
+        Toolkit_Toolbox_PageGatewayAbstract $gateway,
+        $useTitleAsLink = false
+    ) {
+               $this->_gateway = $gateway;
+        $this->_useTitleAsLink = $useTitleAsLink;
+       }
+
+       //      }}}
+
+       //      {{{     finishMenu()
+
+    /**
+     * Render nav with header before it
+     *
+     * Render the entire nav, then prepend the top level parent pages
+        * navigation name as a head above the nav
+     *
+     * @param integer $level Parameter description (if any) ...
+        *
+     * @return void
+     * @access public
+     */
+       public function finishMenu($level)
+       {
+               parent::finishMenu($level);
+               $topParentId = $this->_gateway->findTopParent($_GET['catid']);
+               $page = $this->_gateway->find($topParentId);
+        if ($this->_useTitleAsLink) {
+            $link = Toolkit_Template_Page::getSeoUrl(
+                $this->_gateway, $topParentId
+            );
+        }
+        $title
+            = ($this->_useTitleAsLink)
+            ? "<a href=\"{$link}\">{$page['navigation_name']}</a>"
+            : "{$page['navigation_name']}";
+               $this->_html = "<h2>{$title}</h2>" . $this->_html;
+       }
+
+       //      }}}
+}
diff --git a/Toolkit/Template/Navigation/Renderer/DirectTreeUlId.php b/Toolkit/Template/Navigation/Renderer/DirectTreeUlId.php
new file mode 100644 (file)
index 0000000..0e6a9aa
--- /dev/null
@@ -0,0 +1,125 @@
+<?php
+
+/**
+ * Renderer to create a LI list that has an id on the last element
+ *
+ * PHP version 5
+ *
+ * The license text...
+ *
+ * @category  Toolkit_Template
+ * @package   Template
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2010 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com/ Gaslightmedia
+ * @version   CVS: $Id: DirectTreeRendererLastLi.php,v 1.2 2010/07/27 19:14:02 jamie Exp $
+ * @link      <>
+ * @see       References to other sections (if any)...
+ */
+
+/**
+ * Renderer to create a LI list that has an id on the last element
+ *
+ * @category  Toolkit_Template
+ * @package   Template
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2010 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com/ Gaslightmedia
+ * @version   Release: @package_version@
+ * @link      <>
+ * @see       References to other sections (if any)...
+ */
+class Toolkit_Template_Navigation_Renderer_DirectTreeUlId
+       extends HTML_Menu_DirectTreeRenderer
+{
+       //      {{{     properties
+
+    /**
+     * Id to be assigned to first ul as id
+     * @var    string
+     * @access private
+     */
+       private $_id = null;
+    /**
+     * Id to be assigned to first ul as class
+     * @var    string
+     * @access private
+     */
+    private $_class = null;
+
+    /**
+        * Database gateway for toolbox
+     * @var    string
+     * @access private
+     */
+       private $_gateway;
+
+       //      }}}
+       //      {{{     __construct()
+
+    /**
+     * Class constructor
+     *
+     * @param Toolkit_Toolbox_PageGatewayAbstract $gateway database gateway
+     * @param string                              $id      id to be assigned to last li
+     * @param string                              $class   id to be assigned to first ul as class
+        *
+     * @return void
+     * @access public
+     */
+       public function __construct(
+               Toolkit_Toolbox_PageGatewayAbstract $gateway,
+               $id = null,
+        $class = null
+       ) {
+               $this->_gateway = $gateway;
+
+               if (!is_null($id)) {
+                       $this->_id = $id;
+               }
+        if (!is_null($class)) {
+                       $this->_class = $class;
+               }
+       }
+
+       //      }}}
+       //      {{{     finishMenu()
+
+    /**
+     * Rendering before finishing
+     *
+        * if an id must be assigned to the last li element, then extract that
+        * element from the string and adjust the opening template
+     *
+     * @param integer $level Parameter description (if any) ...
+        *
+     * @return void
+     * @access public
+     */
+    function finishMenu($level)
+    {
+               parent::finishMenu($level);
+               if (!is_null($this->_id)) {
+                       $dom = new DOMDocument();
+                       $htmlUTF8 = mb_convert_encoding($this->_html, 'HTML-ENTITIES', 'UTF-8');
+                       @$dom->loadHTML($htmlUTF8);
+
+                       $xpath = new DOMXPath($dom);
+                       $element = $xpath->query("//*[@id='{$this->_id}']")->item(0);
+
+            $node = $dom->getElementsByTagName('ul')->item(0);
+            $node->setAttribute('id', $this->_id);
+            if (!is_null($this->_class)) {
+                $node->setAttribute('class', $this->_class);
+            }
+
+                       //      Prevent auto tags and doctype from being added to the HTML
+                       $this->_html = substr(
+                               $dom->saveXML($dom->getElementsByTagName('ul')->item(0)),
+                               0
+                       );
+               }
+    }
+
+       //      }}}
+}
diff --git a/Toolkit/Template/Navigation/SideNavigation.php b/Toolkit/Template/Navigation/SideNavigation.php
new file mode 100644 (file)
index 0000000..16611f1
--- /dev/null
@@ -0,0 +1,156 @@
+<?php
+/**
+ * SideNavigation.php
+ *
+ * PHP version 5
+ *
+ * @category  Toolkit
+ * @package   Template/Navigation
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2009 Gaslight Media
+ * @license   Gaslight Media
+ * @version   CVS: $Id: ShortURL.php,v 1.5 2010/05/25 14:07:22 jamie Exp $
+ * @link      <>
+ */
+
+
+/**
+ * Toolkit_Template_Navigation_SideNavigation
+ * 
+ * Description of Toolkit_Template_Navigation_SideNavigation
+ * 
+ * @category  Toolkit
+ * @package   Template/Navigation
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @release   Release: $id$
+ * @link      <>
+ */
+class Toolkit_Template_Navigation_SideNavigation
+       extends Toolkit_NavigationAbstract implements Toolkit_INavigation
+{
+       //      {{{     __construct()
+
+    /**
+     * Class constructor
+     * 
+     * @param HTML_Menu $menu
+     * @param HTML_Menu_Renderer $rEngine 
+     * 
+     * @access public
+     */
+       public function __construct(
+               HTML_Menu $menu,
+               HTML_Menu_Renderer $rEngine
+       ) {
+               $this->menu      = $menu;
+               $this->rEngine   = $rEngine;
+       }
+
+       //      }}}
+       //      {{{     setNavTemplates()
+
+    /**
+     * Set nav templates
+     * 
+     * @access protected
+     * @return void 
+     */
+       protected function setNavTemplates()
+       {
+               $tpl = '<a href="%s" %s>{Title}</a>';
+               $this->rEngine->setEntryTemplate(
+                       HTML_MENU_ENTRY_INACTIVE,
+                       sprintf($tpl, '{url}', '', '{Title}')
+               );
+               $this->rEngine->setEntryTemplate(
+                       HTML_MENU_ENTRY_ACTIVE,
+                       sprintf($tpl, '{url}', 'id="current"', '{Title}')
+               );
+               $this->rEngine->setEntryTemplate(
+                       HTML_MENU_ENTRY_ACTIVEPATH,
+                       sprintf($tpl, '{url}', '', '{Title}')
+               );
+       }
+
+       //      }}}
+       //      {{{     setCurrentIndex()
+
+    /**
+     * Set current index
+     * 
+     * @access protected
+     * @return void 
+     */
+       protected function setCurrentIndex()
+       {
+               $this->menu->forceCurrentIndex(filter_var($_GET['catid'], FILTER_SANITIZE_NUMBER_INT));
+       }
+
+       //      }}}
+       //      {{{     getNavSructure()
+
+    /**
+     * Recursive function to create a multi dimensional array for sub nav
+        *
+        * create tree (A) at the starting page level, pass the tree up to
+        * parent page, where tree (B) is created for that level. append
+        * tree A to tree B as a sub under the correct parent
+        *
+        * @param Toolkit_Toolbox_GatewayAbstract $gateway  Toolbox gateway
+        * @param integer                         $id       id to get subtree for
+        * @param array                           $tree     subtree created to be
+        *                                                  passed up to the parent
+        *                                                  level
+        * @param integer                         $appendTo pageId to append the
+        *                                                  passed up subtree to
+     * @return array navigational array hash
+     * @access public
+     */
+       public function getNavStructure(
+               Toolkit_Toolbox_GatewayAbstract $gateway,
+               $id,
+               array $tree = null,
+               $appendTo = null
+       ) {
+               $currentPage        = $gateway->findNavItem($id);
+               $this->_currentPage = $currentPage['navigation_name'];
+        
+               $subPages = $gateway->findAllByParent($id);
+
+               $nav = array();
+               foreach ($subPages as $subPage) {
+                       // no home page in sub nav
+                       if (   ($subPage['id'] != HOME_ID)
+                               && ($subPage['parent'] != MEMBERS_CATEGORY)
+                               && $subPage['active']
+                       ) {
+                               $nav[$subPage['id']] = array(
+                                       'Title' => htmlspecialchars($subPage['navigation_name']),
+                                       'url' => Toolkit_Template_Page::getSeoUrl($gateway, $subPage['id'])
+                               );
+                       }
+               }
+
+               if (   is_array($tree)
+                       && !empty($tree)
+                       && array_key_exists($appendTo, $nav)
+               ) {
+                       $nav[$appendTo]['sub'] = $tree;
+               }
+
+               if ($currentPage['parent'] != 0) {
+                       return $this->getNavStructure(
+                               $gateway,
+                               $currentPage['parent'],
+                               $nav,
+                               $id
+                       );
+               } else {
+                       return $nav;
+               }
+       }
+
+       //      }}}
+}
diff --git a/Toolkit/Template/Page.php b/Toolkit/Template/Page.php
new file mode 100644 (file)
index 0000000..4f1870d
--- /dev/null
@@ -0,0 +1,140 @@
+<?php
+/**
+ * Page.php
+ *
+ * PHP version 5
+ *
+ * @category  Toolkit
+ * @package   Template
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2009 Gaslight Media
+ * @license   Gaslight Media
+ * @version   CVS: $Id: ShortURL.php,v 1.5 2010/05/25 14:07:22 jamie Exp $
+ * @link      <>
+ */
+
+/**
+ * Toolkit_Template_Page
+ *
+ * Description of Toolkit_Template_Page
+ *
+ * @category  Toolkit
+ * @package   Template
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @release   Release: $id$
+ * @link      <>
+ */
+class Toolkit_Template_Page
+{
+       //      {{{     __construct()
+
+    /**
+     * Class constructor
+     *
+     * @access public
+     */
+    public function __construct()
+    {
+
+    }
+
+       //      }}}
+
+       //      {{{     getSeoUrl()
+
+    /**
+     * Description for getSeoUrl
+     *
+     * @param Toolkit_Toolbox_GatewayAbstract $gateway Gateway
+     * @param string                          $id      ID
+     *
+     * @return boolean|string
+     * @access public
+     */
+
+   
+    public static function getSeoUrl(
+               Toolkit_Toolbox_GatewayAbstract $gateway,
+               $id
+       ) {
+               if (!ctype_digit((string)$id)) {
+                       return false;
+               }
+
+               if ($id == HOME_ID) {
+                       return ($_ENV['GLM_HOST_ID'] != 'PRODUCTION')
+                ? BASE_URL . 'index.php'
+                : BASE_URL;
+               }
+
+               $page = $gateway->findNavItem($id);
+        $shortUrlsOn
+            = (SHORT_URLS)
+            ? true
+            : false;
+
+               if (empty($page['short_url']) || !$shortUrlsOn) {
+                       $name = str_replace(' ', '-', $page['navigation_name']);
+                       $pattern = '/[\/#&?\'"]|amp;/';
+                       $name = preg_replace(
+                               $pattern,
+                               '',
+                               strip_tags(strtolower(trim($name)))
+                       );
+                       $baseUrl
+                = in_array($id, unserialize(SECURE_PAGES))
+                ? BASE_SECURE_URL
+                : BASE_URL;
+                       return $baseUrl . htmlspecialchars($name) . "-$id/";
+               } else {
+                       return BASE_URL . "{$page['short_url']}/";
+               }
+       }
+
+       //      }}}
+
+       //      {{{     getBody()
+
+    /**
+     * Get body
+     *
+     * @param string                            $id          id
+     * @param Toolkit_Template_Page_BodyFactory $bodyFactory body factory
+     *
+     * @return string
+     * @access public
+     */
+    public function getBody(
+               $id,
+               Toolkit_Template_Page_BodyFactory $bodyFactory
+       ) {
+               $body = $bodyFactory->getPageBodyBuilder($id);
+               return $body->getContent($id);
+    }
+
+       //      }}}
+
+    /**
+     * is member only
+     *
+     * @param Toolkit_Toolbox_GatewayAbstract $gateway gateway
+     * @param string                          $id      id
+     *
+     * @return boolean
+     * @access public
+     */
+    public static function isMemberOnly(/*{{{*/
+               Toolkit_Toolbox_GatewayAbstract $gateway,
+        $id
+    ) {
+        $page = $gateway->find($id);
+
+        if ($page['parent'] == MEMBERS_CATEGORY) {
+            return true;
+        } else {
+            return false;
+        }
+    }/*}}}*/
+}
diff --git a/Toolkit/Template/Page/Bad.php b/Toolkit/Template/Page/Bad.php
new file mode 100644 (file)
index 0000000..fed4965
--- /dev/null
@@ -0,0 +1,42 @@
+<?php
+/**
+ * Bad.php
+ *
+ * PHP version 5
+ *
+ * @category  Toolkit
+ * @package   Template/Page
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2009 Gaslight Media
+ * @license   Gaslight Media
+ * @version   CVS: $Id: ShortURL.php,v 1.5 2010/05/25 14:07:22 jamie Exp $
+ * @link      <>
+ */
+
+/**
+ * Toolkit_Template_Page_Bad
+ * 
+ * Description of Toolkit_Template_Page_Bad
+ * 
+ * @category  Toolkit
+ * @package   Template/Page
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @release   Release: $id$
+ * @link      <>
+ */
+class Toolkit_Template_Page_Bad
+{
+    /**
+     * Description of getContent
+     * 
+     * @access public
+     * @return string 
+     */
+       public function getContent()
+       {
+               return '<h1>Sorry this page is down!</h1>';
+       }
+}
+?>
diff --git a/Toolkit/Template/Page/BodyFactory.php b/Toolkit/Template/Page/BodyFactory.php
new file mode 100644 (file)
index 0000000..d5f7000
--- /dev/null
@@ -0,0 +1,218 @@
+<?php
+/**
+ * BodyFactory.php
+ *
+ * PHP version 5
+ *
+ * @category  Toolkit
+ * @package   Template/Page
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2009 Gaslight Media
+ * @license   Gaslight Media
+ * @version   CVS: $Id: ShortURL.php,v 1.5 2010/05/25 14:07:22 jamie Exp $
+ * @link      <>
+ */
+
+/**
+ * Toolkit_Template_Page_BodyFactory
+ *
+ * Description of Toolkit_Template_Page_BodyFactory
+ *
+ * @category  Toolkit
+ * @package   Template/Page
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @release   Release: $id$
+ * @link      <>
+ */
+class Toolkit_Template_Page_BodyFactory
+{
+       //      {{{     properties
+
+    /**
+     * description of $_breadCrumbsFactory
+     *
+     * @var string
+     * @access private
+     */
+       private $_breadCrumbsFactory;
+
+    /**
+     * description of $_pageGateway
+     *
+     * @var string
+     * @access private
+     */
+       private $_pageGateway;
+
+    /**
+     * description of $_paragraphGateway
+     *
+     * @var string
+     * @access private
+     */
+       private $_paragraphGateway;
+
+    /**
+     * description of $_cache
+     *
+     * @var string
+     * @access private
+     */
+       private $_cache;
+
+    /**
+     * description of $_keywordReplacement
+     *
+     * @var string
+     * @access private
+     */
+       private $_keywordReplacement;
+
+       //      }}}
+       //      {{{     __construct()
+
+    /**
+     * Class constructor
+     *
+     * @param Toolkit_BreadCrumbsFactory               $breadCrumbsFactory breadCrumbsFactory
+     * @param Toolkit_Toolbox_PageGatewayAbstract      $pageGateway        pageGateway
+     * @param Toolkit_Toolbox_ParagraphGatewayAbstract $paragraphGateway   paragraphGateway
+     * @param Toolkit_Template_KeywordReplacement      $keywordReplacement keywordReplacement
+     * @param Cache_Lite                               $cache              cache
+     *
+     * @access public
+     */
+       public function __construct(
+               Toolkit_BreadCrumbsFactory $breadCrumbsFactory,
+               Toolkit_Toolbox_PageGatewayAbstract $pageGateway,
+               Toolkit_Toolbox_ParagraphGatewayAbstract $paragraphGateway,
+               Toolkit_Template_KeywordReplacement $keywordReplacement,
+        Cache_Lite $cache
+       ) {
+               $this->_breadCrumbsFactory = $breadCrumbsFactory;
+               $this->_pageGateway        = $pageGateway;
+               $this->_paragraphGateway   = $paragraphGateway;
+               $this->_keywordReplacement = $keywordReplacement;
+               $this->_cache              = $cache;
+
+       }
+
+       //      }}}
+       //      {{{     getPageBodyBuilder()
+
+    /**
+     * description of getPageBodyBuilder
+     *
+     * @param string $id ID
+     *
+     * @return \Toolkit_Members_SiteMapPage|\Toolkit_Template_Page_SiteMap
+     * |\Toolkit_Template_Page_Bad|\Toolkit_Template_Page_GoogleSearch
+     * |\Toolkit_Template_Page_Member|\Toolkit_Template_Page_Toolbox
+     * @access public
+     */
+       public function getPageBodyBuilder($id)
+       {
+        $memberId = filter_var($_REQUEST['member_id'], FILTER_VALIDATE_INT);
+               $page     = $this->_pageGateway->find($id);
+
+               $forgotPassword   = (isset($_GET['forgot']) && $_GET['forgot'] == 1);
+               $siteMap              = (isset($_GET['sitemap']) && $_GET['sitemap'] == 1);
+               $memberDbSiteMap  = (   isset($_GET['memberDbSiteMap'])
+                                                        && $_GET['memberDbSiteMap'] == 1);
+               $pageDoesNotExist = ($page === false);
+        if ($pageDoesNotExist && $memberId) {
+            $pageDoesNotExist = false;
+        }
+               $pageIsTurnedOff  = (   is_array($page)
+                                                        && !$page['active']
+                                                        && !strstr($_SERVER['HTTP_REFERER'], 'admin/')
+                             && !$_REQUEST['preview']);
+               $googleSearch     = (   defined('GOOGLE_SEARCH')
+                                                    && GOOGLE_SEARCH
+                                                    && isset($_GET['query'])
+                                                    && !empty($_GET['query']));
+               $glmSearch        = (   defined('GLM_SEARCH')
+                                                    && GLM_SEARCH
+                                                    && isset($_REQUEST['GLMSearch'])
+                                                    && $_REQUEST['GLMSearch'] == 'true');
+               $memberPage       = (   defined('MEMBERS_DB')
+                                                && MEMBERS_DB
+                             && $page['include_members']
+                             || $memberId
+                         );
+
+               if ($forgotPassword) {
+                       die('Toolkit_Template_Page_BodyFactory::getPageBodyBuilder');
+               } elseif ($memberDbSiteMap) {
+                       return new Toolkit_Members_SiteMapPage(
+                               Toolkit_Database::getInstance(),
+                $this->_pageGateway
+                       );
+               } elseif ($siteMap) {
+                       return new Toolkit_Template_Page_SiteMap(
+                               Toolkit_Database::getInstance(),
+                $this->_pageGateway
+                       );
+               } elseif ($pageDoesNotExist) {
+                       //      page does not exist
+                       //      give 301 redirect and go back to index page
+                       header($_SERVER['SERVER_PROTOCOL'] . ' 301 Moved Permanently');
+                       header('Location: ' . BASE_URL . 'index.php');
+                       exit();
+               } elseif ($pageIsTurnedOff) {
+                       //      page exists, but is turned off
+                       //      give 404 redirect and go back to index page
+                       header($_SERVER['SERVER_PROTOCOL'] . ' 404 Not Found');
+                       return new Toolkit_Template_Page_Bad();
+               } elseif ($googleSearch) {
+                       return new Toolkit_Template_Page_GoogleSearch();
+               } elseif ($glmSearch) {
+                       return new Toolkit_Template_Page_GLMSearch();
+               } elseif ($memberPage) {
+                       $conf = new Config;
+                       $root =& $conf->parseConfig(
+                               BASE . 'Toolkit/Members/config.ini',
+                               'IniFile'
+                       );
+
+                       $imageAdapter  = new Toolkit_FileServer_ImageAdapter();
+
+                       return new Toolkit_Template_Page_Member(
+                               $id,
+                               new Toolkit_Template_Image_Factory($imageAdapter),
+                               $this->_breadCrumbsFactory,
+                               $this->_pageGateway,
+                               $this->_paragraphGateway,
+                               $this->_keywordReplacement,
+                               $this->_cache,
+                               $root
+                       );
+               } else { // regular toolbox page
+                       $imageAdapter = new Toolkit_FileServer_ImageAdapter();
+
+                       $photoGallery = null;
+                       if (defined('PHOTO_GALLERY') && PHOTO_GALLERY) {
+                               $dbh = Toolkit_Database::getInstance();
+                               $photoGallery = new Toolkit_Photos_Gallery(
+                                       $dbh,
+                                       new Toolkit_Photos_Display($dbh)
+                               );
+                       }
+
+                       return new Toolkit_Template_Page_Toolbox(
+                               $id,
+                               new Toolkit_Template_Image_Factory($imageAdapter),
+                               $this->_breadCrumbsFactory,
+                               $this->_pageGateway,
+                               $this->_paragraphGateway,
+                               $this->_keywordReplacement,
+                               $this->_cache,
+                               $photoGallery
+                       );
+               }
+       }
+
+       //      }}}
+}
diff --git a/Toolkit/Template/Page/FileLink/Abstract.php b/Toolkit/Template/Page/FileLink/Abstract.php
new file mode 100644 (file)
index 0000000..3c142fc
--- /dev/null
@@ -0,0 +1,151 @@
+<?php
+
+/**
+ * Abstract base class for file links
+ *
+ * PHP version 5
+ *
+ * The license text...
+ *
+ * @category  Toolkit_Template
+ * @package   Template
+ * @author    Jamie Kahgee <jamie@gaslightmedia.com>
+ * @copyright 2010 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com/ Gaslightmedia
+ * @version   CVS: $Id:$
+ * @link      <>
+ * @see       References to other sections (if any)...
+ */
+
+/**
+ * Abstract base class for file links
+ *
+ * Long description (if any) ...
+ *
+ * @category  Toolkit_Template
+ * @package   Template
+ * @author    Jamie Kahgee <jamie@gaslightmedia.com>
+ * @copyright 2010 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com/ Gaslightmedia
+ * @version   Release: @package_version@
+ * @link      <>
+ * @see       References to other sections (if any)...
+ */
+abstract class Toolkit_Template_Page_FileLink_Abstract
+{
+       //      {{{     properties
+
+
+    /**
+     * Database Id
+     * @var    integer
+     * @access protected
+     */
+       protected $id;
+
+    /**
+     * Filename
+     * @var    string
+     * @access protected
+     */
+       protected $filename;
+
+    /**
+     * Size in bytes of file
+     * @var    integer
+     * @access protected
+     */
+       protected $bytes;
+
+    /**
+     * Type of file
+     * @var    string
+     * @access protected
+     */
+       protected $type;
+
+    /**
+     * Text to display for link
+     * @var    string
+     * @access protected
+     */
+       protected $urltext;
+
+    /**
+     * Paragraph id file belongs to
+     * @var    integer
+     * @access protected
+     */
+       protected $paragraph;
+
+    /**
+     * Position of file
+     * @var    integer
+     * @access protected
+     */
+       protected $pos;
+
+    /**
+     * File link
+     * @var    string
+     * @access protected
+     */
+       protected $format = '<div><a class="file-download %s" href="%s">%s</a></div>';
+
+    /**
+     * File extension
+     * @var    string
+     * @access protected
+     */
+       protected $fileExtension;
+
+       //      }}}
+       //      {{{     __construct()
+
+
+    /**
+     * Constructor
+     *
+     * @param Toolkit_Toolbox_FileExtension $fileExtension file extension helper
+        *
+     * @return void
+     * @access public
+     */
+       public function __construct(Toolkit_Toolbox_FileExtension $fileExtension)
+       {
+               $this->fileExtension = $fileExtension;
+       }
+
+       //      }}}
+       //      {{{     readFileArray()
+
+
+    /**
+     * Reads the file array of info and populates the object
+     *
+     * @param  array $file file data
+        *
+     * @return void
+     * @access public
+     */
+       public function readFileArray (array $file)
+       {
+               foreach ($file as $key => $value) {
+                       $this->$key = $value;
+               }
+       }
+
+       //      }}}
+       //      {{{     getLink()
+
+    /**
+     * Abstract method to subclass
+     *
+        * generates the appropriate link for the type of file
+     *
+     * @access public
+     */
+       abstract public function getLink();
+
+       //      }}}
+}
diff --git a/Toolkit/Template/Page/FileLink/Factory.php b/Toolkit/Template/Page/FileLink/Factory.php
new file mode 100644 (file)
index 0000000..fca84a0
--- /dev/null
@@ -0,0 +1,50 @@
+<?php
+/**
+ * Factory.php
+ *
+ * PHP version 5
+ *
+ * @category  Toolkit
+ * @package   Template/Page/FileLink
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2009 Gaslight Media
+ * @license   Gaslight Media
+ * @version   CVS: $Id: ShortURL.php,v 1.5 2010/05/25 14:07:22 jamie Exp $
+ * @link      <>
+ */
+
+/**
+ * Toolkit_Template_Page_FileLink_Factory
+ * 
+ * Description of Toolkit_Template_Page_FileLink_Factory
+ * 
+ * @category  Toolkit
+ * @package   Template/Page/FileLink
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @release   Release: $id$
+ * @link      <>
+ */
+class Toolkit_Template_Page_FileLink_Factory
+{
+    /**
+     * Create Link Renderer
+     * 
+     * @param array $file File array
+     * 
+     * @return object Either Image or File object
+     * @access public
+     */
+       static public function createLinkRenderer(array $file)
+       {
+               $fileType = substr($file['filename'], 0, 2);
+               $fileExt = new Toolkit_Toolbox_FileExtension;
+               $renderer = ($fileType == 'is')
+                       ? new Toolkit_Template_Page_FileLink_Image($fileExt)
+                       : new Toolkit_Template_Page_FileLink_File($fileExt);
+
+               $renderer->readFileArray($file);
+               return $renderer;
+       }
+}
diff --git a/Toolkit/Template/Page/FileLink/File.php b/Toolkit/Template/Page/FileLink/File.php
new file mode 100644 (file)
index 0000000..8140adc
--- /dev/null
@@ -0,0 +1,57 @@
+<?php
+/**
+ * File.php
+ *
+ * PHP version 5
+ *
+ * @category  Toolkit
+ * @package   Template/Page/FileLink
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2009 Gaslight Media
+ * @license   Gaslight Media
+ * @version   CVS: $Id: ShortURL.php,v 1.5 2010/05/25 14:07:22 jamie Exp $
+ * @link      <>
+ */
+
+
+/**
+ * Toolkit_Template_Page_FileLink_File
+ *
+ * Description of Toolkit_Template_Page_FileLink_File
+ *
+ * @category  Toolkit
+ * @package   Template/Page/FileLink
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @release   Release: $id$
+ * @link      <>
+ */
+class Toolkit_Template_Page_FileLink_File
+    extends Toolkit_Template_Page_FileLink_Abstract
+{
+    //  {{{ getLink()
+
+    /**
+     * Gets the link of a file
+     *
+     * @return string $link Formatted string
+     * @access public
+     */
+    public function getLink()
+    {
+        $folder = substr($this->filename, 2, 2);
+        $urltext = urlencode($this->urltext);
+        $url = UPLOADED_FILES . "_ORIGINAL_/{$this->filename}";
+
+        $link = sprintf(
+            $this->format,
+            $this->fileExtension->getClassForType($this->filename),
+            $url,
+            $this->urltext
+        );
+        return $link;
+    }
+
+    //  }}}
+}
diff --git a/Toolkit/Template/Page/FileLink/Image.php b/Toolkit/Template/Page/FileLink/Image.php
new file mode 100644 (file)
index 0000000..44649b4
--- /dev/null
@@ -0,0 +1,58 @@
+<?php
+/**
+ * Image.php
+ *
+ * PHP version 5
+ *
+ * @category  Toolkit
+ * @package   Template/Page/FileLink
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2009 Gaslight Media
+ * @license   Gaslight Media
+ * @version   CVS: $Id: ShortURL.php,v 1.5 2010/05/25 14:07:22 jamie Exp $
+ * @link      <>
+ */
+
+/**
+ * Toolkit_Template_Page_FileLink_Image
+ * 
+ * Description of Toolkit_Template_Page_FileLink_Image
+ * 
+ * @category  Toolkit
+ * @package   Template/Page/FileLink
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @release   Release: $id$
+ * @link      <>
+ */
+class Toolkit_Template_Page_FileLink_Image
+       extends Toolkit_Template_Page_FileLink_Abstract
+{
+       //      {{{     getLink()
+
+    /**
+     * Gets link of an image
+     * 
+     * @return string $link Formatted string 
+     * @access public
+     */
+       public function getLink()
+       {
+               $GLOBALS['bottomScripts'][] = MEDIA_APP_BASE_URL . 'libjs/glm.js';
+
+               $folder = substr($this->filename, 2, 2);
+               $urltext = urlencode($this->urltext);
+               $url = ORIGINAL . $this->filename;
+
+               $link = sprintf(
+                       $this->format,
+                       $this->fileExtension->getClassForType($this->filename),
+                       $url,
+                       $this->urltext
+               );
+               return $link;
+       }
+
+       //      }}}
+}
diff --git a/Toolkit/Template/Page/GLMSearch.php b/Toolkit/Template/Page/GLMSearch.php
new file mode 100644 (file)
index 0000000..5e33b52
--- /dev/null
@@ -0,0 +1,50 @@
+<?php
+/**
+ * GLMSearch.php
+ *
+ * PHP version 5
+ *
+ * @category  Toolkit
+ * @package   Template/Page
+ * @author    Chuck Scott <cscott@gaslightmedia.com>
+ * @copyright 2013 Gaslight Media
+ * @license   Gaslight Media
+ * @version   CVS: $Id: GLMSearch.php,v 1.5 2010/05/25 14:07:22 cscott Exp $
+ * @link      <>
+ */
+
+/**
+ * Toolkit_Template_Page_GLMSearch
+ *
+ * Description of Toolkit_Template_Page_GLMSearch
+ *
+ * @category  Toolkit
+ * @package   Template/Page
+ * @author    Chuck Scott <cscott@gaslightmedia.com>
+ * @copyright 2013 Gaslight Media
+ * @license   Gaslight Media
+ * @release   Release: $id$
+ * @link      <>
+ */
+class Toolkit_Template_Page_GLMSearch
+{
+    /**
+    * Description for getContent()
+    *
+    * @return string
+    * @access public
+    */
+    public function getContent()
+    {
+        include '/var/www/server/CommonApps/GLMSearch/V1.1/glmSearch.inc';
+        $glmSearch = new glmSearch(
+            GLM_SEARCH_SITE,
+            GLM_SEARCH_LOGIN,
+            GLM_SEARCH_KEY,
+            false,
+            GLM_SEARCH_INDEX
+        );
+        $page = $glmSearch->doSearch();
+        return $page;
+    }
+}
diff --git a/Toolkit/Template/Page/GoogleSearch.php b/Toolkit/Template/Page/GoogleSearch.php
new file mode 100644 (file)
index 0000000..3a608be
--- /dev/null
@@ -0,0 +1,42 @@
+<?php
+/**
+ * GoogleSearch.php
+ *
+ * PHP version 5
+ *
+ * @category  Toolkit
+ * @package   Template/Page
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2009 Gaslight Media
+ * @license   Gaslight Media
+ * @version   CVS: $Id: ShortURL.php,v 1.5 2010/05/25 14:07:22 jamie Exp $
+ * @link      <>
+ */
+
+/**
+ * Toolkit_Template_Page_GoogleSearch
+ * 
+ * Description of Toolkit_Template_Page_GoogleSearch
+ * 
+ * @category  Toolkit
+ * @package   Template/Page
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @release   Release: $id$
+ * @link      <>
+ */
+class Toolkit_Template_Page_GoogleSearch
+{
+    /**
+     * Description for getContent()
+     * 
+     * @return string 
+     * @access public
+     */
+       public function getContent()
+       {
+               return '<div id="searchcontrol"></div>';
+       }
+}
+?>
diff --git a/Toolkit/Template/Page/IBody.php b/Toolkit/Template/Page/IBody.php
new file mode 100644 (file)
index 0000000..3d70bef
--- /dev/null
@@ -0,0 +1,39 @@
+<?php
+/**
+ * IBody.php
+ *
+ * PHP version 5
+ *
+ * @category  Toolkit
+ * @package   Template/Page
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2009 Gaslight Media
+ * @license   Gaslight Media
+ * @version   CVS: $Id: ShortURL.php,v 1.5 2010/05/25 14:07:22 jamie Exp $
+ * @link      <>
+ */
+
+/**
+ * Toolkit_Template_Page_IBody
+ * 
+ * Description of Toolkit_Template_Page_IBody
+ * 
+ * @category  Toolkit
+ * @package   Template/Page
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @release   Release: $id$
+ * @link      <>
+ */
+interface Toolkit_Template_Page_IBody
+{
+    /**
+     * Description of getContent
+     * 
+     * @access public 
+     * @return unknown
+     */
+       public function getContent();
+}
+?>
diff --git a/Toolkit/Template/Page/Member.php b/Toolkit/Template/Page/Member.php
new file mode 100644 (file)
index 0000000..3eff26d
--- /dev/null
@@ -0,0 +1,404 @@
+<?php
+/**
+ * Member.php
+ *
+ * PHP version 5
+ *
+ * @category  Toolkit
+ * @package   Template/Page
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2009 Gaslight Media
+ * @license   Gaslight Media
+ * @version   CVS: $Id: ShortURL.php,v 1.5 2010/05/25 14:07:22 jamie Exp $
+ * @link      <>
+ */
+
+/**
+ * Toolkit_Template_Page_Member
+ *
+ * Description of Toolkit_Template_Page_Member
+ *
+ * @category  Toolkit
+ * @package   Template/Page
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @release   Release: $id$
+ * @link      <>
+ */
+class Toolkit_Template_Page_Member extends Toolkit_Template_Page_Toolbox
+{
+    //    {{{    properties
+
+    /**
+     * Description of $_configContainer
+     *
+     * @var Config_Container
+     * @access private
+     */
+    private $_configContainer;
+
+    //    }}}
+    //    {{{    __construct()
+
+    /**
+     * Class constructor
+     *
+     * @param int                                      $id                 ID
+     * @param Toolkit_Template_Image_Factory           $imageFactory       Image Factory
+     * @param Toolkit_BreadCrumbsFactory               $breadCrumbsFactory Breadcrumbs Factory
+     * @param Toolkit_Toolbox_PageGatewayAbstract      $pageGateway        Page Gateway
+     * @param Toolkit_Toolbox_ParagraphGatewayAbstract $paragraphGateway   Paragraph Gateway
+     * @param Toolkit_Template_KeywordReplacement      $keywordReplacement Keyword Replacement
+     * @param Cache_Lite                               $cache              Cache
+     * @param Config_Container                         $config             Config Container
+     *
+     * @access public
+     */
+    public function __construct(
+        $id,
+        Toolkit_Template_Image_Factory $imageFactory,
+        Toolkit_BreadCrumbsFactory $breadCrumbsFactory,
+        Toolkit_Toolbox_PageGatewayAbstract $pageGateway,
+        Toolkit_Toolbox_ParagraphGatewayAbstract $paragraphGateway,
+        Toolkit_Template_KeywordReplacement $keywordReplacement,
+        Cache_Lite $cache,
+        Config_Container $config
+    ) {
+        $this->id                 = $id;
+        $this->imageFactory       = $imageFactory;
+        $this->breadCrumbsFactory = $breadCrumbsFactory;
+        $this->pageGateway        = $pageGateway;
+        $this->paragraphGateway   = $paragraphGateway;
+        $this->keywordReplacement = $keywordReplacement;
+        $this->cache              = $cache;
+        $this->_configContainer   = $config;
+    }
+
+    //    }}}
+
+    //    {{{    getBreadCrumbs()
+
+    /**
+     * Gets breadcrumbs html array
+     *
+     * @param int $id ID
+     *
+     * @return array
+     * @access public
+     */
+    protected function getBreadCrumbs($id)
+    {
+        $stack = parent::getbreadCrumbsArray($id);
+
+        $lastNode = array_pop($stack);
+        $anchor = '<a href="#">' . $lastNode . '</a>';
+        array_push($stack, $anchor);
+
+        array_push($stack, 'Member Name');
+
+        return parent::getBreadCrumbsHtml($stack);
+    }
+
+    //    }}}
+    //    {{{    _getMemberSearchResults()
+
+    /**
+     * Get member search results
+     *
+     * @param int $id ID
+     *
+     * @return string $html
+     * @access private
+     */
+    private function _getMemberSearchResults($id)
+    {
+        $memberMap = false;
+        $page = $this->pageGateway->find($id);
+        $html = '';
+        if ($page['include_member_map']) {
+            $memberMap = true;
+            $googleMap = new Toolkit_Members_Map();
+            $html = $googleMap->toHtml();
+        }
+
+        $dbh = Toolkit_Database::getInstance();
+
+        $searchQuery = new Toolkit_Members_SearchQueryGenerator(
+            true,
+            $this->_configContainer
+        );
+
+        $sql = $searchQuery->getQuery($dbh);
+        $searchList = new Toolkit_Members_SearchList(
+            $dbh,
+            50,
+            null,
+            null,
+            true
+        );
+
+        $searchList->setConfig($this->_configContainer);
+        $searchList->setMemberMap($memberMap);
+        $searchList->setPageGateway($this->pageGateway);
+        $searchList->setQuery($sql);
+        // if you add any boolean field to this sort
+        // you'll need to make sure you have a case if the boolean field is null
+        // in the SearchGenerator or it will have inconsitant results
+        $nonMembers = $this->_configContainer->getItem('section', 'conf')
+            ->getItem('directive', 'nonMembers')
+            ->getContent();
+        if ($nonMembers) {
+            $defaultSort = array(
+                'nonmember' => 'ASC',
+                'member_name'  => 'ASC'
+            );
+        } else {
+            $defaultSort = array(
+                'member_name' => 'ASC'
+            );
+        }
+        $searchList->setDefaultSort($defaultSort);
+        //    Rendering Engine to use
+        $rEngine = new Structures_DataGrid_Renderer_Flexy();
+        //    Templating Engine to use
+        $tEngine = new HTML_Template_Flexy(
+            Toolkit_Members::getFlexyOptions()
+        );
+        $rEngine->setContainer($tEngine);
+
+        $html .= $searchList->toHtml($rEngine);
+
+        return $html;
+    }
+
+    //    }}}
+    //    {{{    _getMemberSearchForm()
+
+    /**
+     * Gets member search form
+     *
+     * @param int $id ID
+     *
+     * @return mixed $html or mixed from Common.php
+     * @access private
+     */
+    private function _getMemberSearchForm($id)
+    {
+        $page = $this->pageGateway->find($id);
+        $html = '';
+        if ($page['include_member_map']) {
+            $googleMap = new Toolkit_Members_Map();
+            $html = $googleMap->toHtml();
+        }
+        $pageUrl
+            = ($_REQUEST['page_id'])
+            ? MEDIA_BASE_URL . "members-only-area/?page_id={$_REQUEST['page_id']}"
+            : MEDIA_BASE_URL . "index.php?catid={$_GET['catid']}";
+        $form = new Toolkit_Members_UserSearchForm(
+            'SearchForm',
+            'get',
+            $pageUrl,
+            null,
+            null,
+            true
+        );
+
+        $res = $form->setCatid($_GET['catid']);
+        if (!PEAR::isError($res)) {
+            $dbh = Toolkit_Database::getInstance();
+            $form->setPageMemberCategories($this->pageGateway);
+            $form->setPageMemberRegions($this->pageGateway);
+            $form->configureForm($dbh, $this->_configContainer);
+            $html .= $form->toHtml(
+                Toolkit_Members::getFlexyOptions(),
+                $this->pageGateway
+            );
+        } else {
+            return Toolkit_Common::handleError($res);
+        }
+
+        return $html;
+    }
+
+    //    }}}
+    //    {{{    _getMemberProfilePage()
+
+    /**
+     * Gets member profile page
+     *
+     * @param int $id ID
+     *
+     * @return mixed
+     * @access private
+     */
+    private function _getMemberProfilePage($id)
+    {
+        try {
+            $profileCreator = new Toolkit_Members_ProfilePage(
+                Toolkit_Database::getInstance(),
+                $this->pageGateway,
+                $_GET['member_id']
+            );
+
+            $tEngine = new HTML_Template_Flexy(
+                Toolkit_Members::getFlexyOptions()
+            );
+
+            $profileWriter = new Toolkit_Members_ProfileWriter(
+                new Cache_Lite(Toolkit_Members::getCacheOptions()),
+                $tEngine
+            );
+
+            $profileWriter->setTemplate('memberDetail.tpl');
+            $profileWriter->attach(
+                new Toolkit_Members_Exposure(
+                    $profileCreator->getMemberId(),
+                    'detail'
+                )
+            );
+
+            $profileCreator->setCatid($id);
+            $profileCreator->setConfig($this->_configContainer);
+            $profileCreator->setImagePath(MEMBER_RESIZED);
+
+            return $profileWriter->toHtml($profileCreator);
+        } catch (PEAR_Exception $e) {
+            return Toolkit_Common::handleError($e);
+        }
+    }
+
+    //    }}}
+    /**
+     * Description for _getPageCoupons
+     *
+     * @param int $id ID
+     *
+     * @return string|void
+     * @access private
+     */
+    private function _getPageCoupons($id)
+    {
+        $page = $this->pageGateway->find($id);
+        if (!$page['include_coupons']) {
+            return;
+        }
+
+        $widget = new Toolkit_Coupons_PageWidget(
+            new Toolkit_Coupons_WebCouponFactory(),
+            new Toolkit_Coupons_Coupons()
+        );
+
+        $flexyOpts = Toolkit_Coupons_Controller::getFlexyOptions();
+        $tEngine = new HTML_Template_Flexy($flexyOpts);
+
+        $widget->addPageCategories(
+            Toolkit_Database::getInstance(),
+            $id
+        );
+
+        $searchForm = new Toolkit_Coupons_UserSearch(
+            'coupon_search',
+            'get',
+            MEDIA_BASE_URL . 'index.php'
+        );
+        $searchForm->configureForm(
+            Toolkit_Database::getInstance(),
+            $id
+        );
+
+        $widget->setSearchForm($searchForm);
+
+        $couponForm = new HTML_QuickForm(
+            'coupon_form',
+            'post',
+            MEDIA_BASE_URL . 'Toolkit/Coupons/print.php'
+        );
+        $renderer =& $couponForm->defaultRenderer();
+        $renderer->clearAllTemplates();
+        return $widget->toHtml($couponForm, $tEngine, 'coupons.html');
+    }
+
+    private function _isMemberActive($memberId)
+    {
+        try {
+            $dbh = Toolkit_Database::getInstance();
+            $sql = "
+            SELECT active
+              FROM member
+             WHERE member_id = :member_id";
+            $stmt = $dbh->prepare($sql);
+            $stmt->bindParam(':member_id', $memberId, PDO::PARAM_INT);
+            $stmt->execute();
+            return $stmt->fetchColumn();
+        } catch (PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+
+    }
+    //    {{{    getContent()
+
+    /**
+     * Description of getContent()
+     *
+     * @return string
+     * @access public
+     */
+    public function getContent()
+    {
+        $GLOBALS['bottomScripts'][]
+            = MEDIA_APP_BASE_URL . 'libjs/member-link.js';
+        $breadCrumbsBuilder = $this->breadCrumbsFactory->createBreadCrumbsHelper();
+        $page = $this->pageGateway->find($this->id);
+
+        $coupons = null;
+        if (defined('COUPONS') && COUPONS) {
+            $coupons = $this->_getPageCoupons($this->id);
+        }
+
+        if ($memberId = filter_input(INPUT_GET, 'member_id', FILTER_VALIDATE_INT)) {
+            // check and see if the member is inactive
+            // if they are then don't show bread crumb and give out 404 header
+            if (!$this->_isMemberActive($memberId)) {
+                header($_SERVER['SERVER_PROTOCOL'] . ' 404 Not Found');
+                $badPage = new Toolkit_Template_Page_Bad();
+                return $badPage->getContent();
+            } else {
+                $breadCrumbs = $breadCrumbsBuilder->toHtml($this->id);
+                $profile     = $this->_getMemberProfilePage($this->id);
+                return $breadCrumbs .
+                   $profile;
+            }
+        } elseif (!$page['search_form']) {
+            $breadCrumbs         = $breadCrumbsBuilder->toHtml($this->id);
+            $searchResults       = $this->_getMemberSearchResults($this->id);
+            $secondaryParagraphs = $this->getSecondaryParagraphs($this->id);
+            $primaryParagraph    = $this->getPrimaryParagraph($this->id);
+
+            return $breadCrumbs .
+                   $primaryParagraph .
+                   $searchResults .
+                   $coupons .
+                   $secondaryParagraphs;
+        } elseif (isset($_GET['search'])) {
+            $breadCrumbs   = $breadCrumbsBuilder->toHtml($this->id);
+            $searchResults = $this->_getMemberSearchResults($this->id);
+
+            return $breadCrumbs .
+                   $searchResults;
+        } else {
+            $breadCrumbs         = $breadCrumbsBuilder->toHtml($this->id);
+            $searchForm          = $this->_getMemberSearchForm($this->id);
+            $secondaryParagraphs = $this->getSecondaryParagraphs($this->id);
+            $primaryParagraph    = $this->getPrimaryParagraph($this->id);
+
+            return $breadCrumbs .
+                   $primaryParagraph .
+                   $searchForm .
+                   $coupons .
+                   $secondaryParagraphs;
+        }
+    }
+
+    //    }}}
+}
diff --git a/Toolkit/Template/Page/SiteMap.php b/Toolkit/Template/Page/SiteMap.php
new file mode 100644 (file)
index 0000000..6cf1a99
--- /dev/null
@@ -0,0 +1,237 @@
+<?php
+/**
+ * SiteMap.php
+ *
+ * PHP version 5
+ *
+ * @category  Toolkit
+ * @package   Template/Page
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2009 Gaslight Media
+ * @license   Gaslight Media
+ * @version   CVS: $Id: ShortURL.php,v 1.5 2010/05/25 14:07:22 jamie Exp $
+ * @link      <>
+ */
+
+/**
+ * Toolkit_Template_Page_SiteMap
+ *
+ * Description of Toolkit_Template_Page_SiteMap
+ *
+ * @category  Toolkit
+ * @package   Template/Page
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @release   Release: $id$
+ * @link      <>
+ */
+class Toolkit_Template_Page_SiteMap
+{
+    //    {{{    properties
+
+    /**
+     * PDO
+     *
+     * @var PDO
+     * @access private
+     */
+    private $_dbh;
+
+    /**
+     * description of $_pageGateway
+     *
+     * @var string
+     * @access private
+     */
+    private $_pageGateway;
+
+    //    }}}
+    //    {{{    __construct()
+
+    /**
+     * Class constructor
+     *
+     * @param PDO    $dbh          PDO
+     * @param string $pageGatweway Page Gateway
+     *
+     * @access public
+     */
+    public function __construct(PDO $dbh, $pageGatweway)
+    {
+        $this->_dbh         = $dbh;
+        $this->_pageGateway = $pageGatweway;
+    }
+
+    //    }}}
+
+    //    {{{    getContent()
+
+    /**
+     * Description of getContent()
+     *
+     * @return HTML_Template_Flexy
+     * @access public
+     */
+    public function getContent()
+    {
+        $GLOBALS['styleSheets'][] = MEDIA_BASE_URL . 'css/siteMap.css';
+        $navArray = $this->getNavArray(0);
+        $page     = new stdClass();
+        $tpl      = new HTML_Template_Flexy(
+            $GLOBALS['flexyOptions']
+        );
+        $page->nav = $navArray;
+        $tpl->compile('siteMap.html');
+        return $tpl->bufferedOutputObject($page);
+    }
+
+    //    }}}
+
+    /**
+     * Description of getNavArray
+     *
+     * @param string $parent Parent
+     *
+     * @return array
+     * @throws Toolkit_Template_Exception
+     * @access public
+     */
+    public function getNavArray($parent)
+    {
+        if (   defined('MEMBERS_CATEGORY')
+            && defined('MEMBERS_DB')
+            && MEMBERS_DB
+        ) {
+            $sql = "
+                SELECT id, navigation_name, parent, include_members
+                  FROM pages
+                 WHERE parent = :parent
+                   AND active = true
+                   AND id != " . MEMBERS_CATEGORY . "
+                 ORDER BY parent, pos";
+        } else {
+            $sql = "
+                SELECT id, navigation_name, parent
+                  FROM pages
+                 WHERE parent = :parent
+                   AND active = true
+                 ORDER BY parent, pos";
+        }
+
+        try {
+
+            $stmt = $this->_dbh->prepare($sql);
+            $stmt->bindParam(
+                ':parent',
+                $parent,
+                PDO::PARAM_INT
+            );
+            $stmt->execute();
+            while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                $nav[$row['id']] = array(
+                    'label' => $row['navigation_name'],
+                    'url'   => Toolkit_Template_Page::getSeoUrl(
+                        $this->_pageGateway,
+                        $row['id']
+                    )
+                );
+                $subs = array();
+                if ($row['include_members']) {
+                    $subs = $this->_getMembersForPage($row['id']);
+                }
+                $subs2 = $this->getNavArray($row['id']);
+                if ($subs2) {
+                    foreach ($subs2 as $subsub) {
+                        $subs[] = $subsub;
+                    }
+                }
+                if ($subs) {
+                    $nav[$row['id']]['subs'] = $subs;
+                }
+            }
+            return $nav;
+        } catch (PDOException $e) {
+            Toolkit_Logger::logException('DB Error', $e);
+            throw new Toolkit_Template_Exception(
+                'Unable to fetch pages for sitemap'
+            );
+        }
+    }
+
+    private function _getMembersForPage($pageId)
+    {
+        $members    = array();
+        $memberCats = array();
+        $memberRegs = array();
+        try {
+            // get member categories for the page
+            $sql = "
+            SELECT *
+              FROM toolbox.member_categories2toolbox_pages
+             WHERE page = :page";
+            $stmt = $this->_dbh->prepare($sql);
+            $stmt->bindParam(":page", $pageId, PDO::PARAM_INT);
+            $stmt->execute();
+            while ($memberCategory = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                $memberCats[] = $memberCategory['category'];
+            }
+            // get member regions for the page
+            $sql = "
+            SELECT *
+              FROM toolbox.member_regions2toolbox_pages
+             WHERE page = :page";
+            $stmt = $this->_dbh->prepare($sql);
+            $stmt->bindParam(":page", $pageId, PDO::PARAM_INT);
+            $stmt->execute();
+            while ($memberRegions = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                $memberRegs[] = $memberRegions['region'];
+            }
+            // if they're both empty then get list of all members
+            $where = array("active");
+            if (!empty($memberCats)) {
+                $where[] = "member_id IN (
+                    SELECT member_id
+                      FROM member_category
+                      WHERE category_id IN (" . implode(',', $memberCats) . "))";
+            }
+            if (!empty($memberRegs)) {
+                $where[] = "(
+                    region IN (" . implode(',', $memberRegs) . ")
+                    OR
+                    region IS NULL)";
+
+            }
+            $sql = "
+            SELECT member_id,member_name
+              FROM member";
+            if (!empty($where)) {
+                $sql .= " WHERE " . implode(' AND ', $where);
+            }
+            $sql .= " ORDER BY member_name";
+            $stmt = $this->_dbh->query($sql);
+            while ($member = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                $memberName = str_replace(' ', '-', $member['member_name']);
+                $pattern = '/[\/#&?\'"]|amp;/';
+                $name = preg_replace(
+                    $pattern,
+                    '',
+                    strip_tags(strtolower(trim($memberName)))
+                );
+                $url = MEDIA_BASE_URL . "memberProfiles/"
+                . htmlspecialchars($name) . "-{$member['member_id']}.html";
+                $members[$member['member_id']] = array(
+                    'label' => $member['member_name'],
+                    'url'   => $url
+                );
+            }
+        } catch (PDOException $e) {
+            Toolkit_Logger::logException('DB Error', $e);
+            throw new Toolkit_Template_Exception(
+                'Unable to fetch members for sitemap page'
+            );
+        }
+        return $members;
+    }
+}
+?>
diff --git a/Toolkit/Template/Page/Toolbox.php b/Toolkit/Template/Page/Toolbox.php
new file mode 100644 (file)
index 0000000..1e50f90
--- /dev/null
@@ -0,0 +1,452 @@
+<?php
+/**
+ * Toolbox.php
+ *
+ * PHP version 5
+ *
+ * @category  Toolkit
+ * @package   Template/Page
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2009 Gaslight Media
+ * @license   Gaslight Media
+ * @version   CVS: $Id: ShortURL.php,v 1.5 2010/05/25 14:07:22 jamie Exp $
+ * @link      <>
+ */
+
+/**
+ * Toolkit_Template_Page_Toolbox
+ *
+ * Description of Toolkit_Template_Page_Toolbox
+ *
+ * @category  Toolkit
+ * @package   Template/Page
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @release   Release: $id$
+ * @link      <>
+ */
+class Toolkit_Template_Page_Toolbox implements Toolkit_Template_Page_IBody
+{
+       //      {{{     properties
+
+
+    /**
+     * Description of $id
+     *
+     * @var int
+     * @access protected
+     */
+       protected $id;
+
+    /**
+     * Description of $imageFactory
+     *
+     * @var Toolkit_Template_Image_Factory
+     * @access protected
+     */
+       protected $imageFactory;
+
+    /**
+     * Description of $breadCrumbsFactory
+     *
+     * @var Toolkit_BreadCrumbsFactory
+     * @access protected
+     */
+       protected $breadCrumbsFactory;
+
+    /**
+     * Description of $pageGateway
+     *
+     * @var Toolkit_Toolbox_PageGatewayAbstract
+     * @access protected
+     */
+       protected $pageGateway;
+
+    /**
+     * Description of $paragraphGateway
+     *
+     * @var Toolkit_Toolbox_ParagraphGatewayAbstract
+     * @access protected
+     */
+       protected $paragraphGateway;
+
+    /**
+     * Description of $keywordReplacement
+     *
+     * @var Toolkit_Template_KeywordReplacement
+     * @access protected
+     */
+       protected $keywordReplacement;
+
+    /**
+     * Description of $cache
+     *
+     * @var Cache_Lite
+     * @access protected
+     */
+       protected $cache;
+
+    /**
+     * Description of $photoGallery
+     *
+     * @var Toolkit_Photos_Gallery
+     * @access protected
+     */
+       protected $photoGallery;
+
+       //      }}}
+       //      {{{     __construct()
+
+    /**
+     * Class constructor
+     *
+     * @param int                                      $id                 ID
+     * @param Toolkit_Template_Image_Factory           $imageFactory       imageFactory
+     * @param Toolkit_BreadCrumbsFactory               $breadCrumbsFactory breadCrumbsFactory
+     * @param Toolkit_Toolbox_PageGatewayAbstract      $pageGateway        Page Gateway
+     * @param Toolkit_Toolbox_ParagraphGatewayAbstract $paragraphGateway   Paragraph Gateway
+     * @param Toolkit_Template_KeywordReplacement      $keywordReplacement Keyword Replacement
+     * @param Cache_Lite                               $cache              Cache
+     * @param Toolkit_Photos_Gallery                   $photoGallery       Photo Gallery
+     *
+     * @throws RuntimeException
+     * @access public
+     */
+       public function __construct(
+               $id,
+               Toolkit_Template_Image_Factory $imageFactory,
+               Toolkit_BreadCrumbsFactory $breadCrumbsFactory,
+               Toolkit_Toolbox_PageGatewayAbstract $pageGateway,
+               Toolkit_Toolbox_ParagraphGatewayAbstract $paragraphGateway,
+               Toolkit_Template_KeywordReplacement $keywordReplacement,
+        Cache_Lite $cache,
+               Toolkit_Photos_Gallery $photoGallery = null
+       ) {
+               if (!filter_var($id, FILTER_VALIDATE_INT)) {
+                       throw new RuntimeException(
+                               "`$id` is not a valid page id - integer required"
+                       );
+               }
+               $this->id                 = $id;
+               $this->imageFactory       = $imageFactory;
+               $this->breadCrumbsFactory = $breadCrumbsFactory;
+               $this->pageGateway        = $pageGateway;
+               $this->paragraphGateway   = $paragraphGateway;
+               $this->keywordReplacement = $keywordReplacement;
+               $this->cache              = $cache;
+               $this->photoGallery       = $photoGallery;
+       }
+
+       //      }}}
+
+       //      {{{     getContent()
+
+    /**
+     * Get content
+     *
+     * @return string
+     * @access public
+     */
+       public function getContent()
+       {
+               $breadCrumbsBuilder  = $this->breadCrumbsFactory->createBreadCrumbsHelper();
+               $breadCrumbs         = $breadCrumbsBuilder->toHtml($this->id);
+               $secondaryParagraphs = $this->getSecondaryParagraphs($this->id);
+               $primaryParagraph    = $this->getPrimaryParagraph($this->id);
+        $coupons = null;
+               if (defined('COUPONS') && COUPONS) {
+                       $coupons = $this->_getPageCoupons($this->id);
+               }
+               $staticPageContent   = $this->getStaticPageContent($this->id);
+
+               $photoGalleryContent = null;
+               if (defined('PHOTO_GALLERY') && PHOTO_GALLERY) {
+                       $photoGalleryContent = $this->photoGallery->getPageGallery($this->id);
+               }
+
+               return $breadCrumbs .
+                          $primaryParagraph .
+                          $staticPageContent .
+                          $secondaryParagraphs .
+                          $coupons .
+                          $photoGalleryContent;
+       }
+
+       //      }}}
+
+       //      {{{     _getPageCoupons()
+
+    /**
+     * Description for _getPageCoupons
+     *
+     * @param int $id ID
+     *
+     * @return string|void
+     * @access private
+     */
+       private function _getPageCoupons($id)
+       {
+               $page = $this->pageGateway->find($id);
+               if (!$page['include_coupons']) {
+                       return;
+               }
+
+               $widget = new Toolkit_Coupons_PageWidget(
+                       new Toolkit_Coupons_WebCouponFactory(),
+                       new Toolkit_Coupons_Coupons()
+               );
+
+               $flexyOpts = Toolkit_Coupons_Controller::getFlexyOptions();
+               $tEngine = new HTML_Template_Flexy($flexyOpts);
+
+               $widget->addPageCategories(
+                       Toolkit_Database::getInstance(),
+                       $id
+               );
+
+               $searchForm = new Toolkit_Coupons_UserSearch(
+                       'coupon_search',
+                       'get',
+                       MEDIA_BASE_URL . 'index.php'
+               );
+               $searchForm->configureForm(
+                       Toolkit_Database::getInstance(),
+                       $id
+               );
+
+               $widget->setSearchForm($searchForm);
+
+               $couponForm = new HTML_QuickForm(
+                       'coupon_form',
+                       'post',
+                       MEDIA_BASE_URL . 'Toolkit/Coupons/print.php'
+               );
+               $renderer =& $couponForm->defaultRenderer();
+               $renderer->clearAllTemplates();
+               return $widget->toHtml($couponForm, $tEngine, 'coupons.html');
+       }
+
+       //      }}}
+       //      {{{     getParagraphFiles()
+
+    /**
+     * Description for getParagraphFiles()
+     *
+     * @param array $files Files array
+     *
+     * @return void|string
+     * @access protected
+     */
+       protected function getParagraphFiles($files)
+       {
+               if (!is_array($files)) {
+                       return;
+               }
+
+               $filesList = '';
+               foreach ($files as $file) {
+                       $linkRenderer = Toolkit_Template_Page_FileLink_Factory::createLinkRenderer($file);
+                       $filesList .= $linkRenderer->getLink();
+               }
+
+               return $filesList;
+       }
+
+       //      }}}
+       //      {{{     getParagraphImage()
+
+    /**
+     * Description for getParagraphImage()
+     *
+     * @param array  $data Image data
+     * @param string $side Side image is on
+     *
+     * @return string Through Toolkit/Template/Image  /Left or /Right or /Null
+     * @access protected
+     */
+       protected function getParagraphImage(array $data, $side)
+       {
+               $imageBuilder = $this->imageFactory->getImageBuilder($data, $side);
+               return $imageBuilder->getImage(TOOLBOX_RESIZED);
+       }
+
+       //      }}}
+       //      {{{     getParagraphLinks()
+
+    /**
+     * Description for getParagraphLinks()
+     *
+     * @return boolean|string
+     * @access protected
+     */
+       protected function getParagraphLinks()
+       {
+               if (!is_array($this->paragraphLinks)) {
+                       return false;
+               }
+
+               $links  = '<ul id="paragraphLinks">';
+               foreach ($this->paragraphLinks as $anchor) {
+                       $links .= "<li>$anchor</li>";
+               }
+               $links .= '</ul>';
+
+               return $links;
+       }
+
+       //      }}}
+       //      {{{     getPrimaryParagraph()
+
+    /**
+     * Description for getPrimaryParagraph()
+     *
+     * @param int $id ID
+     *
+     * @return string
+     * @access protected
+     */
+       protected function getPrimaryParagraph($id)
+       {
+               $cache = $this->cache->get("page-$id", 'Toolbox');
+
+               if ($cache) {
+                       return $cache;
+               } else {
+                       $page = $this->pageGateway->find($id);
+
+                       switch ($page['template']) {
+                       case '2' :
+                       case '4' :
+                       case '5' :
+                               $side = 'left';
+                               break;
+
+                       default :
+                               $side = 'right';
+                               break;
+                       }
+
+                       $html  = '<div id="category">';
+                       $html .= "<h1>{$page['title']}</h1>";
+                       if ($page['paragraph_links']) {
+                               $html .= $this->getParagraphLinks();
+                       }
+                       $html .= $this->getParagraphImage($page, $side);
+                       $html .= $this->keywordReplacement->findAndReplace($page['description']);
+                       $html .= '</div>';
+
+                       $this->cache->save($html, "page-$id", 'Toolbox');
+               }
+
+               return $html;
+       }
+
+       //      }}}
+       //      {{{     getSecondaryParagraphs()
+
+    /**
+     * Description for getSecondaryParagraphs()
+     *
+     * @param int $id ID
+     *
+     * @return string
+     * @throws Toolkit_Template_Exception
+     * @access protected
+     */
+       protected function getSecondaryParagraphs($id)
+       {
+               $cache = $this->cache->get("paragraphs-$id", 'Toolbox');
+
+               if ($cache) {
+                       return $cache;
+               } else {
+                       $page       = $this->pageGateway->find($id);
+                       $paragraphs = $this->paragraphGateway->findAll($id);
+
+                       $side = array('left', 'right');
+                       switch ($page['template']) {
+                       case '2' :
+                       case '3' :
+                       case '6' :
+                               $key = 0;
+                               $flip = ($page['template'] == 3);
+                               if ($page['templte'] == '3') {
+                                       $flip = true;
+                               }
+                               break;
+
+                       case '1' :
+                       case '4' :
+                       case '5' :
+                               $key = 1;
+                               $flip = ($page['template'] == 4);
+                               break;
+
+                       default :
+                               throw new Toolkit_Template_Exception(
+                                       "Invalid page template for page `$id`"
+                               );
+                               break;
+                       }
+
+                       $html = '';
+                       $linksFormat = '<a href="#sect-%s">%s</a>';
+                       foreach ($paragraphs as $paragraph) {
+                               if ($paragraph['active']) {
+                                       $html .= '<div class="listing" id="sect-'.$paragraph['id'].'">';
+                                       $html .= "<h2>{$paragraph['title']}</h2>";
+                                       $html .= $this->getParagraphImage($paragraph, $side[$key]);
+                                       $html .= $this->keywordReplacement->findAndReplace($paragraph['description']);
+
+                                       $html .= $this->getParagraphFiles($paragraph['files']);
+
+                                       if ($paragraph['back_to_top']) {
+                                               $html .= '<a href="#toolbox">Back to Top</a>';
+                                       }
+
+                                       $html .= '</div>';
+
+                                       $this->paragraphLinks[] = sprintf(
+                                               $linksFormat,
+                                               $paragraph['id'],
+                                               $paragraph['title']
+                                       );
+                                       $key = $flip ? !$key : $key;
+                               }
+                       }
+
+                       $this->cache->save($html, "paragraphs-$id", 'Toolbox');
+                       return $html;
+               }
+       }
+
+       //      }}}
+
+       //      {{{     getStaticPageContent()
+
+    /**
+     * Description for getStaticPageCOntent()
+     *
+     * @param unknown $pageId PageID
+     *
+     * @return string
+     * @access protected
+     */
+    protected function getStaticPageContent($pageId)
+    {
+        $html = '';
+
+        if (file_exists(BASE . "static/$pageId.phtml")) {
+            ob_start();
+            include BASE . "static/$pageId.phtml";
+            $html = ob_get_contents();
+            ob_end_clean();
+        }
+
+        return $html;
+    }
+
+       //      }}}
+}
+?>
diff --git a/Toolkit/Template/PageControllerAbstract.php b/Toolkit/Template/PageControllerAbstract.php
new file mode 100644 (file)
index 0000000..526bbe5
--- /dev/null
@@ -0,0 +1,142 @@
+<?php
+/**
+ * PageControllerAbstract.php
+ *
+ * PHP version 5
+ *
+ * @category  Toolkit
+ * @package   Template
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2009 Gaslight Media
+ * @license   Gaslight Media
+ * @version   CVS: $Id: ShortURL.php,v 1.5 2010/05/25 14:07:22 jamie Exp $
+ * @link      <>
+ */
+
+/**
+ * Toolkit_Template_PageControllerAbstract
+ *
+ * Description of Toolkit_Template_PageControllerAbstract
+ *
+ * @category  Toolkit
+ * @package   Template
+ * @author    Jamie Kahgee <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @release   Release: $id$
+ * @link      <>
+ */
+abstract class Toolkit_Template_PageControllerAbstract
+    extends Toolkit_BaseControllerAbstract implements Toolkit_IController
+{
+    /**
+     * Description for getPageGatewayFactory()
+     *
+     * @access protected
+     * @return unknown
+     */
+    abstract protected function getPageGatewayFactory();
+
+    /**
+     * Description for getParagraphGatewayFactory()
+     *
+     * @access protected
+     * @return unknown
+     */
+    abstract protected function getParagraphGatewayFactory();
+
+    /**
+     * Description for indexAction()
+     *
+     * @return void
+     * @access public
+     */
+    public function indexAction()
+    {
+        //    Initiate HTML_Template_Flexy.
+        $template = new HTML_Template_Flexy($this->registry->flexyOptions);
+        $keywordReplacement = new Toolkit_Template_KeywordReplacement(
+            new Toolkit_Toolbox_PageGatewayPublish(
+                $this->registry->dbh
+            )
+        );
+        $breadCrumbsFactory = new Toolkit_BreadCrumbsFactory(
+            $this->getPageGatewayFactory()
+        );
+        //    Page object used for merging with the flexy template object.
+        //    now using the page class from toolkit
+        $glmPage = new Toolkit_Page(
+            new Toolkit_Template_Page(),
+            $breadCrumbsFactory,
+            $this->getPageGatewayFactory(),
+            $this->getParagraphGatewayFactory(),
+            new Toolkit_Template_Navigation_Factory(),
+            $keywordReplacement,
+            $this->registry->catid
+        );
+        $glmPage->fetchPage();
+        if ($memberId = filter_var($_REQUEST['member_id'], FILTER_VALIDATE_INT)) {
+            // get member name and put in title
+            $sql = "
+            SELECT member_name,description
+            FROM   member
+            WHERE  member_id = {$memberId}";
+            if ($member = $this->registry->dbh->query($sql)->fetch(PDO::FETCH_ASSOC)) {
+                $memberName = htmlentities(strip_tags($member['member_name']));
+                $glmPage->pageTitle = $memberName.' - '.$glmPage->pageTitle;
+                $glmPage->metaTags
+                    = htmlentities(
+                        substr(
+                            trim(strip_tags($member['description'])), 0, 250
+                        ), ENT_QUOTES, 'UTF-8'
+                    );
+                $memberName = str_replace(' ', '-', $memberName);
+                $pattern    = '/[\/#&?\'"]|amp;/';
+                $name       = preg_replace(
+                    $pattern,
+                    '',
+                    strip_tags(strtolower(trim($memberName)))
+                );
+                $glmPage->canonicalUrl = BASE_URL . "memberProfiles/"
+                    . htmlspecialchars($name) . "-{$memberId}.html";
+            }
+        }
+
+        if ($eventId = filter_var($_REQUEST['eventid'], FILTER_VALIDATE_INT)) {
+            // get member name and put in title
+            $sql = "
+            SELECT header,description
+            FROM   events.events
+            WHERE  id = {$eventId}";
+            if ($event = $this->registry->dbh->query($sql)->fetch(PDO::FETCH_ASSOC)) {
+                $eventName = htmlentities(strip_tags($event['header']));
+                $glmPage->pageTitle = $eventName.' - '.$glmPage->pageTitle;
+                $glmPage->metaTags
+                    = htmlentities(
+                        substr(
+                            trim(strip_tags($event['description'])), 0, 250
+                        ), ENT_QUOTES, 'UTF-8'
+                    );
+                $eventName = str_replace(' ', '-', $eventName);
+                $pattern    = '/[\/#&?\'"]|amp;/';
+                $name       = preg_replace(
+                    $pattern,
+                    '',
+                    strip_tags(strtolower(trim($eventName)))
+                );
+                $glmPage->canonicalUrl = BASE_URL . "events/"
+                    . $this->registry->catid . "/{$eventId}/";
+            }
+        }
+
+        $glmPage->topScripts    = Toolkit_Common::getScripts($GLOBALS['topScripts']);
+        $glmPage->bottomScripts = Toolkit_Common::getScripts($GLOBALS['bottomScripts']);
+        $glmPage->styles        = Toolkit_Common::getStyleSheets();
+
+        //    Compile the template.html from the templates directory.
+        $template->compile('template.html');
+        //    Merge compiled template with the $glmPage object.
+        $template->outputObject($glmPage);
+    }
+}
+?>
diff --git a/Toolkit/Toolbox/BreadCrumbsAbstract.php b/Toolkit/Toolbox/BreadCrumbsAbstract.php
new file mode 100644 (file)
index 0000000..c32fb66
--- /dev/null
@@ -0,0 +1,119 @@
+<?php
+
+abstract class Toolkit_Toolbox_BreadCrumbsAbstract
+{
+       //      {{{     properties
+
+    protected $dbh;
+    protected $id;
+    protected $path;
+
+       //      }}}
+       //      {{{     __construct()
+
+    public function __construct(PDO $dbh, $id)
+    {
+        $this->dbh = $dbh;
+
+        if (!ctype_digit((string)$id)) {
+            throw new InvalidArgumentException(
+                "\$id must be an integer `$id` given"
+            );
+        }
+
+        $this->id = $id;
+    }
+
+       //      }}}
+       //      {{{     getPage()
+
+    protected function getPage($id)
+    {
+        try {
+            $sql = "
+                SELECT *
+                  FROM pages
+                 WHERE id = :id";
+
+            $stmt = $this->dbh->prepare($sql);
+            $stmt->bindParam(':id', $id, PDO::PARAM_INT);
+            $stmt->execute();
+
+            return $stmt->fetch(PDO::FETCH_ASSOC);
+        } catch (PDOException $e) {
+            Toolkit_Logger::logException('DB Error', $e);
+            throw new Toolkit_Toolbox_Exception(
+                "Could not fetch parent for page `$id`"
+            );
+        }
+    }
+
+       //      }}}
+       //      {{{     __toString()
+
+    public function __toString()
+    {
+        return '<div id="breadcrumbs">' . $this->getPath() . '</div>';
+    }
+
+       //      }}}
+       //      {{{     getId()
+
+       /**
+        * @return the $id
+        */
+       public function getId()
+       {
+               return $this->id;
+       }
+
+       //      }}}
+       //      {{{     getPageUri()
+
+       abstract protected function getPageUri(array $page);
+
+       //      }}}
+       //      {{{     getPath()
+
+       /**
+        * @return the $path
+        */
+       public function getPath()
+       {
+               if ($this->id == HOME_ID) {
+                       return;
+               }
+
+           $id = $this->id;
+           $stack = array();
+           do {
+               $page = $this->getPage($id);
+
+               $navigationName = $this->id == $id
+                  ? $page['navigation_name']
+                  : $this->getPageUri($page);
+
+               $stack[] = $navigationName;
+               $id = $page['parent'];
+           } while ($id != 0);
+
+           $reverse = array_reverse($stack);
+        $this->path = implode(' > ', $reverse);
+
+               return $this->path;
+       }
+
+       //      }}}
+       //      {{{     setId()
+
+       /**
+        * @param $id the $id to set
+        */
+       public function setId($id)
+       {
+               $this->id = $id;
+       }
+
+       //      }}}
+}
+?>
diff --git a/Toolkit/Toolbox/Database/addIncludeCoupons.sql b/Toolkit/Toolbox/Database/addIncludeCoupons.sql
new file mode 100644 (file)
index 0000000..7dbf908
--- /dev/null
@@ -0,0 +1,19 @@
+--
+-- update the pages tables with new field for include_coupons
+-- and update the rule for history
+--
+
+ALTER TABLE toolbox.pages ADD include_coupons BOOLEAN;
+ALTER TABLE toolbox.pages ALTER include_coupons SET DEFAULT false;
+UPDATE toolbox.pages SET include_coupons = false;
+
+ALTER TABLE toolbox.pages_draft ADD include_coupons BOOLEAN;
+ALTER TABLE toolbox.pages_draft ALTER include_coupons SET DEFAULT false;
+UPDATE toolbox.pages_draft SET include_coupons = false;
+
+ALTER TABLE toolbox.pages_history ADD include_coupons BOOLEAN;
+ALTER TABLE toolbox.pages_history ALTER include_coupons SET DEFAULT false;
+UPDATE toolbox.pages_history SET include_coupons = false;
+
+
+\i ./procedures/pages_update.sql
diff --git a/Toolkit/Toolbox/Database/addIncludeMembers.sql b/Toolkit/Toolbox/Database/addIncludeMembers.sql
new file mode 100644 (file)
index 0000000..44fba29
--- /dev/null
@@ -0,0 +1,19 @@
+--
+-- update the pages tables with new field for include_members
+-- and update the rule for history
+--
+
+ALTER TABLE toolbox.pages ADD include_members BOOLEAN;
+ALTER TABLE toolbox.pages ALTER include_members SET DEFAULT false;
+UPDATE toolbox.pages SET include_members = false;
+
+ALTER TABLE toolbox.pages_draft ADD include_members BOOLEAN;
+ALTER TABLE toolbox.pages_draft ALTER include_members SET DEFAULT false;
+UPDATE toolbox.pages_draft SET include_members = false;
+
+ALTER TABLE toolbox.pages_history ADD include_members BOOLEAN;
+ALTER TABLE toolbox.pages_history ALTER include_members SET DEFAULT false;
+UPDATE toolbox.pages_history SET include_member = false;
+
+
+\i ./procedures/pages_update.sql
diff --git a/Toolkit/Toolbox/Database/application.sql b/Toolkit/Toolbox/Database/application.sql
new file mode 100644 (file)
index 0000000..02859ae
--- /dev/null
@@ -0,0 +1,33 @@
+CREATE SCHEMA toolbox;
+GRANT ALL ON SCHEMA toolbox TO nobody;
+
+--
+-- Tables
+--
+\i ./tables/pages.sql
+\i ./tables/pages_history.sql
+\i ./tables/pages_draft.sql
+\i ./tables/paragraphs.sql
+\i ./tables/paragraphs_history.sql
+\i ./tables/paragraphs_draft.sql
+\i ./tables/files.sql
+\i ./tables/files_history.sql
+\i ./tables/files_draft.sql
+
+--
+-- Procedures
+--
+\i ./procedures/pages_update.sql
+\i ./procedures/pages_pos.sql
+\i ./procedures/paragraphs_update.sql
+\i ./procedures/paragraphs_pos.sql
+\i ./procedures/paragraphs_draft_pos.sql
+\i ./procedures/check_paragraph_pos_consistency.sql
+-- \i ./procedures/check_pages_pos_consistency.sql
+\i ./procedures/files_update.sql
+\i ./procedures/files_pos.sql
+\i ./procedures/delete_subtree.sql
+
+--
+-- Modules
+--
diff --git a/Toolkit/Toolbox/Database/data/applicationData.sql b/Toolkit/Toolbox/Database/data/applicationData.sql
new file mode 100644 (file)
index 0000000..b3db82f
--- /dev/null
@@ -0,0 +1,32 @@
+INSERT INTO toolbox.pages(active, template, keyword, navigation_name, paragraph_links) VALUES (TRUE, 1, 'home', 'Home', TRUE);
+INSERT INTO toolbox.pages(active, template, parent, pos, navigation_name) VALUES(TRUE, 1, 0, 2, 'Parent');
+INSERT INTO toolbox.pages(active, template, parent, pos, navigation_name) VALUES(TRUE, 1, 2, 1, 'Child');
+INSERT INTO toolbox.pages(active, template, parent, pos, navigation_name) VALUES(TRUE, 1, 2, 2, 'Child Sibling');
+INSERT INTO toolbox.pages(active, template, parent, pos, navigation_name) VALUES(TRUE, 1, 3, 1, 'Grand Child');
+INSERT INTO toolbox.pages(active, template, parent, pos, navigation_name) VALUES(TRUE, 1, 3, 2, 'Grand Child Sibling');
+INSERT INTO toolbox.pages(active, template, parent, pos, navigation_name) VALUES(TRUE, 1, 0, 3, 'Sibling');
+
+INSERT INTO toolbox.paragraphs(active, title, description, image, caption, page, pos) VALUES(TRUE, 'Lorem Ipsum Delor', '<p>Dictumst quis adipiscing etiam odio et augue, ut, cum cras adipiscing dolor cursus eros in integer in enim, aliquet in, amet tempor porta scelerisque, a eros sociis nec, porta in, lacus lacus, integer phasellus vut sit? Massa turpis porttitor. In, enim pulvinar? Ac et, ac nascetur porttitor magnis natoque duis! Nisi, ac et, aliquet? Cras tempor dignissim! Adipiscing cursus vel, integer dis penatibus vut! Et? Nec lundium lacus nunc nunc tempor a pid cum turpis magnis urna est eu rhoncus porttitor mauris ridiculus eu! Dolor pid porta ac dis lorem phasellus, tincidunt eros montes auctor adipiscing? Sed adipiscing risus scelerisque scelerisque ridiculus tortor, ut aliquam. Aenean enim et. Lectus enim dapibus scelerisque eros elementum nec, sagittis arcu. Purus augue ac.</p>
+<p> Enim tincidunt pid, augue est magna velit lacus in tincidunt cras sed tempor! Sed. Dis adipiscing odio, urna mus! Natoque arcu elit. Dapibus natoque a ridiculus scelerisque nisi cras. Phasellus ultricies natoque augue sagittis mauris adipiscing porta pulvinar integer! Lacus adipiscing sit porta ultricies mus natoque amet porta etiam, mauris. Egestas! Et urna! Platea risus magnis porta magnis in! Integer turpis cursus enim ac, ac? Phasellus enim. Cras enim! Lectus pulvinar, ac porta elementum nec, vut elementum pulvinar dignissim elementum porta placerat porttitor a lundium, turpis velit et mattis augue hac tempor adipiscing, magnis, lectus, pulvinar diam. Scelerisque scelerisque. Nunc sagittis ut proin, tincidunt ultrices velit parturient, sit vel enim. Porta? Ac non! Magna tincidunt arcu augue aenean eros.  </p>
+<p> Placerat et placerat massa aliquam aliquet scelerisque, urna lorem! Adipiscing rhoncus, pid natoque turpis, auctor. Placerat sit, non sed, mattis augue dis placerat amet elementum vel habitasse, placerat. Etiam cum magnis arcu nisi a proin mus sit tortor montes! Integer aliquam, pellentesque sed sit. Dis mattis magnis! Enim non tincidunt cursus urna urna magna etiam? Facilisis ultricies dapibus porttitor, turpis ac et placerat urna, urna turpis vel, porta purus ac non! Nunc lorem? Tincidunt quis sagittis ridiculus sit, turpis? A ut ac rhoncus. Aenean tristique a tincidunt. Ac in odio dis et nascetur, est proin. Nisi, aliquam elementum, sed. Nunc sociis. Ut enim, ultricies nascetur a magna porta duis ut tincidunt! Aenean, augue cursus tristique ridiculus, integer eros dolor.</p>', '', '', 1, 1);
+INSERT INTO toolbox.paragraphs(active, title, description, image, caption, back_to_top, page, pos) VALUES(TRUE, 'Paragraph A', '<p>Sit, et odio, risus proin adipiscing mattis! Est! Non lorem, et scelerisque sociis! Ac? Porttitor in, pid auctor, etiam hac sagittis dignissim scelerisque elementum? Nisi. Auctor in, lacus, enim vut, ac eros magna cursus nisi et! Vut tortor natoque rhoncus augue est tincidunt tortor in sed elementum, tristique purus. Nec nascetur porta adipiscing cum. Diam porttitor lorem hac parturient cras, pulvinar integer! Vel ac, rhoncus ridiculus ac magna est integer, ut ultrices! Pulvinar hac lundium pid pulvinar dapibus et rhoncus! Montes et pulvinar vut. Sit porta arcu turpis! Dictumst, proin pulvinar et turpis tristique odio aliquam, est odio hac dis? Elit elementum integer adipiscing turpis tincidunt massa? Habitasse dignissim lectus porta, nunc, aliquam est? Integer enim? Lectus, dignissim natoque, turpis.</p>', '', '', TRUE, 1, 1);
+INSERT INTO toolbox.paragraphs(active, title, description, image, caption, back_to_top, page, pos) VALUES(TRUE, 'Paragraph B', '<p>Tortor adipiscing ut magna cras elit elit elit, dis! Eros pid scelerisque, aenean sagittis odio tristique elementum sed sit, porta ridiculus! Eros lundium integer? Integer, pulvinar sociis mid! Enim, magna velit mattis et ultrices egestas sed egestas non pulvinar etiam! Urna porta et pulvinar lorem sed aliquam, ridiculus. Dolor aenean integer enim, amet porta enim penatibus, proin a ut tincidunt, turpis magnis lectus quis enim aliquet, mauris in vel quis! Facilisis dolor. Urna eu, amet quis, mattis sed! Phasellus lorem! Non platea. Sit elit sed proin mus, mauris mauris dignissim, tincidunt et augue placerat tristique elementum purus lundium a cursus platea pid, ultricies platea? Elit sed eu diam, elementum sit rhoncus massa, lorem hac, urna natoque sit a tincidunt vel.</p>', '', '', TRUE, 1, 1);
+INSERT INTO toolbox.paragraphs(active, title, description, image, caption, back_to_top, page, pos) VALUES(TRUE, 'Paragraph C', '<p>Porta, augue facilisis, arcu massa enim massa phasellus nisi elementum, aliquet sit phasellus sed nunc turpis, augue cum, cum lundium, ridiculus, vut sociis auctor! Tincidunt pulvinar, tempor eu mid amet? Odio, lacus ultrices, nunc purus, cursus in diam mattis et penatibus. Dolor. Elementum pid tincidunt magna penatibus risus, odio aliquet. Dapibus amet eros enim diam? Elit, urna elit porttitor platea. Nascetur proin odio habitasse aenean magnis? Facilisis nascetur? A elementum, lorem diam, phasellus phasellus montes magna proin? Habitasse risus, vel porta mauris tortor velit, egestas tortor augue. Vut sed placerat aliquet enim diam porttitor dignissim, montes, mattis enim! Egestas ultrices? Nec porta lundium dapibus! Dictumst turpis augue integer, proin nec lectus dictumst! Platea urna, rhoncus hac ac adipiscing a augue.</p>', '', '', TRUE, 1, 1);
+INSERT INTO toolbox.paragraphs(active, title, description, image, caption, page, pos) VALUES(TRUE, 'Parent Title', '<p>Dictumst quis adipiscing etiam odio et augue, ut, cum cras adipiscing dolor cursus eros in integer in enim, aliquet in, amet tempor porta scelerisque, a eros sociis nec, porta in, lacus lacus, integer phasellus vut sit? Massa turpis porttitor. In, enim pulvinar? Ac et, ac nascetur porttitor magnis natoque duis! Nisi, ac et, aliquet? Cras tempor dignissim! Adipiscing cursus vel, integer dis penatibus vut! Et? Nec lundium lacus nunc nunc tempor a pid cum turpis magnis urna est eu rhoncus porttitor mauris ridiculus eu! Dolor pid porta ac dis lorem phasellus, tincidunt eros montes auctor adipiscing? Sed adipiscing risus scelerisque scelerisque ridiculus tortor, ut aliquam. Aenean enim et. Lectus enim dapibus scelerisque eros elementum nec, sagittis arcu. Purus augue ac.</p>', '', '', 2, 1);
+INSERT INTO toolbox.paragraphs(active, title, description, image, caption, back_to_top, page, pos) VALUES(TRUE, 'Parent Paragraph A', '<p>Sit, et odio, risus proin adipiscing mattis! Est! Non lorem, et scelerisque sociis! Ac? Porttitor in, pid auctor, etiam hac sagittis dignissim scelerisque elementum? Nisi. Auctor in, lacus, enim vut, ac eros magna cursus nisi et! Vut tortor natoque rhoncus augue est tincidunt tortor in sed elementum, tristique purus. Nec nascetur porta adipiscing cum. Diam porttitor lorem hac parturient cras, pulvinar integer! Vel ac, rhoncus ridiculus ac magna est integer, ut ultrices! Pulvinar hac lundium pid pulvinar dapibus et rhoncus! Montes et pulvinar vut. Sit porta arcu turpis! Dictumst, proin pulvinar et turpis tristique odio aliquam, est odio hac dis? Elit elementum integer adipiscing turpis tincidunt massa? Habitasse dignissim lectus porta, nunc, aliquam est? Integer enim? Lectus, dignissim natoque, turpis.</p>', '', '', TRUE, 2, 1);
+INSERT INTO toolbox.paragraphs(active, title, description, image, caption, back_to_top, page, pos) VALUES(TRUE, 'Parent Paragraph B', '<p>Tortor adipiscing ut magna cras elit elit elit, dis! Eros pid scelerisque, aenean sagittis odio tristique elementum sed sit, porta ridiculus! Eros lundium integer? Integer, pulvinar sociis mid! Enim, magna velit mattis et ultrices egestas sed egestas non pulvinar etiam! Urna porta et pulvinar lorem sed aliquam, ridiculus. Dolor aenean integer enim, amet porta enim penatibus, proin a ut tincidunt, turpis magnis lectus quis enim aliquet, mauris in vel quis! Facilisis dolor. Urna eu, amet quis, mattis sed! Phasellus lorem! Non platea. Sit elit sed proin mus, mauris mauris dignissim, tincidunt et augue placerat tristique elementum purus lundium a cursus platea pid, ultricies platea? Elit sed eu diam, elementum sit rhoncus massa, lorem hac, urna natoque sit a tincidunt vel.</p>', '', '', TRUE, 2, 1);
+INSERT INTO toolbox.paragraphs(active, title, description, page, pos) VALUES(TRUE, 'Child Title', 'Child Description', 3, 1);
+INSERT INTO toolbox.paragraphs(active, title, description, page, pos) VALUES(TRUE, 'Sibling Title', 'Sibling Description', 7, 1);
+INSERT INTO toolbox.paragraphs(active, title, page, pos) VALUES(TRUE, 'Sibling Paragraph A', 7, 1);
+INSERT INTO toolbox.paragraphs(active, title, page, pos) VALUES(TRUE, 'Sibling Paragraph B', 7, 1);
+INSERT INTO toolbox.paragraphs(active, title, page, pos) VALUES(TRUE, 'p Paragraph', 4, 1);
+INSERT INTO toolbox.paragraphs(active, title, page, pos) VALUES(TRUE, 'p Paragraph', 5, 1);
+INSERT INTO toolbox.paragraphs(active, title, page, pos) VALUES(TRUE, 'p Paragraph', 6, 1);
+
+
+INSERT INTO members.city(state_id, city_name) VALUES (34, 'Cary');
+INSERT INTO members.city(state_id, city_name) VALUES (23, 'Petoskey');
+
+INSERT INTO members.member(member_name, member_login, member_passwd, street, lat, lon, phone, process_email, url, city_id, state_id, zip, member_contact_email, mailing_address, primary_contact_fname, primary_contact_lname, active, mailing_city, mailing_state_id, mailing_zip, new_member) values('1111 AAA Test', 'uname', 'pword', '120 E. Lake St.', 45.374893, -84.958404, '(231) 487-0692', 'jamie@gaslightmedia.com', 'http://www.gaslightmedia.com', 3, 23, 49770, 'jamie@gaslightmedia.com', '139 Dove Cottage Ln.', 'Jamie', 'Kahgee', true, 'Cary', 34, 27519, false);
+
+INSERT INTO coupons.coupons(title, url, sdate, edate, expiration, category, active, member) VALUES ('Coupon Test', 'http://www.google.com', '2010-04-22', '2010-10-22', '2010-10-22', 'Accommodations', true, 1);
diff --git a/Toolkit/Toolbox/Database/procedures/check_pages_pos_consistency.sql b/Toolkit/Toolbox/Database/procedures/check_pages_pos_consistency.sql
new file mode 100644 (file)
index 0000000..085ff72
--- /dev/null
@@ -0,0 +1,38 @@
+--DROP LANGUAGE IF EXISTS plpgsql RESTRICT;
+CREATE LANGUAGE plpgsql;
+
+DROP TRIGGER IF EXISTS check_pages_pos_consistency ON toolbox.pages;
+
+--
+-- Function and trigger checking for duplicate positions when a paragraph
+-- has been inserted or updated.
+--
+-- This trigger only happens after an insert or update and is initially
+-- deferred which allows us to wrap our movements in a transaction and
+-- examine the results when we are done
+--
+-- This is a final check to help ensure data integrity
+--
+CREATE OR REPLACE FUNCTION toolbox.check_pages_pos_consistency() RETURNS TRIGGER AS $check_pages_pos_consistency$
+       DECLARE
+               duplicates INTEGER;
+       BEGIN
+
+        SELECT
+          INTO duplicates count(*)
+          FROM toolbox.pages
+         WHERE parent = NEW.parent
+           AND pos  = NEW.pos;
+
+               IF (duplicates > 1) THEN
+                       RAISE EXCEPTION 'Duplicate page positions!';
+               END IF;
+
+               RETURN NULL;
+       END;
+$check_pages_pos_consistency$ LANGUAGE plpgsql;
+
+CREATE CONSTRAINT TRIGGER trigger_pages_pos_check_consistency
+AFTER INSERT OR UPDATE ON toolbox.pages
+       DEFERRABLE INITIALLY DEFERRED
+       FOR EACH ROW EXECUTE PROCEDURE toolbox.check_pages_pos_consistency();
diff --git a/Toolkit/Toolbox/Database/procedures/check_paragraph_pos_consistency.sql b/Toolkit/Toolbox/Database/procedures/check_paragraph_pos_consistency.sql
new file mode 100644 (file)
index 0000000..a6b46ae
--- /dev/null
@@ -0,0 +1,38 @@
+--DROP LANGUAGE IF EXISTS plpgsql RESTRICT;
+CREATE LANGUAGE plpgsql;
+
+DROP TRIGGER IF EXISTS check_paragraphs_pos_consistency ON toolbox.paragraphs;
+
+--
+-- Function and trigger checking for duplicate positions when a paragraph
+-- has been inserted or updated.
+--
+-- This trigger only happens after an insert or update and is initially
+-- deferred which allows us to wrap our movements in a transaction and
+-- examine the results when we are done
+--
+-- This is a final check to help ensure data integrity
+--
+CREATE OR REPLACE FUNCTION toolbox.check_paragraphs_pos_consistency() RETURNS TRIGGER AS $check_paragraphs_pos_consistency$
+       DECLARE
+               duplicates INTEGER;
+       BEGIN
+
+        SELECT
+          INTO duplicates count(*)
+          FROM toolbox.paragraphs
+         WHERE page = NEW.page
+           AND pos  = NEW.pos;
+
+               IF (duplicates > 1) THEN
+                       RAISE EXCEPTION 'Duplicate page positions!';
+               END IF;
+
+               RETURN NULL;
+       END;
+$check_paragraphs_pos_consistency$ LANGUAGE plpgsql;
+
+CREATE CONSTRAINT TRIGGER trigger_paragraphs_pos_check_consistency
+AFTER INSERT OR UPDATE ON toolbox.paragraphs
+       DEFERRABLE INITIALLY DEFERRED
+       FOR EACH ROW EXECUTE PROCEDURE toolbox.check_paragraphs_pos_consistency();
diff --git a/Toolkit/Toolbox/Database/procedures/delete_subtree.sql b/Toolkit/Toolbox/Database/procedures/delete_subtree.sql
new file mode 100644 (file)
index 0000000..55b1781
--- /dev/null
@@ -0,0 +1,67 @@
+DROP TABLE IF EXISTS WorkingTable;
+CREATE LOCAL TEMPORARY TABLE WorkingTable
+(id INTEGER NOT NULL)
+ON COMMIT DELETE ROWS;
+
+CREATE OR REPLACE FUNCTION toolbox.delete_subtree (IN dead_guy INTEGER) RETURNS void AS $$
+       DECLARE
+               old_pos INTEGER; -- current position of the target node
+               old_par INTEGER; -- current parent of the target node
+       BEGIN
+               -- defer consraint that forbids (parent = id)
+               SET CONSTRAINTS ALL DEFERRED;
+
+               --       set the old_pos and old_par variable
+        SELECT pos
+          INTO old_pos
+          FROM toolbox.pages
+         WHERE id = dead_guy;
+
+        SELECT parent
+          INTO old_par
+          FROM toolbox.pages
+         WHERE id = dead_guy;
+
+               -- mark root of subtree and immediate subordinates
+               UPDATE toolbox.pages
+                  SET id  = CASE WHEN id = dead_guy
+                                        THEN -99999 ELSE id END,
+                          parent = CASE WHEN parent = dead_guy
+                                               THEN -99999 ELSE parent END
+                WHERE dead_guy IN (id, parent);
+
+               WHILE EXISTS -- mark leaf nodes
+                        (SELECT *
+                          FROM toolbox.pages
+                         WHERE parent = -99999
+                               AND id > -99999)
+               LOOP -- get list of next level subordinates
+
+                               DELETE FROM WorkingTable;
+                               INSERT INTO WorkingTable
+                               SELECT id FROM toolbox.pages WHERE parent = -99999;
+
+                       -- delete old markers so we don't keep an infinite loop
+                        DELETE FROM toolbox.pages
+                         WHERE parent = -99999;
+
+                       -- mark next level of subordinates
+                        UPDATE toolbox.pages
+                               SET parent = -99999
+                         WHERE parent IN (SELECT id FROM WorkingTable);
+
+               END LOOP;
+
+               -- delete all marked nodes
+               DELETE FROM toolbox.pages
+                WHERE id = -99999;
+
+               -- reset all the positions at the target nodes level
+        UPDATE toolbox.pages
+           SET pos    = pos - 1
+         WHERE parent = old_par
+           AND pos    > old_pos;
+
+               SET CONSTRAINTS ALL IMMEDIATE;
+       END;
+$$ LANGUAGE plpgsql;
diff --git a/Toolkit/Toolbox/Database/procedures/files_pos.sql b/Toolkit/Toolbox/Database/procedures/files_pos.sql
new file mode 100644 (file)
index 0000000..d0a27a6
--- /dev/null
@@ -0,0 +1,28 @@
+--DROP LANGUAGE IF EXISTS plpgsql RESTRICT;
+CREATE LANGUAGE plpgsql;
+
+DROP TRIGGER IF EXISTS files_pos ON toolbox.files;
+
+--
+-- Function and trigger setting a page pos when inserted
+--
+CREATE OR REPLACE FUNCTION toolbox.files_pos() RETURNS TRIGGER AS $files_pos$
+       DECLARE
+               new_pos INTEGER;
+       BEGIN
+               
+        SELECT 
+          INTO new_pos COALESCE(MAX(pos) + 1, 1)
+          FROM toolbox.files
+         WHERE paragraph = NEW.paragraph;
+
+               NEW.pos := new_pos;
+
+               RETURN NEW;
+
+       END;
+$files_pos$ LANGUAGE plpgsql;
+
+CREATE TRIGGER files_pos 
+BEFORE INSERT ON toolbox.files
+       FOR EACH ROW EXECUTE PROCEDURE toolbox.files_pos();
diff --git a/Toolkit/Toolbox/Database/procedures/files_update.sql b/Toolkit/Toolbox/Database/procedures/files_update.sql
new file mode 100644 (file)
index 0000000..d018224
--- /dev/null
@@ -0,0 +1,20 @@
+--DROP LANGUAGE IF EXISTS plpgsql RESTRICT;
+CREATE LANGUAGE plpgsql;
+
+DROP TRIGGER IF EXISTS files_update ON toolbox.files;
+
+--
+-- Function and trigger copying a file revision
+-- everytime it is updated.
+--
+CREATE OR REPLACE FUNCTION toolbox.files_update() RETURNS TRIGGER AS $files_update$
+       BEGIN
+
+               INSERT INTO toolbox.files_history SELECT NEW.*;
+                       RETURN NEW;
+       END;
+$files_update$ LANGUAGE plpgsql;
+
+CREATE TRIGGER files_update
+AFTER INSERT OR UPDATE ON toolbox.files
+       FOR EACH ROW EXECUTE PROCEDURE toolbox.files_update();
diff --git a/Toolkit/Toolbox/Database/procedures/pages_pos.sql b/Toolkit/Toolbox/Database/procedures/pages_pos.sql
new file mode 100644 (file)
index 0000000..9deffe1
--- /dev/null
@@ -0,0 +1,28 @@
+--DROP LANGUAGE IF EXISTS plpgsql RESTRICT;
+CREATE LANGUAGE plpgsql;
+
+DROP TRIGGER IF EXISTS pages_pos ON toolbox.pages;
+
+--
+-- Function and trigger setting a page pos when inserted
+--
+CREATE OR REPLACE FUNCTION toolbox.pages_pos() RETURNS TRIGGER AS $pages_pos$
+       DECLARE
+               new_pos INTEGER;
+       BEGIN
+               
+        SELECT 
+          INTO new_pos COALESCE(MAX(pos) + 1, 1)
+          FROM toolbox.pages
+         WHERE parent = NEW.parent;
+
+               NEW.pos := new_pos;
+
+               RETURN NEW;
+
+       END;
+$pages_pos$ LANGUAGE plpgsql;
+
+CREATE TRIGGER pages_pos 
+BEFORE INSERT ON toolbox.pages
+       FOR EACH ROW EXECUTE PROCEDURE toolbox.pages_pos();
diff --git a/Toolkit/Toolbox/Database/procedures/pages_update.sql b/Toolkit/Toolbox/Database/procedures/pages_update.sql
new file mode 100644 (file)
index 0000000..df5fba3
--- /dev/null
@@ -0,0 +1,30 @@
+--DROP LANGUAGE IF EXISTS plpgsql RESTRICT;
+CREATE LANGUAGE plpgsql;
+
+DROP TRIGGER IF EXISTS pages_update ON toolbox.pages;
+
+--
+-- Function and trigger copying a page revision
+-- everytime it is updated.
+--
+CREATE OR REPLACE FUNCTION toolbox.pages_update() RETURNS TRIGGER AS $pages_update$
+    BEGIN
+
+        INSERT INTO toolbox.pages_history 
+        (id,active,mobile_active,headline,headline_intro,include_member_map,
+        keyword,meta_description,meta_title,navigation_name,
+        paragraph_links,parent,pos,search_form,region,short_url,
+        template,include_members,include_coupons)
+        SELECT 
+        NEW.id,NEW.active,NEW.mobile_active,NEW.headline,NEW.headline_intro,NEW.include_member_map,
+        NEW.keyword,NEW.meta_description,NEW.meta_title,NEW.navigation_name,
+        NEW.paragraph_links,NEW.parent,NEW.pos,NEW.search_form,NEW.region,
+        NEW.short_url,NEW.template,NEW.include_members,NEW.include_coupons;
+        RETURN NEW;
+
+    END;
+$pages_update$ LANGUAGE plpgsql;
+
+CREATE TRIGGER pages_update 
+AFTER INSERT OR UPDATE ON toolbox.pages
+    FOR EACH ROW EXECUTE PROCEDURE toolbox.pages_update();
diff --git a/Toolkit/Toolbox/Database/procedures/paragraphs_draft_pos.sql b/Toolkit/Toolbox/Database/procedures/paragraphs_draft_pos.sql
new file mode 100644 (file)
index 0000000..3eb789d
--- /dev/null
@@ -0,0 +1,28 @@
+--DROP LANGUAGE IF EXISTS plpgsql RESTRICT;
+CREATE LANGUAGE plpgsql;
+
+DROP TRIGGER IF EXISTS paragraphs_draft_pos ON toolbox.paragraphs_draft;
+
+--
+-- Function and trigger setting a page pos when inserted
+--
+CREATE OR REPLACE FUNCTION toolbox.paragraphs_draft_pos() RETURNS TRIGGER AS $paragraphs_draft_pos$
+       DECLARE
+               new_pos INTEGER;
+       BEGIN
+               
+        SELECT 
+          INTO new_pos COALESCE(MAX(pos) + 1, 1)
+          FROM toolbox.paragraphs_draft
+         WHERE page = NEW.page;
+
+               NEW.pos := new_pos;
+
+               RETURN NEW;
+
+       END;
+$paragraphs_draft_pos$ LANGUAGE plpgsql;
+
+CREATE TRIGGER paragraphs_draft_pos 
+BEFORE INSERT ON toolbox.paragraphs_draft
+       FOR EACH ROW EXECUTE PROCEDURE toolbox.paragraphs_draft_pos();
diff --git a/Toolkit/Toolbox/Database/procedures/paragraphs_pos.sql b/Toolkit/Toolbox/Database/procedures/paragraphs_pos.sql
new file mode 100644 (file)
index 0000000..9bfe0ee
--- /dev/null
@@ -0,0 +1,48 @@
+--DROP LANGUAGE IF EXISTS plpgsql RESTRICT;
+CREATE LANGUAGE plpgsql;
+
+DROP TRIGGER IF EXISTS paragraphs_pos ON toolbox.paragraphs;
+
+--
+-- Function and trigger setting a page pos when inserted
+--
+CREATE OR REPLACE FUNCTION toolbox.paragraphs_pos() RETURNS TRIGGER AS $paragraphs_pos$
+       DECLARE
+               new_pos INTEGER;
+       BEGIN
+
+        SELECT
+          INTO new_pos COALESCE(MAX(pos) + 1, 1)
+          FROM toolbox.paragraphs
+         WHERE page = NEW.page;
+
+               --      Always use the new position when doing an insert
+               IF (TG_OP = 'INSERT') THEN
+                       NEW.pos := new_pos;
+               ELSIF (TG_OP = 'UPDATE') THEN
+                       --      Only update the paragraph position
+                       --      on an update if we are changing the
+                       --      page the paragraph is assigned to
+                       IF (OLD.page != NEW.page) THEN
+                               NEW.pos := new_pos;
+
+                               --      update the old page paragraph positions so
+                               --      we aren't left with any gaps in the sequence
+                UPDATE toolbox.paragraphs
+                   SET pos  = pos - 1
+                 WHERE pos  > OLD.pos
+                   AND page = OLD.page
+                                  AND EXISTS (SELECT * -- ensure the old page still exists
+                                                                FROM toolbox.pages
+                                                               WHERE id = OLD.page);
+                       END IF;
+               END IF;
+
+               RETURN NEW;
+
+       END;
+$paragraphs_pos$ LANGUAGE plpgsql;
+
+CREATE TRIGGER paragraphs_pos
+BEFORE INSERT OR UPDATE ON toolbox.paragraphs
+       FOR EACH ROW EXECUTE PROCEDURE toolbox.paragraphs_pos();
diff --git a/Toolkit/Toolbox/Database/procedures/paragraphs_update.sql b/Toolkit/Toolbox/Database/procedures/paragraphs_update.sql
new file mode 100644 (file)
index 0000000..dd553bc
--- /dev/null
@@ -0,0 +1,21 @@
+--DROP LANGUAGE IF EXISTS plpgsql RESTRICT;
+CREATE LANGUAGE plpgsql;
+
+DROP TRIGGER IF EXISTS paragraphs_update ON toolbox.paragraphs;
+
+--
+-- Function and trigger copying a paragraph revision
+-- everytime it is updated.
+--
+CREATE OR REPLACE FUNCTION toolbox.paragraphs_update() RETURNS TRIGGER AS $paragraphs_update$
+       BEGIN
+
+               INSERT INTO toolbox.paragraphs_history SELECT NEW.*;
+               RETURN NEW;
+
+       END;
+$paragraphs_update$ LANGUAGE plpgsql;
+
+CREATE TRIGGER paragraphs_update 
+AFTER INSERT OR UPDATE ON toolbox.paragraphs
+       FOR EACH ROW EXECUTE PROCEDURE toolbox.paragraphs_update();
diff --git a/Toolkit/Toolbox/Database/removeApplication.sql b/Toolkit/Toolbox/Database/removeApplication.sql
new file mode 100644 (file)
index 0000000..1a393a9
--- /dev/null
@@ -0,0 +1,8 @@
+--
+--     This will drop everything in the toolbox schema.
+--     Nothing better be in here except toolbox related objects
+--     or it will be dropped
+--
+--     The force is strong w/ this one, use it wisely.
+--
+DROP SCHEMA IF EXISTS toolbox CASCADE;
diff --git a/Toolkit/Toolbox/Database/tables/files.sql b/Toolkit/Toolbox/Database/tables/files.sql
new file mode 100644 (file)
index 0000000..b6490d2
--- /dev/null
@@ -0,0 +1,17 @@
+DROP TABLE IF EXISTS toolbox.files CASCADE;
+
+CREATE TABLE toolbox.files
+(id SERIAL,
+ filename TEXT,
+ bytes INTEGER NOT NULL DEFAULT 0,
+ "type" TEXT NOT NULL,
+ urltext TEXT,
+ paragraph INTEGER NOT NULL
+       REFERENCES toolbox.paragraphs(id)
+       ON UPDATE CASCADE
+       ON DELETE CASCADE,
+ pos INTEGER NOT NULL DEFAULT 1,
+ PRIMARY KEY (id));
+
+GRANT ALL ON toolbox.files_id_seq TO nobody;
+GRANT ALL ON toolbox.files TO nobody;
diff --git a/Toolkit/Toolbox/Database/tables/files_draft.sql b/Toolkit/Toolbox/Database/tables/files_draft.sql
new file mode 100644 (file)
index 0000000..8e9c7a1
--- /dev/null
@@ -0,0 +1,12 @@
+DROP TABLE IF EXISTS toolbox.files_draft CASCADE;
+
+CREATE TABLE toolbox.files_draft
+(LIKE toolbox.files
+       INCLUDING DEFAULTS,
+ FOREIGN KEY (paragraph) REFERENCES toolbox.paragraphs_draft(id)
+       ON UPDATE CASCADE
+       ON DELETE CASCADE,
+ PRIMARY KEY (id)
+);
+
+GRANT ALL ON toolbox.files_draft TO nobody;
diff --git a/Toolkit/Toolbox/Database/tables/files_history.sql b/Toolkit/Toolbox/Database/tables/files_history.sql
new file mode 100644 (file)
index 0000000..47b1665
--- /dev/null
@@ -0,0 +1,10 @@
+DROP TABLE IF EXISTS toolbox.files_history CASCADE;
+
+CREATE TABLE toolbox.files_history
+(LIKE toolbox.files
+       INCLUDING DEFAULTS,
+ revision SERIAL NOT NULL
+);
+
+GRANT ALL ON toolbox.files_history_revision_seq TO nobody;
+GRANT ALL ON toolbox.files_history TO nobody;
diff --git a/Toolkit/Toolbox/Database/tables/pages.sql b/Toolkit/Toolbox/Database/tables/pages.sql
new file mode 100644 (file)
index 0000000..551f06d
--- /dev/null
@@ -0,0 +1,36 @@
+DROP TABLE IF EXISTS toolbox.pages CASCADE;
+
+CREATE TABLE toolbox.pages
+(id SERIAL,
+ active BOOLEAN NOT NULL
+       DEFAULT FALSE,
+ mobile_active BOOLEAN NOT NULL
+       DEFAULT FALSE,
+ headline BOOLEAN NOT NULL
+       DEFAULT FALSE,
+ headline_intro TEXT,
+ include_member_map BOOLEAN NOT NULL
+       DEFAULT FALSE,
+ keyword TEXT,
+ meta_description TEXT,
+ meta_title TEXT,
+ navigation_name TEXT,
+ paragraph_links BOOLEAN NOT NULL
+       DEFAULT FALSE,
+ parent INTEGER NOT NULL -- 0 is top level
+       DEFAULT 0,
+ pos INTEGER NOT NULL
+       DEFAULT 1,
+ search_form BOOLEAN NOT NULL
+       DEFAULT FALSE,
+ region INTEGER,
+ short_url TEXT,
+ template INTEGER NOT NULL,
+ include_members BOOLEAN DEFAULT FALSE,
+ include_coupons BOOLEAN DEFAULT FALSE,
+ PRIMARY KEY (id),
+ CHECK (id <> parent), -- Page cannot be own parent,
+ CHECK (pos > 0)); -- Page position cannot fall below zero
+
+GRANT ALL ON toolbox.pages_id_seq TO nobody;
+GRANT ALL ON toolbox.pages TO nobody;
diff --git a/Toolkit/Toolbox/Database/tables/pages_draft.sql b/Toolkit/Toolbox/Database/tables/pages_draft.sql
new file mode 100644 (file)
index 0000000..070b744
--- /dev/null
@@ -0,0 +1,14 @@
+DROP TABLE IF EXISTS toolbox.pages_draft CASCADE;
+
+CREATE TABLE toolbox.pages_draft
+(LIKE toolbox.pages
+       INCLUDING DEFAULTS,
+ published_page INTEGER
+       DEFAULT NULL
+       REFERENCES toolbox.pages (id)
+       ON UPDATE CASCADE
+       ON DELETE CASCADE,
+ PRIMARY KEY (id)
+);
+
+GRANT ALL ON toolbox.pages_draft TO nobody;
diff --git a/Toolkit/Toolbox/Database/tables/pages_history.sql b/Toolkit/Toolbox/Database/tables/pages_history.sql
new file mode 100644 (file)
index 0000000..e1d84d1
--- /dev/null
@@ -0,0 +1,12 @@
+DROP TABLE IF EXISTS toolbox.pages_history CASCADE;
+
+CREATE TABLE toolbox.pages_history
+(LIKE toolbox.pages
+       INCLUDING DEFAULTS,
+ revision_timestamp TIMESTAMP NOT NULL
+       DEFAULT CURRENT_TIMESTAMP,
+ revision SERIAL NOT NULL
+);
+
+GRANT ALL ON toolbox.pages_history_revision_seq TO nobody;
+GRANT ALL ON toolbox.pages_history TO nobody;
diff --git a/Toolkit/Toolbox/Database/tables/pages_mobile.sql b/Toolkit/Toolbox/Database/tables/pages_mobile.sql
new file mode 100644 (file)
index 0000000..e6d816f
--- /dev/null
@@ -0,0 +1,15 @@
+DROP TABLE IF EXISTS toolbox.pages_mobile CASCADE;
+
+CREATE TABLE toolbox.pages_mobile
+(id SERIAL,
+ page INTEGER NOT NULL
+    REFERENCES toolbox.pages(id)
+    ON DELETE CASCADE,
+ mobile_active BOOLEAN NET NULL
+    DEFAULT FALSE,
+PRIMARY KEY (id));
+
+GRANT ALL ON toolbox.pages_mobile TO nobody;
+GRANT ALL ON toolbox.pages_mobile_id_seq TO nobody;
+
+CREATE UNIQUE INDEX page_mobile_page_indx ON toolbox.pages_mobile(page); 
diff --git a/Toolkit/Toolbox/Database/tables/pages_preview.sql b/Toolkit/Toolbox/Database/tables/pages_preview.sql
new file mode 100644 (file)
index 0000000..3f4e5ae
--- /dev/null
@@ -0,0 +1,7 @@
+CREATE TEMPORARY TABLE toolbox.pages_preview
+(LIKE toolbox.pages
+       INCLUDING DEFAULTS,
+ PRIMARY KEY (id)
+);
+
+GRANT ALL ON toolbox.pages_preview TO nobody;
diff --git a/Toolkit/Toolbox/Database/tables/paragraphs.sql b/Toolkit/Toolbox/Database/tables/paragraphs.sql
new file mode 100644 (file)
index 0000000..26aa4ee
--- /dev/null
@@ -0,0 +1,23 @@
+DROP TABLE IF EXISTS toolbox.paragraphs CASCADE;
+
+CREATE TABLE toolbox.paragraphs
+(id SERIAL,
+ active BOOLEAN NOT NULL
+       DEFAULT TRUE,
+ back_to_top BOOLEAN NOT NULL
+       DEFAULT FALSE,
+ caption TEXT,
+ description TEXT,
+ image TEXT,
+ page INTEGER NOT NULL
+       REFERENCES toolbox.pages (id)
+       ON UPDATE CASCADE
+       ON DELETE CASCADE,
+ pos INTEGER NOT NULL
+       DEFAULT 1,
+ title TEXT,
+ PRIMARY KEY (id),
+ CHECK (pos > 0)); -- paragraph position cannot fall below zero
+
+GRANT ALL ON toolbox.paragraphs_id_seq TO nobody;
+GRANT ALL ON toolbox.paragraphs TO nobody;
diff --git a/Toolkit/Toolbox/Database/tables/paragraphs_draft.sql b/Toolkit/Toolbox/Database/tables/paragraphs_draft.sql
new file mode 100644 (file)
index 0000000..85c3e33
--- /dev/null
@@ -0,0 +1,12 @@
+DROP TABLE IF EXISTS toolbox.paragraphs_draft CASCADE;
+
+CREATE TABLE toolbox.paragraphs_draft
+(LIKE toolbox.paragraphs
+       INCLUDING DEFAULTS,
+ FOREIGN KEY (page) REFERENCES toolbox.pages_draft(id)
+       ON UPDATE CASCADE
+       ON DELETE CASCADE,
+ PRIMARY KEY (id)
+);
+
+GRANT ALL ON toolbox.paragraphs_draft TO nobody;
diff --git a/Toolkit/Toolbox/Database/tables/paragraphs_history.sql b/Toolkit/Toolbox/Database/tables/paragraphs_history.sql
new file mode 100644 (file)
index 0000000..6515b02
--- /dev/null
@@ -0,0 +1,10 @@
+DROP TABLE IF EXISTS toolbox.paragraphs_history CASCADE;
+
+CREATE TABLE toolbox.paragraphs_history
+(LIKE toolbox.paragraphs
+       INCLUDING DEFAULTS,
+ revision SERIAL NOT NULL
+);
+
+GRANT ALL ON toolbox.paragraphs_history_revision_seq TO nobody;
+GRANT ALL ON toolbox.paragraphs_history TO nobody;
diff --git a/Toolkit/Toolbox/Database/tables/paragraphs_preview.sql b/Toolkit/Toolbox/Database/tables/paragraphs_preview.sql
new file mode 100644 (file)
index 0000000..b669e3f
--- /dev/null
@@ -0,0 +1,6 @@
+CREATE TEMPORARY TABLE toolbox.paragraphs_preview
+(LIKE toolbox.paragraphs
+       INCLUDING DEFAULTS
+);
+
+GRANT ALL ON toolbox.paragraphs_preview TO nobody;
diff --git a/Toolkit/Toolbox/DraftPagesTree.php b/Toolkit/Toolbox/DraftPagesTree.php
new file mode 100644 (file)
index 0000000..64c5b9b
--- /dev/null
@@ -0,0 +1,42 @@
+<?php
+class Toolkit_Toolbox_DraftPagesTree extends Toolkit_Toolbox_PagesTree
+{
+       //      {{{     createTree()
+
+       protected function createTree(array $tree, $leaf, $level = 0)
+       {
+               $html = !$level ? $this->rootNodeStart : $this->subTreeStart;
+
+               if (is_array($leaf) && !empty($leaf)) {
+                       foreach ($tree as $parent => $children) {
+                               foreach ($children as $branch) {
+                                       $html .= sprintf($this->leafStart, $branch['id'], null);
+                                       $previewUrl = BASE_URL . "index.php?rt=Draft&amp;catid={$branch['id']}";
+
+                                       $html .= "<strong>{$branch['navigation_name']}</strong>";
+
+                                       $html .= '<div class="right-element">';
+
+                                       $html .= '<a href="'.MEDIA_BASE_URL.'admin/toolbox.php?rt=EditDraft&amp;id='.$branch['id'].'" class="editPage">[Edit]</a> ';
+                                       $html .= '<a href="'.MEDIA_BASE_URL.'admin/toolbox.php?rt=ParagraphsDraft&amp;pageid='.$branch['id'].'" class="editParagraphs">[Paragraphs]</a> ';
+                                       $html .= '<a href="'.$previewUrl.'" class="pagePreview">[Preview]</a> ';
+                                       $html .= $this->getActiveBall($branch);
+
+                                       $html .= '</div>';
+
+                                       $html .= $this->leafEnd;
+                               }
+                       }
+               }
+
+               $html .= $this->treeEnd;
+               if ($level) {
+                       $html .= $this->leafEnd;
+               }
+
+               return $html;
+       }
+
+       //      }}}
+}
+?>
diff --git a/Toolkit/Toolbox/EditDraftController.php b/Toolkit/Toolbox/EditDraftController.php
new file mode 100644 (file)
index 0000000..bb7e35e
--- /dev/null
@@ -0,0 +1,200 @@
+<?php
+
+class Toolkit_Toolbox_EditDraftController
+       extends Toolkit_Toolbox_FormControllerAbstract
+{
+       //      {{{     cancel()
+
+       protected function cancel()
+       {
+               header('Location: ' . MEDIA_BASE_URL . 'admin/toolbox.php?rt=EditDraft');
+               exit();
+       }
+
+       //      }}}
+
+       //      {{{     delete()
+
+       protected function delete($id)
+       {
+               $gateway = new Toolkit_Toolbox_PageGatewayDraft($this->registry->dbh);
+               $gateway->delete($id);
+       }
+
+       //      }}}
+
+       //      {{{     indexAction()
+
+       public function indexAction()
+       {
+           if (ctype_digit($_GET['id'])) {
+            $breadCrumbs = new Toolkit_Toolbox_PageDraftBreadCrumbs(
+                $this->registry->dbh,
+                $_GET['id']
+            );
+            $html = (string) $breadCrumbs;
+           }
+
+               $form = $this->getForm(
+                       'EditDraft',
+                       new Toolkit_Toolbox_PageGatewayDraftFactory($this->registry->dbh)
+               );
+               $html .= $form->toHtml($this->registry->dbh);
+
+               return $html;
+       }
+
+       //      }}}
+
+       protected function preview($form)
+       {
+               die('preview');
+       }
+
+       //      {{{     processAction()
+
+       public function processAction()
+       {
+               return $this->processForm(
+                       'EditDraft',
+                       new Toolkit_Toolbox_PageGatewayDraftFactory($this->registry->dbh),
+                       new Cache_Lite($GLOBALS['cacheOptions'])
+               );
+       }
+
+       //      }}}
+       //      {{{     publishPage()
+
+       protected function publishPage(HTML_QuickForm $form, Cache_Lite $cache)
+       {
+               $pageGatewayDraft = new Toolkit_Toolbox_PageGatewayDraft(
+                       $this->registry->dbh
+               );
+               $pageGatewayPublish = new Toolkit_Toolbox_PageGatewayPublish(
+                       $this->registry->dbh
+               );
+               $paragraphGatewayDraft = new Toolkit_Toolbox_ParagraphGatewayDraft(
+                       $this->registry->dbh
+               );
+               $paragraphGatewayPublish = new Toolkit_Toolbox_ParagraphGatewayPublish(
+                       $this->registry->dbh
+               );
+
+               if ($form->validate()) {
+                       $draftPageId = $form->getSubmitValue('id');
+                       $existingDraft = $pageGatewayDraft->find($draftPageId);
+                       if (is_null($existingDraft['published_page'])) {
+                               $publishId = $pageGatewayPublish->insert(
+                                       $form->getSubmitValues()
+                               );
+
+                               $paragraphs = $paragraphGatewayDraft->findAll(
+                                       $form->getSubmitValue('id')
+                               );
+
+                               $this->_convertOldFilesToUploadedFiles(
+                                       $paragraphGatewayPublish,
+                                       $paragraphs,
+                                       $publishId
+                               );
+                       } else {
+                               $pageGatewayPublish->update(
+                                       $form->getSubmitValues(),
+                                       $existingDraft['published_page']
+                               );
+
+                               $existingParagraphs = $paragraphGatewayPublish->findAll(
+                                       $existingDraft['published_page']
+                               );
+
+                               foreach ($existingParagraphs as $paragraph) {
+                                       $paragraphGatewayPublish->delete($paragraph['id']);
+                               }
+
+                               $updatedParagraphs = $paragraphGatewayDraft->findAll(
+                                       $form->getSubmitValue('id')
+                               );
+
+                               $this->_convertOldFilesToUploadedFiles(
+                                       $paragraphGatewayPublish,
+                                       $updatedParagraphs,
+                                       $existingDraft['published_page']
+                               );
+                       }
+
+                       $pageGatewayDraft->delete($draftPageId);
+            $cache->clean('Nav');
+            if ($existingDraft['published_page']) {
+                $cache->remove("page-{$existingDraft['published_page']}", 'Toolbox');
+                $cache->remove("paragraphs-{$existingDraft['published_page']}", 'Toolbox');
+                $cache->remove("sectionLinks-{$existingDraft['published_page']}", 'Toolbox');
+            }
+                       
+                       header('Location: ' . MEDIA_BASE_URL . 'admin/toolbox.php');
+                       exit();
+               } else {
+                       $return  = $form->getErrorMessage();
+                       $return .= $form->toHtml();
+
+                       return $return;
+               }
+       }
+
+       //      }}}
+
+       //      {{{     _convertOldFilesToUploadedFiles()
+
+       private function _convertOldFilesToUploadedFiles(
+               Toolkit_Toolbox_ParagraphGatewayAbstract $paragraphGateway,
+               &$paragraphs,
+               $newPageId
+       ) {
+               if (is_array($paragraphs)) {
+                       foreach ($paragraphs as &$paragraph) {
+                               if (is_array($paragraph['files'])) {
+                                       $paragraph['uploaded_files'] = array(
+                                               'urltext' => array(),
+                                               'filename' => array(),
+                                               'bytes' => array(),
+                                               'type' => array()
+                                       );
+                                       foreach ($paragraph['files'] as $file) {
+                                               $paragraph['uploaded_files']['urltext'][] = $file['urltext'];
+                                               $paragraph['uploaded_files']['filename'][] = $file['filename'];
+                                               $paragraph['uploaded_files']['bytes'][] = $file['bytes'];
+                                               $paragraph['uploaded_files']['type'][] = $file['type'];
+                                       }
+                               }
+                               $paragraph['page'] = $newPageId;
+                               $paragraphGateway->insert($paragraph);
+                       }
+               }
+       }
+
+       //      }}}
+
+       //      {{{     saveDraft()
+
+       protected function saveDraft(HTML_QuickForm $form)
+       {
+               $gateway = new Toolkit_Toolbox_PageGatewayDraft($this->registry->dbh);
+               if ($form->validate()) {
+                       $pageId = $form->getSubmitValue('id');
+                       if ($pageId) {
+                               $gateway->update($form->getSubmitValues(), $pageId);
+                       } else {
+                               $gateway->insert($form->getSubmitValues());
+                       }
+                       header('Location: ' . MEDIA_BASE_URL . 'admin/toolbox.php');
+                       exit();
+               } else {
+                       $return  = $form->getErrorMessage();
+                       $return .= $form->toHtml();
+
+                       return $return;
+               }
+       }
+
+       //      }}}
+}
+?>
diff --git a/Toolkit/Toolbox/EditPageController.php b/Toolkit/Toolbox/EditPageController.php
new file mode 100644 (file)
index 0000000..3d47d78
--- /dev/null
@@ -0,0 +1,148 @@
+<?php
+
+class Toolkit_Toolbox_EditPageController
+       extends Toolkit_Toolbox_FormControllerAbstract
+{
+       //      {{{     cancel()
+
+       protected function cancel()
+       {
+               header('Location: ' . MEDIA_BASE_URL . 'admin/toolbox.php');
+               exit();
+       }
+
+       //      }}}
+
+       //      {{{     delete ()
+
+       protected function delete($id)
+       {
+               $gateway = new Toolkit_Toolbox_PageGatewayPublish($this->registry->dbh);
+               $gateway->delete($id);
+       }
+
+       //      }}}
+
+       //      {{{     indexAction()
+
+       public function indexAction()
+       {
+               if ($pageId = filter_input(INPUT_GET, 'id', FILTER_VALIDATE_INT)) {
+            $breadCrumbs = new Toolkit_Toolbox_PageBreadCrumbs(
+                $this->registry->dbh,
+                $pageId
+            );
+            $html = (string) $breadCrumbs;
+           }
+
+               $form = $this->getForm(
+                       'EditPage',
+                       new Toolkit_Toolbox_PageGatewayPublishFactory($this->registry->dbh)
+               );
+               $html .= $form->toHtml($this->registry->dbh);
+
+               return $html;
+       }
+
+       //      }}}
+
+       //      {{{     processAction()
+
+       public function processAction()
+       {
+               return $this->processForm(
+                       'EditPage',
+                       new Toolkit_Toolbox_PageGatewayPublishFactory($this->registry->dbh),
+                       new Cache_Lite($GLOBALS['cacheOptions'])
+               );
+       }
+
+       //      }}}
+       //      {{{     publishPage()
+
+       protected function publishPage(HTML_QuickForm $form, Cache_Lite $cache)
+       {
+               $gateway = new Toolkit_Toolbox_PageGatewayPublish($this->registry->dbh);
+               if ($form->validate()) {
+                       $pageId = $form->getSubmitValue('id');
+                       if ($pageId) {
+                               $gateway->update($form->getSubmitValues(), $pageId);
+                       } else {
+                               $gateway->insert($form->getSubmitValues());
+                       }
+
+                       $cache->clean('Nav');
+                       $cache->remove("page-$pageId", 'Toolbox');
+                       $cache->remove("paragraphs-$pageId", 'Toolbox');
+                       $cache->remove("sectionLinks-$pageId", 'Toolbox');
+
+                       header('Location: ' . MEDIA_BASE_URL . 'admin/toolbox.php');
+                       exit();
+               } else {
+                       $return  = $form->getErrorMessage();
+                       $return .= $form->toHtml();
+
+                       return $return;
+               }
+       }
+
+       //      }}}
+
+       //      {{{     saveDraft()
+
+       protected function saveDraft(HTML_QuickForm $form)
+       {
+               $pageGatewayDraft = new Toolkit_Toolbox_PageGatewayDraft(
+                       $this->registry->dbh
+               );
+               $paragraphGatewayPublish = new Toolkit_Toolbox_ParagraphGatewayPublish(
+                       $this->registry->dbh
+               );
+               $paragraphGatewayDraft = new Toolkit_Toolbox_ParagraphGatewayDraft(
+                       $this->registry->dbh
+               );
+
+               if ($form->validate()) {
+                       $draftId = $pageGatewayDraft->insert($form->getSubmitValues());
+
+                       //      drafting an already published page,
+                       //      need to bring over the paragraphs
+                       if (ctype_digit($form->getSubmitValue('id'))) {
+                               $paragraphs = $paragraphGatewayPublish->findAll(
+                                       $form->getSubmitValue('id')
+                               );
+
+                               if (is_array($paragraphs)) {
+                                       foreach ($paragraphs as &$paragraph) {
+                                               if (is_array($paragraph['files'])) {
+                                                       $paragraph['uploaded_files'] = array(
+                                                               'urltext' => array(),
+                                                               'filename' => array(),
+                                                               'bytes' => array(),
+                                                               'type' => array()
+                                                       );
+                                                       foreach ($paragraph['files'] as $file) {
+                                                               $paragraph['uploaded_files']['urltext'][] = $file['urltext'];
+                                                               $paragraph['uploaded_files']['filename'][] = $file['filename'];
+                                                               $paragraph['uploaded_files']['bytes'][] = $file['bytes'];
+                                                               $paragraph['uploaded_files']['type'][] = $file['type'];
+                                                       }
+                                               }
+                                               $paragraph['page'] = $draftId;
+                                               $paragraphGatewayDraft->insert($paragraph);
+                                       }
+                               }
+                       }
+
+                       header('Location: ' . MEDIA_BASE_URL . 'admin/toolbox.php?rt=ListDrafts');
+                       exit();
+               } else {
+                       $return  = $form->getErrorMessage();
+                       $return .= $form->toHtml();
+
+                       return $return;
+               }
+       }
+
+       //      }}}
+}
diff --git a/Toolkit/Toolbox/Exception.php b/Toolkit/Toolbox/Exception.php
new file mode 100644 (file)
index 0000000..4e925dd
--- /dev/null
@@ -0,0 +1,3 @@
+<?php
+class Toolkit_Toolbox_Exception extends Exception {}
+?>
diff --git a/Toolkit/Toolbox/FileExtension.php b/Toolkit/Toolbox/FileExtension.php
new file mode 100644 (file)
index 0000000..93f8925
--- /dev/null
@@ -0,0 +1,133 @@
+<?php
+/**
+ * FileExtension.php
+ *
+ * PHP version 5
+ *
+ * @category Toolkit
+ * @package  Toolbox
+ * @author   Jamie Kahgee <steve@gaslightmedia.com>
+ * @license  http://www.gaslightmedia.com Gaslightmedia
+ * @link     <>
+ */
+
+/**
+ * Toolkit_Toolbox_FileExtension
+ *
+ * Description for Toolkit_Toolbox_FileExtension
+ *
+ * @category Toolkit
+ * @package  Toolbox
+ * @author   Jamie Kahgee <steve@gaslightmedia.com>
+ * @license  http://www.gaslightmedia.com Gaslightmedia
+ * @link     <>
+ */
+class Toolkit_Toolbox_FileExtension
+{
+       //      {{{     getImage()
+
+    /**
+     * Description for getImage()
+     * 
+     * @param string $filename File name
+     * 
+     * @return string 
+     * @access public
+     */
+       public function getImage($filename)
+       {
+               switch (pathinfo($filename, PATHINFO_EXTENSION)) {
+               case 'avi' :
+                       $img = 'avi.gif';
+                       break;
+
+               case 'mp3' :
+                       $img = 'mp3.gif';
+                       break;
+
+               case 'pdf' :
+                       $img = 'pdf.png';
+                       break;
+
+               case 'xls' :
+               case 'xlsx' :
+                       $img = 'xls.gif';
+                       break;
+
+               case 'ppt' :
+                       $img = 'ppt.gif';
+                       break;
+
+               case 'mov' :
+                       $img = 'mov.gif';
+                       break;
+
+               case 'cad' :
+                       $img = 'cad.gif';
+                       break;
+
+               case 'html' :
+                       $img = 'html.gif';
+                       break;
+
+               case 'doc' :
+               case 'docx' :
+               case 'msword' :
+                       $img = 'doc.gif';
+                       break;
+
+               case 'txt' :
+                       $img = 'txt.png';
+                       break;
+
+               case 'zip' :
+                       $img = 'zip.png';
+                       break;
+
+               case 'rar' :
+                       $img = 'rar.gif';
+                       break;
+
+               case 'png' : // image does not exist yet.
+               case 'jpeg' :
+               case 'jpg' :
+                       $img = 'jpg.gif';
+                       break;
+
+               case 'gif' :
+                       $img = 'gif.gif';
+                       break;
+
+               case 'wmv' :
+                       $img = 'wmv.gif';
+                       break;
+
+               default :
+                       $img = 'download.gif';
+                       break;
+               }
+
+               return $img;
+       }
+
+       //      }}}
+       //      {{{     getClassForType()
+
+    /**
+     * Description for getClassForType()
+     * 
+     * @param string $filename File name
+     * 
+     * @return string
+     * @access public  
+     */
+       public function getClassForType($filename)
+       {
+               $image = $this->getImage($filename);
+               $pieces = explode('.', $image);
+               reset($pieces);
+               return current($pieces);
+       }
+
+       //      }}}
+}
diff --git a/Toolkit/Toolbox/FormControllerAbstract.php b/Toolkit/Toolbox/FormControllerAbstract.php
new file mode 100644 (file)
index 0000000..5c87fdf
--- /dev/null
@@ -0,0 +1,118 @@
+<?php
+
+abstract class Toolkit_Toolbox_FormControllerAbstract
+       extends Toolkit_BaseControllerAbstract implements Toolkit_IController
+{
+       //      {{{     getForm()
+
+       protected function getForm(
+               $controller,
+               Toolkit_Toolbox_GatewayFactoryAbstract $factory
+       ) {
+               //      Need three separate objects otherwise,
+               //      new parses overwrite existing root variable data.
+
+               if (defined('MEMBERS_DB') && MEMBERS_DB) {
+                       $memConf = new Config;
+                       $memRoot =& $memConf->parseConfig(
+                               BASE . 'Toolkit/Members/config.ini',
+                               'IniFile'
+                       );
+               }
+
+               if (defined('COUPONS') && COUPONS) {
+                       $cpnConf = new Config;
+                       $cpnRoot =& $cpnConf->parseConfig(
+                               BASE . 'Toolkit/Coupons/config.ini',
+                               'IniFile'
+                       );
+               }
+               $tlbConf = new Config;
+               $tbxRoot =& $tlbConf->parseConfig(
+                       BASE . 'Toolkit/Toolbox/config.ini',
+                       'IniFile'
+               );
+
+               $form = new Toolkit_Toolbox_Forms_EditPage(
+                       'edit_page',
+                       'post',
+                       MEDIA_BASE_URL . "admin/toolbox.php?rt=$controller&ac=process"
+               );
+
+               $form->configureForm(
+                       $this->registry->dbh,
+                       $factory,
+                       new Toolkit_FileServer_ImageAdapter(),
+                       $memRoot,
+                       $cpnRoot
+               );
+
+               return $form;
+       }
+
+       //      }}}
+
+       //      {{{     savePage()
+
+       protected function savePage(
+               Toolkit_Toolbox_PageGatewayAbstract $gateway,
+               HTML_QuickForm $form
+       ) {
+               if ($form->validate()) {
+                       $pageId = $form->getSubmitValue('id');
+                       if ($pageId) {
+                               $gateway->update($form->getSubmitValues(), $pageId);
+                       } else {
+                               $gateway->insert($form->getSubmitValues());
+                       }
+                       header('Location: ' . MEDIA_BASE_URL . 'admin/toolbox.php');
+                       exit();
+               } else {
+                       $return  = $form->getErrorMessage();
+                       $return .= $form->toHtml();
+
+                       return $return;
+               }
+       }
+
+       //      }}}
+
+       //      {{{     processForm()
+
+       protected function processForm(
+               $controller,
+               Toolkit_Toolbox_GatewayFactoryAbstract $factory,
+               Cache_Lite $cache
+       ) {
+               $form = $this->getForm($controller, $factory);
+
+               if ($form->isSubmitted()) {
+                       if ($form->getSubmitValue('cancel')) {
+                               //      do nothing
+                               $this->cancel();
+                       } elseif ($form->getSubmitValue('previewPage')) {
+                               $this->preview();
+                       } elseif ($form->getSubmitValue('saveDraft')) {
+                               //      save draft
+                               $return = $this->saveDraft($form);
+                       } elseif ($form->getSubmitValue('publishPage')) {
+                               //      publish page
+                               $return = $this->publishPage($form, $cache);
+                       } elseif ($form->getSubmitValue('deletePage')) {
+                               //      delete page
+                               $this->delete($form->getSubmitValue('id'));
+                               $navTitle = $form->getSubmitValue('navigation_name');
+                               $return = "[<b>$navTitle</b>] successfully deleted.";
+                       } else {
+                               $return  = $form->getErrorMessage();
+                               $return .= $form->toHtml();
+                       }
+               } else {
+                       $return = $form->toHtml();
+               }
+
+               return $return;
+       }
+
+       //      }}}
+}
diff --git a/Toolkit/Toolbox/Forms/EditPage.php b/Toolkit/Toolbox/Forms/EditPage.php
new file mode 100644 (file)
index 0000000..10a9cc1
--- /dev/null
@@ -0,0 +1,1158 @@
+<?php
+//  vim:set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker syntax=php:
+
+/**
+ * Toolbox page edit form
+ *
+ * PHP version 5
+ *
+ * @category Toolbox
+ * @package  Toolkit_Toolbox
+ * @author   Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @release  CVS: $Id: EditPage.php,v 1.10 2010/08/15 19:29:57 jamie Exp $:
+ * @license  http://www.gaslightmedia.com Gaslightmedia
+ * @link     http://demo.gaslightmedia.com
+ */
+
+require_once BASE . 'Toolkit/Forms/Rules/Image.php';
+require_once BASE . 'Toolkit/Forms/Rules/ShortUrl.php';
+
+/**
+ * Edit Toolbox page
+ *
+ * Handles form to insert/edit a toolbox page
+ *
+ * @category  Toolbox
+ * @package   Toolkit_Toolbox
+ * @author    Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @link      http://demo.gaslightmedia.com
+ */
+class Toolkit_Toolbox_Forms_EditPage
+    extends Toolkit_FormBuilder
+{
+    //  {{{ properties
+
+    /**
+     * How many levels deep do you want to show in the parent select list
+     *
+     * 0 = Show All Levels
+     *
+     * @var    integer
+     * @access protected
+     */
+    protected $maxDepth = 0;
+
+    /**
+     * Don't need to register any rules for this form.
+     * @var    array
+     * @access protected
+     */
+    protected $registeredRules = array();
+
+    //  }}}
+
+    //  {{{ configureConstants()
+
+    /**
+     * Configure form constants
+     *
+     * @param PDO $dbh Database handler
+     *
+     * @return void
+     * @access public
+     */
+    public function configureConstants(PDO $dbh)
+    {
+        $c = array(
+            'id' => $_GET['id']
+        );
+
+        $this->setupConstants($c);
+    }
+
+    //  }}}
+    //  {{{ configureDefaults()
+
+    /**
+     * Initializes default form values
+     *
+     * @param Toolkit_Toolbox_GatewayFactoryAbstract $factory Gateway factory
+     * @param integer                                $id      page id (optional)
+     *
+     * @return void
+     * @access public
+     */
+    public function configureDefaults(
+        Toolkit_Toolbox_GatewayFactoryAbstract $factory,
+        $id = null
+    ) {
+        if (!is_null($id)) {
+            $gateway = $factory->createGateway();
+            $page = $gateway->find($id);
+        } else {
+            $page = array(
+                'template' => 1,
+                'current_image_thumb' => 'Image not yet uploaded',
+                'search_form' => 1
+            );
+        }
+        $this->setupDefaults($page);
+    }
+
+    //  }}}
+    //  {{{ configureElements()
+
+    /**
+     * Form element definitions
+     *
+     * @param PDO $dbh Database handler
+     * @param Config_Container $memRoot Configuration object
+     * @param Config_Container $cpnRoot Configuration object
+     *
+     * @return void
+     * @access public
+     */
+    public function configureElements(
+        PDO $dbh,
+        Config_Container $memRoot = null,
+        Config_Container $cpnRoot = null,
+        $id = null
+    ) {
+        $e = array();
+
+        if (!is_null($memRoot)) {
+            $pluralMem = $memRoot->getItem('section', 'listing type')
+                            ->getItem('directive', 'plural')
+                            ->getContent();
+            $singularMem = $memRoot->getItem('section', 'listing type')
+                            ->getItem('directive', 'singular')
+                            ->getContent();
+            $hasRegions = $memRoot->getItem('section', 'conf')
+                ->getItem('directive', 'regions')
+                ->getContent();
+        }
+
+        if (!is_null($cpnRoot)) {
+            $pluralCpn = $cpnRoot->getItem('section', 'listing type')
+                            ->getItem('directive', 'plural')
+                            ->getContent();
+            $singularCpn = $cpnRoot->getItem('section', 'listing type')
+                            ->getItem('directive', 'singular')
+                            ->getContent();
+        }
+
+        $hideDelete = (!is_null($id) && $id == HOME_ID);
+        $dir = dir(BASE . 'static');
+        $staticPages = array();
+        while (false !== ($page = $dir->read())) {
+            $pieces = explode('.', $page);
+            if ($pageId = filter_var($pieces[0], FILTER_VALIDATE_INT)) {
+                $staticPages[] = $pageId;
+            }
+        }
+        $hideDelete = ($hideDelete || in_array($id, $staticPages) || is_null($id));
+
+        $showParentPage = ($id != HOME_ID);
+
+        //  Grouped Elements are defined here.
+        $submitBtns = array();
+
+        $submitBtns[] = array(
+            'type'    => 'submit',
+            'req'     => false,
+            'name'    => 'publishPage',
+            'display' => 'Publish Page'
+        );
+        $submitBtns[] = array(
+            'type'    => 'submit',
+            'req'     => false,
+            'name'    => 'saveDraft',
+            'display' => 'Save as draft'
+        );
+/*
+        $submitBtns[] = array(
+            'type'    => 'submit',
+            'req'     => false,
+            'name'    => 'previewPage',
+            'display' => 'Preview Changes'
+        );
+*/
+        $submitBtns[] = array(
+            'type'    => 'submit',
+            'req'     => false,
+            'name'    => 'cancel',
+            'display' => 'Cancel'
+        );
+        if (!$hideDelete) {
+            $submitBtns[] = array(
+                'type'    => 'submit',
+                'req'     => false,
+                'name'    => 'deletePage',
+                'display' => 'Delete Page'
+            );
+        }
+
+        $templates = array();
+
+        $options = range(1, 6);
+        foreach ($options as $i) {
+            $img = "<img src=\"".MEDIA_BASE_URL.'Toolkit/Toolbox/assets/'."template$i.gif\" alt=\"page template $i\">";
+
+            $templates[] = array(
+                'type'    => 'radio',
+                'req'     => false,
+                'name'    => 'template',
+                'att'     => $i,
+                'opts'    => "Template $i<br>$img",
+            );
+        }
+
+        //  All Elements are created here.
+        //  This includes group element definitions.
+        $e[] = array(
+            'type'    => 'header',
+            'req'     => false,
+            'name'    => 'pageContentHdr',
+            'display' => 'Page Content'
+        );
+        $e[] = array(
+            'type'    => 'hidden',
+            'req'     => false,
+            'name'    => 'id'
+        );
+        $e[] = array(
+            'type'    => 'hidden',
+            'req'     => false,
+            'name'    => 'published_page'
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => true,
+            'name'    => 'navigation_name',
+            'display' => 'Navigation Name',
+            'opts' => array('size' => 35)
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => 'title',
+            'display' => 'Page Title',
+            'opts' => array('size' => 35)
+        );
+        if ($_GET['rt'] == 'EditPage' && $showParentPage) {
+            $e[] = array(
+                'type'    => 'select',
+                'req'     => false,
+                'name'    => 'parent',
+                'display' => 'Parent Page',
+                'opts'    => array('0' => '-- No Parent --'),
+            );
+        } else {
+            $e[] = array(
+                'type' => 'hidden',
+                'req' => false,
+                'name' => 'parent'
+            );
+        }
+        $e[] = array(
+            'type'    => 'textarea',
+            'req'     => false,
+            'name'    => 'description',
+            'opts'    => array(
+                'cols' => 60,
+                'rows' => 60,
+                'id' => 'description'
+            ),
+            'noCharLimit' => true
+        );
+        $e[] = array(
+            'type'    => 'file',
+            'req'     => false,
+            'name'    => 'file',
+            'display' => 'New Image'
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => 'caption',
+            'display' => 'Image Caption',
+            'opts' => array('size' => 35)
+        );
+        $e[] = array(
+            'type'    => 'static',
+            'req'     => false,
+            'name'    => 'current_image_thumb',
+            'display' => 'Current Image'
+        );
+        $e[] = array(
+            'type'    => 'hidden',
+            'req'     => false,
+            'name'    => 'image',
+        );
+        if (!empty($this->_defaultValues['image'])
+            || ($this->isSubmitted() && $this->getSubmitValue('remove_image'))
+        ) {
+            $e[] = array(
+                'type'    => 'checkbox',
+                'req'     => false,
+                'name'    => 'remove_image',
+                'display' => 'Remove Current Image'
+            );
+        }
+
+        $e[] = array(
+            'type'    => 'html',
+            'req'     => false,
+            'name'    => '</tbody>',
+        );
+        $e[] = array(
+            'type'    => 'html',
+            'req'     => false,
+            'name'    => '<tbody id="attributes" class="section">',
+        );
+        $e[] = array(
+            'type'    => 'header',
+            'req'     => false,
+            'name'    => 'pageAttributesHdr',
+            'display' => 'Page Attributes'
+        );
+        if (defined('SHORT_URLS') && SHORT_URLS) {
+            $e[] = array(
+                'type'    => 'static',
+                'req'     => false,
+                'name'    => 'short_url_instructions',
+                'display' => '',
+                'opts' => "Short URL's Must not contain any spaces or non alpha characters.<br>Only A-Z, a-z, 0-9, _(underscore), -(dash) allowed"
+            );
+            $e[] = array(
+                'type'    => 'text',
+                'req'     => false,
+                'name'    => 'short_url',
+                'display' => 'Short URL',
+            );
+        }
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => 'keyword',
+            'display' => 'Keyword',
+            'opts' => array('size' => 35)
+        );
+        $e[] = array(
+            'type'    => 'advcheckbox',
+            'req'     => false,
+            'name'    => 'paragraph_links',
+            'display' => 'Paragraph Links',
+            'opts'    => 'Show links to the paragraph headlines at the top of the page',
+            'val'     => array(0, 1)
+        );
+
+        if (!is_null($memRoot)) {
+            $e[] = array(
+                'type'    => 'html',
+                'req'     => false,
+                'name'    => '</tbody>',
+            );
+            $e[] = array(
+                'type'    => 'html',
+                'req'     => false,
+                'name'    => '<tbody id="members" class="section">',
+            );
+            $e[] = array(
+                'type'    => 'header',
+                'req'     => false,
+                'name'    => 'memberDbHdr',
+                'display' => $pluralMem,
+            );
+            $e[] = array(
+                'type'    => 'advcheckbox',
+                'req'     => false,
+                'name'    => 'include_members',
+                'display' => "Members",
+                'opts' => "Include Members On Page",
+                'val'     => array(0, 1)
+            );
+            $e[] = array(
+                'type' => 'html',
+                'req'  => false,
+                'name' => '<tr>
+                    <td class="labelcell"></td>
+                    <td class="fieldcell">
+                        To include all members, turn this feature on and
+                        don\'t select categories or regions below.<br>
+                        To restrict to specific categories, select desired
+                        categories under Category Filter.<br>
+                        To restrict to specific regions, select desired
+                        regions under Region Filter.
+                    </td>
+                </tr>'
+            );
+            //  Note that we call to populate this select list at the
+            //  bottom of this function after the element is made
+            //  so we load attributes (classes) into each option as needed.
+            $e[] = array(
+                'type'    => 'select',
+                'req'     => false,
+                'name'    => 'member_categories',
+                'display' => 'Category Filter<br>(none = all)',
+                'opts'    => array(),
+                'att'     => array(
+                    'multiple' => 'multiple',
+                    'size'     => 7,
+                    'title' => '-- Select to Add --',
+                    'id' => 'MemberCategories'
+                )
+            );
+            if ($hasRegions) {
+            $e[] = array(
+                'type'    => 'select',
+                'req'     => false,
+                'name'    => 'member_regions',
+                'display' => 'Region Filter<br>(none = all)',
+                'opts'    => array(),
+                'att'     => array(
+                    'multiple' => 'multiple',
+                    'size'     => 7,
+                    'title' => '-- Select to Add --',
+                    'id' => 'MemberRegions'
+                )
+            );
+            }
+            $e[] = array(
+                'type'    => 'advcheckbox',
+                'req'     => false,
+                'name'    => 'include_member_map',
+                'display' => "Google Map",
+                'opts' => "Include Map On Page",
+                'val'     => array(0, 1)
+            );
+            $e[] = array(
+                'type'    => 'advcheckbox',
+                'req'     => false,
+                'name'    => 'search_form',
+                'display' => "Search Box",
+                'opts' => 'Include Search Box',
+                'val'     => array(0, 1)
+            );
+        }
+
+        if (!is_null($cpnRoot)) {
+            $e[] = array(
+                'type'    => 'html',
+                'req'     => false,
+                'name'    => '</tbody>',
+            );
+            $e[] = array(
+                'type'    => 'html',
+                'req'     => false,
+                'name'    => '<tbody id="coupons" class="section">',
+            );
+            $e[] = array(
+                'type'    => 'header',
+                'req'     => false,
+                'name'    => 'couponsHdr',
+                'display' => $pluralCpn
+            );
+            $e[] = array(
+                'type'    => 'advcheckbox',
+                'req'     => false,
+                'name'    => 'include_coupons',
+                'display' => "Coupons",
+                'opts' => "Include Coupons On Page",
+                'val'     => array(0, 1)
+            );
+            $e[] = array(
+                'type' => 'html',
+                'req'  => false,
+                'name' => '<tr>
+                    <td class="labelcell"></td>
+                    <td class="fieldcell">
+                        To include all coupons, turn this feature on and
+                        don\'t select categories below.<br>
+                        To restrict to specific categories, select desired
+                        categories under Categories Filter.
+                    </td>
+                </tr>'
+            );
+            //  Note that we call to populate this select list at the
+            //  bottom of this function after the element is made
+            //  so we load attributes (classes) into each option as needed.
+            $e[] = array(
+                'type'    => 'select',
+                'req'     => false,
+                'name'    => 'coupon_categories',
+                'display' => 'Category Filter<br>(none = all)',
+                'opts'    => $this->_getCouponCategories($dbh),
+                'att'     => array(
+                    'multiple' => 'multiple',
+                    'size'     => 4,
+                    'title' => '-- Select to Add --',
+                    'id' => 'CouponCategories'
+                )
+            );
+        }
+
+        if (defined('HOME_HEADLINES') && HOME_HEADLINES) {
+            $e[] = array(
+                'type'    => 'html',
+                'req'     => false,
+                'name'    => '</tbody>',
+            );
+            $e[] = array(
+                'type'    => 'html',
+                'req'     => false,
+                'name'    => '<tbody id="headlines" class="section">',
+            );
+            $e[] = array(
+                'type'    => 'header',
+                'req'     => false,
+                'name'    => 'homePageHeadlinesHdr',
+                'display' => 'Home Page Headlines'
+            );
+            $e[] = array(
+                'type'    => 'advcheckbox',
+                'req'     => false,
+                'name'    => 'headline',
+                'display' => 'Headline',
+                'opts' => 'Include this page in the Home Page Headlines',
+                'val'     => array(0, 1)
+            );
+            $e[] = array(
+                'type'    => 'text',
+                'req'     => false,
+                'name'    => 'headline_intro',
+                'display' => 'Intro',
+                'opts' => array('size' => 55),
+                'noCharLimit' => true
+            );
+        }
+        $e[] = array(
+            'type'    => 'html',
+            'req'     => false,
+            'name'    => '</tbody>',
+        );
+        if ($_SERVER['PHP_AUTH_USER'] == 'MediaAdmin' || DEVELOPMENT) {
+            $e[] = array(
+                'type'    => 'html',
+                'req'     => false,
+                'name'    => '<tbody id="metadata" class="section">',
+            );
+            $e[] = array(
+                'type'    => 'header',
+                'req'     => false,
+                'name'    => 'metaHdr',
+                'display' => 'Metadata Information'
+            );
+            $e[] = array(
+                'type'    => 'text',
+                'req'     => false,
+                'name'    => 'meta_title',
+                'display' => 'Title Tag',
+                'opts' => array('size' => 35)
+            );
+            $e[] = array(
+                'type'    => 'textarea',
+                'req'     => false,
+                'name'    => 'meta_description',
+                'display' => 'Description',
+                'opts'    => array(
+                    'cols' => 40,
+                    'rows' => 5
+                ),
+            );
+            $e[] = array(
+                'type'    => 'html',
+                'req'     => false,
+                'name'    => '</tbody>',
+            );
+        } else {
+            $e[] = array(
+                'type'    => 'hidden',
+                'req'     => false,
+                'name'    => 'meta_title'
+            );
+            $e[] = array(
+                'type'    => 'hidden',
+                'req'     => false,
+                'name'    => 'meta_description'
+            );
+        }
+        $e[] = array(
+            'type'    => 'html',
+            'req'     => false,
+            'name'    => '<tbody id="template" class="section">',
+        );
+        $e[] = array(
+            'type'    => 'header',
+            'req'     => false,
+            'name'    => 'templatesHdr',
+            'display' => 'Page Layout'
+        );
+        $e[] = array(
+            'type'       => 'group',
+            'req'        => false,
+            'name'       => 'page_layout',
+            'group'      => $templates,
+            'seperator'  => '',
+            'appendName' => false
+        );
+        $e[] = array(
+            'type'    => 'html',
+            'req'     => false,
+            'name'    => '</tbody>',
+        );
+        if (isset($_GET['id']) && ctype_digit($_GET['id'])) {
+            $e[] = array(
+                'type'    => 'html',
+                'req'     => false,
+                'name'    => '<tbody id="data" class="section">',
+            );
+            $e[] = array(
+                'type'    => 'header',
+                'req'     => false,
+                'name'    => 'templatesHdr',
+                'display' => 'Page Information'
+            );
+            $e[] = array(
+                'type'    => 'static',
+                'req'     => false,
+                'name'    => 'page_id',
+                'display' => 'Page ID',
+            );
+            $e[] = array(
+                'type'    => 'static',
+                'req'     => false,
+                'name'    => 'active_alt',
+                'display' => 'State',
+            );
+            $e[] = array(
+                'type'    => 'static',
+                'req'     => false,
+                'name'    => 'revised',
+                'display' => 'Revised',
+            );
+            $e[] = array(
+                'type'    => 'static',
+                'req'     => false,
+                'name'    => 'created',
+                'display' => 'Created Date',
+            );
+            $e[] = array(
+                'type'    => 'static',
+                'req'     => false,
+                'name'    => 'last_modified',
+                'display' => 'Last Modified Date',
+            );
+            $e[] = array(
+                'type'    => 'html',
+                'req'     => false,
+                'name'    => '</tbody>',
+            );
+        }
+        $e[] = array(
+            'type'    => 'html',
+            'req'     => false,
+            'name'    => '<tbody>',
+        );
+
+        //  If we are editing a page, show three submit buttons
+        //  otherwise, just show one insert button.
+        $e[] = array(
+            'type'       => 'group',
+            'req'        => false,
+            'name'       => 'submit_buttons',
+            'group'      => $submitBtns,
+            'label'      => '',
+            'seperator'  => '',
+            'appendName' => false,
+        );
+
+        $this->setupElements($e);
+
+        //  Do the same for the pages
+        if ($_GET['rt'] == 'EditPage' && $showParentPage) {
+            $this->loadParentPages($dbh);
+        }
+
+        //  Load the member categories after the elements have been created
+        //  so we can get more control how the options are rendered
+        //  ie (adding classes to them)
+        if (defined('MEMBERS_DB') && MEMBERS_DB) {
+            $this->loadMemberCategories($dbh);
+            if ($hasRegions) {
+                $this->loadMemberRegions($dbh);
+            }
+        }
+    }
+
+    //  }}}
+    //  {{{ configureFilters()
+
+    /**
+     * Form filter definitions
+     *
+     * Applies a data filter for the given fields when the form is submitted
+     *
+     * @return void
+     * @access public
+     */
+    public function configureFilters()
+    {
+        $f = array();
+
+        $f[] = array(
+            'element' => '__ALL__',
+            'filter'  => 'trim'
+        );
+
+        $this->setupFilters($f);
+    }
+
+    //  }}}
+    //  {{{ configureForm()
+
+    /**
+     * Bundle all form configuration calls into one function call
+     *
+     * @param PDO                                    $dbh     Database handler
+     * @param Toolkit_Toolbox_GatewayFactoryAbstract $factory Gateway factory
+     * @param Toolkit_FileServer_ImageApater         $is      Image Server
+     * @param Config_Container                       $memRoot (optional) Configuration object
+     * @param Config_Container                       $cpnRoot (optional) Configuration object
+     *
+     * @return void
+     * @access public
+     */
+    public function configureForm(
+        PDO $dbh,
+        Toolkit_Toolbox_GatewayFactoryAbstract $factory,
+        Toolkit_FileServer_ImageAdapter $is,
+        Config_Container $memRoot = null,
+        Config_Container $cpnRoot = null
+    ) {
+        $this->configureDefaults($factory, $_GET['id']);
+        $this->configureElements($dbh, $memRoot, $cpnRoot, $_GET['id']);
+        $this->configureRules($is);
+        $this->configureFilters();
+        $this->configureConstants($dbh);
+    }
+
+    //  }}}
+    //  {{{ configureRules()
+
+    /**
+     * Form rule definitions
+     *
+     * Adds validation rules for the given fields
+     *
+     * @param Toolkit_FileServer_ImageAdapter $is Image Server
+     *
+     * @return void
+     * @access public
+     */
+    public function configureRules(Toolkit_FileServer_ImageAdapter $is)
+    {
+        $r = array();
+
+        //  Form Rules
+        $r[] = array(
+            'element'    => 'short_url',
+            'message'    => 'ERROR: Short URLs may only contain alpha numeric characters plus - (dash) or _ (underscore)!',
+            'type'       => 'ShortUrl',
+            'format'     => null,
+            'validation' => $this->validationType,
+            'reset'      => false,
+            'force'      => false
+        );
+        if (!empty($_FILES['file']['tmp_name'])) {
+            $r[] = array(
+                'element' => 'file',
+                'message'    => 'ERROR: Incorrect File Type (.gif, .png, .jpg) only!',
+                'type' => 'mimetype',
+                'format' => $is->getAllowedMimeTypes(),
+                'validation' => 'server',
+                'reset' => false,
+                'force' => false
+            );
+        }
+        $r[] = array(
+            'element' => 'file',
+            'message' => 'ERROR: Error uploading image!',
+            'type' => 'Image',
+            'format' => array(
+                'form' => $this,
+                'fieldName' => 'file',
+                'imageField' => 'image',
+                'is' => $is,
+                'deleteExistingImage' => false,
+                'injectImage' => array('tgtElement' => 'current_image_thumb')
+            ),
+            'validation' => 'server',
+            'reset' => false,
+            'force' => false
+        );
+
+        $this->setupRules($r);
+    }
+
+    //  }}}
+
+    // {{{ getCouponCategories()
+
+    /**
+     * Fetches all coupon categories
+     *
+     * @param PDO $dbh Database handler
+     *
+     * @return array coupon categories
+     */
+    private function _getCouponCategories(PDO $dbh)
+    {
+        $sql = "
+            SELECT *
+              FROM coupon_category
+             ORDER BY name";
+        $couponCats = array();
+        foreach ($dbh->query($sql) as $row) {
+            $couponCats[$row['id']] = $row['name'];
+        }
+        return $couponCats;
+    }
+
+    // }}}
+
+    //  {{{ loadParentPages()
+
+    /**
+     * Load option elements into the parent select list
+     *
+     * These options are loaded via this seperate function vs inline w/ the
+     * element definition b/c we need a little more control defining
+     * the class names for each option so they will render nice when a user
+     * is looking at the list.
+     *
+     * @param PDO $dbh Database handler
+     *
+     * @return void
+     * @throws PDOException throws exception on sql error
+     * @access public
+     */
+    public function loadParentPages(PDO $dbh)
+    {
+        try {
+            //  Get a tree list of categories in linear order with
+            //  category keys in the values and their level in the tree
+            //  in the value
+            $c = Toolkit_Common::getHierarchicalTreeStructure(
+                $dbh,
+                'pages',
+                'id',
+                'parent',
+                'pos',
+                0,
+                $this->maxDepth
+            );
+
+            //  unset the home page, this is never an option to have children
+            //  underneath it.
+            unset($c[HOME_ID]);
+
+            //  If we are editing a page, then we don't want that page
+            //  to show up as an option in the select list.
+            if (is_numeric($_GET['id'])) {
+                reset($c);
+                //  Get us to the point in the array were this page is located
+                while (key($c) != $_GET['id'] && current($c) !== false) {
+                    next($c);
+                }
+                //  Make sure we didn't traverse off the end of the array
+                if (current($c) !== false) {
+                    //  get the starting level we are currently at
+                    $sl = current($c);
+                    //  remove this page (the one we're editing) from the
+                    //  array and advance the internal array pointer
+                    unset($c[key($c)]);
+                    //  now we need to make sure all sub pages beneath this
+                    //  page are also not being shown
+
+                    //  while we don't traverse off the end of the array
+                    while (current($c) !== false) {
+                        //  get the current sub level we are at
+                        $csl = current($c);
+                        //  if the current sub-level is the same as the
+                        //  starting level, that means we have traversed through
+                        //  all the sub-pages and can break out of the loop
+                        if ($csl <= $sl) {
+                            break;
+                        } else {
+                            //  we are still in a sub-level page, so unset
+                            //  this page so it doesn't show, and advance
+                            //  the internal array pointer
+                            unset($c[key($c)]);
+                        }
+                    }
+                }
+            }
+
+            //  Get all the data about each category
+            $sql = "
+                SELECT *
+                  FROM pages
+                 WHERE id = ?";
+
+            $stmt = $dbh->prepare($sql);
+            //  Get the member categories select list element
+            $e =& $this->getElement('parent');
+            foreach ($c as $i => $j) {
+                $stmt->execute(array($i));
+                $row = $stmt->fetch();
+                //  the class level is always 1 less than what is reported
+                //  from our $c array
+                $x = $j - 1;
+                //  Add the option data to the select list.
+                $e->addOption(
+                    $row['navigation_name'],
+                    $i,
+                    array('class' => "level-$x")
+                );
+            }
+        } catch (PDOException $e) {
+            Toolkit_Logger::logException('DB error', $e);
+            throw new Toolkit_Toolbox_Exception(
+                "Error loading parent pages"
+            );
+        }
+    }
+
+    //  }}}
+    //  {{{ loadMemberCategories()
+
+    /**
+     * Loads member categories into the select list
+     *
+     * Gets an array structure of the member categories in a linear tree order
+     * Then walk through the array and load each category into the select list
+     *
+     * @param PDO $dbh Database handler
+     *
+     * @return void
+     * @access public
+     */
+    public function loadMemberCategories(PDO $dbh)
+    {
+        try {
+            //  Get a tree list of categories in linear order with
+            //  category keys in the values and their level in the tree
+            //  in the value
+            $c = Toolkit_Common::getHierarchicalTreeStructure(
+                $dbh,
+                'category',
+                'category_id',
+                'parent_id',
+                'name'
+            );
+
+            //  Get all the data about each category
+            $sql = "
+                SELECT *
+                  FROM category
+                 WHERE category_id = ?";
+
+            $stmt = $dbh->prepare($sql);
+            //  Get the member categories select list element
+            $e =& $this->getElement('member_categories');
+            if (is_array($c)) {
+                foreach ($c as $i => $j) {
+                    $stmt->execute(array($i));
+                    $row = $stmt->fetch();
+                    //  the class level is always 1 less than what is reported
+                    //  from our $c array
+                    $x = $j - 1;
+                    //  Add the option data to the select list.
+                    $e->addOption($row['name'], $i, array('class' => "level-$x"));
+                }
+            }
+        } catch (PDOException $e) {
+            Toolkit_Logger::logException('DB error', $e);
+            throw new Toolkit_Toolbox_Exception(
+                "Error loading member categories"
+            );
+        }
+    }
+
+    //  }}}
+    //  {{{ loadMemberRegions()
+
+    /**
+     * Loads member Cities into the select list
+     *
+     * Gets an array structure of the member Cities in a sorted order by name
+     * Then walk through the array and load each category into the select list
+     *
+     * @param PDO $dbh Database handler
+     *
+     * @return void
+     * @access public
+     */
+    public function loadMemberRegions(PDO $dbh)
+    {
+        try {
+            //  Get all the data about each category
+            $sql = "
+               SELECT *
+                FROM region
+            ORDER BY region_name";
+
+            $stmt = $dbh->query($sql);
+            //  Get the member categories select list element
+            $e =& $this->getElement('member_regions');
+            while ($row = $stmt->fetch()) {
+                //  Add the option data to the select list.
+                $e->addOption($row['region_name'], $row['region_id']);
+            }
+        } catch (PDOException $e) {
+            Toolkit_Logger::logException('DB error', $e);
+            throw new Toolkit_Toolbox_Exception(
+                "Error loading member regions"
+            );
+        }
+    }
+
+    //  }}}
+
+    //  {{{ setMaxDepth()
+
+    /**
+     * Sets the max depth level that the parent page select list will show
+     *
+     * @param integer $md New max depth
+     *
+     * @return void
+     * @access public
+     */
+    public function setMaxDepth($md)
+    {
+        $this->maxDepth = $md;
+    }
+
+    //  }}}
+    //  {{{ setupRenderers()
+
+    /**
+     * Custom rendering templates for special fields on the form
+     *
+     * @return void
+     * @access protected
+     */
+    protected function setupRenderers()
+    {
+        parent::setupRenderers();
+        $renderer =& $this->defaultRenderer();
+
+        $required  = '<!-- BEGIN required -->';
+        $required .=    '<span class="req"> * </span>';
+        $required .= '<!-- END required -->';
+
+        $error     = '<!-- BEGIN error -->';
+        $error    .=    '<div class="req">{error}</div>';
+        $error    .= '<!-- END error -->';
+
+        $baseUrl = MEDIA_BASE_URL;
+
+        $renderer->setFormTemplate(
+            "<div class=\"webform\">\n
+                <form{attributes}>\n
+                    <div class=\"hiddenElements\">
+                        {hidden}
+                    </div>
+                    {requiredNote}
+                    <table>\n
+                        <tbody id=\"pageContent\" class=\"section\"> {content} </tbody>\n
+                    </table>\n
+                </form>\n
+            </div>"
+        );
+
+        $renderer->setElementTemplate(
+            "<tr>\n
+                <td class=\"labelcell\">$required{label}</td>\n
+                <td class=\"fieldcell\">{$error}{$baseUrl}{element}/</td>\n
+            </tr>\n",
+            'short_url'
+        );
+        $renderer->setElementTemplate(
+            "<tr align=\"center\">\n
+                <td colspan=\"2\">$required{label}$error{element}</td>
+            </tr>",
+            'description'
+        );
+        $renderer->setElementTemplate(
+            "<tr align=\"center\">\n
+                <td colspan=\"2\">$required{label}$error{element}</td>
+            </tr>",
+            'submit_buttons'
+        );
+        $renderer->setElementTemplate(
+            "<tr align=\"center\">\n
+                <td colspan=\"2\">$required{label}$error{element}</td>\n
+            </tr>",
+            'edit'
+        );
+
+        $renderer->setElementTemplate(
+            "<tr align=\"center\">\n
+                <td colspan=\"2\">$required{label}$error{element}</td>\n
+            </tr>\n",
+            'page_layout'
+        );
+        $renderer->setGroupTemplate(
+            "<table id=\"templates\">\n
+                <tr>{content}</tr>\n
+            </table>\n",
+            'page_layout'
+        );
+        $renderer->setGroupElementTemplate(
+            "<td>\n
+                {element}<br>{label}\n
+            </td>\n",
+            'page_layout'
+        );
+    }
+
+    //  }}}
+
+    //  {{{ toHtml()
+
+    /**
+     * Handles how to display the current step the user is at in the form
+     *
+     * @return string rendered html form
+     * @access public
+     */
+    public function toHtml()
+    {
+        $GLOBALS['topScripts'][]
+            = MEDIA_APP_BASE_URL . 'libjs/jquery/jquery-1.4.2.min.js';
+        $GLOBALS['bottomScripts'][]
+            = CKEDITOR_JS . '';
+        $GLOBALS['bottomScripts'][]
+            = MEDIA_APP_BASE_URL . 'libjs/plugins/asmselect/1.0.4a/jquery.asmselect.js';
+        $GLOBALS['bottomScripts'][]
+            = MEDIA_BASE_URL . 'Toolkit/Toolbox/libjs/edit-page.js';
+
+        $GLOBALS['styleSheets'][]
+            = MEDIA_APP_BASE_URL . 'libjs/plugins/asmselect/1.0.4a/jquery.asmselect.css';
+        $GLOBALS['styleSheets'][]
+            = MEDIA_BASE_URL . 'css/contactform.css';
+        $GLOBALS['styleSheets'][]
+            = MEDIA_BASE_URL . 'Toolkit/Toolbox/styles.css';
+
+        $this->setupRenderers();
+
+        return parent::toHtml();
+    }
+
+    //  }}}
+}
diff --git a/Toolkit/Toolbox/Forms/EditParagraph.php b/Toolkit/Toolbox/Forms/EditParagraph.php
new file mode 100644 (file)
index 0000000..f83c4a4
--- /dev/null
@@ -0,0 +1,503 @@
+<?php
+//  vim:set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker syntax=php:
+
+/**
+ * Edit a page paragraph form
+ *
+ * PHP version 5
+ *
+ * @category Toolbox
+ * @package  Toolkit_Toolbox
+ * @author   Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @license  http://www.gaslightmedia.com Gaslightmedia
+ * @version  CVS: $Id: EditParagraph.php,v 1.4 2010/07/20 18:39:40 jamie Exp $
+ * @link     http://demo.gaslightmedia.com
+ */
+
+require_once BASE . 'Toolkit/Forms/Rules/Image.php';
+
+/**
+ * Edit a page paragraph form
+ *
+ * @category  Toolbox
+ * @package   Toolkit_Toolbox
+ * @author    Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2009 Jamie Kahgee
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @link      http://demo.gaslightmedia.com
+ */
+class Toolkit_Toolbox_Forms_EditParagraph extends Toolkit_FormBuilder
+{
+    //  {{{ configureDefaults()
+
+    /**
+     * Initializes default form values
+     *
+     * @param Toolkit_Toolbox_GatewayFactoryAbstract $factory Gateway factory
+     * @param integer                                $id      pargraph id (optional)
+     * @return void
+     * @access public
+     */
+    public function configureDefaults(
+        Toolkit_Toolbox_GatewayFactoryAbstract $factory,
+        Toolkit_Toolbox_FileExtension $fileExtension,
+        $id = null
+    ) {
+        if (!is_null($id)) {
+            $gateway = $factory->createGateway();
+            $paragraph = $gateway->find($id);
+            if (   isset($paragraph['files'])
+                && is_array($paragraph['files'])
+            ) {
+                $files = array();
+                foreach ($paragraph['files'] as $file) {
+                    $linkRenderer
+                        = Toolkit_Template_Page_FileLink_Factory::createLinkRenderer(
+                            $file
+                    );
+                    $fileLink = $linkRenderer->getLink();
+                    $extImg = $fileExtension->getImage($file['filename']);
+                    $id = preg_replace('/[^A-Za-z0-9]/', '', $file['urltext']);
+                    $files[] = '
+                        <li id="pFile-'.$file['id'].'" class="ui-state-default">
+                            <span class="ui-icon ui-icon-arrowthick-2-n-s"></span>
+                            '.$fileLink.'
+                            File Name:
+                            <input type="text" size="35" name="uploaded_files[urltext][]" value="'.htmlspecialchars($file['urltext']).'"><br>
+                            <label>
+                            <input type="checkbox" name="deleteFile[]" value="'.htmlspecialchars($file['filename']).'">
+                            Remove File
+                            </label>
+                            <input type="hidden" value="'.htmlspecialchars($file['filename']).'" name="uploaded_files[filename][]">
+                            <input type="hidden" value="'.htmlspecialchars($file['bytes']).'" name="uploaded_files[bytes][]">
+                            <input type="hidden" value="'.htmlspecialchars($file['type']).'" name="uploaded_files[type][]">
+                        </li>';
+                }
+                $paragraph['uploaded_files'] = '<ul class="files">' . implode('', $files) . '</ul>';
+            }
+        } else {
+            $paragraph = array(
+                'current_image_thumb' => 'Image not yet uploaded',
+                'page' => $_GET['pageid'],
+                'active' => true,
+            );
+        }
+
+        $this->setupDefaults($paragraph);
+    }
+
+    //  }}}
+    //  {{{ configureElements()
+
+    /**
+     * Form element definitions
+     *
+     * @param PDO $dbh Database handler
+     *
+     * @return void
+     * @access public
+     */
+    public function configureElements(PDO $dbh)
+    {
+        $e = array();
+        //  Grouped Elements are defined here.
+        $submitBtns = array();
+
+        $submitBtns[] = array(
+            'type'    => 'submit',
+            'req'     => false,
+            'name'    => 'saveParagraph',
+            'display' => 'Save Paragraph'
+        );
+        $submitBtns[] = array(
+            'type'    => 'submit',
+            'req'     => false,
+            'name'    => 'cancel',
+            'display' => 'Cancel'
+        );
+        $submitBtns[] = array(
+            'type'    => 'submit',
+            'req'     => false,
+            'name'    => 'deleteParagraph',
+            'display' => 'Delete Paragraph'
+        );
+
+        //  All Elements are created here.
+        //  This includes group element definitions.
+        $e[] = array(
+            'type'    => 'hidden',
+            'req'     => false,
+            'name'    => 'id'
+        );
+        $e[] = array(
+            'type'    => 'hidden',
+            'req'     => false,
+            'name'    => 'active'
+        );
+        if ($_GET['rt'] == 'Paragraphs') {
+            $e[] = array(
+                'type'    => 'select',
+                'req'     => false,
+                'name'    => 'page',
+                'display' => 'Page',
+                'opts'    => array(),
+            );
+        } else {
+            $e[] = array(
+                'type'    => 'hidden',
+                'req'     => false,
+                'name'    => 'page'
+            );
+        }
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => 'title',
+            'display' => 'Title',
+            'opts' => array('size' => 40)
+        );
+        $e[] = array(
+            'type'    => 'textarea',
+            'req'     => false,
+            'name'    => 'description',
+            'opts'    => array(
+                'cols' => 60,
+                'rows' => 60,
+                'id' => 'description'
+            ),
+            'noCharLimit' => true
+        );
+        $e[] = array(
+            'type'    => 'file',
+            'req'     => false,
+            'name'    => 'file',
+            'display' => 'New Image'
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => 'caption',
+            'display' => 'Image Caption',
+            'opts' => array('size' => 35)
+        );
+        $e[] = array(
+            'type'    => 'static',
+            'req'     => false,
+            'name'    => 'current_image_thumb',
+            'display' => 'Current Image'
+        );
+        $e[] = array(
+            'type'    => 'hidden',
+            'req'     => false,
+            'name'    => 'image',
+        );
+        if (!empty($this->_defaultValues['image'])) {
+            $e[] = array(
+                'type'    => 'checkbox',
+                'req'     => false,
+                'name'    => 'remove_image',
+                'display' => 'Remove Current Image'
+            );
+        }
+        $e[] = array(
+            'type' => 'advcheckbox',
+            'req' => false,
+            'name' => 'back_to_top',
+            'display' => "Insert 'Back to Top' link",
+            'opts' => 'Yes',
+            'val' => array(0, 1)
+        );
+
+        $e[] = array(
+            'type'    => 'header',
+            'req'     => false,
+            'name'    => 'templatesHdr',
+            'display' => 'Files'
+        );
+        $e[] = array(
+            'type'    => 'text',
+            'req'     => false,
+            'name'    => 'fileurltext',
+            'display' => 'New File Name',
+            'opts' => array('size' => 35)
+        );
+        $e[] = array(
+            'type' => 'file',
+            'req' => false,
+            'name' => 'filename',
+            'display' => 'New File',
+            'opts' => array('id' => 'filename')
+        );
+        $e[] = array(
+            'type' => 'static',
+            'req' => false,
+            'name' => 'uploaded_files',
+            'display' => 'Uploaded Files',
+            'opts' => '<ul class="files"></ul>'
+        );
+
+        //  If we are editing a page, show three submit buttons
+        //  otherwise, just show one insert button.
+        $e[] = array(
+            'type'       => 'group',
+            'req'        => false,
+            'name'       => 'submit_buttons',
+            'group'      => $submitBtns,
+            'label'      => '',
+            'seperator'  => '',
+            'appendName' => false,
+        );
+
+        $this->setupElements($e);
+        //  Do the same for the pages
+        if ($_GET['rt'] == 'Paragraphs') {
+            $this->loadParagraphPages($dbh);
+        }
+    }
+
+    //  }}}
+    //  {{{ configureFilters()
+
+    /**
+     * Form filter definitions
+     *
+     * Applies a data filter for the given fields when the form is submitted
+     *
+     * @return void
+     * @access public
+     */
+    public function configureFilters()
+    {
+        $f = array();
+
+        $f[] = array(
+            'element' => '__ALL__',
+            'filter'  => 'trim'
+        );
+
+        $this->setupFilters($f);
+    }
+
+    //  }}}
+    //  {{{ configureForm()
+
+    /**
+     * Bundle all form configuration calls into one function call
+     *
+     * @param PDO $dbh Database handler              $dbh     Database handler
+     * @param Toolkit_Toolbox_GatewayFactoryAbstract $factory Gateway factory
+     * @param Toolkit_FileServer_ImageAdapter        $is      Image adapter for file server
+     * @param Toolkit_FileServer_FileAdapter         $fs      File adapter for file server
+     *
+     * @return void
+     * @access public
+     */
+    public function configureForm(
+        PDO $dbh,
+        Toolkit_Toolbox_GatewayFactoryAbstract $factory,
+        Toolkit_FileServer_ImageAdapter $is,
+        Toolkit_FileServer_FileAdapter $fs,
+        Toolkit_Toolbox_FileExtension $fileExtension
+    ) {
+        $this->configureDefaults($factory, $fileExtension, $_GET['id']);
+        $this->configureElements($dbh);
+        $this->configureRules($is, $fs);
+        $this->configureFilters();
+    }
+
+    //  }}}
+    //  {{{ configureRules()
+
+    /**
+     * Form rule definitions
+     *
+     * Adds validation rules for the given fields
+     *
+     * @param Toolkit_FileServer_ImageAdapter $is Image adapter for file server
+     * @param Toolkit_FileServer_FileAdapter  $fs File adapter for file server
+     *
+     * @return void
+     * @access public
+     */
+    public function configureRules(
+        Toolkit_FileServer_ImageAdapter $is,
+        Toolkit_FileServer_FileAdapter $fs
+    ) {
+        $r = array();
+        //  Form Rules
+        if (!empty($_FILES['file']['tmp_name'])) {
+            $r[] = array(
+                'element' => 'file',
+                'message' => 'ERROR: Incorrect File Type (.gif, .png, .jpg) only!',
+                'type' => 'mimetype',
+                'format' => $is->getAllowedMimeTypes(),
+                'validation' => 'server',
+                'reset' => false,
+                'force' => false
+            );
+        }
+        $r[] = array(
+            'element' => 'file',
+            'message' => 'ERROR: Error uploading image!',
+            'type' => 'Image',
+            'format' => array(
+                'form' => $this,
+                'fieldName' => 'file',
+                'imageField' => 'image',
+                'is' => $is,
+                'deleteExistingImage' => false,
+                'injectImage' => array('tgtElement' => 'current_image_thumb')
+            ),
+            'validation' => 'server',
+            'reset' => false,
+            'force' => false
+        );
+
+        $this->setupRules($r);
+    }
+
+    //  }}}
+
+    //  {{{ loadParagraphPages()
+
+    /**
+     * Load option elements into the parent select list
+     *
+     * These options are loaded via this seperate function vs inline w/ the
+     * element definition b/c we need a little more control defining
+     * the class names for each option so they will render nice when a user
+     * is looking at the list.
+     *
+     * @param PDO $dbh Database handler
+     *
+     * @return void
+     * @throws PDOException throws exception on sql error
+     * @access public
+     */
+    public function loadParagraphPages(PDO $dbh)
+    {
+        try {
+            //  Get a tree list of categories in linear order with
+            //  category keys in the values and their level in the tree
+            //  in the value
+            $c = Toolkit_Common::getHierarchicalTreeStructure(
+                $dbh,
+                'pages',
+                'id',
+                'parent',
+                'pos'
+            );
+
+            //  Get all the data about each category
+            $sql = "
+                SELECT *
+                  FROM pages
+                 WHERE id = ?";
+
+            $stmt = $dbh->prepare($sql);
+            //  Get the member categories select list element
+            $e =& $this->getElement('page');
+            foreach ($c as $i => $j) {
+                $stmt->execute(array($i));
+                $row = $stmt->fetch(PDO::FETCH_ASSOC);
+                //  the class level is always 1 less than what is reported
+                //  from our $c array
+                $x = $j - 1;
+                //  Add the option data to the select list.
+                $e->addOption($row['navigation_name'], $i, array('class' => "level-$x"));
+            }
+        } catch (PDOException $e) {
+            Toolkit_Logger::logException('DB error', $e);
+            throw new Toolkit_Toolbox_Exception(
+                "Error loading parent pages for paragraph"
+            );
+        }
+    }
+
+    //  }}}
+
+    //  {{{ setupRenderers()
+
+    /**
+     * Custom rendering templates for special fields on the form
+     *
+     * @return void
+     * @access protected
+     */
+    protected function setupRenderers()
+    {
+        parent::setupRenderers();
+        $renderer =& $this->defaultRenderer();
+
+        $required  = '<!-- BEGIN required -->';
+        $required .=    '<span class="req"> * </span>';
+        $required .= '<!-- END required -->';
+
+        $error     = '<!-- BEGIN error -->';
+        $error    .=    '<div class="req">{error}</div>';
+        $error    .= '<!-- END error -->';
+
+        $renderer->setElementTemplate(
+            "<tr align=\"center\">\n
+                <td colspan=\"2\">$required{label}$error{element}</td>
+            </tr>",
+            'submit_buttons'
+        );
+        $renderer->setElementTemplate(
+            "<tr align=\"center\">\n
+                <td colspan=\"2\">$required{label}$error{element}</td>\n
+            </tr>",
+            'insert'
+        );
+
+        $renderer->setElementTemplate(
+            "<tr align=\"center\">\n
+                <td colspan=\"2\">$required{label}$error{element}</td>
+            </tr>",
+            'description'
+        );
+    }
+
+    //  }}}
+
+    //  {{{ toHtml()
+
+    /**
+     * Handles how to display the current step the user is at in the form
+     *
+     * @param PDO $dbh Database handler
+     *
+     * @return string rendered html form
+     * @access public
+     */
+    public function toHtml(PDO $dbh)
+    {
+        $GLOBALS['topScripts'][]
+            = MEDIA_APP_BASE_URL . 'libjs/jquery/jquery-1.4.2.min.js';
+        $GLOBALS['bottomScripts'][]
+            = CKEDITOR_JS . '';
+        $GLOBALS['bottomScripts'][]
+            = MEDIA_APP_BASE_URL . 'libjs/plugins/ajaxUpload/3.9/ajaxupload.js';
+        $GLOBALS['bottomScripts'][]
+            = MEDIA_APP_BASE_URL . 'libjs/jqueryui/1.8.13/js/jquery-ui-1.8.13.custom.min.js';
+        $GLOBALS['bottomScripts'][]
+            = MEDIA_APP_BASE_URL . 'libjs/jqueryui/1.8.13/development-bundle/ui/minified/jquery.ui.sortable.min.js';
+        $GLOBALS['bottomScripts'][]
+            = MEDIA_BASE_URL . 'Toolkit/Toolbox/libjs/edit-paragraph.js';
+
+        $GLOBALS['styleSheets'][] = MEDIA_BASE_URL . 'css/contactform.css';
+        $GLOBALS['styleSheets'][] = MEDIA_BASE_URL . 'Toolkit/Toolbox/styles.css';
+        $GLOBALS['styleSheets'][]
+            = MEDIA_APP_BASE_URL . 'libjs/jqueryui/1.8.13/css/smoothness/jquery-ui-1.8.13.custom.css';
+
+        $this->setupRenderers();
+        $out
+            = ($_REQUEST['g'] == '1')
+            ? '<div class="form-success">Form Submit Successfull!</div>'
+            : '';
+
+        return $out . parent::toHtml();
+    }
+
+    //  }}}
+}
diff --git a/Toolkit/Toolbox/Forms/SearchForm.php b/Toolkit/Toolbox/Forms/SearchForm.php
new file mode 100644 (file)
index 0000000..d0e367e
--- /dev/null
@@ -0,0 +1,174 @@
+<?php
+class Toolkit_Toolbox_Forms_SearchForm
+    extends Toolkit_FormBuilder
+{
+    //  {{{ properties
+
+    /**
+     * Don't need to register any rules for this form.
+     * @var    array
+     * @access protected
+     */
+       protected $registeredRules = array();
+
+    //  }}}
+
+       //      {{{     configureConstants()
+
+    /**
+     * Form constant definitions
+     *
+     * @return void
+     * @access public
+     */
+       public function configureConstants()
+       {
+        $c = array('rt' => 'Search');
+
+               $this->setupConstants($c);
+       }
+
+       //      }}}
+       //      {{{     configureElements()
+
+    /**
+     * Form element definitions
+     *
+     * @return void
+     * @access public
+     */
+       public function configureElements()
+       {
+        $e = array();
+
+               //      All Elements are created here.
+        //  This includes group element definitions.
+               $e[] = array(
+            'type'    => 'hidden',
+            'req'     => false,
+            'name'    => 'rt'
+        );
+
+        $elements = array();
+
+               $elements[] = array(
+                       'type'    => 'text',
+                       'req'     => false,
+                       'name'    => 'q',
+                       'opts' => array(
+                               'size' => 75,
+                               'id' => 'q'
+                       )
+               );
+               $elements[] = array(
+                       'type'    => 'submit',
+                       'req'     => false,
+                       'name'    => 'submit',
+                       'display' => 'Search Page Titles'
+               );
+
+               $e[] = array(
+            'type'       => 'group',
+            'req'        => false,
+            'name'       => 'fields',
+            'group'         => $elements,
+            'seperator'  => '',
+            'appendName' => false
+        );
+               $this->setupElements($e);
+       }
+
+       //      }}}
+       //      {{{     configureFilters()
+
+    /**
+     * Form filter definitions
+     *
+        * Applies a data filter for the given fields when the form is submitted
+     *
+     * @return void
+     * @access public
+     */
+       public function configureFilters()
+       {
+        $f = array();
+
+               $f[] = array(
+            'element' => '__ALL__',
+            'filter'  => 'trim'
+        );
+
+        $this->setupFilters($f);
+       }
+
+       //      }}}
+    //  {{{ configureForm()
+
+    /**
+     * Bundle all form configuration calls into one function call
+     *
+     * @return void
+     * @access public
+     */
+    public function configureForm()
+       {
+               $this->configureConstants();
+               $this->configureElements();
+               $this->configureFilters();
+    }
+
+    //  }}}
+
+       //      {{{     setupRenderers()
+
+    /**
+     * Custom rendering templates for special fields on the form
+     *
+     * @return void
+     * @access protected
+     */
+       protected function setupRenderers()
+       {
+               parent::setupRenderers();
+               $renderer =& $this->defaultRenderer();
+
+               $required  = '<!-- BEGIN required -->';
+               $required .=    '<span class="req"> * </span>';
+               $required .= '<!-- END required -->';
+
+               $error     = '<!-- BEGIN error -->';
+               $error    .=    '<div class="req">{error}</div>';
+               $error    .= '<!-- END error -->';
+
+               $renderer->setElementTemplate(
+                       "<tr>\n
+                               <td colspan=\"2\">$required{label}$error{element}</td>\n
+                       </tr>\n",
+                       'fields'
+               );
+       }
+
+       //      }}}
+
+       //      {{{     toHtml()
+
+    /**
+     * Handles how to display the current step the user is at in the form
+     *
+     * @return string rendered html form
+     * @access public
+     */
+       public function toHtml()
+       {
+               $GLOBALS['styleSheets'][]
+                       = MEDIA_BASE_URL . 'css/contactform.css';
+               $GLOBALS['styleSheets'][]
+                       = MEDIA_BASE_URL . 'Toolkit/Toolbox/styles.css';
+
+               $this->setupRenderers();
+
+               return parent::toHtml();
+       }
+
+       //      }}}
+}
diff --git a/Toolkit/Toolbox/GatewayAbstract.php b/Toolkit/Toolbox/GatewayAbstract.php
new file mode 100644 (file)
index 0000000..627ffd1
--- /dev/null
@@ -0,0 +1,32 @@
+<?php
+abstract class Toolkit_Toolbox_GatewayAbstract
+{
+    //  {{{ properties
+
+    protected $dbh;
+
+    //  }}}
+    //  {{{ __construct()
+
+    public function __construct(PDO $dbh)
+    {
+        $this->dbh = $dbh;
+    }
+
+    //  }}}
+    //  {{{ find()
+
+    abstract public function find($id);
+
+    //  }}}
+    //  {{{ update()
+
+    abstract public function update(array $data, $id);
+
+    //  }}}
+    //  {{{ insert()
+
+    abstract public function insert(array $data);
+
+    //  }}}
+}
diff --git a/Toolkit/Toolbox/GatewayFactoryAbstract.php b/Toolkit/Toolbox/GatewayFactoryAbstract.php
new file mode 100644 (file)
index 0000000..7294fda
--- /dev/null
@@ -0,0 +1,12 @@
+<?php
+abstract class Toolkit_Toolbox_GatewayFactoryAbstract
+{
+       protected $dbh;
+
+       public function __construct(PDO $dbh)
+       {
+               $this->dbh = $dbh;
+       }
+
+       abstract public function createGateway();
+}
diff --git a/Toolkit/Toolbox/IndexController.php b/Toolkit/Toolbox/IndexController.php
new file mode 100644 (file)
index 0000000..bddb1e7
--- /dev/null
@@ -0,0 +1,36 @@
+<?php
+
+class Toolkit_Toolbox_IndexController extends Toolkit_BaseControllerAbstract
+       implements Toolkit_IController
+{
+       //      {{{     indexAction()
+
+       public function indexAction()
+       {
+               $searchForm = new Toolkit_Toolbox_Forms_SearchForm(
+                       'search_form',
+                       'get',
+                       MEDIA_BASE_URL . 'admin/toolbox.php?rt=search',
+                       null,
+                       null,
+                       true
+               );
+               $searchForm->configureForm();
+
+               $html = $searchForm->toHtml();
+
+               $toolboxConfig = new Config;
+               $toolboxConfigRoot =& $toolboxConfig->parseConfig(
+                       BASE . 'Toolkit/Toolbox/config.ini',
+                       'IniFile'
+               );
+               $tree = new Toolkit_Toolbox_PagesTree($toolboxConfigRoot);
+               $html .= $tree->toHtml(
+                       new Toolkit_Toolbox_PageGatewayPublish($this->registry->dbh)
+               );
+
+               return $html;
+       }
+
+       //      }}}
+}
diff --git a/Toolkit/Toolbox/ListDraftsController.php b/Toolkit/Toolbox/ListDraftsController.php
new file mode 100644 (file)
index 0000000..aac13e8
--- /dev/null
@@ -0,0 +1,26 @@
+<?php
+
+class Toolkit_Toolbox_ListDraftsController
+       extends Toolkit_Toolbox_IndexController implements Toolkit_IController
+{
+       //      {{{     indexAction()
+
+       public function indexAction()
+       {
+
+               $toolboxConfig = new Config;
+               $toolboxConfigRoot =& $toolboxConfig->parseConfig(
+                       BASE . 'Toolkit/Toolbox/config.ini',
+                       'IniFile'
+               );
+               $tree = new Toolkit_Toolbox_DraftPagesTree($toolboxConfigRoot);
+               $html .= $tree->toHtml(
+                       new Toolkit_Toolbox_PageGatewayDraft($this->registry->dbh)
+               );
+
+               return $html;
+       }
+
+       //      }}}
+}
+?>
diff --git a/Toolkit/Toolbox/Navigation.php b/Toolkit/Toolbox/Navigation.php
new file mode 100644 (file)
index 0000000..528d4b7
--- /dev/null
@@ -0,0 +1,105 @@
+<?php
+
+class Toolkit_Toolbox_Navigation extends Toolkit_NavigationAbstract
+       implements Toolkit_INavigation
+{
+       //      {{{     __construct()
+
+       public function __construct(
+               HTML_Menu $menu,
+               HTML_Menu_Renderer $rEngine
+       ) {
+               $this->menu      = $menu;
+               $this->rEngine   = $rEngine;
+               $this->currIndex = 'listToolbox';
+       }
+
+       //      }}}
+       //      {{{     setNavTemplates()
+
+       protected function setNavTemplates()
+       {
+               $tpl = '<li><a href="%s" title="%s">{Title}</a></li>';
+               $this->rEngine->setEntryTemplate(
+                       HTML_MENU_ENTRY_INACTIVE,
+                       sprintf($tpl, '{url}', '{desc}', '{Title}')
+               );
+               $this->rEngine->setEntryTemplate(
+                       HTML_MENU_ENTRY_ACTIVE,
+                       sprintf($tpl, '{url}', '{desc}', '{Title}')
+               );
+               $this->rEngine->setEntryTemplate(
+                       HTML_MENU_ENTRY_ACTIVEPATH,
+                       sprintf($tpl, '{url}', '{desc}', '{Title}')
+               );
+               $this->rEngine->setMenuTemplate('', '');
+               $this->rEngine->setRowTemplate('<ul class="admin_nav">', '</ul>');
+       }
+
+       //      }}}
+       //      {{{     setCurrentIndex()
+
+       protected function setCurrentIndex()
+       {
+               $this->menu->forceCurrentIndex($_GET['rt']);
+       }
+
+       //      }}}
+       //      {{{     getNavSructure()
+       //      @codeCoverageIgnoreStart
+
+    /**
+     * Sets up a multi dimensional array used for the nav structure
+        *
+        * @param Config_Container $c Application configuration
+     *
+     * @return array navigational array hash
+     * @access public
+     */
+       public function getNavStructure(Config_Container $c)
+       {
+        //  get reference to [listing type] section of config file
+        $appName = $c->getItem('section', 'conf')
+                         ->getItem('directive', 'applicationName')
+                         ->getContent();
+
+               $nav = array(
+                       'publishedPages' => array(
+                               'Title' => "Published Pages",
+                               'url' => MEDIA_BASE_URL . 'admin/toolbox.php',
+                               'desc' => "Display all the published pages",
+                       ),
+                       'draftPages' => array(
+                               'Title' => 'Page Drafts',
+                               'url' => MEDIA_BASE_URL . 'admin/toolbox.php?rt=ListDrafts',
+                               'desc' => 'Display all the page drafts'
+
+                       ),
+                       'editPage' => array(
+                               'Title' => "Create New Page",
+                               'url' => MEDIA_BASE_URL . 'admin/toolbox.php?rt=EditPage',
+                               'desc' => "Create a new {$appName} Page"
+                       ),
+               );
+
+               if (isset($_GET['rt']) && $_GET['rt'] == 'Paragraphs') {
+                       $nav['editParagraph'] = array(
+                               'Title' => "Create Page Paragraph",
+                               'url' => MEDIA_BASE_URL . "admin/toolbox.php?rt=Paragraphs&ac=edit&pageid={$_GET['pageid']}",
+                               'desc' => "Edit a Page Paragraph"
+                       );
+               } elseif (isset($_GET['rt']) && $_GET['rt'] == 'ParagraphsDraft') {
+                       $nav['editParagraph'] = array(
+                               'Title' => "Create Page Paragraph",
+                               'url' => MEDIA_BASE_URL . "admin/toolbox.php?rt=ParagraphsDraft&ac=edit&pageid={$_GET['pageid']}",
+                               'desc' => "Edit a Page Paragraph"
+                       );
+               }
+
+               return $nav;
+       }
+
+       //      @codeCoverageIgnoreEnd
+       //      }}}
+}
+?>
diff --git a/Toolkit/Toolbox/Page.php b/Toolkit/Toolbox/Page.php
new file mode 100644 (file)
index 0000000..681e56f
--- /dev/null
@@ -0,0 +1,322 @@
+<?php
+class Toolkit_Toolbox_Page
+{
+       //      {{{     properties
+       public $content;
+       public $members;
+       public $coupons;
+       public $attributes;
+       public $headlines;
+       public $metaData;
+
+       private $_createDate;
+       private $_lastModifiedDate;
+       private $_revisions;
+       private $_active;
+
+       //      }}}
+
+       //      {{{     __construct()
+       public function __construct(
+               Toolkit_Toolbox_ComponentFactory $cf,
+               array $data,
+               $id = null
+       ) {
+               if (!empty($id)) {
+                       if (ctype_digit($id)) {
+                               $this->_id = (int) $id;
+                       } else {
+                               throw new Toolkit_Toolbox_Exception("Invalid page id `$id`");
+                       }
+               }
+
+               $this->content           = $cf->getComponent('content', $data);
+               $this->members           = $cf->getComponent('members', $data);
+               $this->coupons           = $cf->getComponent('coupons', $data);
+               $this->attributes        = $cf->getComponent('attributes', $data);
+               $this->headlines         = $cf->getComponent('headlines', $data);
+               $this->metaData          = $cf->getComponent('metadata', $data);
+               $this->_createDate       = $data['createDate'];
+               $this->_lastModifiedDate = $data['lastModifiedDate'];
+               $this->_revisions        = $data['revisions'];
+       }
+
+       //      }}}
+
+       //      {{{     delete()
+
+       /**
+        * Delete a toolbox page
+        *
+        * @param PDO $dbh Database handler
+        * @param integer $id Toolbox page id to delete
+        *
+        * @return boolean Result of delete query
+        * @access public
+        * @throws Toolkit_Toolbox_Exception
+        */
+       public static function delete(PDO $dbh, $id)
+       {
+               try {
+                       $sql = "
+                DELETE FROM bus_category
+                 WHERE id = :id";
+
+                       $stmt = $dbh->prepare($sql);
+                       $stmt->bindParam(':id', $id, PDO::PARAM_INT);
+                       return $stmt->execute();
+               } catch(PDOException $e) {
+                       Toolkit_Logger::logException('DB Error', $e);
+                       throw new Toolkit_Toolbox_Exception(
+                               "Error deleting the toolbox page `$id`"
+                       );
+               }
+       }
+
+       //      }}}
+
+       //      {{{     fetch()
+
+       public static function fetch(PDO $dbh, $id)
+       {
+               if (!ctype_digit((string)$id)) {
+                       throw new Toolkit_Toolbox_Exception("Invalid page id `$id`");
+               }
+               try {
+                       $sql = "
+                               SELECT *
+                                 FROM bus_category
+                                WHERE id = :id";
+
+                       $stmt = $dbh->prepare($sql);
+                       $stmt->bindParam(':id', $id, PDO::PARAM_INT);
+                       $stmt->execute();
+                       $d = $stmt->fetch();
+
+                       $sql = "
+                               SELECT *
+                                 FROM bus_cat_member
+                                WHERE catid = :id";
+
+                       $stmt = $dbh->prepare($sql);
+                       $stmt->bindParam(':id', $id, PDO::PARAM_INT);
+                       $stmt->execute();
+
+                       $d['memberCat'] = array();
+                       while ($row = $stmt->fetch()) {
+                               $d['memberCat'][] = $row['memb_type'];
+                       }
+
+                       $sql = "
+                               SELECT *
+                                 FROM coupon_categories2toolbox_pages
+                                WHERE toolbox_catid = :id";
+
+                       $stmt = $dbh->prepare($sql);
+                       $stmt->bindParam(':id', $id, PDO::PARAM_INT);
+                       $stmt->execute();
+
+                       $d['couponCat'] = array();
+                       while ($row = $stmt->fetch()) {
+                               $d['couponCat'][] = $row['category_id'];
+                       }
+
+                       $sql = "
+                SELECT CASE
+                       WHEN active THEN 'Active'
+                       ELSE 'In-Active'
+                       END AS active
+                  FROM bus_category
+                 WHERE id = :id";
+
+                       $stmt = $dbh->prepare($sql);
+                       $stmt->bindParam(':id', $id, PDO::PARAM_INT);
+                       $stmt->execute();
+                       $row = $stmt->fetch();
+                       $d['active'] = $row['active'];
+
+                       $sql = "
+                               SELECT to_char(min(revision_timestamp), 'FMDay, DD Month FMHH12:MIpm') AS created,
+                                      to_char(max(revision_timestamp), 'FMDay, DD Month FMHH12:MIpm') AS last_modified,
+                                          count(*) AS total
+                  FROM bus_category_history
+                 WHERE id = :id";
+
+                       $stmt = $dbh->prepare($sql);
+                       $stmt->bindParam(':id', $id, PDO::PARAM_INT);
+                       $stmt->execute();
+                       $row = $stmt->fetch();
+                       $d['revisions'] = $row['total'];
+                       $d['createDate'] = $row['created'];
+                       $d['lastModifiedDate'] = $row['last_modified'];
+               } catch (PDOException $e) {
+                       Toolkit_Logger::logException('DB Error', $e);
+                       throw new Toolkit_Toolbox_Exception(
+                               "Error retrieving defaults for page `$id`"
+                       );
+               }
+
+               return new self(new Toolkit_Toolbox_ComponentFactory(), $d, $id);
+       }
+
+       //      }}}
+       //      {{{     getBusCategoryData()
+       public function getBusCategoryData()
+       {
+               if ($this->content->getRemoveImage()) {
+                       $this->content->setImage(Toolkit_Toolbox_ImageFactory::getImage());
+                       $this->content->setImageName(null);
+               }
+               return array(
+                       'intro' => $this->content->getIntro(),
+                       'description' => $this->content->getDescription(),
+                       'image' => $this->content->getImage()->getSrc(),
+                       'imagename' => $this->content->getImageName(),
+                       'parent' => $this->attributes->getParent(),
+                       'category' => $this->attributes->getCategory(),
+                       'keyword' => $this->attributes->getKeyword(),
+                       'template' => $this->attributes->getTemplate(),
+                       'section_links' => $this->attributes->getSectionLinks(),
+                       'short_url' => $this->attributes->getShortUrl(),
+                       'featured' => $this->headlines->getFeatured(),
+                       'feature_intro' => $this->headlines->getFeatureIntro(),
+                       'no_search_form' => $this->members->getSearch(),
+                       'include_member_map' => $this->members->getMap(),
+                       'meta_descr' => $this->metaData->getDescription(),
+                       'title' => $this->metaData->getTitle(),
+               );
+       }
+
+       //      }}}
+
+       //      {{{     save()
+       public function save(PDO $dbh)
+       {
+               try {
+                       $dbh->beginTransaction();
+
+                       $busCategoryData = $this->getBusCategoryData();
+
+                       $busCategorySql = Toolkit_Common::createSQLInsert(
+                               'bus_category',
+                               array_keys($busCategoryData)
+                       );
+
+                       $stmt = Toolkit_Common::prepareQuery(
+                               $dbh,
+                               'bus_category',
+                               $busCategorySql,
+                               $busCategoryData
+                       );
+
+                       $stmt->execute();
+                       $catidSql = "
+                               SELECT *
+                                 FROM bus_category
+                                ORDER BY id DESC LIMIT 1";
+
+                       $row = $dbh->query($catidSql)->fetch(PDO::FETCH_ASSOC);
+                       $this->_id = (int) $row['id'];
+
+                       $this->coupons->saveCategories($dbh, $this->_id);
+                       $this->members->saveCategories($dbh, $this->_id);
+
+                       return $dbh->commit();
+               } catch (PDOException $e) {
+                       $dbh->rollback();
+                       Toolkit_Logger::logException('db error', $e);
+                       throw new Toolkit_Toolbox_Exception('Error saving page in Database');
+               }
+       }
+
+       //      }}}
+
+       //      {{{     update()
+       public function update(PDO $dbh)
+       {
+               try {
+                       if (!isset($this->_id)) {
+                               throw new RuntimeException('Page does not exist yet');
+                       }
+                       $dbh->beginTransaction();
+
+                       $busCategoryData = $this->getBusCategoryData();
+
+                       $busCategorySql = Toolkit_Common::createSQLUpdate(
+                               'bus_category',
+                               array_keys($busCategoryData),
+                               array("id = {$this->_id}")
+                       );
+
+                       $stmt = Toolkit_Common::prepareQuery(
+                               $dbh,
+                               'bus_category',
+                               $busCategorySql,
+                               $busCategoryData
+                       );
+
+                       $stmt->execute();
+
+                       $this->coupons->updateCategories($dbh, $this->_id);
+                       $this->members->updateCategories($dbh, $this->_id);
+
+                       return $dbh->commit();
+
+               } catch (PDOException $e) {
+                       Toolkit_Logger::logException('db error', $e);
+                       throw new Toolkit_Toolbox_Exception('Error updating page in Database');
+               } catch (RuntimeException $e) {
+                       Toolkit_Logger::logException('Runtime Error', $e);
+                       throw new Toolkit_Toolbox_Exception($e->getMessage());
+               }
+       }
+
+       //      }}}
+
+       //      {{{     getCreateDate()
+
+       public function getCreateDate()
+       {
+               return $this->_createDate;
+       }
+
+       //      }}}
+       //      {{{     getLastModificationDate()
+
+       public function getLastModificationDate()
+       {
+               return $this->_lastModifiedDate;
+       }
+
+       //      }}}
+       //      {{{     getNumberOfRevisions()
+
+       public function getNumberOfRevisions()
+       {
+               return $this->_revisions;
+       }
+
+       //      }}}
+       //      {{{     getActive()
+
+       public function getActive()
+       {
+               return $this->_active;
+       }
+
+       //      }}}
+       //      {{{     getActiveText()
+
+       public function getActiveText()
+       {
+               return $this->_active ? 'Active' : 'In-Active';
+       }
+
+       //      }}}
+
+       public function getId()
+       {
+           return $this->_id;
+       }
+}
+?>
diff --git a/Toolkit/Toolbox/PageBreadCrumbs.php b/Toolkit/Toolbox/PageBreadCrumbs.php
new file mode 100644 (file)
index 0000000..d25ca29
--- /dev/null
@@ -0,0 +1,12 @@
+<?php
+
+class Toolkit_Toolbox_PageBreadCrumbs
+       extends Toolkit_Toolbox_BreadCrumbsAbstract
+{
+       protected function getPageUri(array $page)
+       {
+           $uri = MEDIA_BASE_URL . "admin/toolbox.php?rt=EditPage&amp;id={$page['id']}";
+           return "<a href=\"{$uri}\">{$page['navigation_name']}</a>";
+       }
+}
+?>
diff --git a/Toolkit/Toolbox/PageDraftBreadCrumbs.php b/Toolkit/Toolbox/PageDraftBreadCrumbs.php
new file mode 100644 (file)
index 0000000..356d9e5
--- /dev/null
@@ -0,0 +1,72 @@
+<?php
+
+class Toolkit_Toolbox_PageDraftBreadCrumbs
+       extends Toolkit_Toolbox_BreadCrumbsAbstract
+{
+       //      {{{     getPageUri()
+
+       protected function getPageUri(array $page)
+       {
+           $uri = MEDIA_BASE_URL . "admin/toolbox.php?rt=EditPage&id={$page['id']}";
+           return "<a href=\"{$uri}\">{$page['navigation_name']}</a>";
+       }
+
+       //      }}}
+       //      {{{     getDraft()
+
+    protected function getDraft($id)
+    {
+        try {
+            $sql = "
+                SELECT *
+                  FROM pages_draft
+                 WHERE id = :id";
+
+            $stmt = $this->dbh->prepare($sql);
+            $stmt->bindParam(':id', $id, PDO::PARAM_INT);
+            $stmt->execute();
+
+            return $stmt->fetch(PDO::FETCH_ASSOC);
+        } catch (PDOException $e) {
+            Toolkit_Logger::logException('DB Error', $e);
+            throw new Toolkit_Toolbox_Exception(
+                "Could not fetch parent for page `$id`"
+            );
+        }
+    }
+
+       //      }}}
+       //      {{{     getPath()
+
+       /**
+        * @return the $path
+        */
+       public function getPath()
+       {
+               if ($this->id == HOME_ID) {
+                       return;
+               }
+
+               $draft = $this->getDraft($this->id);
+               $stack = array($draft['navigation_name']);
+
+               $publishedPage = $this->getPage($draft['published_page']);
+               $id = $publishedPage['parent'];
+               while ($id != 0) {
+               $page = $this->getPage($id);
+
+               $navigationName = $this->getPageUri($page);
+
+               $stack[] = $navigationName;
+               $id = $page['parent'];
+           }
+
+           $reverse = array_reverse($stack);
+        $this->path = implode(' > ', $reverse);
+
+               return $this->path;
+       }
+
+       //      }}}
+}
+?>
diff --git a/Toolkit/Toolbox/PageGatewayAbstract.php b/Toolkit/Toolbox/PageGatewayAbstract.php
new file mode 100644 (file)
index 0000000..727b341
--- /dev/null
@@ -0,0 +1,280 @@
+<?php
+abstract class Toolkit_Toolbox_PageGatewayAbstract
+       extends Toolkit_Toolbox_GatewayAbstract
+{
+       protected $page;
+
+       //      {{{     hasMemberDb()
+
+       protected function hasMemberDb()
+       {
+               return (defined('MEMBERS_DB') && MEMBERS_DB);
+       }
+
+       //      }}}
+       //      {{{     hasCouponDb()
+
+       protected function hasCouponDb()
+       {
+               return (defined('COUPONS') && COUPONS);
+       }
+
+       //      }}}
+       //      {{{     hasHeadlines()
+
+       protected function hasHeadlines()
+       {
+               return (defined('HOME_HEADLINES') && HOME_HEADLINES);
+       }
+
+       //      }}}
+       //      {{{     hasPhotoGallery()
+
+       protected function hasPhotoGallery()
+       {
+               return (defined('PHOTO_GALLERY') && PHOTO_GALLERY);
+       }
+
+       //      }}}
+
+       //      {{{     findAll()
+
+       abstract public function findAll();
+
+       //      }}}
+       //      {{{     findAllByParent()
+
+       public function findAllByParent($parent)
+       {
+               try {
+                       $sql = "
+                               SELECT id,navigation_name,parent,active
+                                 FROM pages
+                                WHERE parent = :parent
+                                ORDER by parent, pos";
+
+                       $stmt = $this->dbh->prepare($sql);
+                       $stmt->bindParam(':parent', $parent, PDO::PARAM_INT);
+                       $stmt->execute();
+                       return $stmt->fetchAll(PDO::FETCH_ASSOC);
+               } catch (PDOException $e) {
+                       Toolkit_Logger::logException('DB Error', $e);
+                       throw new Toolkit_Toolbox_Exception(
+                               "Error fetching all pages by parent `$parent`"
+                       );
+               }
+       }
+
+       //      }}}
+       //      {{{     findByKeyword()
+
+       abstract public function findByKeyword($keyword);
+
+       //      }}}
+       //      {{{     findTopParent()
+
+       abstract public function findTopParent($pageId);
+
+       //      }}}
+       //      {{{     findPage()
+
+       protected function findPage($id, $pageSql)
+       {
+        //     if the page has already been fetched, just return it.
+               if (is_array($this->page) && $this->page['id'] == $id) {
+                       return $this->page;
+               }
+
+               $pageStatsSql = "
+                       SELECT count(*) AS revised,
+                               MIN(revision_timestamp) AS created,
+                           MAX(revision_timestamp) AS last_modified
+              FROM pages_history
+             WHERE id = :id";
+
+               $pageStmt = $this->dbh->prepare($pageSql);
+               $pageStmt->bindParam(':id', $id, PDO::PARAM_INT);
+               $pageStmt->execute();
+
+               $page = $pageStmt->fetch(PDO::FETCH_ASSOC);
+
+               if ($page === false) { // Page doesn't exist
+                       return false;
+               }
+
+               if ($this->hasMemberDb()) {
+                       $page['member_categories']
+                               = $this->getMemberCategoriesForPage($id);
+                       $page['member_regions']
+                               = $this->getMemberRegionsForPage($id);
+               }
+
+               if ($this->hasCouponDb()) {
+                       $page['coupon_categories']
+                               = $this->getCouponCategoriesForPage($id);
+               }
+
+               if ($this->hasPhotoGallery()) {
+                       $page['photo_galleries']
+                               = $this->getPhotoGalleriesForPage($id);
+               }
+
+               $pageStatsStmt = $this->dbh->prepare($pageStatsSql);
+               $pageStatsStmt->bindParam(':id', $id, PDO::PARAM_INT);
+               $pageStatsStmt->execute();
+
+               $pageStats = $pageStatsStmt->fetch(PDO::FETCH_ASSOC);
+
+               $date = new Date();
+               $date->setDate(strtotime($pageStats['created']), DATE_FORMAT_UNIXTIME);
+               $pageStats['created'] = $date->format('%D %r');
+               $date->setDate(strtotime($pageStats['last_modified']), DATE_FORMAT_UNIXTIME);
+               $pageStats['last_modified'] = $date->format('%D %r');
+               $page = array_merge($page, $pageStats);
+
+               if (!empty($page['image'])) {
+                       $imgFormat = "<img alt=\"%s\" src=\"%s\">";
+                       $page['current_image_original'] = sprintf(
+                               $imgFormat,
+                               $page['image'],
+                               TOOLBOX_ORIGINAL . $page['image']
+                       );
+                       $page['current_image_resized'] = sprintf(
+                               $imgFormat,
+                               $page['image'],
+                               TOOLBOX_RESIZED . $page['image']
+                       );
+                       $page['current_image_midsized'] = sprintf(
+                               $imgFormat,
+                               $page['image'],
+                               TOOLBOX_MIDSIZED . $page['image']
+                       );
+                       $page['current_image_thumb'] = sprintf(
+                               $imgFormat,
+                               $page['image'],
+                               TOOLBOX_THUMB . $page['image']
+                       );
+               } else {
+                       $page['current_image_original'] = 'Image not yet uploaded';
+                       $page['current_image_resized']  = 'Image not yet uploaded';
+                       $page['current_image_midsized'] = 'Image not yet uploaded';
+                       $page['current_image_thumb']    = 'Image not yet uploaded';
+               }
+
+               $this->page = $page;
+               return $this->page;
+       }
+
+       //      }}}
+
+       //      {{{     getCurrentPage()
+
+       /**
+        * Gets the current page for the template if it exists
+        *
+        * @return mixed array of current page or false if no current page is set
+        * @access public
+        */
+       public function getCurrentPage()
+       {
+               return is_array($this->page) ? $this->page : false;
+       }
+
+       //      }}}
+       //      {{{     getHomePageHeadlines()
+
+       public function getHomePageHeadlines()
+       {
+        $wordCounter = array(
+            1 => 'one',
+            2 => 'two',
+            3 => 'three',
+            4 => 'four'
+        );
+               $headlines = array();
+               $sql = "
+                         SELECT p1.id, p1.navigation_name, p1.headline_intro, p2.image
+                               FROM pages p1 join paragraphs p2 on (p1.id = p2.page)
+                          WHERE p1.headline = true
+                                AND p1.active = true
+                                AND p2.pos = 1
+                       ORDER BY p1.parent, p1.pos";
+
+               $count = 1;
+               foreach ($this->dbh->query($sql)->fetchAll(PDO::FETCH_ASSOC) as $row) {
+                       $headlines[] = array(
+                               'count'  => $wordCounter[$count],
+                               'href'   => Toolkit_Template_Page::getSeoUrl($this, $row['id']),
+                               'img'    => ($row['image'])
+                                       ? HOMEPAGE_HEADLINE_THUMB . $row['image']
+                                       : '',
+                               'header' => $row['navigation_name'],
+                               'descr'  => nl2br($row['headline_intro']),
+                       );
+                       if (++$count > 4) {
+                               $count = 1;
+                       }
+               }
+
+               return $headlines;
+       }
+
+       //      }}}
+
+       //      {{{     setPageVars()
+
+       protected function setPageVars(PDOStatement &$stmt, $data)
+       {
+               if ($this->hasHeadlines()) {
+                       $data['headline'] = (bool)$data['headline'];
+                       $stmt->bindParam(
+                               ':headline',
+                               $data['headline'],
+                               PDO::PARAM_BOOL
+                       );
+                       $stmt->bindParam(':headline_intro', $data['headline_intro']);
+               }
+
+               if ($this->hasMemberDb()) {
+                       $data['include_member_map'] = (bool)$data['include_member_map'];
+                       $data['search_form'] = (bool)$data['search_form'];
+                       $stmt->bindParam(
+                               ':include_member_map',
+                               $data['include_member_map'],
+                               PDO::PARAM_BOOL
+                       );
+                       $stmt->bindParam(
+                               ':search_form',
+                               $data['search_form'],
+                               PDO::PARAM_BOOL
+                       );
+               }
+
+               $stmt->bindParam(':keyword', $data['keyword']);
+               $stmt->bindParam(':meta_title', $data['meta_title']);
+               $stmt->bindParam(':meta_description', $data['meta_description']);
+               $stmt->bindParam(':navigation_name', $data['navigation_name']);
+               $stmt->bindParam(':parent', $data['parent']);
+               $stmt->bindParam(':paragraph_links', $data['paragraph_links']);
+               $stmt->bindParam(':short_url', $data['short_url']);
+               $stmt->bindParam(':template', $data['template']);
+               $stmt->bindParam(':include_members', $data['include_members']);
+               $stmt->bindParam(':include_coupons', $data['include_coupons']);
+       }
+
+       //      }}}
+       //      {{{     setParagraphVars()
+
+       protected function setParagraphVars(PDOStatement &$stmt, $data)
+       {
+               if ($data['remove_image']) {
+                       $data['image'] = '';
+               }
+               $stmt->bindParam(':title', $data['title']);
+               $stmt->bindParam(':description', $data['description']);
+               $stmt->bindParam(':image', $data['image']);
+               $stmt->bindParam(':caption', $data['caption']);
+       }
+
+       //      }}}
+}
diff --git a/Toolkit/Toolbox/PageGatewayDraft.php b/Toolkit/Toolbox/PageGatewayDraft.php
new file mode 100644 (file)
index 0000000..7a4e165
--- /dev/null
@@ -0,0 +1,555 @@
+<?php
+class Toolkit_Toolbox_PageGatewayDraft
+    extends Toolkit_Toolbox_PageGatewayAbstract
+{
+    //  {{{ delete()
+
+    public function delete($id)
+    {
+        $pageSql = "
+            DELETE
+              FROM pages_draft
+             WHERE id = :id";
+
+        try {
+            $stmt = $this->dbh->prepare($pageSql);
+            $stmt->bindParam(':id', $id, PDO::PARAM_INT);
+            return $stmt->execute();
+        } catch (PDOException $e) {
+            Toolkit_Logger::logException('DB Error', $e);
+            throw new Toolkit_Toolbox_Exception(
+                "Unable to delete page draft `$id`"
+            );
+        }
+    }
+
+    //  }}}
+    public function findNavItem($id)
+    {
+        $sql = "
+            SELECT id,navigation_name,parent,short_url
+              FROM pages
+             WHERE id = :id";
+
+        try {
+            $stmt = $this->dbh->prepare($sql);
+            $stmt->bindParam(
+                ':id',
+                $id,
+                PDO::PARAM_INT
+            );
+            $stmt->execute();
+            return $stmt->fetch(PDO::FETCH_ASSOC);
+        } catch (PDOException $e) {
+            Toolkit_Logger::logException('DB Error', $e);
+            throw new Toolkit_Toolbox_Exception("Unable to find page `$id`");
+        }
+    }
+    //  {{{ find()
+
+    public function find($id)
+    {
+        $pageSql = "
+            SELECT p1.*, p1.id AS page_id,
+                   CASE p1.active
+                   WHEN true THEN 'active'
+                   ELSE 'In-Active'
+                   END AS active, p2.title, p2.description, p2.image, p2.caption
+              FROM pages_draft p1
+              LEFT JOIN paragraphs_draft p2
+                ON p1.id  = p2.page
+             WHERE p1.id  = :id
+               AND (p2.pos = 1 OR p2.pos IS NULL)";
+
+        try {
+            return $this->findPage($id, $pageSql);
+        } catch (PDOException $e) {
+            Toolkit_Logger::logException('DB Error', $e);
+            throw new Toolkit_Toolbox_Exception(
+                "Unable to find page draft `$id`"
+            );
+        }
+    }
+
+    //  }}}
+    //  {{{ findAll()
+
+    public function findAll()
+    {
+        try {
+            $sql = "
+                SELECT *
+                  FROM pages_draft
+                 ORDER by parent, pos";
+
+            return $this->dbh->query($sql)->fetchAll(PDO::FETCH_ASSOC);
+        } catch (PDOException $e) {
+            Toolkit_Logger::logException('DB Error', $e);
+            throw new Toolkit_Toolbox_Exception(
+                'Error fetching all draft pages'
+            );
+        }
+    }
+
+    //  }}}
+    //  {{{ findByKeyword()
+
+    public function findByKeyword($keyword)
+    {
+        try {
+            $pageSql = "
+                SELECT id
+                  FROM pages_draft
+                 WHERE keyword = :keyword";
+
+            $stmt = $this->dbh->prepare($pageSql);
+            $stmt->bindParam(':keyword', $keyword);
+            $stmt->execute();
+
+            // Bind by column number
+            $stmt->bindColumn(1, $id);
+
+            $stmt->fetch(PDO::FETCH_ASSOC);
+
+            return $this->find($id);
+        } catch (PDOException $e) {
+            Toolkit_Logger::logException('DB Error', $e);
+            throw new Toolkit_Toolbox_Exception(
+                "Unable to find page draft `$id`"
+            );
+        }
+    }
+
+    //  }}}
+    //  {{{ findTopParent()
+
+    public function findTopParent($pageId, $useDraftTable = true)
+    {
+        if (!ctype_digit((string)$pageId)) {
+            throw new runtimeException("Invalid pageId `$pageId` to fetch");
+        }
+
+        try {
+            if ($useDraftTable) {
+                $sql = "
+                    SELECT *
+                      FROM pages_draft
+                     WHERE id = :id";
+            } else {
+                $sql = "
+                    SELECT *
+                      FROM pages
+                     WHERE id = :id";
+            }
+
+            $stmt = $this->dbh->prepare($sql);
+            $stmt->bindParam(':id', $pageId, PDO::PARAM_INT);
+            $stmt->execute();
+
+            $row = $stmt->fetch(PDO::FETCH_ASSOC);
+
+            if ($row['parent'] == '0') {
+                return $row['id'];
+            } else {
+                return $this->findTopParent($row['parent'], false);
+            }
+        } catch (PDOException $e) {
+            Toolkit_Logger::logException('DB Error', $e);
+            throw new Toolkit_Toolbox_Exception(
+                "Unable to find parent for page `$pageId`"
+            );
+        }
+    }
+
+    //  }}}
+
+    //  {{{ insert()
+
+    public function insert(array $data)
+    {
+        if (empty($data['published_page'])) {
+            settype($data['published_page'], 'null');
+        }
+
+        if ($this->hasHeadlines()) {
+            $headlineColumns = 'headline, headline_intro,';
+            $headlineParams  = ':headline, :headline_intro,';
+        }
+
+        if ($this->hasMemberDb()) {
+            $memberColumns = 'include_member_map, search_form, ';
+            $memberParams  = ':include_member_map, :search_form, ';
+        }
+
+        $pageSql = "
+            INSERT INTO pages_draft (
+                $headlineColumns $memberColumns keyword, meta_title,
+                meta_description, navigation_name, parent, paragraph_links,
+                short_url, template, published_page, include_members, include_coupons)
+            VALUES (
+                $headlineParams $memberParams :keyword, :meta_title,
+                :meta_description, :navigation_name, :parent,
+                :paragraph_links, :short_url, :template, :published_page, :include_members, :include_coupons)";
+
+        $paragraphSql = "
+            INSERT INTO paragraphs_draft (
+                active, title, description, image, caption, page)
+            VALUES (
+                true, :title, :description, :image, :caption, :page)";
+
+        try {
+            $this->dbh->beginTransaction();
+
+            $pageStmt = $this->dbh->prepare($pageSql);
+            $this->setPageVars($pageStmt, $data);
+            $pageStmt->bindParam(':published_page', $data['published_page']);
+            $pageStmt->execute();
+
+            $row = $this->dbh
+                ->query('select id from pages_draft order by id desc limit 1')
+                ->fetch(PDO::FETCH_ASSOC);
+
+            if (defined('MEMBERS_DB') && MEMBERS_DB) {
+                $this->_updateMemberCategories(
+                    $data['member_categories'],
+                    $row['id']
+                );
+            }
+
+            if (defined('COUPONS') && COUPONS) {
+                $this->_updateCouponCategories(
+                    $data['coupon_categories'],
+                    $row['id']
+                );
+                if (defined('MEMBERS_DB') && MEMBERS_DB) {
+                    $this->_updateMemberCities(
+                        $data['member_regions'],
+                        $row['id']
+                    );
+                }
+            }
+
+            $paragraphStmt = $this->dbh->prepare($paragraphSql);
+            $this->setParagraphVars($paragraphStmt, $data);
+            $paragraphStmt->bindParam(':page', $row['id']);
+            $paragraphStmt->execute();
+
+            $this->dbh->commit();
+
+            return $row['id'];
+        } catch (PDOException $e) {
+            $this->dbh->rollback();
+            Toolkit_Logger::logException('DB Error', $e);
+            $content = serialize($data);
+            throw new Toolkit_Toolbox_Exception(
+                "Unable to insert page draft [$content]"
+            );
+        }
+    }
+
+    //  }}}
+
+    //  {{{ update()
+
+    public function update(array $data, $id)
+    {
+        if ($this->hasHeadlines()) {
+            $headlineColumns = '
+                headline = :headline,
+                headline_intro = :headline_intro, ';
+        }
+
+        if ($this->hasMemberDb()) {
+            $memberColumns = '
+                include_member_map = :include_member_map,
+                search_form = :search_form, ';
+        }
+
+        $pageSql = "
+            UPDATE pages_draft
+               SET $headlineColumns
+                   $memberColumns
+                   keyword = :keyword,
+                   meta_title = :meta_title,
+                   meta_description = :meta_description,
+                   navigation_name = :navigation_name,
+                   parent = :parent,
+                   paragraph_links = :paragraph_links,
+                   short_url = :short_url,
+                   template = :template,
+                   include_members = :include_members,
+                   include_coupons = :include_coupons
+             WHERE id = :id";
+
+        $paragraphSql = "
+            UPDATE paragraphs_draft
+               SET title = :title,
+                   description = :description,
+                   image = :image,
+                   caption = :caption
+             WHERE page = :page
+               AND pos = 1";
+
+        try {
+            $this->dbh->beginTransaction();
+
+            $pageStmt = $this->dbh->prepare($pageSql);
+            $this->setPageVars($pageStmt, $data);
+            $pageStmt->bindParam(':id', $id, PDO::PARAM_INT);
+            $pageStmt->execute();
+
+            if (defined('MEMBERS_DB') && MEMBERS_DB) {
+                $this->_updateMemberCategories($data['member_categories'], $id);
+                $this->_updateMemberCities($data['member_regions'], $id);
+            }
+            if (defined('COUPONS') && COUPONS) {
+                $this->_updateCouponCategories($data['coupon_categories'], $id);
+            }
+
+            $paragraphStmt = $this->dbh->prepare($paragraphSql);
+            $this->setParagraphVars($paragraphStmt, $data);
+            $paragraphStmt->bindParam(':page', $id);
+            $paragraphStmt->execute();
+
+            return $this->dbh->commit();
+        } catch (PDOException $e) {
+            $this->dbh->rollback();
+            Toolkit_Logger::logException('DB Error', $e);
+            $content = serialize($data);
+            throw new Toolkit_Toolbox_Exception(
+                "Unable to update page draft [$content]"
+            );
+        }
+    }
+
+    //  }}}
+
+    //  {{{ _updateMemberCategories()
+
+    private function _updateMemberCategories(array $data = null, $id)
+    {
+        $deleteCatsSql = "
+            DELETE FROM member_categories2toolbox_pages_draft
+             WHERE page = :id";
+        $delStmt = $this->dbh->prepare($deleteCatsSql);
+        $delStmt->bindParam(':id', $id, PDO::PARAM_INT);
+        $delStmt->execute();
+
+        if (is_array($data)) {
+            $insertCatsSql = "
+                INSERT INTO member_categories2toolbox_pages_draft (page, category)
+                VALUES (:page, :category)";
+            $insStmt = $this->dbh->prepare($insertCatsSql);
+            $insStmt->bindParam(':page', $id, PDO::PARAM_INT);
+            foreach ($data as $category) {
+                $insStmt->bindParam(':category', $category, PDO::PARAM_INT);
+                $insStmt->execute();
+            }
+        }
+    }
+
+    //  }}}
+    //  {{{ getMemberCategoriesForPage()
+
+    protected function getMemberCategoriesForPage($id)
+    {
+        try {
+            $sql = "
+                SELECT *
+                  FROM member_categories2toolbox_pages_draft
+                 WHERE page = :id";
+
+            $stmt = $this->dbh->prepare($sql);
+            $stmt->bindParam(':id', $id);
+            $stmt->execute();
+
+            $categories = array();
+            while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                $categories[] = $row['category'];
+            }
+
+            return $categories;
+        } catch (PDOException $e) {
+            Toolkit_Logger::logException('DB Error', $e);
+            throw new Toolkit_Toolbox_Exception(
+                "Unable to fetch member categories for page draft `$id`"
+            );
+        }
+    }
+
+    //  }}}
+
+    //  {{{ _updateMemberCities()
+
+    private function _updateMemberCities(array $data = null, $id)
+    {
+        $deleteCitiesSql = "
+            DELETE FROM member_regions2toolbox_pages_draft
+             WHERE page = :id";
+        $delStmt = $this->dbh->prepare($deleteCitiesSql);
+        $delStmt->bindParam(':id', $id, PDO::PARAM_INT);
+        $delStmt->execute();
+
+        if (is_array($data)) {
+            $insertCitiesSql = "
+                INSERT INTO member_regions2toolbox_pages_draft (page, region)
+                VALUES (:page, :region)";
+            $insStmt = $this->dbh->prepare($insertCitiesSql);
+            $insStmt->bindParam(':page', $id, PDO::PARAM_INT);
+            foreach ($data as $region) {
+                $insStmt->bindParam(':region', $region, PDO::PARAM_INT);
+                $insStmt->execute();
+            }
+        }
+    }
+
+    //  }}}
+    //  //  {{{ getMemberRegionsForPage()
+
+    protected function getMemberRegionsForPage($id)
+    {
+        try {
+            $sql = "
+                SELECT *
+                  FROM member_regions2toolbox_pages_draft
+                 WHERE page = :id";
+
+            $stmt = $this->dbh->prepare($sql);
+            $stmt->bindParam(':id', $id);
+            $stmt->execute();
+
+            $regions = array();
+            while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                $regions[] = $row['region'];
+            }
+
+            return $regions;
+        } catch (PDOException $e) {
+            Toolkit_Logger::logException('DB Error', $e);
+            throw new Toolkit_Toolbox_Exception(
+                "Unable to fetch member regions for page `$id`"
+            );
+        }
+    }
+
+    //  }}}
+    //  {{{ getMemberCitiesForPage()
+
+    protected function getMemberCitiesForPage($id)
+    {
+        try {
+            $sql = "
+                SELECT *
+                  FROM member_regions2toolbox_pages_draft
+                 WHERE page = :id";
+
+            $stmt = $this->dbh->prepare($sql);
+            $stmt->bindParam(':id', $id);
+            $stmt->execute();
+
+            $regions = array();
+            while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                $regions[] = $row['region'];
+            }
+
+            return $regions;
+        } catch (PDOException $e) {
+            Toolkit_Logger::logException('DB Error', $e);
+            throw new Toolkit_Toolbox_Exception(
+                "Unable to fetch member regions for page `$id`"
+            );
+        }
+    }
+
+    //  }}}
+
+    //  {{{ _updateCouponCategories()
+
+    private function _updateCouponCategories(array $data = null, $id)
+    {
+        $deleteCatsSql = "
+            DELETE FROM coupon_categories2toolbox_pages_draft
+             WHERE page = :id";
+        $delStmt = $this->dbh->prepare($deleteCatsSql);
+        $delStmt->bindParam(':id', $id, PDO::PARAM_INT);
+        $delStmt->execute();
+
+        if (is_array($data)) {
+            $insertCatsSql = "
+                INSERT INTO coupon_categories2toolbox_pages_draft (page, category)
+                VALUES (:page, :category)";
+            $insStmt = $this->dbh->prepare($insertCatsSql);
+            $insStmt->bindParam(':page', $id, PDO::PARAM_INT);
+            foreach ($data as $category) {
+                $insStmt->bindParam(':category', $category, PDO::PARAM_INT);
+                $insStmt->execute();
+            }
+        }
+    }
+
+    //  }}}
+    //  {{{ getCouponCategoriesForPage()
+
+    protected function getCouponCategoriesForPage($id)
+    {
+        try {
+            $sql = "
+                SELECT *
+                  FROM coupon_categories2toolbox_pages_draft
+                 WHERE page = :id";
+
+            $stmt = $this->dbh->prepare($sql);
+            $stmt->bindParam(':id', $id);
+            $stmt->execute();
+
+            $categories = array();
+            while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                $categories[] = $row['category'];
+            }
+
+            return $categories;
+        } catch (PDOException $e) {
+            Toolkit_Logger::logException('DB Error', $e);
+            throw new Toolkit_Toolbox_Exception(
+                "Unable to fetch coupon categories for page draft `$id`"
+            );
+        }
+    }
+
+    //  }}}
+
+    //  {{{ getPhotoGalleriesForPage()
+
+    protected function getPhotoGalleriesForPage($id)
+    {
+        try {
+            $sql = "
+                SELECT pc.*
+                  FROM photo_category pc
+                  JOIN photo_category_bus pcb
+                    ON (pc.id             = pcb.photocat_id)
+                  JOIN pages_draft pd
+                    ON (pd.published_page = pcb.buscat_id)
+                 WHERE pcb.buscat_id      = :id";
+
+            $stmt = $this->dbh->prepare($sql);
+            $stmt->bindParam(':id', $id);
+            $stmt->execute();
+
+            $photoGalleries = array();
+            while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                $photoGalleries[$row['id']] = $row['category'];
+            }
+
+            return $photoGalleries;
+        } catch (PDOException $e) {
+            Toolkit_Logger::logException('DB Error', $e);
+            throw new Toolkit_Toolbox_Exception(
+                "Unable to fetch member categories for page `$id`"
+            );
+        }
+    }
+
+    //  }}}
+}
+?>
diff --git a/Toolkit/Toolbox/PageGatewayDraftFactory.php b/Toolkit/Toolbox/PageGatewayDraftFactory.php
new file mode 100644 (file)
index 0000000..1375f78
--- /dev/null
@@ -0,0 +1,10 @@
+<?php
+class Toolkit_Toolbox_PageGatewayDraftFactory
+       extends Toolkit_Toolbox_GatewayFactoryAbstract
+{
+       public function createGateway()
+       {
+               return new Toolkit_Toolbox_PageGatewayDraft($this->dbh);
+       }
+}
+?>
diff --git a/Toolkit/Toolbox/PageGatewayPublish.php b/Toolkit/Toolbox/PageGatewayPublish.php
new file mode 100644 (file)
index 0000000..6a9c237
--- /dev/null
@@ -0,0 +1,610 @@
+<?php
+class Toolkit_Toolbox_PageGatewayPublish
+    extends Toolkit_Toolbox_PageGatewayAbstract
+{
+    //    {{{    delete()
+
+    public function delete($id)
+    {
+        $pageSql = "SELECT delete_subtree(:id)";
+
+        $createTableSql = "
+            CREATE LOCAL TEMPORARY TABLE WorkingTable
+            (id INTEGER NOT NULL)
+            ON COMMIT DELETE ROWS";
+
+        try {
+
+            $this->dbh->beginTransaction();
+
+            $this->dbh->query($createTableSql);
+
+            $stmt = $this->dbh->prepare($pageSql);
+            $stmt->bindParam(':id', $id, PDO::PARAM_INT);
+            $stmt->execute();
+
+            return $this->dbh->commit();
+        } catch (PDOException $e) {
+            $this->dbh->rollback();
+            Toolkit_Logger::logException('DB Error', $e);
+            throw new Toolkit_Toolbox_Exception(
+                "Unable to delete subtree `$id`"
+            );
+        }
+    }
+
+    //    }}}
+
+    //    {{{    find()
+
+    public function find($id)
+    {
+        $pageSql = "
+            SELECT p1.*, p1.id AS page_id, p1.id AS published_page,
+                   CASE p1.active
+                   WHEN CAST(1 AS BOOLEAN) THEN 'active'
+                   ELSE 'In-Active'
+                   END AS active_alt, p2.title, p2.description, p2.image, p2.caption
+              FROM pages p1
+              LEFT JOIN paragraphs p2
+                ON p1.id  = p2.page
+             WHERE p1.id  = :id
+               AND (p2.pos = 1 OR p2.pos IS NULL)";
+
+        try {
+            return $this->findPage($id, $pageSql);
+        } catch (PDOException $e) {
+            Toolkit_Logger::logException('DB Error', $e);
+            throw new Toolkit_Toolbox_Exception("Unable to find page `$id`");
+        }
+    }
+
+    //    }}}
+    public function findNavItem($id)
+    {
+        $sql = "
+            SELECT id,navigation_name,parent,short_url
+              FROM pages
+             WHERE id = :id";
+
+        try {
+            $stmt = $this->dbh->prepare($sql);
+            $stmt->bindParam(
+                ':id',
+                $id,
+                PDO::PARAM_INT
+            );
+            $stmt->execute();
+            return $stmt->fetch(PDO::FETCH_ASSOC);
+        } catch (PDOException $e) {
+            Toolkit_Logger::logException('DB Error', $e);
+            throw new Toolkit_Toolbox_Exception("Unable to find page `$id`");
+        }
+    }
+    //    {{{    findAll()
+
+    public function findAll()
+    {
+        try {
+            $sql = "
+                SELECT *
+                  FROM pages
+                 ORDER by parent, pos";
+
+            return $this->dbh->query($sql)->fetchAll(PDO::FETCH_ASSOC);
+        } catch (PDOException $e) {
+            Toolkit_Logger::logException('DB Error', $e);
+            throw new Toolkit_Toolbox_Exception(
+                'Error fetching all published pages'
+            );
+        }
+    }
+
+    //    }}}
+    //    {{{    findByKeyword()
+
+    public function findByKeyword($keyword)
+    {
+        $keyword = strtolower(trim($keyword));
+        try {
+            $pageSql = "
+                SELECT id
+                  FROM pages
+                 WHERE lower(trim(keyword)) = :keyword";
+
+            $stmt = $this->dbh->prepare($pageSql);
+            $stmt->bindParam(':keyword', $keyword);
+            $stmt->execute();
+
+            // Bind by column number
+            $stmt->bindColumn(1, $id);
+
+            $stmt->fetch(PDO::FETCH_ASSOC);
+
+            return $this->findNavItem($id);
+        } catch (PDOException $e) {
+            Toolkit_Logger::logException('DB Error', $e);
+            throw new Toolkit_Toolbox_Exception(
+                "Unable to find keyword `$keyword`"
+            );
+        }
+    }
+
+    //    }}}
+    //    {{{    findTopParent()
+
+    public function findTopParent($pageId)
+    {
+        if (!filter_var($pageId, FILTER_VALIDATE_INT)) {
+            throw new runtimeException("Invalid pageId `$pageId` to fetch");
+        }
+
+        try {
+            $sql = "
+                SELECT parent,id
+                  FROM pages
+                 WHERE id  = :id";
+
+            $stmt = $this->dbh->prepare($sql);
+            $stmt->bindParam(':id', $pageId, PDO::PARAM_INT);
+            $stmt->execute();
+
+            $row = $stmt->fetch(PDO::FETCH_ASSOC);
+            if (!$stmt->rowCount()) {
+                return false;
+            }
+
+            if ($row['parent'] == '0') {
+                return $row['id'];
+            } else {
+                return $this->findTopParent($row['parent']);
+            }
+        } catch (PDOException $e) {
+            Toolkit_Logger::logException('DB Error', $e);
+            throw new Toolkit_Toolbox_Exception(
+                "Unable to find parent for page `$pageId`"
+            );
+        }
+    }
+
+    //    }}}
+
+    //    {{{    insert()
+
+    public function insert(array $data)
+    {
+        if ($this->hasHeadlines()) {
+            $headlineColumns = 'headline, headline_intro,';
+            $headlineParams  = ':headline, :headline_intro,';
+        }
+
+        if ($this->hasMemberDb()) {
+            $memberColumns = 'include_member_map, search_form, ';
+            $memberParams  = ':include_member_map, :search_form, ';
+        }
+
+        $pageSql = "
+            INSERT INTO pages (
+                $headlineColumns $memberColumns keyword, meta_title,
+                meta_description, navigation_name, parent, paragraph_links,
+                short_url, template, include_members, include_coupons)
+            VALUES (
+                $headlineParams $memberParams :keyword, :meta_title,
+                :meta_description, :navigation_name, :parent,
+                :paragraph_links, :short_url, :template, :include_members, :include_coupons)
+        RETURNING id";
+
+        $paragraphSql = "
+            INSERT INTO paragraphs (
+                active, title, description, image, caption, page)
+            VALUES (
+                true, :title, :description, :image, :caption, :page)";
+
+        try {
+            $this->dbh->beginTransaction();
+
+            $pageStmt = $this->dbh->prepare($pageSql);
+            $this->setPageVars($pageStmt, $data);
+            $pageStmt->execute();
+
+            $pageId = $pageStmt->fetchColumn();
+
+            if (defined('MEMBERS_DB') && MEMBERS_DB) {
+                $this->_updateMemberCategories(
+                    $data['member_categories'],
+                    $pageId
+                );
+                $this->_updateMemberRegions(
+                    $data['member_regions'],
+                    $pageId
+                );
+            }
+
+            if (defined('COUPONS') && COUPONS) {
+                $this->_updateCouponCategories(
+                    $data['coupon_categories'],
+                    $pageId
+                );
+            }
+
+            $paragraphStmt = $this->dbh->prepare($paragraphSql);
+            $this->setParagraphVars($paragraphStmt, $data);
+            $paragraphStmt->bindParam(':page', $pageId);
+            $paragraphStmt->execute();
+
+            // for some reason when Jodie is adding new pages the page position
+            // get messed up maybe more than one person it adding or updating
+            // pages I don't know either way when adding in new pages
+            // we'll have to go through all pages that have the same
+            // parent and redo the page positions so they don't get off order
+            $sql = "
+              SELECT id,pos
+                FROM pages
+               WHERE parent = :parent
+            ORDER BY pos";
+            $stmt = $this->dbh->prepare($sql);
+            $sql = "
+            UPDATE pages
+               SET pos = :pos
+             WHERE id = :id";
+            $updatePositionOfPage = $this->dbh->prepare($sql);
+            $stmt->bindParam(':parent', $data['parent'], PDO::PARAM_INT);
+            $stmt->execute();
+            $pos = 1;
+            while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                $updatePositionOfPage->bindParam(
+                    ':id',
+                    $row['id'],
+                    PDO::PARAM_INT
+                );
+                $updatePositionOfPage->bindParam(
+                    ':pos',
+                    $pos,
+                    PDO::PARAM_INT
+                );
+                $updatePositionOfPage->execute();
+                ++$pos;
+            }
+
+            $this->dbh->commit();
+            return $pageId;
+        } catch (PDOException $e) {
+            $this->dbh->rollback();
+            Toolkit_Logger::logException('DB Error', $e);
+            $content = serialize($data);
+            throw new Toolkit_Toolbox_Exception(
+                "Unable to insert page [$content]"
+            );
+        }
+    }
+
+    //    }}}
+
+    //    {{{    update()
+
+    public function update(array $data, $id)
+    {
+        if ($this->hasHeadlines()) {
+            $headlineColumns = '
+                headline = :headline,
+                headline_intro = :headline_intro, ';
+        }
+
+        if ($this->hasMemberDb()) {
+            $memberColumns = '
+                include_member_map = :include_member_map,
+                search_form = :search_form, ';
+        }
+
+        try {
+            $this->dbh->beginTransaction();
+            // need to know if we're moving this to another parent
+            $sql = "
+                SELECT parent, pos
+                  FROM pages
+                 WHERE id = :id";
+            $stmt = $this->dbh->prepare($sql);
+            $stmt->bindParam(
+                ":id",
+                $id,
+                PDO::PARAM_INT
+            );
+            $stmt->execute();
+
+            // the old parent of the page
+            $oldData   = $stmt->fetch(PDO::FETCH_ASSOC);
+            $oldParent = $oldData['parent'];
+            $oldPos    = $oldData['pos'];
+            if ($data['parent'] != $oldParent) {
+                // adjust the old level to normal
+                $sql = "
+                UPDATE pages
+                   SET pos = pos - 1
+                 WHERE pos > :pos
+                  AND parent = :parent
+                    ";
+                $preStmt = $this->dbh->prepare($sql);
+                $preStmt->bindParam(
+                    ":parent",
+                    $oldParent,
+                    PDO::PARAM_INT
+                );
+                $preStmt->bindParam(
+                    ":pos",
+                    $oldPos,
+                    PDO::PARAM_INT
+                );
+                $preStmt->execute();
+                // if it is then get next position number for new parent
+                $sql = "
+                    SELECT max(pos) + 1
+                      FROM pages
+                     WHERE parent = :parent";
+                $stmt = $this->dbh->prepare($sql);
+                $stmt->bindParam(
+                    ":parent",
+                    $data['parent'],
+                    PDO::PARAM_INT
+                );
+                $stmt->execute();
+                $pos = $stmt->fetchColumn();
+            }
+
+            $pageSql = "
+                UPDATE pages
+                   SET $headlineColumns
+                       $memberColumns
+                       keyword = :keyword,
+                       meta_title = :meta_title,
+                       meta_description = :meta_description,
+                       navigation_name = :navigation_name,
+                       parent = :parent,
+                       paragraph_links = :paragraph_links,
+                       short_url = :short_url,
+                       template = :template ,
+                       include_members = :include_members,
+                       include_coupons = :include_coupons";
+            if ($pos) {
+                $pageSql .= ", pos = $pos ";
+            }
+
+            $pageSql .= "
+                 WHERE id = :id";
+
+            $paragraphSql = "
+                UPDATE paragraphs
+                   SET title = :title,
+                       description = :description,
+                       image = :image,
+                       caption = :caption
+                 WHERE page = :page
+                   AND pos = 1";
+
+            $pageStmt = $this->dbh->prepare($pageSql);
+            $this->setPageVars($pageStmt, $data);
+            $pageStmt->bindParam(':id', $id, PDO::PARAM_INT);
+            $pageStmt->execute();
+
+            if (defined('MEMBERS_DB') && MEMBERS_DB) {
+                $this->_updateMemberCategories($data['member_categories'], $id);
+                $this->_updateMemberRegions($data['member_regions'], $id);
+            }
+            if (defined('COUPONS') && COUPONS) {
+                $this->_updateCouponCategories($data['coupon_categories'], $id);
+            }
+
+            $paragraphStmt = $this->dbh->prepare($paragraphSql);
+            $this->setParagraphVars($paragraphStmt, $data);
+            $paragraphStmt->bindParam(':page', $id);
+            $paragraphStmt->execute();
+
+            return $this->dbh->commit();
+        } catch (PDOException $e) {
+            $this->dbh->rollback();
+            Toolkit_Logger::logException('DB Error', $e);
+            $content = serialize($data);
+            throw new Toolkit_Toolbox_Exception(
+                "Unable to update page [$content]"
+            );
+        }
+    }
+
+    //    }}}
+
+    //    {{{    _updateMemberCategories()
+
+    private function _updateMemberCategories(array $data = null, $id)
+    {
+        $deleteCatsSql = "
+            DELETE FROM member_categories2toolbox_pages
+             WHERE page = :id";
+        $delStmt = $this->dbh->prepare($deleteCatsSql);
+        $delStmt->bindParam(':id', $id, PDO::PARAM_INT);
+        $delStmt->execute();
+
+        if (is_array($data)) {
+            $insertCatsSql = "
+                INSERT INTO member_categories2toolbox_pages (page, category)
+                VALUES (:page, :category)";
+            $insStmt = $this->dbh->prepare($insertCatsSql);
+            $insStmt->bindParam(':page', $id, PDO::PARAM_INT);
+            foreach ($data as $category) {
+                $insStmt->bindParam(':category', $category, PDO::PARAM_INT);
+                $insStmt->execute();
+            }
+        }
+    }
+
+    //    }}}
+    //    {{{    getMemberCategoriesForPage()
+
+    protected function getMemberCategoriesForPage($id)
+    {
+        try {
+            $sql = "
+                SELECT *
+                  FROM member_categories2toolbox_pages
+                 WHERE page = :id";
+
+            $stmt = $this->dbh->prepare($sql);
+            $stmt->bindParam(':id', $id);
+            $stmt->execute();
+
+            $categories = array();
+            while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                $categories[] = $row['category'];
+            }
+
+            return $categories;
+        } catch (PDOException $e) {
+            Toolkit_Logger::logException('DB Error', $e);
+            throw new Toolkit_Toolbox_Exception(
+                "Unable to fetch member categories for page `$id`"
+            );
+        }
+    }
+
+    //    }}}
+
+    //    {{{    _updateMemberRegions()
+
+    private function _updateMemberRegions(array $data = null, $id)
+    {
+        $deleteRegionsSql = "
+            DELETE FROM member_regions2toolbox_pages
+             WHERE page = :id";
+        $delStmt = $this->dbh->prepare($deleteRegionsSql);
+        $delStmt->bindParam(':id', $id, PDO::PARAM_INT);
+        $delStmt->execute();
+
+        if (is_array($data)) {
+            $insertRegionsSql = "
+                INSERT INTO member_regions2toolbox_pages (page, region)
+                VALUES (:page, :region)";
+            $insStmt = $this->dbh->prepare($insertRegionsSql);
+            $insStmt->bindParam(':page', $id, PDO::PARAM_INT);
+            foreach ($data as $region) {
+                $insStmt->bindParam(':region', $region, PDO::PARAM_INT);
+                $insStmt->execute();
+            }
+        }
+    }
+
+    //    }}}
+    //    {{{    getMemberRegionsForPage()
+
+    protected function getMemberRegionsForPage($id)
+    {
+        try {
+            $sql = "
+                SELECT *
+                  FROM member_regions2toolbox_pages
+                 WHERE page = :id";
+
+            $stmt = $this->dbh->prepare($sql);
+            $stmt->bindParam(':id', $id);
+            $stmt->execute();
+
+            $regions = array();
+            while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                $regions[] = $row['region'];
+            }
+
+            return $regions;
+        } catch (PDOException $e) {
+            Toolkit_Logger::logException('DB Error', $e);
+            throw new Toolkit_Toolbox_Exception(
+                "Unable to fetch member regions for page `$id`"
+            );
+        }
+    }
+
+    //    }}}
+
+    //    {{{    _updateCouponCategories()
+
+    private function _updateCouponCategories(array $data = null, $id)
+    {
+        $deleteCatsSql = "
+            DELETE FROM coupon_categories2toolbox_pages
+             WHERE page = :id";
+        $delStmt = $this->dbh->prepare($deleteCatsSql);
+        $delStmt->bindParam(':id', $id, PDO::PARAM_INT);
+        $delStmt->execute();
+
+        if (is_array($data)) {
+            $insertCatsSql = "
+                INSERT INTO coupon_categories2toolbox_pages (page, category)
+                VALUES (:page, :category)";
+            $insStmt = $this->dbh->prepare($insertCatsSql);
+            $insStmt->bindParam(':page', $id, PDO::PARAM_INT);
+            foreach ($data as $category) {
+                $insStmt->bindParam(':category', $category, PDO::PARAM_INT);
+                $insStmt->execute();
+            }
+        }
+    }
+
+    //    }}}
+    //    {{{    getCouponCategoriesForPage()
+
+    protected function getCouponCategoriesForPage($id)
+    {
+        try {
+            $sql = "
+                SELECT *
+                  FROM coupon_categories2toolbox_pages
+                 WHERE page = :id";
+
+            $stmt = $this->dbh->prepare($sql);
+            $stmt->bindParam(':id', $id);
+            $stmt->execute();
+
+            $categories = array();
+            while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                $categories[] = $row['category'];
+            }
+
+            return $categories;
+        } catch (PDOException $e) {
+            Toolkit_Logger::logException('DB Error', $e);
+            throw new Toolkit_Toolbox_Exception(
+                "Unable to fetch coupon categories for page `$id`"
+            );
+        }
+    }
+
+    //    }}}
+
+    //    {{{    getPhotoGalleriesForPage()
+
+    protected function getPhotoGalleriesForPage($id)
+    {
+        try {
+            $sql = "
+                SELECT pc.*
+                  FROM photo_category pc
+                  JOIN photo_category_bus pcb
+                    ON (pc.id        = pcb.photocat_id)
+                 WHERE pcb.buscat_id = :id";
+
+            $stmt = $this->dbh->prepare($sql);
+            $stmt->bindParam(':id', $id);
+            $stmt->execute();
+
+            $photoGalleries = array();
+            while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                $photoGalleries[$row['id']] = $row['category'];
+            }
+
+            return $photoGalleries;
+        } catch (PDOException $e) {
+            Toolkit_Logger::logException('DB Error', $e);
+            throw new Toolkit_Toolbox_Exception(
+                "Unable to fetch member categories for page `$id`"
+            );
+        }
+    }
+
+    //    }}}
+}
diff --git a/Toolkit/Toolbox/PageGatewayPublishFactory.php b/Toolkit/Toolbox/PageGatewayPublishFactory.php
new file mode 100644 (file)
index 0000000..7876931
--- /dev/null
@@ -0,0 +1,9 @@
+<?php
+class Toolkit_Toolbox_PageGatewayPublishFactory
+       extends Toolkit_Toolbox_GatewayFactoryAbstract
+{
+       public function createGateway()
+       {
+               return new Toolkit_Toolbox_PageGatewayPublish($this->dbh);
+       }
+}
diff --git a/Toolkit/Toolbox/PagesTree.php b/Toolkit/Toolbox/PagesTree.php
new file mode 100644 (file)
index 0000000..4ee4b5e
--- /dev/null
@@ -0,0 +1,289 @@
+<?php
+class Toolkit_Toolbox_PagesTree extends Toolkit_Toolbox_TreeAbstract
+{
+       //      {{{     properties
+
+       protected $rootNodeStart = "<ul id=\"toolbox\">\n";
+       protected $leafStart = "\n\t<li id=\"cat_%s\" %s>\n";
+
+       protected $toolbox;
+
+       //      }}}
+       //      {{{     getActiveBall()
+
+       protected function getActiveBall(array $branch)
+       {
+               $key = array_key_exists('published_page', $branch)
+                       ? 'published_page'
+                       : 'id';
+               $homePage = ($branch[$key] == HOME_ID);
+               $memberDbHomePage = (defined('MEMBERS_DB')
+                                                        && MEMBERS_DB
+                                                        && $branch[$key] == MEMBERS_ONLY_HOME_PAGE);
+
+               if ($homePage || $memberDbHomePage) {
+                       return '<a class="tOff"><img src="'.MEDIA_BASE_URL . 'Toolkit/Toolbox/assets/grnball.gif" alt="Active Ball"></a>';
+               }
+
+               $activeFormat  = '<a class="active-ball" rel="%s" href="%s" title="%s">';
+               $activeFormat .=        $this->getActiveBallImage($branch);
+               $activeFormat .= '</a>';
+
+               return sprintf(
+                       $activeFormat,
+                       $branch['id'],
+                       MEDIA_BASE_URL . "admin/toolbox.php?id={$branch['id']}",
+                       $branch['active_alt']
+               );
+       }
+
+    // }}}
+       //      {{{     getActiveMobileBall()
+
+       protected function getActiveMobileBall(array $branch)
+       {
+               $key = array_key_exists('published_page', $branch)
+                       ? 'published_page'
+                       : 'id';
+        $nonMobilePages
+            = (defined('NON_MOBILE_PAGES'))
+            ? unserialize(NON_MOBILE_PAGES)
+            : array();
+        $nonMobilePages[] = HOME_ID;
+        if (defined('MEMBERS_DB') && MEMBERS_DB) {
+            $nonMobilePages[] = MEMBERS_ONLY_HOME_PAGE;
+            $nonMobilePages[] = MEMBERS_CATEGORY;
+        }
+
+               if (in_array($branch[$key], $nonMobilePages)) {
+                       return '<a class="tOff"><img src="'.MEDIA_BASE_URL . 'Toolkit/Toolbox/assets/grnball.gif" alt="Active Ball"></a>';
+               }
+
+               $activeFormat  = '<a class="mobile-active-ball" rel="%s" href="%s" title="%s">';
+               $activeFormat .=        $this->getActiveMobileBallImage($branch);
+               $activeFormat .= '</a>';
+
+               return sprintf(
+                       $activeFormat,
+                       $branch['id'],
+                       MEDIA_BASE_URL . "admin/toolbox.php?id={$branch['id']}",
+                       $branch['active_alt']
+               );
+       }
+
+    // }}}
+       //      {{{     _getMoveArrows()
+
+       private function _getMoveArrows($branch)
+       {
+               $homePage = ($branch['id'] == HOME_ID);
+               $memberDbHomePage = (defined('MEMBERS_DB')
+                                                        && MEMBERS_DB
+                                                        && $branch['id'] == MEMBERS_ONLY_HOME_PAGE);
+
+               if ($homePage || $memberDbHomePage) {
+                       $format = '<img src="%s" class="tOff" alt="%s">';
+
+                       $up = sprintf(
+                               $format,
+                               MEDIA_APP_BASE_URL . "assets/icons/arrow_up.png",
+                               'Move Up Arrow'
+                       );
+
+                       $down = sprintf(
+                               $format,
+                               MEDIA_APP_BASE_URL . "assets/icons/arrow_down.png",
+                               'Move Down Arrow'
+                       );
+               } else {
+                       $format = '<img src="%s" alt="Move %s Arrow" class="move%s">';
+
+                       $up = sprintf(
+                               $format,
+                               MEDIA_APP_BASE_URL . "assets/icons/arrow_up.png",
+                               'Up',
+                               'Up'
+                       );
+
+                       $down = sprintf(
+                               $format,
+                               MEDIA_APP_BASE_URL . "assets/icons/arrow_down.png",
+                               'Down',
+                               'Down'
+                       );
+               }
+
+
+               return $up . $down;
+       }
+
+       //      }}}
+    
+    private function _getPositionSelect($branch)/*{{{*/
+    {
+               $homePage = ($branch['id'] == HOME_ID);
+               $memberDbHomePage = (defined('MEMBERS_DB')
+                                                        && MEMBERS_DB
+                                                        && $branch['id'] == MEMBERS_ONLY_HOME_PAGE);
+               if ($homePage || $memberDbHomePage) {
+            return '';
+        } else {
+            $addClass 
+                = ($branch['parent'] == 0 || $branch['parent'] == MEMBERS_CATEGORY)
+                ? 'pos-select parent-level-sel'
+                : 'pos-select';
+            $showOn 
+                = ($_GET['showpos'])
+                ? 'display:'
+                : 'display:none;';
+            return '<select style="'.$showOn.'" class="'.$addClass.'" name="pos'.$branch['id'].'" rel="'.$branch['pos'].'"></select>';
+        }
+    }/*}}}*/
+
+       //      {{{     createTree()
+
+       protected function createTree(array $tree, $leaf, $level = 0)
+       {
+               $html = !$level ? $this->rootNodeStart : $this->subTreeStart;
+
+               if ($level == 0) {
+                       $lockMainNavPages = $this->config
+                               ->getItem('section', 'conf')
+                               ->getItem('directive', 'lockMainNavPages')
+                               ->getContent();
+               } else {
+                       $lockMainNavPages = false;
+               }
+
+               if (is_array($leaf) && !empty($leaf)) {
+                       while (list($parent, $branch) = each($leaf)) {
+                               if ($branch['id'] == HOME_ID) {
+                                       $html .= sprintf($this->leafStart, $branch['id'], 'rel="root"');
+                               } else {
+                                       $html .= sprintf($this->leafStart, $branch['id'], null);
+                               }
+                if (   defined('MEMBERS_CATEGORY')
+                    && MEMBERS_CATEGORY
+                    && $branch['parent'] == MEMBERS_CATEGORY
+                ) {
+                    $memberOnlyBase = MEDIA_BASE_URL . "members-only-area/";
+                    switch ($branch['id']) {
+                    case MEMBERS_PROFILE_FORM_PAGE :
+                        $previewUrl = $memberOnlyBase
+                            . "?rt=EditProfile&tab=info";
+                        break;
+                    case MEMBERS_COUPONS_PAGE :
+                        $previewUrl = $memberOnlyBase
+                            . "?rt=Coupons&page_id={$branch['id']}";
+                        break;
+                    case MEMBERS_EVENTS_PAGE :
+                        $previewUrl = $memberOnlyBase
+                            . "?rt=Events&page_id={$branch['id']}";
+                        break;
+                    case MEMBERS_LEADS_PAGE :
+                        $previewUrl = $memberOnlyBase
+                            . "?rt=Leads&page_id={$branch['id']}";
+                        break;
+                    case MEMBERS_REPORTS_PAGE :
+                        $previewUrl = $memberOnlyBase
+                            . "?rt=Reports&page_id={$branch['id']}";
+                        break;
+                    case MEMBERS_ADD_JOB_PAGE :
+                        $previewUrl = $memberOnlyBase
+                            . "?rt=Jobs&ac=addJob&page_id={$branch['id']}";
+                        break;
+                    case MEMBERS_SHOW_JOBS_PAGE :
+                        $previewUrl = $memberOnlyBase
+                            . "?rt=Jobs&page_id={$branch['id']}";
+                        break;
+                    default:
+                        $previewUrl = $memberOnlyBase
+                            . "?page_id={$branch['id']}";
+                        break;
+                    }
+                    
+                } else {
+                    $previewUrl = BASE_URL . "index.php?catid={$branch['id']}&preview=1";
+                }
+
+                               $html .= '<div class="right-element">';
+
+                               $html .= '<a href="'.MEDIA_BASE_URL.'admin/toolbox.php?rt=EditPage&amp;id='.$branch['id'].'" class="editPage" title="Edit Page">Top 
+Section </a> ';
+                               $html .= '<a href="'.MEDIA_BASE_URL.'admin/toolbox.php?rt=Paragraphs&amp;pageid='.$branch['id'].'" class="editParagraphs" title="Page 
+Paragraphs">Paragraphs</a> ';
+                               $html .= '<a href="'.$previewUrl.'" class="pagePreview" title="Preview Page">[Preview]</a> ';
+                               if (!$lockMainNavPages) {
+                                       $html .= $this->getActiveBall($branch);
+                    if (defined("MOBILE_SITE") && MOBILE_SITE) {
+                                           $html .= $this->getActiveMobileBall($branch);
+                    }
+                                       $html .= $this->_getPositionSelect($branch);
+                               }
+
+                               $html .= '</div>';
+                               $html .= "<strong>{$branch['navigation_name']}</strong>";
+
+                               if ($tree[$parent]) {
+                                       $html .= $this->createTree($tree, $tree[$parent], $level + 1);
+                               } else {
+                                       $html .= $this->leafEnd;
+                               }
+                       }
+               }
+
+               $html .= $this->treeEnd;
+               if ($level) {
+                       $html .= $this->leafEnd;
+               }
+               return $html;
+       }
+
+       //      }}}
+
+       //      {{{     fetchContent()
+
+       protected function fetchContent(Toolkit_Toolbox_GatewayAbstract $gateway)
+       {
+               $pages = $gateway->findAll();
+               if (is_array($pages)) {
+                       $threads = array();
+                       foreach ($pages as $page) {
+                               if (!empty($page['keyword'])) {
+                                       $page['navigation_name'] .= ' {' . $page['keyword'] . '}';
+                               }
+                               $page['active_alt'] = $page['active'] ? 'On' : 'Off';
+                               $page['children'] = array();
+                               $threads[] = $page;
+                       }
+
+                       $children = array();
+                       while (list($key, $value) = each ($threads)) {
+                               $children[$value['parent']][$value['id']] = $value;
+                       }
+
+                       $this->tree = $children;
+               } else {
+                       $this->tree = array();
+               }
+       }
+
+       //      }}}
+
+       //      {{{     toHtml()
+
+       public function toHtml(Toolkit_Toolbox_GatewayAbstract $gateway)
+       {
+               $GLOBALS['bottomScripts'][] = MEDIA_BASE_URL . 'Toolkit/Toolbox/libjs/pagesTree.js';
+               $GLOBALS['bottomScripts'][] = MEDIA_APP_BASE_URL . 'libjs/plugins/jsTree/0.9.9a2/jquery.tree.js';
+               $GLOBALS['bottomScripts'][] = MEDIA_APP_BASE_URL . 'libjs/plugins/jsTree/0.9.9a2/lib/jquery.cookie.js';
+               $GLOBALS['bottomScripts'][] = MEDIA_APP_BASE_URL . 'libjs/plugins/jsTree/0.9.9a2/plugins/jquery.tree.cookie.js';
+
+               $this->fetchContent($gateway);
+               $html = parent::toHtml();
+
+               return "<div id=\"tree\">$html</div>";
+       }
+
+       //      }}}
+}
diff --git a/Toolkit/Toolbox/ParagraphBreadCrumbs.php b/Toolkit/Toolbox/ParagraphBreadCrumbs.php
new file mode 100644 (file)
index 0000000..357d865
--- /dev/null
@@ -0,0 +1,46 @@
+<?php
+
+class Toolkit_Toolbox_ParagraphBreadCrumbs
+       extends Toolkit_Toolbox_BreadCrumbsAbstract
+{
+       //      {{{     getPageUri()
+
+       protected function getPageUri(array $page)
+       {
+               //      go to parent edit page form
+           $uri = MEDIA_BASE_URL . "admin/toolbox.php?rt=EditPage&amp;id={$page['id']}";
+           return "<a href=\"{$uri}\">{$page['navigation_name']}</a>";
+       }
+
+       //      }}}
+       //      {{{     getPath()
+
+       /**
+        * @return the $path
+        */
+       public function getPath()
+       {
+           $id = $this->id;
+           $stack = array();
+           do {
+               $page = $this->getPage($id);
+
+               $stack[] = $this->getPageUri($page);
+               $id = $page['parent'];
+           } while ($id != 0);
+
+           $reverse = array_reverse($stack);
+
+               if (filter_input(INPUT_GET, 'id', FILTER_VALIDATE_INT)) {
+                       $uri = MEDIA_BASE_URL . "admin/toolbox.php?rt=Paragraphs&amp;pageid={$this->id}";
+                       $reverse[] = "<a href=\"{$uri}\">Paragraphs</a>";
+               }
+
+        $this->path = implode(' > ', $reverse);
+
+               return $this->path;
+       }
+
+       //      }}}
+}
+?>
diff --git a/Toolkit/Toolbox/ParagraphDraftBreadCrumbs.php b/Toolkit/Toolbox/ParagraphDraftBreadCrumbs.php
new file mode 100644 (file)
index 0000000..1e20995
--- /dev/null
@@ -0,0 +1,80 @@
+<?php
+
+class Toolkit_Toolbox_ParagraphDraftBreadCrumbs
+       extends Toolkit_Toolbox_BreadCrumbsAbstract
+{
+       //      {{{     getPageUri()
+
+       protected function getPageUri(array $page)
+       {
+               //      go to parent paragraphs
+           $uri = MEDIA_BASE_URL . "admin/toolbox.php?rt=Paragraphs&pageid={$page['id']}";
+               //      go to parent edit page form
+           $uri = MEDIA_BASE_URL . "admin/toolbox.php?rt=EditPage&id={$page['id']}";
+           return "<a href=\"{$uri}\">{$page['navigation_name']}</a>";
+       }
+
+       //      }}}
+       //      {{{     getDraft()
+
+    protected function getDraft($id)
+    {
+        try {
+            $sql = "
+                SELECT *
+                  FROM pages_draft
+                 WHERE id = :id";
+
+            $stmt = $this->dbh->prepare($sql);
+            $stmt->bindParam(':id', $id, PDO::PARAM_INT);
+            $stmt->execute();
+
+            return $stmt->fetch(PDO::FETCH_ASSOC);
+        } catch (PDOException $e) {
+            Toolkit_Logger::logException('DB Error', $e);
+            throw new Toolkit_Toolbox_Exception(
+                "Could not fetch parent for page `$id`"
+            );
+        }
+    }
+
+       //      }}}
+       //      {{{     getPath()
+
+       /**
+        * @return the $path
+        */
+       public function getPath()
+       {
+               $draft = $this->getDraft($this->id);
+
+               if (filter_input(INPUT_GET, 'id', FILTER_VALIDATE_INT)) {
+                       $uri = MEDIA_BASE_URL . "admin/toolbox.php?rt=ParagraphsDraft&amp;pageid={$this->id}";
+                       $stack = array("<a href=\"{$uri}\">Paragraphs</a>");
+               }
+
+               if (is_array($stack)) {
+                       $stack[] = $this->getPageUri($draft);
+               } else {
+                       $stack = array($draft['navigation_name']);
+               }
+
+               $publishedPage = $this->getPage($draft['published_page']);
+               $id = $publishedPage['parent'];
+           while ($id != 0) {
+               $page = $this->getPage($id);
+
+               $stack[] = $this->getPageUri($page);
+               $id = $page['parent'];
+           }
+
+           $reverse = array_reverse($stack);
+
+        $this->path = implode(' > ', $reverse);
+
+               return $this->path;
+       }
+
+       //      }}}
+}
+?>
diff --git a/Toolkit/Toolbox/ParagraphGatewayAbstract.php b/Toolkit/Toolbox/ParagraphGatewayAbstract.php
new file mode 100644 (file)
index 0000000..2c0d153
--- /dev/null
@@ -0,0 +1,49 @@
+<?php
+abstract class Toolkit_Toolbox_ParagraphGatewayAbstract
+       extends Toolkit_Toolbox_GatewayAbstract
+{
+       //      {{{     findAll()
+
+       abstract public function findAll($page);
+
+       //      }}}
+
+       //      {{{     setParagraphVars()
+
+       protected function setParagraphVars(PDOStatement &$stmt, $data)
+       {
+               $data['active'] = (bool) $data['active'];
+               $data['back_to_top'] = (bool) $data['back_to_top'];
+               if ($data['remove_image']) {
+                       $data['image'] = '';
+               }
+
+               $stmt->bindParam(':active', $data['active'], PDO::PARAM_BOOL);
+               $stmt->bindParam(':title', $data['title']);
+               $stmt->bindParam(':description', $data['description']);
+               $stmt->bindParam(':image', $data['image']);
+               $stmt->bindParam(':caption', $data['caption']);
+               $stmt->bindParam(':page', $data['page']);
+               $stmt->bindParam(':back_to_top', $data['back_to_top'], PDO::PARAM_BOOL);
+       }
+
+       //      }}}
+       //      {{{     setFileVars()
+
+       protected function setFileVars(PDOStatement &$stmt, $data, $key)
+       {
+               $stmt->bindParam(':filename', $data['filename'][$key]);
+               $stmt->bindParam(':bytes', $data['bytes'][$key]);
+               $stmt->bindParam(':urltext', $data['urltext'][$key]);
+               if ($data['type'][$key] != DIRECTORY_SEPARATOR) {
+                       $stmt->bindParam(':type', $data['type'][$key]);
+               } else {
+                       $pieces = explode('.', $data['filename'][$key]);
+                       $extension = end($pieces);
+                       $stmt->bindParam(':type', $extension);
+               }
+       }
+
+       //      }}}
+}
+?>
diff --git a/Toolkit/Toolbox/ParagraphGatewayDraft.php b/Toolkit/Toolbox/ParagraphGatewayDraft.php
new file mode 100644 (file)
index 0000000..dd07c58
--- /dev/null
@@ -0,0 +1,225 @@
+<?php
+class Toolkit_Toolbox_ParagraphGatewayDraft
+       extends Toolkit_Toolbox_ParagraphGatewayAbstract
+{
+       //      {{{     delete()
+
+       public function delete($id)
+       {
+               $pageSql = "
+            DELETE
+              FROM paragraphs_draft
+             WHERE id = :id";
+
+               try {
+                       $stmt = $this->dbh->prepare($pageSql);
+                       $stmt->bindParam(':id', $id, PDO::PARAM_INT);
+                       return $stmt->execute();
+               } catch (PDOException $e) {
+                       Toolkit_Logger::logException('DB Error', $e);
+                       throw new Toolkit_Toolbox_Exception(
+                               "Unable to delete paragraph draft `$id`"
+                       );
+               }
+       }
+
+       //      }}}
+
+       //      {{{     find()
+
+       public function find($id)
+       {
+               $paragraphSql = "
+            SELECT *
+              FROM paragraphs_draft
+             WHERE id  = :id ";
+
+               $fileSql = "
+                       SELECT *
+                         FROM files_draft
+                        WHERE paragraph = :paragraph
+                        ORDER BY pos";
+
+               try {
+                       $paragraphStmt = $this->dbh->prepare($paragraphSql);
+                       $paragraphStmt->bindParam(':id', $id, PDO::PARAM_INT);
+                       $paragraphStmt->execute();
+
+                       $paragraph = $paragraphStmt->fetch(PDO::FETCH_ASSOC);
+
+                       if (!empty($paragraph['image'])) {
+                               $imgFormat = "<img src=\"%s{$paragraph['image']}\">";
+                               $paragraph['current_image_original']
+                                       = sprintf($imgFormat, TOOLBOX_ORIGINAL);
+                               $paragraph['current_image_resized']
+                                       = sprintf($imgFormat, TOOLBOX_RESIZED);
+                               $paragraph['current_image_midsized']
+                                       = sprintf($imgFormat, TOOLBOX_MIDSIZED);
+                               $paragraph['current_image_thumb']
+                                       = sprintf($imgFormat, TOOLBOX_THUMB);
+                       } else {
+                               $paragraph['current_image_original'] = 'Image not yet uploaded';
+                               $paragraph['current_image_resized']  = 'Image not yet uploaded';
+                               $paragraph['current_image_midsized'] = 'Image not yet uploaded';
+                               $paragraph['current_image_thumb']    = 'Image not yet uploaded';
+                       }
+
+                       $fileStmt = $this->dbh->prepare($fileSql);
+                       $fileStmt->bindParam(':paragraph', $id, PDO::PARAM_INT);
+                       $fileStmt->execute();
+
+                       $paragraph['files'] = $fileStmt->fetchAll(PDO::FETCH_ASSOC);
+
+                       return $paragraph;
+               } catch (PDOException $e) {
+                       Toolkit_Logger::logException('DB Error', $e);
+                       throw new Toolkit_Toolbox_Exception(
+                               "Unable to find paragraph draft `$id`"
+                       );
+               }
+       }
+
+       //      }}}
+       //      {{{     findAll()
+
+       public function findAll($page)
+       {
+               try {
+                       $sql = "
+                               SELECT *
+                                 FROM paragraphs_draft
+                                WHERE page = :page
+                                  AND pos > 1
+                                ORDER BY pos";
+
+                       $stmt = $this->dbh->prepare($sql);
+                       $stmt->bindParam(':page', $page);
+                       $stmt->execute();
+
+                       $paragraphs = array();
+                       while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                               $paragraphs[] = $this->find($row['id']);
+                       }
+
+                       return $paragraphs;
+               } catch (PDOException $e) {
+                       Toolkit_Logger::logException('DB Error', $e);
+                       throw new Toolkit_Toolbox_Exception(
+                               "Error fetching all paragraph drafts for page `$page`"
+                       );
+               }
+       }
+
+       //      }}}
+
+       //      {{{     insert()
+
+       public function insert(array $data)
+       {
+               $paragraphSql = "
+                       INSERT INTO paragraphs_draft (active, back_to_top, title,
+                       description, image, caption, page)
+                       VALUES (:active, :back_to_top, :title, :description, :image,
+                              :caption, :page)";
+
+               $filesInsertSql = "
+                       INSERT INTO files_draft (filename, bytes, type, urltext, paragraph)
+                       VALUES (:filename, :bytes, :type, :urltext, :paragraph)";
+
+               try {
+                       $this->dbh->beginTransaction();
+
+                       $paragraphStmt = $this->dbh->prepare($paragraphSql);
+                       $this->setParagraphVars($paragraphStmt, $data);
+                       $paragraphStmt->execute();
+
+                       $row = $this->dbh
+                               ->query('select id from paragraphs_draft order by id desc limit 1')
+                               ->fetch(PDO::FETCH_ASSOC);
+
+                       if (   isset($data['uploaded_files'])
+                               && is_array($data['uploaded_files'])
+                       ) {
+                               $fileInsertStmt = $this->dbh->prepare($filesInsertSql);
+                               $fileInsertStmt->bindParam(':paragraph', $row['id']);
+                               $length = count($data['uploaded_files']['type']);
+                               for ($i = 0; $i < $length; ++$i) {
+                                       $this->setFileVars($fileInsertStmt, $data['uploaded_files'], $i);
+                                       $fileInsertStmt->execute();
+                               }
+                       }
+
+                       return $this->dbh->commit();
+               } catch (PDOException $e) {
+                       $this->dbh->rollback();
+                       Toolkit_Logger::logException('DB Error', $e);
+                       $content = serialize($data);
+                       throw new Toolkit_Toolbox_Exception(
+                               "Unable to insert paragraph draft [$content]"
+                       );
+               }
+       }
+
+       //      }}}
+
+       //      {{{     update()
+
+       public function update(array $data, $id)
+       {
+               $paragraphSql = "
+                       UPDATE paragraphs_draft
+                          SET back_to_top = :back_to_top,
+                                  active = :active,
+                                  title = :title,
+                                  description = :description,
+                                  image = :image,
+                                  caption = :caption,
+                                  page = :page
+                        WHERE id = :id";
+
+               $filesDeleteSql = "
+            DELETE FROM files_draft
+             WHERE paragraph = :id";
+
+               $filesInsertSql = "
+                       INSERT INTO files_draft (filename, bytes, type, urltext, paragraph)
+                       VALUES (:filename, :bytes, :type, :urltext, :paragraph)";
+
+               try {
+                       $this->dbh->beginTransaction();
+
+                       $paragraphStmt = $this->dbh->prepare($paragraphSql);
+                       $this->setParagraphVars($paragraphStmt, $data);
+                       $paragraphStmt->bindParam(':id', $id);
+                       $paragraphStmt->execute();
+
+                       $filesDeleteStmt = $this->dbh->prepare($filesDeleteSql);
+                       $filesDeleteStmt->bindParam(':id', $id);
+                       $filesDeleteStmt->execute();
+
+                       if (   isset($data['uploaded_files'])
+                               && is_array($data['uploaded_files'])
+                       ) {
+                               $fileInsertStmt = $this->dbh->prepare($filesInsertSql);
+                               $fileInsertStmt->bindParam(':paragraph', $id);
+                               $length = count($data['uploaded_files']['type']);
+                               for ($i = 0; $i < $length; ++$i) {
+                                       $this->setFileVars($fileInsertStmt, $data['uploaded_files'], $i);
+                                       $fileInsertStmt->execute();
+                               }
+                       }
+
+                       return $this->dbh->commit();
+               } catch (PDOException $e) {
+                       $this->dbh->rollback();
+                       Toolkit_Logger::logException('DB Error', $e);
+                       $content = serialize($data);
+                       throw new Toolkit_Toolbox_Exception(
+                               "Unable to update paragraph draft [$content]"
+                       );
+               }
+       }
+
+       //      }}}
+}
+?>
diff --git a/Toolkit/Toolbox/ParagraphGatewayDraftFactory.php b/Toolkit/Toolbox/ParagraphGatewayDraftFactory.php
new file mode 100644 (file)
index 0000000..f02720e
--- /dev/null
@@ -0,0 +1,10 @@
+<?php
+class Toolkit_Toolbox_ParagraphGatewayDraftFactory
+       extends Toolkit_Toolbox_GatewayFactoryAbstract
+{
+       public function createGateway()
+       {
+               return new Toolkit_Toolbox_ParagraphGatewayDraft($this->dbh);
+       }
+}
+?>
diff --git a/Toolkit/Toolbox/ParagraphGatewayPublish.php b/Toolkit/Toolbox/ParagraphGatewayPublish.php
new file mode 100644 (file)
index 0000000..1621ab2
--- /dev/null
@@ -0,0 +1,293 @@
+<?php
+class Toolkit_Toolbox_ParagraphGatewayPublish
+       extends Toolkit_Toolbox_ParagraphGatewayAbstract
+{
+       //      {{{     delete()
+
+       public function delete($id)
+       {
+               $pageSql = "
+            DELETE
+              FROM paragraphs
+             WHERE id = :id";
+
+               try {
+                       $stmt = $this->dbh->prepare($pageSql);
+                       $stmt->bindParam(':id', $id, PDO::PARAM_INT);
+                       return $stmt->execute();
+               } catch (PDOException $e) {
+                       Toolkit_Logger::logException('DB Error', $e);
+                       throw new Toolkit_Toolbox_Exception(
+                               "Unable to delete paragraph `$id`"
+                       );
+               }
+       }
+
+       //      }}}
+
+       //      {{{     find()
+
+       public function find($id)
+       {
+               $paragraphSql = "
+            SELECT *
+              FROM paragraphs
+             WHERE id  = :id ";
+
+               $fileSql = "
+                       SELECT id,filename,bytes,type,
+                   coalesce(urltext,filename,'empty') as urltext,
+                   paragraph,pos
+                         FROM files
+                        WHERE paragraph = :paragraph
+                        ORDER BY pos";
+
+               try {
+                       $paragraphStmt = $this->dbh->prepare($paragraphSql);
+                       $paragraphStmt->bindParam(':id', $id, PDO::PARAM_INT);
+                       $paragraphStmt->execute();
+
+                       $paragraph = $paragraphStmt->fetch(PDO::FETCH_ASSOC);
+
+                       if (!empty($paragraph['image'])) {
+                               $imgFormat = "<img src=\"%s{$paragraph['image']}\">";
+                               $paragraph['current_image_original']
+                                       = sprintf($imgFormat, TOOLBOX_ORIGINAL);
+                               $paragraph['current_image_resized']
+                                       = sprintf($imgFormat, TOOLBOX_RESIZED);
+                               $paragraph['current_image_midsized']
+                                       = sprintf($imgFormat, TOOLBOX_MIDSIZED);
+                               $paragraph['current_image_thumb']
+                                       = sprintf($imgFormat, TOOLBOX_THUMB);
+                       } else {
+                               $paragraph['current_image_original'] = 'Image not yet uploaded';
+                               $paragraph['current_image_resized']  = 'Image not yet uploaded';
+                               $paragraph['current_image_midsized'] = 'Image not yet uploaded';
+                               $paragraph['current_image_thumb']    = 'Image not yet uploaded';
+                       }
+
+                       $fileStmt = $this->dbh->prepare($fileSql);
+                       $fileStmt->bindParam(':paragraph', $id, PDO::PARAM_INT);
+                       $fileStmt->execute();
+
+                       $paragraph['files'] = $fileStmt->fetchAll(PDO::FETCH_ASSOC);
+
+                       return $paragraph;
+               } catch (PDOException $e) {
+                       Toolkit_Logger::logException('DB Error', $e);
+                       throw new Toolkit_Toolbox_Exception(
+                               "Unable to find paragraph `$id`"
+                       );
+               }
+       }
+
+       //      }}}
+       //      {{{     findAll()
+
+       public function findAll($page)
+       {
+               try {
+                       $sql = "
+                               SELECT *
+                                 FROM paragraphs
+                                WHERE page = :page
+                                  AND pos > 1
+                                ORDER BY pos";
+
+                       $stmt = $this->dbh->prepare($sql);
+                       $stmt->bindParam(':page', $page);
+                       $stmt->execute();
+
+                       $paragraphs = array();
+                       while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
+                               $paragraphs[] = $this->find($row['id']);
+                       }
+
+                       return $paragraphs;
+               } catch (PDOException $e) {
+                       Toolkit_Logger::logException('DB Error', $e);
+                       throw new Toolkit_Toolbox_Exception(
+                               "Error fetching all paragraphs for page `$page`"
+                       );
+               }
+       }
+
+       //      }}}
+
+       //      {{{     insert()
+
+       public function insert(array $data)
+       {
+        // add the part where it throw the file to the server
+        // and returns the data for it
+        if (is_array($_FILES['filename']) && $_FILES['filename']['error'] == 0) {
+            $fs = new Toolkit_FileServer_FileAdapter();
+            try {
+                if ($res = $fs->upload('filename')) {
+                    $data['uploaded_files']['filename'][] = $res['name'];
+                    $data['uploaded_files']['urltext'][] 
+                        = ($data['fileurltext']) 
+                        ? $data['fileurltext']
+                        : $_FILES['filename']['name'];
+                    $data['uploaded_files']['bytes'][] = $res['size'];
+                    $data['uploaded_files']['type'][] = $res['type'];
+                }
+
+            } catch (Toolkit_FileServer_Exception $e) {
+                Toolkit_Logger::logException('File Server', $e);
+                echo -1; // Don't return "false", it will mess up the JS plugin.
+                return;
+            }
+        }
+               $paragraphSql = "
+                       INSERT INTO paragraphs (active, back_to_top, title, description,
+                       image, caption, page)
+                       VALUES (:active, :back_to_top, :title, :description, :image,
+                              :caption, :page)";
+
+               $filesInsertSql = "
+                       INSERT INTO files (filename, bytes, type, urltext, paragraph)
+                       VALUES (:filename, :bytes, :type, :urltext, :paragraph)";
+
+               try {
+                       $this->dbh->beginTransaction();
+
+                       $paragraphStmt = $this->dbh->prepare($paragraphSql);
+                       $this->setParagraphVars($paragraphStmt, $data);
+                       $paragraphStmt->execute();
+
+                       $row = $this->dbh
+                               ->query('select id from paragraphs order by id desc limit 1')
+                               ->fetch(PDO::FETCH_ASSOC);
+
+                       if (   isset($data['uploaded_files'])
+                               && is_array($data['uploaded_files'])
+                       ) {
+                               $fileInsertStmt = $this->dbh->prepare($filesInsertSql);
+                               $fileInsertStmt->bindParam(':paragraph', $row['id']);
+                               $length = count($data['uploaded_files']['type']);
+                               for ($i = 0; $i < $length; ++$i) {
+                                       $this->setFileVars($fileInsertStmt, $data['uploaded_files'], $i);
+                                       $fileInsertStmt->execute();
+                               }
+                       }
+
+            $this->dbh->commit();
+                       return $row['id'];
+               } catch (PDOException $e) {
+                       $this->dbh->rollback();
+                       Toolkit_Logger::logException('DB Error', $e);
+                       $content = serialize($data);
+                       throw new Toolkit_Toolbox_Exception(
+                               "Unable to insert paragraph [$content]"
+                       );
+               }
+       }
+
+       //      }}}
+
+       //      {{{     update()
+
+       public function update(array $data, $id)
+       {
+        if (is_array($data['deleteFile'])) {
+            foreach ($data['deleteFile'] as $fileNameToDelete) {
+                $delKey = array_search(
+                    $fileNameToDelete, 
+                    $data['uploaded_files']['filename']
+                );
+                unset(
+                    $data['uploaded_files']['urltext'][$delKey],
+                    $data['uploaded_files']['filename'][$delKey],
+                    $data['uploaded_files']['bytes'][$delKey],
+                    $data['uploaded_files']['type'][$delKey]
+                );
+            }
+        }
+
+        // add the part where it throw the file to the server
+        // and returns the data for it
+        if (is_array($_FILES['filename']) && $_FILES['filename']['error'] == 0) {
+            $fs = new Toolkit_FileServer_FileAdapter();
+            try {
+                if ($res = $fs->upload('filename')) {
+                    $data['uploaded_files']['filename'][] = $res['name'];
+                    $data['uploaded_files']['urltext'][] 
+                        = ($data['fileurltext']) 
+                        ? $data['fileurltext']
+                        : $_FILES['filename']['name'];
+                    $data['uploaded_files']['bytes'][] = $res['size'];
+                    $data['uploaded_files']['type'][] = $res['type'];
+                }
+
+            } catch (Toolkit_FileServer_Exception $e) {
+                Toolkit_Logger::logException('File Server', $e);
+                echo -1; // Don't return "false", it will mess up the JS plugin.
+                return;
+            }
+        }
+        //echo '<pre>'.print_r($data, true).'</pre>';
+        //exit;
+        unset(
+            $data['deleteFile'],
+            $data['fileurltext']
+        );
+
+               $paragraphSql = "
+                       UPDATE paragraphs
+                          SET back_to_top = :back_to_top,
+                                  active = :active,
+                                  title = :title,
+                                  description = :description,
+                                  image = :image,
+                                  caption = :caption,
+                                  page = :page
+                        WHERE id = :id";
+
+               $filesDeleteSql = "
+            DELETE FROM files
+             WHERE paragraph = :id";
+
+               $filesInsertSql = "
+                       INSERT INTO files (filename, bytes, type, urltext, paragraph)
+                       VALUES (:filename, :bytes, :type, :urltext, :paragraph)";
+
+               try {
+                       $this->dbh->beginTransaction();
+
+                       $paragraphStmt = $this->dbh->prepare($paragraphSql);
+                       $this->setParagraphVars($paragraphStmt, $data);
+                       $paragraphStmt->bindParam(':id', $id);
+                       $paragraphStmt->execute();
+
+                       $filesDeleteStmt = $this->dbh->prepare($filesDeleteSql);
+                       $filesDeleteStmt->bindParam(':id', $id);
+                       $filesDeleteStmt->execute();
+
+                       if (   isset($data['uploaded_files'])
+                               && is_array($data['uploaded_files'])
+                       ) {
+                               $fileInsertStmt = $this->dbh->prepare($filesInsertSql);
+                               $fileInsertStmt->bindParam(':paragraph', $id);
+                               $length = count($data['uploaded_files']['type']);
+                $fileArrayKeys = array_keys($data['uploaded_files']['type']);
+                foreach ($fileArrayKeys as $i) {
+                                       $this->setFileVars($fileInsertStmt, $data['uploaded_files'], $i);
+                                       $fileInsertStmt->execute();
+                               }
+                       }
+
+                       return $this->dbh->commit();
+               } catch (PDOException $e) {
+                       $this->dbh->rollback();
+                       Toolkit_Logger::logException('DB Error', $e);
+                       $content = serialize($data);
+                       throw new Toolkit_Toolbox_Exception(
+                               "Unable to update paragraph [$content]"
+                       );
+               }
+       }
+
+       //      }}}
+}
+?>
diff --git a/Toolkit/Toolbox/ParagraphGatewayPublishFactory.php b/Toolkit/Toolbox/ParagraphGatewayPublishFactory.php
new file mode 100644 (file)
index 0000000..3e7083c
--- /dev/null
@@ -0,0 +1,10 @@
+<?php
+class Toolkit_Toolbox_ParagraphGatewayPublishFactory
+       extends Toolkit_Toolbox_GatewayFactoryAbstract
+{
+       public function createGateway()
+       {
+               return new Toolkit_Toolbox_ParagraphGatewayPublish($this->dbh);
+       }
+}
+?>
diff --git a/Toolkit/Toolbox/ParagraphsController.php b/Toolkit/Toolbox/ParagraphsController.php
new file mode 100644 (file)
index 0000000..6bc23e8
--- /dev/null
@@ -0,0 +1,315 @@
+<?php
+/**
+ * ParagraphsController.php
+ *
+ * PHP version 5
+ *
+ * @category Toolkit
+ * @package  Toolbox
+ * @author   Jamie Kahgee <steve@gaslightmedia.com>
+ * @license  http://www.gaslightmedia.com Gaslightmedia
+ * @link     <>
+ */
+
+/**
+ * Toolkit_Toolbox_ParagraphsController
+ *
+ * Description for Toolkit_Toolbox_ParagraphsController
+ *
+ * @category Toolkit
+ * @package  Toolbox
+ * @author   Jamie Kahgee <steve@gaslightmedia.com>
+ * @license  http://www.gaslightmedia.com Gaslightmedia
+ * @link     <>
+ */
+class Toolkit_Toolbox_ParagraphsController
+       extends Toolkit_BaseControllerAbstract implements Toolkit_IController
+{
+       //      {{{     _getForm()
+
+    /**
+     * Description for _getForm()
+     *
+     * @return \Toolkit_Toolbox_Forms_EditParagraph
+     * @access private
+     */
+    private function _getForm()
+    {
+        //  Need three separate objects otherwise, new parses overwrite existing
+        //  root variable data.
+        $tlbConf = new Config;
+        $memConf = new Config;
+        $cpnConf = new Config;
+
+        $memRoot =& $memConf->parseConfig(
+            BASE . 'Toolkit/Members/config.ini',
+            'IniFile'
+        );
+        $cpnRoot =& $cpnConf->parseConfig(
+            BASE . 'Toolkit/Coupons/config.ini',
+            'IniFile'
+        );
+        $tbxRoot =& $tlbConf->parseConfig(
+            BASE . 'Toolkit/Toolbox/config.ini',
+            'IniFile'
+        );
+
+        $form = new Toolkit_Toolbox_Forms_EditParagraph(
+            'edit_paragraph',
+            'post',
+            MEDIA_BASE_URL . 'admin/toolbox.php?rt=Paragraphs&ac=process'
+        );
+               $form->configureForm(
+                       $this->registry->dbh,
+                       new Toolkit_Toolbox_ParagraphGatewayPublishFactory($this->registry->dbh),
+                       new Toolkit_FileServer_ImageAdapter(),
+                       new Toolkit_FileServer_FileAdapter(),
+                       new Toolkit_Toolbox_FileExtension
+               );
+
+        return $form;
+    }
+
+       //      }}}
+
+       //      {{{     editAction()
+
+    /**
+     * Description for editAction()
+     *
+     * @return string
+     * @access public
+     */
+       public function editAction()
+       {
+           if (ctype_digit($_GET['pageid'])) {
+                       $breadCrumbs = new Toolkit_Toolbox_ParagraphBreadCrumbs(
+                           $this->registry->dbh,
+                           $_GET['pageid']
+                       );
+                       $html = (string) $breadCrumbs;
+           }
+
+           $form = $this->_getForm();
+               $html .= $form->toHtml($this->registry->dbh);
+
+               return $html;
+       }
+
+       //      }}}
+
+       //      {{{     indexAction()
+
+    /**
+     * Description for indexAction()
+     *
+     * @return string
+     * @throws Toolkit_Toolbox_Exception
+     * @throws RuntimeException
+     * @access public
+     */
+       public function indexAction()
+       {
+           if (ctype_digit($_GET['pageid'])) {
+                       $breadCrumbs = new Toolkit_Toolbox_ParagraphBreadCrumbs(
+                               $this->registry->dbh,
+                               $_GET['pageid']
+                       );
+                       $html = (string) $breadCrumbs;
+           }
+
+               try {
+                       if (!ctype_digit($_GET['pageid'])) {
+                               throw new RuntimeException(
+                                       "Category id `{$_GET['pageid']}` is not an integer"
+                               );
+                       }
+
+                       $toolboxConfig = new Config;
+                       $toolboxConfigRoot =& $toolboxConfig->parseConfig(
+                               BASE . 'Toolkit/Toolbox/config.ini',
+                               'IniFile'
+                       );
+                       $tree = new Toolkit_Toolbox_ParagraphsTree($toolboxConfigRoot);
+                       $html .= $tree->toHtml(
+                               new Toolkit_Toolbox_ParagraphGatewayPublish($this->registry->dbh)
+                       );
+
+                       return $html;
+               } catch (RuntimeException $e) {
+                       Toolkit_Logger::logException('Runtime Error', $e);
+                       throw new Toolkit_Toolbox_Exception(
+                               'Category id must be an integer'
+                       );
+               }
+       }
+
+       //      }}}
+
+       //      {{{     processAction()
+
+    /**
+     * Description for processAction()
+     *
+     * @return string
+     * @access public
+     */
+       public function processAction()
+       {
+        $form = $this->_getForm();
+
+               if ($form->isSubmitted()) {
+                       if ($form->getSubmitValue('cancel')) {
+                               //      do nothing
+                               $pageid = $form->getSubmitValue('page');
+                               header('Location: ' . MEDIA_BASE_URL . "admin/toolbox.php?rt=Paragraphs&pageid=$pageid");
+                               exit();
+                       } elseif ($form->getSubmitValue('previewParagraph')) {
+                       } elseif ($form->getSubmitValue('saveParagraph')) {
+                               //      save paragraph
+                               $gateway = new Toolkit_Toolbox_ParagraphGatewayPublish($this->registry->dbh);
+
+                               if ($form->validate()) {
+                                       $paragraphId = $form->getSubmitValue('id');
+                                       if ($paragraphId) {
+                        // before we update this paragraph need to remove the cache
+                        try {
+                            $sql = "
+                            SELECT page
+                              FROM paragraphs
+                             WHERE id = :id";
+                            $paraStmt = $this->registry
+                                ->dbh
+                                ->prepare($sql);
+                            $paraStmt->bindParam(':id', $paragraphId, PDO::PARAM_INT);
+                            $paraStmt->execute();
+                            $oldPageId = $paraStmt->fetchColumn();
+                            if ($oldPageId) {
+                                $cache = new Cache_Lite($GLOBALS['cacheOptions']);
+                                $cache->remove("page-$oldPageId", 'Toolbox');
+                                $cache->remove("paragraphs-$oldPageId", 'Toolbox');
+                                $cache->remove("sectionLinks-$oldPageId", 'Toolbox');
+                            }
+                        } catch (PDOException $e) {
+                            Toolkit_Common::handleError($e);
+                        }
+
+                        $gateway->update($form->getSubmitValues(), $paragraphId);
+                                       } else {
+                                               $paragraphId = $gateway->insert($form->getSubmitValues());
+                                       }
+                                       $pageid = $form->getSubmitValue('page');
+                    try {
+                        $deleteFile = $form->getSubmitValue('deleteFile');
+                        if (is_array($deleteFile) && !empty($deleteFile)) {
+                            $fs = new Toolkit_FileServer_FileAdapter();
+                            foreach ($deleteFile as $delFile) {
+                                $fs->delete($delFile);
+                            }
+                        }
+                        $image = $form->getSubmitValue('image');
+                        if ($form->getSubmitValue('remove_image') == '1'
+                            && $image
+                        ) {
+                            $is = new Toolkit_FileServer_ImageAdapter();
+                            $is->delete($image);
+                        }
+                    } catch(Toolkit_FileServer_Exception $fileError) {
+                        Toolkit_Common::handleError($fileError);
+                    }
+
+                                       $cache = new Cache_Lite($GLOBALS['cacheOptions']);
+                                       $cache->remove("page-$pageid", 'Toolbox');
+                                       $cache->remove("paragraphs-$pageid", 'Toolbox');
+                                       $cache->remove("sectionLinks-$pageid", 'Toolbox');
+
+                    header(
+                        'Location: '
+                        . MEDIA_BASE_URL
+                        . "admin/toolbox.php?rt=Paragraphs"
+                        . "&ac=edit&id={$paragraphId}&pageid={$pageid}&g=1"
+                    );
+                                       exit();
+                               } else {
+                                       $return  = $form->getErrorMessage();
+                                       $return .= $form->toHtml($this->registry->dbh);
+                               }
+                       } elseif ($form->getSubmitValue('deleteParagraph')) {
+                try {
+                    $files = $form->getSubmitValue('uploaded_files');
+                    $deleteFile = $files['filename'];
+                    if (is_array($deleteFile) && !empty($deleteFile)) {
+                        $fs = new Toolkit_FileServer_FileAdapter();
+                        foreach ($deleteFile as $delFile) {
+                            $fs->delete($delFile);
+                        }
+                    }
+                    $image = $form->getSubmitValue('image');
+                    if ($image) {
+                        $is = new Toolkit_FileServer_ImageAdapter();
+                        $is->delete($image);
+                    }
+                } catch(Toolkit_FileServer_Exception $fileError) {
+                        Toolkit_Common::handleError($fileError);
+                }
+                               //      delete paragraph
+                               $gateway = new Toolkit_Toolbox_ParagraphGatewayPublish($this->registry->dbh);
+                               $gateway->delete($form->getSubmitValue('id'));
+
+                               $pageid = $form->getSubmitValue('page');
+
+                               $cache = new Cache_Lite($GLOBALS['cacheOptions']);
+                               $cache->remove("page-$pageid", 'Toolbox');
+                               $cache->remove("paragraphs-$pageid", 'Toolbox');
+                               $cache->remove("sectionLinks-$pageid", 'Toolbox');
+
+                               $paragraphTitle = $form->getSubmitValue('title');
+                               $return = "[<b>$paragraphTitle</b>] successfully deleted.";
+                try {
+                    $dbh = Toolkit_Database::getInstance();
+                    $sql = "
+                    UPDATE paragraphs
+                       SET pos = :pos
+                     WHERE id = :id";
+                    $updateParagraphPos = $dbh->prepare($sql);
+                    $sql = "
+                    SELECT id,page,pos
+                      FROM paragraphs
+                     WHERE page = :page
+                     ORDER BY page,pos";
+                    $res = $dbh->prepare($sql);
+                    $res->bindParam(':page', $pageid, PDO::PARAM_INT);
+                    $res->execute();
+                    while ($row = $res->fetch(PDO::FETCH_ASSOC)) {
+                        $paragraphData[$row['page']][] = $row['id'];
+                    }
+                    if (is_array($paragraphData)) {
+                        $curPage = 0;
+                        $pos = 1;
+                        foreach ($paragraphData as $page => $paragraphs) {
+                            if ($curPage != $page) {
+                                $curPage = $page;
+                                $pos = 1;
+                            }
+                            foreach ($paragraphs as $paragraphId) {
+                                $updateParagraphPos->bindParam(":pos", $pos, PDO::PARAM_INT);
+                                $updateParagraphPos->bindParam(":id", $paragraphId, PDO::PARAM_INT);
+                                $updateParagraphPos->execute();
+                                ++$pos;
+                            }
+                        }
+                    }
+                } catch(PDOException $e) {
+                    die($e->getMessage());
+                }
+                       }
+               } else {
+                       $return = $form->toHtml($this->registry->dbh);
+               }
+
+        return $return;
+       }
+
+       //      }}}
+}
+?>
diff --git a/Toolkit/Toolbox/ParagraphsDraftController.php b/Toolkit/Toolbox/ParagraphsDraftController.php
new file mode 100644 (file)
index 0000000..5553293
--- /dev/null
@@ -0,0 +1,154 @@
+<?php
+
+class Toolkit_Toolbox_ParagraphsDraftController
+       extends Toolkit_BaseControllerAbstract implements Toolkit_IController
+{
+       //      {{{     _getForm()
+
+    private function _getForm()
+    {
+        //  Need three separate objects otherwise, new parses overwrite existing
+        //  root variable data.
+        $tlbConf = new Config;
+        $memConf = new Config;
+        $cpnConf = new Config;
+
+        $memRoot =& $memConf->parseConfig(
+            BASE . 'Toolkit/Members/config.ini',
+            'IniFile'
+        );
+        $cpnRoot =& $cpnConf->parseConfig(
+            BASE . 'Toolkit/Coupons/config.ini',
+            'IniFile'
+        );
+        $tbxRoot =& $tlbConf->parseConfig(
+            BASE . 'Toolkit/Toolbox/config.ini',
+            'IniFile'
+        );
+
+        $form = new Toolkit_Toolbox_Forms_EditParagraph(
+            'edit_paragraph',
+            'post',
+            MEDIA_BASE_URL . 'admin/toolbox.php?rt=ParagraphsDraft&ac=process'
+        );
+               $form->configureForm(
+                       $this->registry->dbh,
+                       new Toolkit_Toolbox_ParagraphGatewayDraftFactory($this->registry->dbh),
+                       new Toolkit_FileServer_ImageAdapter(),
+                       new Toolkit_FileServer_FileAdapter(),
+                       new Toolkit_Toolbox_FileExtension
+               );
+
+        return $form;
+    }
+
+       //      }}}
+
+       //      {{{     editAction()
+
+       public function editAction()
+       {
+           if (ctype_digit($_GET['pageid'])) {
+                       $breadCrumbs = new Toolkit_Toolbox_ParagraphDraftBreadCrumbs(
+                               $this->registry->dbh,
+                               $_GET['pageid']
+                       );
+                       $html = (string) $breadCrumbs;
+               }
+
+           $form = $this->_getForm();
+               $html .= $form->toHtml($this->registry->dbh);
+
+               return $html;
+       }
+
+       //      }}}
+
+       //      {{{     indexAction()
+
+       public function indexAction()
+       {
+           if (ctype_digit($_GET['pageid'])) {
+                       $breadCrumbs = new Toolkit_Toolbox_ParagraphDraftBreadCrumbs(
+                               $this->registry->dbh,
+                               $_GET['pageid']
+                       );
+                       $html = (string) $breadCrumbs;
+               }
+
+               try {
+                       if (!ctype_digit($_GET['pageid'])) {
+                               throw new RuntimeException(
+                                       "Category id `{$_GET['pageid']}` is not an integer"
+                               );
+                       }
+
+                       $toolboxConfig = new Config;
+                       $toolboxConfigRoot =& $toolboxConfig->parseConfig(
+                               BASE . 'Toolkit/Toolbox/config.ini',
+                               'IniFile'
+                       );
+                       $tree = new Toolkit_Toolbox_ParagraphsDraftTree($toolboxConfigRoot);
+                       $html .= $tree->toHtml(
+                               new Toolkit_Toolbox_ParagraphGatewayDraft($this->registry->dbh)
+                       );
+
+                       return $html;
+               } catch (RuntimeException $e) {
+                       Toolkit_Logger::logException('Runtime Error', $e);
+                       throw new Toolkit_Toolbox_Exception(
+                               'Category id must be an integer'
+                       );
+               }
+       }
+
+       //      }}}
+
+       //      {{{     processAction()
+
+       public function processAction()
+       {
+        $form = $this->_getForm();
+
+               if ($form->isSubmitted()) {
+                       if ($form->getSubmitValue('cancel')) {
+                               //      do nothing
+                               $pageid = $form->getSubmitValue('page');
+                               header('Location: ' . MEDIA_BASE_URL . "admin/toolbox.php?rt=ParagraphsDraft&pageid=$pageid");
+                               exit();
+                       } elseif ($form->getSubmitValue('previewParagraph')) {
+                       } elseif ($form->getSubmitValue('saveParagraph')) {
+                               //      save paragraph
+                               $gateway = new Toolkit_Toolbox_ParagraphGatewayDraft($this->registry->dbh);
+                               if ($form->validate()) {
+                                       $paragraphId = $form->getSubmitValue('id');
+                                       if ($paragraphId) {
+                                               $gateway->update($form->getSubmitValues(), $paragraphId);
+                                       } else {
+                                               $gateway->insert($form->getSubmitValues());
+                                       }
+                                       $pageid = $form->getSubmitValue('page');
+                                       header('Location: ' . MEDIA_BASE_URL . "admin/toolbox.php?rt=ParagraphsDraft&pageid=$pageid");
+                                       exit();
+                               } else {
+                                       $return  = $form->getErrorMessage();
+                                       $return .= $form->toHtml();
+                               }
+                       } elseif ($form->getSubmitValue('deleteParagraph')) {
+                               //      delete paragraph
+                               $gateway = new Toolkit_Toolbox_ParagraphGatewayDraft($this->registry->dbh);
+                               $gateway->delete($form->getSubmitValue('id'));
+
+                               $paragraphTitle = $form->getSubmitValue('title');
+                               $return = "[<b>$paragraphTitle</b>] successfully deleted.";
+                       }
+               } else {
+                       $return = $form->toHtml();
+               }
+
+        return $return;
+       }
+
+       //      }}}
+}
+?>
diff --git a/Toolkit/Toolbox/ParagraphsDraftTree.php b/Toolkit/Toolbox/ParagraphsDraftTree.php
new file mode 100644 (file)
index 0000000..7ffc526
--- /dev/null
@@ -0,0 +1,86 @@
+<?php
+class Toolkit_Toolbox_ParagraphsDraftTree extends Toolkit_Toolbox_TreeAbstract
+{
+       //      {{{     getActiveBall()
+
+       protected function getActiveBall(array $branch)
+       {
+               $activeFormat  = '<a href="%s" title="%s">';
+               $activeFormat .=        $this->getActiveBallImage($branch);
+               $activeFormat .= '</a>';
+
+               return sprintf(
+                       $activeFormat,
+                       MEDIA_BASE_URL . "admin/toolbox.php?rt=ParagraphsDraft&id={$_GET['id']}",
+                       $branch['active_alt']
+               );
+       }
+
+       //      }}}
+
+       //      {{{     createTree()
+
+       protected function createTree(array $tree, $leaf, $level = 0)
+       {
+               if (empty($leaf)) {
+                       return '<h2>No paragraph drafts created for this page yet</h2>'; // page has no paragraphs
+               }
+
+               $html = $this->rootNodeStart;
+
+               while (list($parent, $branch) = each($leaf)) {
+                       $html .= sprintf($this->leafStart, $branch['id'], null);
+
+                       $html .= "<strong>{$branch['title']}</strong>";
+                       $html .= '<div class="right-element">';
+                       $html .= '<a href="'.MEDIA_BASE_URL.'admin/toolbox.php?rt=ParagraphsDraft&ac=edit&id='.$branch['id'].'&pageid='.$_GET['pageid'].'">[Edit]</a> ';
+                       $html .= $this->getActiveBall($branch);
+                       $html .= '</div>';
+                       $html .= $this->leafEnd;
+               }
+
+               $html .= $this->treeEnd;
+               return $html;
+       }
+
+       //      }}}
+
+       //      {{{     fetchContent()
+
+       protected function fetchContent(Toolkit_Toolbox_GatewayAbstract $gateway)
+       {
+               $paragraphs = $gateway->findAll($_GET['pageid']);
+               if (is_array($paragraphs)) {
+                       $threads = array();
+                       foreach ($paragraphs as $paragraph) {
+                               $threads[] = $paragraph;
+                       }
+
+                       $children = array();
+                       while (list($key, $value) = each ($threads)) {
+                               $children[0][$value['id']] = $value;
+                       }
+
+                       $this->tree = $children;
+               } else {
+                       $this->tree = array();
+               }
+       }
+
+       //      }}}
+       //      {{{     toHtml()
+
+       public function toHtml(Toolkit_Toolbox_GatewayAbstract $gateway)
+       {
+               $GLOBALS['bottomScripts'][] = MEDIA_BASE_URL . 'Toolkit/Toolbox/libjs/paragraph-tree.js';
+               $GLOBALS['bottomScripts'][] = MEDIA_APP_BASE_URL . 'libjs/plugins/jsTree/0.9.9a2/jquery.tree.js';
+
+               $this->fetchContent($gateway);
+               $html = parent::toHtml();
+
+               return "<div id=\"tree\">$html</div>";
+       }
+
+       //      }}}
+}
+?>
diff --git a/Toolkit/Toolbox/ParagraphsTree.php b/Toolkit/Toolbox/ParagraphsTree.php
new file mode 100644 (file)
index 0000000..1f836c5
--- /dev/null
@@ -0,0 +1,120 @@
+<?php
+class Toolkit_Toolbox_ParagraphsTree extends Toolkit_Toolbox_TreeAbstract
+{
+       protected $leafStart = "\n\t<li id=\"cat_%s\" %s>\n";
+       //      {{{     getActiveBall()
+
+       protected function getActiveBall(array $branch)
+       {
+               $activeFormat  = '<a class="active-ball" rel="%s" href="%s" title="%s">';
+               $activeFormat .=        $this->getActiveBallImage($branch);
+               $activeFormat .= '</a>';
+
+               return sprintf(
+                       $activeFormat,
+                       $branch['id'],
+                       MEDIA_BASE_URL . "admin/toolbox.php?rt=Paragraphs&amp;id={$branch['id']}",
+                       $branch['active_alt']
+               );
+       }
+
+       //      }}}
+       //      {{{     _getMoveArrows()
+
+       private function _getMoveArrows()
+       {
+               $format = '<img src="%s" alt="Move %s Arrow" class="move%s">';
+
+               $up = sprintf(
+                       $format,
+                       MEDIA_APP_BASE_URL . "assets/icons/arrow_up.png",
+                       'Up',
+                       'Up'
+               );
+
+               $down = sprintf(
+                       $format,
+                       MEDIA_APP_BASE_URL . "assets/icons/arrow_down.png",
+                       'Down',
+                       'Down'
+               );
+
+               return $up . $down;
+       }
+
+       //      }}}
+
+    private function _getPositionSelect($branch)/*{{{*/
+    {
+        return '<select class="pos-select" name="pos'.$branch['id'].'" rel="'.$branch['pos'].'"></select>';
+    }/*}}}*/
+       //      {{{     createTree()
+
+       protected function createTree(array $tree, $leaf, $level = 0)
+       {
+               if (empty($leaf)) {
+                       return '<h2>No paragraphs created for this page yet</h2>'; // page has no paragraphs
+               }
+
+               $html = $this->rootNodeStart;
+
+               while (list($parent, $branch) = each($leaf)) {
+                       $html .= sprintf($this->leafStart, $branch['id'], null);
+
+                       $html .= '<div class="right-element paragraph-line">';
+                       $html .= '<a class="editPage" href="'.MEDIA_BASE_URL.'admin/toolbox.php?rt=Paragraphs&amp;ac=edit&amp;id='.$branch['id'].'&amp;pageid='.$_GET['pageid'].'">[Edit]</a> ';
+                       $html .= $this->getActiveBall($branch);
+                       if (count($leaf) > 1) {
+                           $html .= $this->_getPositionSelect($branch);
+                       }
+                       $html .= "<strong>{$branch['title']}</strong>";
+                       $html .= '</div>';
+
+                       $html .= $this->leafEnd;
+               }
+
+               $html .= $this->treeEnd;
+               return $html;
+       }
+
+       //      }}}
+
+       //      {{{     fetchContent()
+
+       protected function fetchContent(Toolkit_Toolbox_GatewayAbstract $gateway)
+       {
+               $paragraphs = $gateway->findAll($_GET['pageid']);
+               if (is_array($paragraphs)) {
+                       $threads = array();
+                       foreach ($paragraphs as $paragraph) {
+                               $threads[] = $paragraph;
+                       }
+
+                       $children = array();
+                       while (list($key, $value) = each ($threads)) {
+                               $children[0][$value['id']] = $value;
+                       }
+
+                       $this->tree = $children;
+               } else {
+                       $this->tree = array();
+               }
+       }
+
+       //      }}}
+       //      {{{     toHtml()
+
+       public function toHtml(Toolkit_Toolbox_GatewayAbstract $gateway)
+       {
+               $GLOBALS['bottomScripts'][] = MEDIA_BASE_URL . 'Toolkit/Toolbox/libjs/paragraph-tree.js';
+               $GLOBALS['bottomScripts'][] = MEDIA_APP_BASE_URL . 'libjs/plugins/jsTree/0.9.9a2/jquery.tree.js';
+
+               $this->fetchContent($gateway);
+               $html = parent::toHtml();
+
+               return "<div id=\"tree\">$html</div>";
+       }
+
+       //      }}}
+}
+?>
diff --git a/Toolkit/Toolbox/SearchController.php b/Toolkit/Toolbox/SearchController.php
new file mode 100644 (file)
index 0000000..56fbb25
--- /dev/null
@@ -0,0 +1,50 @@
+<?php
+
+class Toolkit_Toolbox_SearchController extends Toolkit_BaseControllerAbstract
+       implements Toolkit_IController
+{
+       //      {{{     index()
+
+       public function indexAction()
+       {
+               $searchForm = new Toolkit_Toolbox_PageSearchForm(
+                       'search_form',
+                       'get',
+                       MEDIA_BASE_URL . 'admin/toolbox.php?rt=search',
+                       null,
+                       null,
+                       true
+               );
+               $searchForm->configureForm();
+
+               $html = $searchForm->toHtml();
+
+        $toolbox = new GLM_TEMPLATE( NULL );
+               $sql = "
+                       SELECT id, parent, pos, active,
+                                  CASE WHEN keyword IS NOT NULL AND keyword <> ''
+                                               THEN category || ' {' || keyword || '}'
+                                               ELSE category
+                                       END AS category,
+                                  CASE WHEN active THEN 'Display'
+                                               ELSE 'Don\'t Display'
+                                       END AS active_alt
+                         FROM bus_category
+             WHERE lower(category) LIKE :search";
+
+               $stmt = $this->registry->dbh->prepare($sql);
+               $stmt->bindParam(':search', strtolower("%{$_GET['q']}%"), PDO::PARAM_STR);
+               $stmt->execute();
+
+               $threads = $stmt->fetchAll(PDO::FETCH_ASSOC);
+
+               $tree = new Toolkit_Toolbox_SearchTree(new GLM_TEMPLATE(null));
+               $sortedLeafs = $tree->sortChildren($threads, true);
+               $html .= $tree->toHtml($sortedLeafs, $sortedLeafs[0]);
+
+               return $html;
+       }
+
+       //      }}}
+}
+?>
diff --git a/Toolkit/Toolbox/SearchTree.php b/Toolkit/Toolbox/SearchTree.php
new file mode 100644 (file)
index 0000000..b5a4b0a
--- /dev/null
@@ -0,0 +1,35 @@
+<?php
+class Toolkit_Toolbox_SearchTree extends Toolkit_Toolbox_PagesTree
+{
+       //      {{{     getActiveBall()
+
+       protected function getActiveBall(array $branch)
+       {
+               $activeFormat = '<a href="%s" title="%s"><img src="%s" alt="%s"></a>';
+               $img = $branch['active'] ? 'grnball.gif' : 'redball.gif';
+               $q = urlencode(stripslashes($_GET['q']));
+               return sprintf(
+                       $activeFormat,
+                       MEDIA_BASE_URL . "admin/toolbox.php?id={$branch['id']}&amp;_qf__search_form=&amp;rt=Search&amp;q=$q",
+                       $branch['active_alt'],
+                       MEDIA_BASE_URL . "Toolkit/Toolbox/assets/$img",
+                       $branch['active_alt']
+               );
+       }
+
+       //      }}}
+       //      {{{     sortChildren()
+
+       public function sortChildren(array $threads)
+       {
+               $children = array();
+               while (list($key, $value) = each ($threads)) {
+                       $children[0][$value['id']] = $value;
+               }
+
+               return $children;
+       }
+
+       //      }}}
+}
+?>
diff --git a/Toolkit/Toolbox/TreeAbstract.php b/Toolkit/Toolbox/TreeAbstract.php
new file mode 100644 (file)
index 0000000..58d2abc
--- /dev/null
@@ -0,0 +1,230 @@
+<?php
+/**
+ * TreeAbstract.php
+ *
+ * PHP version 5
+ *
+ * @category Toolkit
+ * @package  Toolbox
+ * @author   Jamie Kahgee <steve@gaslightmedia.com>
+ * @license  http://www.gaslightmedia.com Gaslightmedia
+ * @link     <>
+ */
+
+/**
+ * Toolkit_Toolbox_TreeAbstract
+ *
+ * Description for Toolkit_Toolbox_TreeAbstract
+ *
+ * @category Toolkit
+ * @package  Toolbox
+ * @author   Jamie Kahgee <steve@gaslightmedia.com>
+ * @license  http://www.gaslightmedia.com Gaslightmedia
+ * @link     <>
+ */
+abstract class Toolkit_Toolbox_TreeAbstract
+{
+       //      {{{     properties
+
+    
+    /**
+     * Description for $rootNodeStart
+     * @var string
+     * @access protected
+     */
+       protected $rootNodeStart = "<ul>\n";
+    
+    /**
+     * Description for $subTreeStart
+     * @var string
+     * @access protected
+     */
+       protected $subTreeStart = "\n<ul>\n";
+    
+    /**
+     * Description for $treeEnd
+     * @var string
+     * @access protected
+     */
+       protected $treeEnd = "\n</ul>\n";
+    
+    /**
+     * Description for $leafStart
+     * @var string
+     * @access protected
+     */
+       protected $leafStart = "\n\t<li>\n";
+    
+    /**
+     * Description for $leafEnd
+     * @var string
+     * @access protected
+     */
+       protected $leafEnd = "\n\t</li>\n";
+    
+    /**
+     * Description for $config
+     * @var Config_Container
+     * @access protected
+     */
+       protected $config;
+    
+    /**
+     * Description for $tree
+     * @var array
+     * @access protected
+     */
+       protected $tree;
+
+       //      }}}
+       //      {{{     __construct()
+
+    /**
+     * Class constructor
+     * 
+     * @param Config_Container $config Config container
+     * 
+     * @return void
+     * @access public
+     */
+       public function __construct(Config_Container $config)
+       {
+               $this->config = $config;
+       }
+
+       //      }}}
+
+       //      {{{     getActiveBallImage()
+
+    /**
+     * Description for getActiveBallImage()
+     * 
+     * @param array $branch Branch array
+     * 
+     * @return string
+     * @access protected 
+     */
+       protected function getActiveBallImage(array $branch)
+       {
+               $imgFormat = '<img %s src="%s" alt="%s" border="0">';
+
+               $show = sprintf(
+                       $imgFormat,
+                       ($branch['active']) ? '' : 'style="display: none;"',
+                       MEDIA_BASE_URL . "Toolkit/Toolbox/assets/grnball.gif",
+                       'Active Ball'
+               );
+
+               $hide = sprintf(
+                       $imgFormat,
+                       ($branch['active']) ? 'style="display: none;"' : '',
+                       MEDIA_BASE_URL . "Toolkit/Toolbox/assets/redball.gif",
+                       "In-active Ball"
+               );
+
+               return $show . $hide;
+       }
+
+    // }}}
+       //      {{{     getActiveMobileBallImage()
+
+    /**
+     * Description for getActiveMobileBallImage()
+     * 
+     * @param array $branch Branch array
+     * 
+     * @return string
+     * @access protected
+     */
+       protected function getActiveMobileBallImage(array $branch)
+       {
+               $imgFormat = '<img %s src="%s" alt="%s" border="0">';
+
+               $show = sprintf(
+                       $imgFormat,
+                       ($branch['mobile_active']) ? '' : 'style="display: none;"',
+                       MEDIA_BASE_URL . "Toolkit/Toolbox/assets/mobilemgreen.jpg",
+                       'Active Ball'
+               );
+
+               $hide = sprintf(
+                       $imgFormat,
+                       ($branch['mobile_active']) ? 'style="display: none;"' : '',
+                       MEDIA_BASE_URL . "Toolkit/Toolbox/assets/mobilemred.jpg",
+                       "In-active Ball"
+               );
+
+               return $show . $hide;
+       }
+
+    // }}}
+       //      {{{     getActiveBall()
+
+    /**
+     * Description for getActiveBall()
+     * 
+     * @param array $branch Branch array
+     * 
+     * @access protected 
+     * @return void
+     */
+       abstract protected function getActiveBall(array $branch);
+
+       //      }}}
+
+       //      {{{     createTree()
+
+    /**
+     * Description for createTree
+     * 
+     * @param array   $tree  Description for $tree ...
+     * @param unknown $leaf  Description for $leaf ...
+     * @param int     $level Description for $level ...
+     * 
+     * @return void
+     * @access protected
+     */
+       abstract protected function createTree(array $tree, $leaf, $level = 0);
+
+       //      }}}
+       //      {{{     fetchContent()
+
+    /**
+     * Description for fetchContent
+     * 
+     * @param Toolkit_Toolbox_GatewayAbstract $gateway Toolbox Gateway
+     * 
+     * @return void
+     * @access protected 
+     */
+       abstract protected function fetchContent(
+               Toolkit_Toolbox_GatewayAbstract $gateway
+       );
+
+       //      }}}
+
+       //      {{{     toHtml()
+
+    /**
+     * Description of toHtml()
+     * 
+     * @return string
+     * @access public 
+     */
+       public function toHtml()
+       {
+               $GLOBALS['styleSheets'][] = MEDIA_BASE_URL . 'admin/main.css';
+               $GLOBALS['styleSheets'][] = MEDIA_BASE_URL . 'Toolkit/Toolbox/styles.css';
+               $GLOBALS['topScripts'][]
+                       = MEDIA_APP_BASE_URL . 'libjs/jquery/jquery-1.4.2.min.js';
+
+               if (is_array($this->tree)) {
+                       $html = $this->createTree($this->tree, reset($this->tree));
+               }
+
+               return $html;
+       }
+
+       //      }}}
+}
+?>
diff --git a/Toolkit/Toolbox/assets/.keepme b/Toolkit/Toolbox/assets/.keepme
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/Toolkit/Toolbox/assets/arrow.png b/Toolkit/Toolbox/assets/arrow.png
new file mode 100755 (executable)
index 0000000..ee11ab4
Binary files /dev/null and b/Toolkit/Toolbox/assets/arrow.png differ
diff --git a/Toolkit/Toolbox/assets/arrowClosed.png b/Toolkit/Toolbox/assets/arrowClosed.png
new file mode 100755 (executable)
index 0000000..3b34262
Binary files /dev/null and b/Toolkit/Toolbox/assets/arrowClosed.png differ
diff --git a/Toolkit/Toolbox/assets/arrowOpen.png b/Toolkit/Toolbox/assets/arrowOpen.png
new file mode 100755 (executable)
index 0000000..6967dea
Binary files /dev/null and b/Toolkit/Toolbox/assets/arrowOpen.png differ
diff --git a/Toolkit/Toolbox/assets/collapse.png b/Toolkit/Toolbox/assets/collapse.png
new file mode 100755 (executable)
index 0000000..d10e051
Binary files /dev/null and b/Toolkit/Toolbox/assets/collapse.png differ
diff --git a/Toolkit/Toolbox/assets/expand.png b/Toolkit/Toolbox/assets/expand.png
new file mode 100755 (executable)
index 0000000..38dcfc0
Binary files /dev/null and b/Toolkit/Toolbox/assets/expand.png differ
diff --git a/Toolkit/Toolbox/assets/grnball.gif b/Toolkit/Toolbox/assets/grnball.gif
new file mode 100755 (executable)
index 0000000..5f7740b
Binary files /dev/null and b/Toolkit/Toolbox/assets/grnball.gif differ
diff --git a/Toolkit/Toolbox/assets/mobilemgreen.jpg b/Toolkit/Toolbox/assets/mobilemgreen.jpg
new file mode 100644 (file)
index 0000000..a5fc6ce
Binary files /dev/null and b/Toolkit/Toolbox/assets/mobilemgreen.jpg differ
diff --git a/Toolkit/Toolbox/assets/mobilemred.jpg b/Toolkit/Toolbox/assets/mobilemred.jpg
new file mode 100644 (file)
index 0000000..217826d
Binary files /dev/null and b/Toolkit/Toolbox/assets/mobilemred.jpg differ
diff --git a/Toolkit/Toolbox/assets/redball.gif b/Toolkit/Toolbox/assets/redball.gif
new file mode 100755 (executable)
index 0000000..7005133
Binary files /dev/null and b/Toolkit/Toolbox/assets/redball.gif differ
diff --git a/Toolkit/Toolbox/assets/template1.gif b/Toolkit/Toolbox/assets/template1.gif
new file mode 100755 (executable)
index 0000000..327d943
Binary files /dev/null and b/Toolkit/Toolbox/assets/template1.gif differ
diff --git a/Toolkit/Toolbox/assets/template2.gif b/Toolkit/Toolbox/assets/template2.gif
new file mode 100755 (executable)
index 0000000..edf3d8b
Binary files /dev/null and b/Toolkit/Toolbox/assets/template2.gif differ
diff --git a/Toolkit/Toolbox/assets/template3.gif b/Toolkit/Toolbox/assets/template3.gif
new file mode 100755 (executable)
index 0000000..16e2107
Binary files /dev/null and b/Toolkit/Toolbox/assets/template3.gif differ
diff --git a/Toolkit/Toolbox/assets/template4.gif b/Toolkit/Toolbox/assets/template4.gif
new file mode 100755 (executable)
index 0000000..32174dd
Binary files /dev/null and b/Toolkit/Toolbox/assets/template4.gif differ
diff --git a/Toolkit/Toolbox/assets/template5.gif b/Toolkit/Toolbox/assets/template5.gif
new file mode 100755 (executable)
index 0000000..6c36b50
Binary files /dev/null and b/Toolkit/Toolbox/assets/template5.gif differ
diff --git a/Toolkit/Toolbox/assets/template6.gif b/Toolkit/Toolbox/assets/template6.gif
new file mode 100644 (file)
index 0000000..28d140e
Binary files /dev/null and b/Toolkit/Toolbox/assets/template6.gif differ
diff --git a/Toolkit/Toolbox/config.ini b/Toolkit/Toolbox/config.ini
new file mode 100644 (file)
index 0000000..3d81908
--- /dev/null
@@ -0,0 +1,3 @@
+[conf]
+applicationName = "Toolbox"
+lockMainNavPages = Off
diff --git a/Toolkit/Toolbox/fixPageNoContent.php b/Toolkit/Toolbox/fixPageNoContent.php
new file mode 100644 (file)
index 0000000..8b59bad
--- /dev/null
@@ -0,0 +1,48 @@
+<?php
+require_once '../../setup.phtml';
+$dbh = Toolkit_Database::getInstance();
+$dbh->beginTransaction();
+try {
+    $checkSql = 
+        "SELECT id
+           FROM paragraphs
+          WHERE page = :page
+            AND pos = 1";
+    $checkStmt = $dbh->prepare($checkSql);
+    $addSql = "
+        INSERT INTO paragraphs
+        (active,page,pos)
+        VALUES
+        (true,:page,1)";
+    $addStmt = $dbh->prepare($addSql);
+    $sql = "
+      SELECT id
+        FROM pages
+    ORDER BY parent,pos";
+    $stmt = $dbh->query($sql);
+    while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
+        $checkStmt->bindParam(
+            ':page',
+            $row['id'],
+            PDO::PARAM_INT
+        );
+        $checkStmt->execute();
+        if ($checkStmt->rowCount() == 0) {
+            // nothing set yet need to add one
+            $addStmt->bindParam(
+                ':page',
+                $row['id'],
+                PDO::PARAM_INT
+            );
+            $addStmt->execute();
+            echo '<p style="color:white;background-color:red;">
+                Adding paragraph! PageId:'.$row['id'].'</p>';
+        } else {
+            echo '<p style="color:white;background-color:green;">
+                Good Page PageId:'.$row['id'].'</p>';
+        }
+    }
+    $dbh->commit();
+} catch(PDOException $e) {
+    die($e->getMessage());
+}
diff --git a/Toolkit/Toolbox/fixPagePos.php b/Toolkit/Toolbox/fixPagePos.php
new file mode 100644 (file)
index 0000000..c414a6d
--- /dev/null
@@ -0,0 +1,70 @@
+<?php
+require_once '../../setup.phtml';
+$dbh = Toolkit_Database::getInstance();
+$dbh->beginTransaction();
+$pageData = array();
+try {
+    $sql = "
+    UPDATE pages 
+       SET pos = :pos 
+     WHERE id = :id";
+    $updatePagePos = $dbh->prepare($sql);
+    $sql = "
+    SELECT id,parent,pos
+      FROM pages
+     ORDER BY parent,pos";
+    $res = $dbh->query($sql);
+    while ($row = $res->fetch(PDO::FETCH_ASSOC)) {
+        $pageData[$row['parent']][] = $row['id'];
+    }
+    echo '<p>fixing page positions</p>';
+    if (is_array($pageData)) {
+        $curParent = 0;
+        $pos = 1;
+        foreach ($pageData as $parent => $pages) {
+            if ($curParent != $parent) {
+                $curParent = $parent;
+                $pos = 1;
+            }
+            foreach ($pages as $pageId) {
+                $updatePagePos->bindParam(":pos", $pos, PDO::PARAM_INT);
+                $updatePagePos->bindParam(":id", $pageId, PDO::PARAM_INT);
+                $updatePagePos->execute();
+                ++$pos;
+            }
+        }
+    }
+    $sql = "
+    UPDATE paragraphs 
+       SET pos = :pos 
+     WHERE id = :id";
+    $updateParagraphPos = $dbh->prepare($sql);
+    $sql = "
+    SELECT id,page,pos
+      FROM paragraphs
+     ORDER BY page,pos";
+    $res = $dbh->query($sql);
+    while ($row = $res->fetch(PDO::FETCH_ASSOC)) {
+        $paragraphData[$row['page']][] = $row['id'];
+    }
+    echo '<p>fixing paragraphs positions</p>';
+    if (is_array($paragraphData)) {
+        $curPage = 0;
+        $pos = 1;
+        foreach ($paragraphData as $page => $paragraphs) {
+            if ($curPage != $page) {
+                $curPage = $page;
+                $pos = 1;
+            }
+            foreach ($paragraphs as $paragraphId) {
+                $updateParagraphPos->bindParam(":pos", $pos, PDO::PARAM_INT);
+                $updateParagraphPos->bindParam(":id", $paragraphId, PDO::PARAM_INT);
+                $updateParagraphPos->execute();
+                ++$pos;
+            }
+        }
+    }
+} catch(PDOException $e) {
+    die($e->getMessage());
+}
+$dbh->commit();
diff --git a/Toolkit/Toolbox/libjs/edit-page.js b/Toolkit/Toolbox/libjs/edit-page.js
new file mode 100644 (file)
index 0000000..f4f4045
--- /dev/null
@@ -0,0 +1,239 @@
+var EditPage =
+{
+       openSection: '#pageContent',
+       sectionHeaders: null,
+       sectionAttributes: null,
+
+       init: function()//      {{{
+       {
+               $('input[name=deletePage]:submit').click(EditPage.confirmPageDelete);
+        $('#MemberCategories').change(function() {
+            $('input[name=include_members]').attr('checked', 'checked');
+        });
+        $('#MemberRegions').change(function() {
+            $('input[name=include_members]').attr('checked', 'checked');
+        });
+        $('#CouponCategories').change(function() {
+            $('input[name=include_coupons]').attr('checked', 'checked');
+        });
+               EditPage.pageAttributes();
+               EditPage.memberAttributes();
+               EditPage.couponAttributes();
+               EditPage.headlineAttributes();
+
+               EditPage.sectionHeaders = $('tbody tr.hdr td');
+               EditPage.sectionAttributes = $('span.attributes');
+
+
+               //      If there was an error with validation, then
+               //      find the first error and open that section.
+               var $firstFormValidationError = $('div.req:first');
+               if ($firstFormValidationError.length > 0) {
+                       //      Hide all sections first
+                       $("tbody.section > tr:not(.hdr)").toggle();
+
+                       //      Then open the target section
+                       $firstFormValidationError
+                               .parents("tbody.section")
+                               .children("tr:not(.hdr)")
+                               .toggle();
+                       $firstFormValidationError
+                               .parents("tbody.section")
+                               .children('tr.hdr')
+                               .children()
+                               .toggleClass('open');
+               } else {
+                       //      Initially hide all sections EXCEPT the first section
+                       //      which has an id #pageContent
+                       $("tbody.section > tr:not(.hdr)")
+                               .not("tbody#pageContent > tr")
+                               .toggle();
+               }
+
+               $("tbody.section tr.hdr").click(function() {
+                       var $currId = $(this).parents('tbody').attr('id');
+                       if ($currId == EditPage.openSection) {
+                               return false;
+                       }
+
+                       //      Set this section to the current section open
+                       EditPage.openSection = $currId
+
+                       //      Remove the class open from all the section headers.
+                       //      This will reset the arrow in the header back to closed (>)
+                       EditPage.sectionHeaders.each(function() {
+                               $(this).removeClass('open');
+                       });
+
+                       //      default shows all the section "quick at a glance" attributes
+                       EditPage.sectionAttributes.show();
+                       //      hide current sections "quick at a glance" attributes
+                       $(this).find('.attributes').hide();
+
+                       //      hide all the sections
+                       $('tbody.section > tr:not(.hdr)').hide();
+                       //      unhide the section that was clicked
+                       $(this).siblings().toggle();
+                       //      make this section open, w/ a down arrow (v)
+                       $(this).children().toggleClass('open');
+               });
+
+               $("select[multiple]").asmSelect({
+                       animate: true
+               });
+
+               if ($("#description").is("textarea")) {
+                       //      Only try to replace the textarea if the
+                       //      CKEditor is compatible w/ the browser.
+                       if (CKEDITOR.env.isCompatible) {
+                               CKEDITOR.replace('description',
+                                       {
+                                               toolbar : 'Default',
+//                                             width : 570,
+//                                             height : 400,
+                                               filebrowserImageBrowseUrl : '../Toolkit/CKImages/browser.php?folder=1',
+                                               filebrowserImageUploadUrl : '../Toolkit/CKImages/controller.php?command=Upload',
+                                               filebrowserImageWindowWidth : '760',
+                                               filebrowserImageWindowHeight : '500'
+                                       });
+//    config.scayt_autoStartup = true;
+//    config.scayt_uiTabs = '0,0,0';
+                       }
+               }
+       },//    }}}
+
+       confirmPageDelete: function(event)// {{{
+       {
+               if (!confirm("Are you sure you wish to delete this page and its paragraphs?\n\n" +
+                                        "All sub pages along with existing page drafts will also be deleted!")) {
+                       event.preventDefault();
+               }
+       },// }}}
+       createCategoryShortcutView: function(element, section, container)//     {{{
+       {
+               var target = section.find('select[name="'+element+'[]"]');
+               if (target.length) {
+                       container.append('<span class="'+element+'"></span>');
+                       var $holder = container.find('.'+element),
+                               $numSelected = target.find(':selected').length;
+                       if ($numSelected) {
+                               EditPage.updateText($holder, '<tt>['+$numSelected+']</tt>Categories');
+                       }
+                       target.change(function() {
+                               var $numSelected = target.find(':selected').length;
+                               EditPage.updateText($holder, '<tt>['+$numSelected+']</tt>Categories');
+                       });
+               }
+       },//    }}}
+       createRegionShortcutView: function(element, section, container)//       {{{
+       {
+               var target = section.find('select[name="'+element+'[]"]');
+               if (target.length) {
+                       container.append('<span class="'+element+'"></span>');
+                       var $holder = container.find('.'+element),
+                               $numSelected = target.find(':selected').length;
+                       if ($numSelected) {
+                               EditPage.updateText($holder, '<tt>['+$numSelected+']</tt>Regions');
+                       }
+                       target.change(function() {
+                               var $numSelected = target.find(':selected').length;
+                               EditPage.updateText($holder, '<tt>['+$numSelected+']</tt>Regions');
+                       });
+               }
+       },//    }}}
+       createCheckboxShortcutView: function(element, section, container, text)//       {{{
+       {
+               var target = section.find('input[name='+element+']:checkbox');
+               if (target.length) {
+                       container.append('<span class="'+element+'"></span>');
+                       var $holder = container.find('.'+element),
+                               $checked = target.is(':checked')
+                               ? '<tt>[X]</tt>'
+                               : '<tt>[ ]</tt>';
+                       EditPage.updateText($holder, $checked + text);
+
+                       target.change(function() {
+                               var $checked = target.is(':checked')
+                                       ? '<tt>[X]</tt>'
+                                       : '<tt>[ ]</tt>';
+                               EditPage.updateText($holder, $checked + text);
+                       });
+               }
+       },//    }}}
+       createTextShortcutView: function(element, section, container)// {{{
+       {
+               var target = section.find('input[name='+element+']');
+               if (target.length) {
+                       container.append('<span class="'+element+'"></span>');
+                       var $holder = container.find('.'+element);
+                       if (target.val() != '') {
+                               EditPage.updateText($holder, '{' + target.val() + '}');
+                       }
+                       target.change(function() {
+                               if ($(this).val() != '') {
+                                       EditPage.updateText($holder, '{' + $(this).val() + '}');
+                               } else {
+                                       EditPage.updateText($holder, '');
+                               }
+                       });
+               }
+       },//    }}}
+
+       couponAttributes: function()//  {{{
+       {
+               var $section = $('#coupons');
+               $section.find('tr.hdr td')
+                               .append('<span class="attributes"></span>');
+
+               $attributes = $section.find('tr.hdr .attributes');
+
+               EditPage.createCheckboxShortcutView('include_coupons', $section, $attributes, 'Display Coupons');
+               EditPage.createCategoryShortcutView('coupon_categories', $section, $attributes);
+       },//    }}}
+
+       headlineAttributes: function()//        {{{
+       {
+               var $section = $('#headlines');
+               $section.find('tr.hdr td')
+                               .append('<span class="attributes"></span>');
+
+               $attributes = $section.find('tr.hdr .attributes');
+
+               EditPage.createCheckboxShortcutView('headline', $section, $attributes, 'Headline');
+       },//    }}}
+
+       pageAttributes: function()//    {{{
+       {
+               var $section = $('#attributes');
+               $section.find('tr.hdr td')
+                               .append('<span class="attributes"></span>');
+
+               $attributes = $section.find('tr.hdr .attributes');
+
+               EditPage.createTextShortcutView('keyword', $section, $attributes);
+               EditPage.createTextShortcutView('short_url', $section, $attributes);
+               EditPage.createCheckboxShortcutView('paragraph_links', $section, $attributes, 'Links');
+       },//    }}}
+
+       memberAttributes: function()//  {{{
+       {
+               var $section = $('#members');
+               $section.find('tr.hdr td')
+                               .append('<span class="attributes"></span>');
+
+               $attributes = $section.find('tr.hdr .attributes');
+
+        EditPage.createCheckboxShortcutView('include_members', $section, $attributes, 'Display Members');
+               EditPage.createCategoryShortcutView('member_categories', $section, $attributes);
+               EditPage.createRegionShortcutView('member_regions', $section, $attributes);
+               EditPage.createCheckboxShortcutView('include_member_map', $section, $attributes, 'Map');
+               EditPage.createCheckboxShortcutView('search_form', $section, $attributes, 'Search');
+       },//    }}}
+
+       updateText: function(holder, content)// {{{
+       {
+               holder.html(content);
+       }//     }}}
+};
+
+$(document).ready(EditPage.init);
diff --git a/Toolkit/Toolbox/libjs/edit-paragraph.js b/Toolkit/Toolbox/libjs/edit-paragraph.js
new file mode 100644 (file)
index 0000000..68bfcf8
--- /dev/null
@@ -0,0 +1,149 @@
+var EditParagraph =
+{
+       init: function()// {{{
+       {
+               $('.files').sortable({
+                       placeholder: 'ui-state-highlight'
+               });
+
+               $('input[name="deleteParagraph"]').click(EditParagraph.confirmParagraphDelete);
+
+               //      Bind click events to the remove buttons for uploaded files
+               //      everytime a button is clicked, an entire li element is removed
+               //      so we can get rid of all the info stored in hidden fields
+               //      about the file (bytes, filename, etc...).
+               $('.remove').live('click', function(event) {
+                       event.preventDefault();
+                       $(this).parent('li').remove();
+               });
+
+               $('#filename').after('<span id="upload_notification" style="display: none;">Uploading</span>');
+               $('#filename').after('<span id="success_notification" style="display: none;"><span></span> Successfully uploaded</span>');
+               var $uploadNotification = $('#upload_notification');
+
+               $("tbody.section tr:not(.hdr)").toggle();
+               var $hdr = $('tbody.section tr.hdr');
+
+               $hdr.click(function() {
+                       $(this).siblings()
+                                  .toggle();
+                       $(this).children()
+                                  .toggleClass('open');
+               });
+
+               $("div.req").each(function() {
+                       $(this).parents("tbody.section")
+                                  .children("tr:not(.hdr)")
+                                  .toggle();
+                       $(this).parents("tbody.section")
+                                  .children("tr.hdr")
+                                  .children()
+                                  .toggleClass('open');
+               });
+
+               if ($("#description").is("textarea")) {
+                       //      Only try to replace the textarea if the
+                       //      CKEditor is compatible w/ the browser.
+                       if (CKEDITOR.env.isCompatible) {
+                               CKEDITOR.replace('description',
+                                       {
+                                               toolbar : 'Default',
+//                                             width : 570,
+//                                             height : 400,
+                                               filebrowserImageBrowseUrl : '../Toolkit/CKImages/browser.php?folder=1',
+                                               filebrowserImageUploadUrl : '../Toolkit/CKImages/controller.php?command=Upload',
+                                               filebrowserImageWindowWidth : '760',
+                                               filebrowserImageWindowHeight : '500'
+                                       });
+                       }
+               }
+       },// }}}
+
+       cleanName: function(filename)// {{{
+       {
+               var $alphaNumeric = filename.replace(/[^A-Za-z0-9]/g, '');
+
+               return $alphaNumeric;
+       },// }}}
+       confirmParagraphDelete: function(event)// {{{
+       {
+               if (!confirm("Are you sure you wish to delete this paragraph and its files?")) {
+                       event.preventDefault();
+               }
+       },// }}}
+
+       getExtensionImage: function(ext)// {{{
+       {
+               var img;
+               switch (ext) {
+               case 'mp3' :
+                       img = 'mp3.gif';
+                       break;
+
+               case 'avi' :
+                       img = 'avi.gif';
+                       break;
+
+               case 'html' :
+                       img = 'html.gif';
+                       break;
+
+               case 'mov' :
+                       img = 'mov.gif'
+                       break;
+
+               case 'wmv' :
+                       img = 'wmv.gif';
+                       break;
+
+               case 'ppt' :
+                       img = 'ppt.gif';
+                       break;
+
+               case 'zip' :
+                       img = 'zip.png';
+                       break;
+
+               case 'rar' :
+               case 'tar' :
+                       img = 'rar.gif';
+                       break;
+
+               case 'txt' :
+                       img = 'txt.png';
+                       break;
+
+               case 'xlsx' :
+               case 'xls' :
+                       img = 'xls.gif';
+                       break;
+
+               case 'pdf' :
+                       img = 'pdf.png';
+                       break;
+
+               case 'doc' :
+               case 'docx' :
+                       img = 'doc.gif';
+                       break;
+
+               case 'png' : // image does not exist yet.
+               case 'jpeg' :
+               case 'jpg' :
+                       img = 'jpg.gif';
+                       break;
+
+               case 'gif' :
+                       img = 'gif.gif';
+                       break;
+
+               default :
+                       img = 'download.gif';
+                       break;
+               }
+
+               return img;
+       }// }}}
+};
+
+$(document).ready(EditParagraph.init);
diff --git a/Toolkit/Toolbox/libjs/pagesTree.js b/Toolkit/Toolbox/libjs/pagesTree.js
new file mode 100644 (file)
index 0000000..473420d
--- /dev/null
@@ -0,0 +1,242 @@
+var PagesTree =
+{
+       init: function()
+       {
+               PagesTree.updateMoveArrowVisibility();
+               $('.admin_nav')
+                       .append('<li><a id="expand_all" href="#">Expand All</a></li>')
+            .append('<li><a id="collapse_all" href="#">Collapse All</a></li>')
+            .append('<li><a id="show_pos" href="#">Show Positions</a></li>')
+            .append('<li><a id="hide_pos" href="#">Hide Positions</a></li>')
+            ;
+
+               var $myTree = $('#tree');
+
+               if ($myTree.children('ul').size() > 0) {
+                       $myTree.tree({
+                               callback : {
+                                       onsearch : function (NODES, TREE_OBJ) {
+                                               TREE_OBJ.container.find('strong.search').removeClass('search');
+                                               NODES.addClass('search');
+
+                                               if (NODES.length == 0) {
+                                                       alert('No matches found - Please refine your search.');
+                                               }
+                                       }
+                               },
+                               plugins : {
+                                       cookie : {
+                                               prefix : "jstree_toolbox_",
+                                               types : {
+                                                       selected : false
+                                               }
+                                       }
+                               }
+                       });
+            $('#show_pos').click(function(event) {
+                               event.preventDefault();
+                $('select').show();
+            });
+            $('#hide_pos').click(function(event) {
+                               event.preventDefault();
+                $('select').hide();
+            });
+                       $('#expand_all').click(function(event) {
+                               event.preventDefault();
+                               $.tree.focused().open_all();
+                       });
+                       $('#collapse_all').click(function(event) {
+                               event.preventDefault();
+                               $.tree.focused().close_all();
+                       });
+
+                       $("#search_form").submit(function(event) {
+                               var $search = $("#q").val().toUpperCase();
+                               event.preventDefault();
+                               jQuery.expr[':'].Contains = function(a, i, m) {
+                                       return jQuery(a).text().toUpperCase().indexOf(m[3].toUpperCase())>=0;
+                               };
+                               $.tree.focused().search($search, "contains('"+$search+"'),strong:Contains");
+                       });
+
+                       $('a.pagePreview').click(function(event) {
+                               var newWindow = window.open($(this).attr('href'));
+                               newWindow.focus();
+                               return false;
+                       });
+
+                       $('img.moveDown').click(function() {
+                               var $li = $(this).closest('li'),
+                                       $sibling = $li.next();
+                               if ($sibling.length == 1) {
+                                       var $id = $li.attr('id');
+                                       $li.detach();
+                                       $li.insertAfter($sibling);
+                                       PagesTree.updateMoveArrowVisibility();
+                                       $.ajax({
+                                               url : '../toolbox-page-move/'+$id+'/down/',
+                        cache: false,
+                                               async: false,
+                                               success: function(html) {
+                                                       if (html == 0) {
+                                                               PagesTree.moveBack($li, $sibling);
+                                                       } else if ($sibling.hasClass('last')) {
+                                                               $sibling.removeClass('last');
+                                                               $li.addClass('last');
+                                                       }
+                                               },
+                                               error: function() {
+                                                       PagesTree.moveBack($li, $sibling);
+                                               }
+                                       });
+                               }
+                       });
+
+                       $('img.moveUp').click(function() {
+                               var $li = $(this).closest('li'),
+                                       $sibling = $li.prev();
+                               if ($sibling.length == 1) {
+                                       var $id = $li.attr('id');
+                                       $li.detach();
+                                       $li.insertBefore($sibling);
+                                       PagesTree.updateMoveArrowVisibility();
+                                       $.ajax({
+                                               url : '../toolbox-page-move/'+$id+'/up/',
+                        cache: false,
+                                               async: false,
+                                               success: function(html) {
+                                                       if (html == 0) {
+                                                               PagesTree.moveBack($li, $sibling);
+                                                       } else if ($li.hasClass('last')) {
+                                                               $li.removeClass('last');
+                                                               $sibling.addClass('last');
+                                                       }
+                                               },
+                                               error: function() {
+                                                       PagesTree.moveBack($li, $sibling);
+                                               }
+                                       });
+                               }
+                       });
+
+                       $('.active-ball').click(function(event) {
+                               event.preventDefault();
+                               var id = $(this).attr('rel'),
+                                       _this = $(this);
+                    $.get('../toolbox-change-state/'+id+'/?t=' + event.timeStamp, function(data) {
+                                       if (data) {
+                                               _this.children().toggle();
+                                               var newTitle = _this.attr('title') == 'Display'
+                                                       ? "Don't Display"
+                                                       : 'Display';
+                                               _this.attr('title', newTitle);
+                                       }
+                               });
+                       });
+            $('.mobile-active-ball').click(function(event) {
+                               event.preventDefault();
+                               var id = $(this).attr('rel'),
+                                       _this = $(this);
+                               $.get('../toolbox-change-state-mobile/'+id+'/?t=' + event.timeStamp, function(data) {
+                                       if (data) {
+                                               _this.children().toggle();
+                                               var newTitle = _this.attr('title') == 'Display'
+                                                       ? "Don't Display"
+                                                       : 'Display';
+                                               _this.attr('title', newTitle);
+                                       }
+                               });
+                       });
+               }
+        $('.closed').hover(function(){
+            $(this).css('background', 'lightgrey');
+            //$(this).children('.right-element').show();
+        },function(){
+            $(this).css('background', 'none');
+            //$(this).children('.right-element').hide();
+        });
+        $('.leaf').hover(function(){
+            $(this).css('background', 'lightgrey');
+            //$(this).children('.right-element').show();
+        },function(){
+            $(this).css('background', 'none');
+            //$(this).children('.right-element').hide();
+        });
+        $('li.open').each(function(){
+            var count = $(this).children('ul').children('li').size();
+            PagesTree.loadSelectOptions(
+                $(this).children('ul').children('li').children('div.right-element'), 
+                count
+            );
+        });
+        $('li.closed').each(function(){
+            var count = $(this).children('ul').children('li').size();
+            PagesTree.loadSelectOptions(
+                $(this).children('ul').children('li').children('div.right-element'), 
+                count
+            );
+        });
+        $('select.pos-select').each(function(){
+            $(this).change(function(){
+                var id = $(this).attr('name');
+                var oldpos = $(this).attr('rel');
+                var newpos = $(this).attr('value');
+                window.location.href = '../toolbox-page-move-sel/' + id + '/' + oldpos + '/' + newpos + '/';
+            });
+        });
+        $('ul.ltr').each(function(){
+            var count = $(this).children('li').size();
+            PagesTree.loadSelectOptions(
+                $(this).children('li').children('div.right-element'), 
+                count
+            );
+        });
+       },
+
+    loadSelectOptions: function(ul, count)
+    {
+        ul.each(function(){
+            var selCount = $(this).children('select').attr('rel');
+            if ($(this).children('select').hasClass('parent-level-sel')) {
+                var begin = 2;
+            } else {
+                var begin = 1;
+            }
+            for (i = begin; i <= count; ++i) {
+                var html = '<option value="'+i+'"';
+                if (selCount == i) {
+                    html += ' selected="selected"';
+                }
+                html += '>'+i+'</option>';
+                $(this).children('select').append(html);
+            }
+        });
+    },
+
+       moveBack: function($li, $sibling)
+       {
+               alert('There was an error moving your page');
+               $li.detach();
+               $li.insertBefore($sibling);
+               PagesTree.updateMoveArrowVisibility();
+       },
+
+       updateMoveArrowVisibility: function()
+       {
+               $('img.moveUp, img.moveDown').show().removeClass('tOff');
+
+               var $tree = $('#tree');
+               $tree.find('ul').each(function() {
+                       var $firstUpArrow = $(this).find('img.moveUp:first'),
+                               $lis = $(this).children().last();
+                       $firstUpArrow.addClass('tOff');
+
+                       $lis.each(function() {
+                               var $lastDownArrow = $(this).find('.right-element:first img.moveDown:last');
+                               $lastDownArrow.addClass('tOff');
+                       });
+               });
+       }
+};
+
+$(document).ready(PagesTree.init);
diff --git a/Toolkit/Toolbox/libjs/paragraph-tree.js b/Toolkit/Toolbox/libjs/paragraph-tree.js
new file mode 100644 (file)
index 0000000..f0fdba6
--- /dev/null
@@ -0,0 +1,143 @@
+var ParagraphTree =
+{
+       init: function()
+       {
+               ParagraphTree.updateMoveArrowVisibility();
+               var $myTree = $('#tree');
+               if ($myTree.children('ul').size() > 0) {
+                       $myTree.tree();
+               }
+
+               $('img.moveDown').click(function() {
+                       var $li = $(this).closest('li'),
+                               $sibling = $li.next();
+                       if ($sibling.length == 1) {
+                               var $id = $li.attr('id');
+                               $li.detach();
+                               $li.insertAfter($sibling);
+                               ParagraphTree.updateMoveArrowVisibility();
+                               $.ajax({
+                                       url : '../toolbox-paragraph-move/'+$id+'/down/',
+                                       async: false,
+                    cache: false,
+                                       success: function(html) {
+                                               if (html == 0) {
+                                                       ParagraphTree.moveBack($li, $sibling);
+                                               } else if ($sibling.hasClass('last')) {
+                                                       $sibling.removeClass('last');
+                                                       $li.addClass('last');
+                                               }
+                                       },
+                                       error: function() {
+                                               ParagraphTree.moveBack($li, $sibling);
+                                       }
+                               });
+                       }
+               });
+
+               $('img.moveUp').click(function() {
+                       var $li = $(this).closest('li'),
+                               $sibling = $li.prev();
+                       if ($sibling.length == 1) {
+                               var $id = $li.attr('id');
+                               $li.detach();
+                               $li.insertBefore($sibling);
+                               if ($li.hasClass('last')) {
+                                       $li.removeClass('last');
+                                       $sibling.addClass('last');
+                               }
+                               ParagraphTree.updateMoveArrowVisibility();
+                               $.ajax({
+                                       url : '../toolbox-paragraph-move/'+$id+'/up/',
+                                       async: false,
+                    cache: false,
+                                       success: function(html) {
+                                               if (html == 0) {
+                                                       ParagraphTree.moveBack($li, $sibling);
+                                               } else if ($li.hasClass('last')) {
+                                                       $li.removeClass('last');
+                                                       $sibling.addClass('last');
+                                               }
+                                       },
+                                       error: function() {
+                                               ParagraphTree.moveBack($li, $sibling);
+                                       }
+                               });
+                       }
+               });
+
+               $('.active-ball').click(function(event) {
+                       event.preventDefault();
+                       var id = $(this).attr('rel');
+                       var _this = $(this);
+                       $.get('../toolbox-paragraph-change-state/'+id+'/?t=' + event.timeStamp, function(data) {
+                               if (data) {
+                                       _this.children().toggle();
+                                       var newTitle = _this.attr('title') == 'Display'
+                                               ? "Don't Display"
+                                               : 'Display';
+                                       _this.attr('title', newTitle);
+                               }
+                       });
+               });
+        $('ul.ltr').each(function(){
+            var count = $(this).children('li').size();
+            ParagraphTree.loadSelectOptions(
+                $(this).children('li').children('div.right-element'), 
+                count
+            );
+        });
+        $('select.pos-select').each(function(){
+            $(this).change(function(){
+                var id = $(this).attr('name');
+                var oldpos = $(this).attr('rel');
+                var newpos = $(this).attr('value');
+                window.location.href = '../toolbox-paragraph-move-sel/' + id + '/' + oldpos + '/' + newpos + '/';
+            });
+        });
+       },
+
+    loadSelectOptions: function(ul, count)
+    {
+        ul.each(function(){
+            var selCount = $(this).children('select').attr('rel');
+            var begin = 1;
+            for (i = begin; i <= count; ++i) {
+                var realNum = i + 1;
+                var html = '<option value="'+realNum+'"';
+                if (selCount == realNum) {
+                    html += ' selected="selected"';
+                }
+                html += '>'+ i +'</option>';
+                $(this).children('select').append(html);
+            }
+        });
+    },
+
+       moveBack: function($li, $sibling)
+       {
+               alert('There was an error moving your page');
+               $li.detach();
+               $li.insertBefore($sibling);
+               ParagraphTree.updateMoveArrowVisibility();
+       },
+
+       updateMoveArrowVisibility: function()
+       {
+               $('img.moveUp, img.moveDown').show().removeClass('tOff');
+
+               var $tree = $('#tree');
+               $tree.find('ul').each(function() {
+                       var $firstUpArrow = $(this).find('img.moveUp:first');
+                       $firstUpArrow.addClass('tOff');
+
+                       var $lis = $(this).children().last();
+                       $lis.each(function() {
+                               var $lastDownArrow = $(this).find('.right-element:first img.moveDown:last');
+                               $lastDownArrow.addClass('tOff');
+                       });
+               });
+       }
+};
+
+$(document).ready(ParagraphTree.init);
diff --git a/Toolkit/Toolbox/moveBySelect.php b/Toolkit/Toolbox/moveBySelect.php
new file mode 100644 (file)
index 0000000..35dfcb3
--- /dev/null
@@ -0,0 +1,126 @@
+<?php
+require_once '../../setup.phtml';
+$dbh = Toolkit_Database::getInstance();
+//echo '<pre>'.print_r($_REQUEST, true).'</pre>';
+$someNewPos = 999;
+try {
+    // start transaction
+    $dbh->beginTransaction();
+    $sql = "
+    SELECT parent
+      FROM pages
+     WHERE id = :id";
+    $stmt = $dbh->prepare($sql);
+    $stmt->bindParam(
+        ":id",
+        $_REQUEST['id'],
+        PDO::PARAM_INT
+    );
+    $stmt->execute();
+    $parent = $stmt->fetchColumn();
+    //var_dump($parent);
+    $updateSql = "
+    UPDATE pages 
+       SET pos = :pos
+     WHERE id = :id";
+    $updateStmt = $dbh->prepare($updateSql);
+    $moveDownSql = "
+      SELECT id
+        FROM pages 
+       WHERE parent = :parent
+         AND pos >= :oldpos
+         AND pos <= :newpos
+    ORDER BY pos";
+    $moveDownStmt = $dbh->prepare($moveDownSql);
+    $moveUpSql = "
+    UPDATE pages 
+       SET pos = pos + 1
+     WHERE parent = :parent
+       AND pos BETWEEN :newpos AND :oldpos";
+    $moveUpStmt = $dbh->prepare($moveUpSql);
+
+    $updateStmt->bindParam(
+        ':pos',
+        $someNewPos,
+        PDO::PARAM_INT
+    );
+    $updateStmt->bindParam(
+        ':id',
+        $_REQUEST['id'],
+        PDO::PARAM_INT
+    );
+    $updateStmt->execute();
+    // going to a higher pos (moving down)
+    if ($_REQUEST['newpos'] > $_REQUEST['oldpos']) {
+        //echo '<pre>'.print_r($_REQUEST, true).'</pre>';
+        //var_dump($parent);
+        $moveDownStmt->bindParam(
+            ':parent',
+            $parent,
+            PDO::PARAM_INT
+        );
+        $moveDownStmt->bindParam(
+            ':oldpos',
+            $_REQUEST['oldpos'],
+            PDO::PARAM_INT
+        );
+        $moveDownStmt->bindParam(
+            ':newpos',
+            $_REQUEST['newpos'],
+            PDO::PARAM_INT
+        );
+        $moveDownStmt->execute();
+        $pos = $_REQUEST['oldpos'];
+        while ($row = $moveDownStmt->fetch(PDO::FETCH_ASSOC)) {
+            //var_dump($row);
+            $updateStmt->bindParam(
+                ':pos',
+                $pos,
+                PDO::PARAM_INT
+            );
+            $updateStmt->bindParam(
+                ':id',
+                $row['id']
+            );
+            $updateStmt->execute();
+            ++$pos;
+        }
+        // moving up
+    } else {
+        $moveUpStmt->bindParam(
+            ':parent',
+            $parent,
+            PDO::PARAM_INT
+        );
+        $moveUpStmt->bindParam(
+            ':oldpos',
+            $_REQUEST['oldpos'],
+            PDO::PARAM_INT
+        );
+        $moveUpStmt->bindParam(
+            ':newpos',
+            $_REQUEST['newpos'],
+            PDO::PARAM_INT
+        );
+        $moveUpStmt->execute();
+    }
+
+    $updateStmt->bindParam(
+        ':pos',
+        $_REQUEST['newpos'],
+        PDO::PARAM_INT
+    );
+    $updateStmt->bindParam(
+        ':id',
+        $_REQUEST['id'],
+        PDO::PARAM_INT
+    );
+    $updateStmt->execute();
+    $dbh->commit();
+} catch (PDOException $e) {
+       $dbh->rollback();
+    Toolkit_Logger::logException('DB Error', $e);
+}
+$cache = new Cache_Lite($GLOBALS['cacheOptions']);
+$cache->clean('Toolbox');
+header('Location: '.MEDIA_BASE_URL.'admin/toolbox.php?showpos=1');
diff --git a/Toolkit/Toolbox/moveBySelectPara.php b/Toolkit/Toolbox/moveBySelectPara.php
new file mode 100644 (file)
index 0000000..b0d0865
--- /dev/null
@@ -0,0 +1,119 @@
+<?php
+require_once '../../setup.phtml';
+$dbh = Toolkit_Database::getInstance();
+$someNewPos = 999;
+try {
+    $dbh->beginTransaction();
+    $sql = "
+    SELECT page
+      FROM paragraphs
+     WHERE id = :id";
+    $stmt = $dbh->prepare($sql);
+    $stmt->bindParam(
+        ":id",
+        $_REQUEST['id'],
+        PDO::PARAM_INT
+    );
+    $stmt->execute();
+    $page = $stmt->fetchColumn();
+    $updateSql = "
+    UPDATE paragraphs 
+       SET pos = :pos
+     WHERE id = :id";
+    $updateStmt = $dbh->prepare($updateSql);
+    $moveDownSql = "
+      SELECT id
+        FROM paragraphs
+       WHERE page = :page
+         AND pos >= :oldpos
+         AND pos <= :newpos
+    ORDER BY pos";
+    $moveDownStmt = $dbh->prepare($moveDownSql);
+    $moveUpSql = "
+    UPDATE paragraphs 
+       SET pos = pos + 1
+     WHERE page = :page
+       AND pos BETWEEN :newpos AND :oldpos";
+    $moveUpStmt = $dbh->prepare($moveUpSql);
+
+    $updateStmt->bindParam(
+        ':pos',
+        $someNewPos,
+        PDO::PARAM_INT
+    );
+    $updateStmt->bindParam(
+        ':id',
+        $_REQUEST['id'],
+        PDO::PARAM_INT
+    );
+    $updateStmt->execute();
+    if ($_REQUEST['newpos'] > $_REQUEST['oldpos']) {
+        $moveDownStmt->bindParam(
+            ':page',
+            $page,
+            PDO::PARAM_INT
+        );
+        $moveDownStmt->bindParam(
+            ':oldpos',
+            $_REQUEST['oldpos'],
+            PDO::PARAM_INT
+        );
+        $moveDownStmt->bindParam(
+            ':newpos',
+            $_REQUEST['newpos'],
+            PDO::PARAM_INT
+        );
+        $moveDownStmt->execute();
+        $pos = $_REQUEST['oldpos'];
+        while ($row = $moveDownStmt->fetch(PDO::FETCH_ASSOC)) {
+            //var_dump($row);
+            $updateStmt->bindParam(
+                ':pos',
+                $pos,
+                PDO::PARAM_INT
+            );
+            $updateStmt->bindParam(
+                ':id',
+                $row['id']
+            );
+            $updateStmt->execute();
+            ++$pos;
+        }
+    } else {
+        $moveUpStmt->bindParam(
+            ':page',
+            $page,
+            PDO::PARAM_INT
+        );
+        $moveUpStmt->bindParam(
+            ':oldpos',
+            $_REQUEST['oldpos'],
+            PDO::PARAM_INT
+        );
+        $moveUpStmt->bindParam(
+            ':newpos',
+            $_REQUEST['newpos'],
+            PDO::PARAM_INT
+        );
+        $moveUpStmt->execute();
+    }
+
+    $updateStmt->bindParam(
+        ':pos',
+        $_REQUEST['newpos'],
+        PDO::PARAM_INT
+    );
+    $updateStmt->bindParam(
+        ':id',
+        $_REQUEST['id'],
+        PDO::PARAM_INT
+    );
+    $updateStmt->execute();
+    $dbh->commit();
+} catch (PDOException $e) {
+       $dbh->rollback();
+    Toolkit_Logger::logException('DB Error', $e);
+}
+$cache = new Cache_Lite($GLOBALS['cacheOptions']);
+$cache->clean('Toolbox');
+header('Location: '.MEDIA_BASE_URL.'admin/toolbox.php?rt=Paragraphs&pageid='.$page);
diff --git a/Toolkit/Toolbox/pageChangeState.php b/Toolkit/Toolbox/pageChangeState.php
new file mode 100644 (file)
index 0000000..d4da710
--- /dev/null
@@ -0,0 +1,32 @@
+<?php
+require_once '../../setup.phtml';
+
+if (!isset($_GET['kpass'])) {
+       return false;
+}
+if (!ctype_digit($_GET['catid'])) {
+       return false;
+}
+
+$dbh = Toolkit_Database::getInstance();
+
+try {
+       $sql = "
+        UPDATE pages
+           SET active = not active
+         WHERE id     = :id";
+
+       $stmt = $dbh->prepare($sql);
+       $stmt->bindParam(':id', $_GET['catid'], PDO::PARAM_INT);
+       $res = $stmt->execute();
+
+       $cache = new Cache_Lite($GLOBALS['cacheOptions']);
+       $cache->clean('Toolbox');
+
+       echo (int) $res;
+       return $res;
+} catch (PDOException $e) {
+       Toolkit_Logger::logException('DB Error', $e);
+       return false;
+}
+?>
diff --git a/Toolkit/Toolbox/pageMobileChangeState.php b/Toolkit/Toolbox/pageMobileChangeState.php
new file mode 100644 (file)
index 0000000..015aa55
--- /dev/null
@@ -0,0 +1,31 @@
+<?php
+require_once '../../setup.phtml';
+
+if (!isset($_GET['kpass'])) {
+       return false;
+}
+if (!ctype_digit($_GET['catid'])) {
+       return false;
+}
+
+$dbh = Toolkit_Database::getInstance();
+try {
+    $sql = "
+        UPDATE pages
+           SET mobile_active = not mobile_active
+         WHERE id = :id";
+
+    $stmt = $dbh->prepare($sql);
+    $stmt->bindParam(':id', $_GET['catid'], PDO::PARAM_INT);
+    $res = $stmt->execute();
+    
+       $cache = new Cache_Lite($GLOBALS['cacheOptions']);
+       $cache->clean('Toolbox');
+
+       echo (int) $res;
+       return $res;
+} catch (PDOException $e) {
+       Toolkit_Logger::logException('DB Error', $e);
+       return false;
+}
+?>
diff --git a/Toolkit/Toolbox/pageMove.php b/Toolkit/Toolbox/pageMove.php
new file mode 100644 (file)
index 0000000..b989d7b
--- /dev/null
@@ -0,0 +1,96 @@
+<?php
+if (!isset($_GET['kpass'])) {
+       die();
+}
+
+require_once '../../setup.phtml';
+
+$writer = new Zend_Log_Writer_Firebug();
+$logger = new Zend_Log($writer);
+
+$request  = new Zend_Controller_Request_Http();
+$response = new Zend_Controller_Response_Http();
+$channel  = Zend_Wildfire_Channel_HttpHeaders::getInstance();
+$channel->setRequest($request);
+$channel->setResponse($response);
+
+//     start output buffering
+ob_start();
+
+//     Now you can make calls to the logger
+
+$dbh = Toolkit_Database::getInstance();
+
+extract($_GET);
+
+try {
+       $dbh->beginTransaction();
+       if ($direction == 'down') {
+               $sql = "
+            UPDATE pages
+               SET pos    =  pos - 1
+             WHERE id     <> :id
+               AND id     <> :home_id
+               AND pos    =  (
+                                       SELECT pos + 1
+                                         FROM pages
+                                        WHERE id =  :id)
+               AND parent =  (
+                                       SELECT parent
+                                         FROM pages
+                                        WHERE id =  :id)";
+               $stmt = $dbh->prepare($sql);
+               $stmt->bindParam(':id', $id, PDO::PARAM_INT);
+               $stmt->bindValue(':home_id', HOME_ID, PDO::PARAM_INT);
+               $stmt->execute();
+
+               $sql = "
+            UPDATE pages
+               SET pos = pos + 1
+             WHERE id  = :id";
+               $stmt = $dbh->prepare($sql);
+               $stmt->bindParam(':id', $id, PDO::PARAM_INT);
+               $stmt->execute();
+       } elseif ($direction == 'up') {
+               $sql = "
+            UPDATE pages
+               SET pos    =  pos + 1
+             WHERE id     <> :id
+               AND id     <> :home_id
+               AND pos    =  (
+                                       SELECT pos - 1
+                                         FROM pages
+                                        WHERE id = :id)
+               AND parent =  (
+                                       SELECT parent
+                                         FROM pages
+                                        WHERE id = :id)";
+               $stmt = $dbh->prepare($sql);
+               $stmt->bindParam(':id', $id, PDO::PARAM_INT);
+               $stmt->bindValue(':home_id', HOME_ID, PDO::PARAM_INT);
+               $stmt->execute();
+
+               $sql = "
+            UPDATE pages
+               SET pos = pos - 1
+             WHERE id  = :id";
+               $stmt = $dbh->prepare($sql);
+               $stmt->bindParam(':id', $id, PDO::PARAM_INT);
+               $stmt->execute();
+       }
+
+       $dbh->commit();
+       echo 1;
+} catch (PDOException $e) {
+       $dbh->rollback();
+       if ('production' == strtolower($_SERVER['GLM_HOST_ID'])) {
+               $logger->crit($e->getMessage());
+       } else {
+               Toolkit_Logger::logException('DB Error', $e);
+       }
+       echo 0;
+}
+//     Flush log data to browser
+$channel->flush();
+$response->sendHeaders();
+die();
diff --git a/Toolkit/Toolbox/paragraphChangeState.php b/Toolkit/Toolbox/paragraphChangeState.php
new file mode 100644 (file)
index 0000000..88aa1a9
--- /dev/null
@@ -0,0 +1,44 @@
+<?php
+require_once '../../setup.phtml';
+
+if (!isset($_GET['kpass'])) {
+       return false;
+}
+if (!ctype_digit($_GET['paragraphId'])) {
+       return false;
+}
+
+$dbh = Toolkit_Database::getInstance();
+
+try {
+       $sql = "
+        UPDATE paragraphs
+           SET active = not active
+         WHERE id     = :id";
+
+       $stmt = $dbh->prepare($sql);
+       $stmt->bindParam(':id', $_GET['paragraphId'], PDO::PARAM_INT);
+       $res = $stmt->execute();
+
+
+       $sql = "
+               SELECT page
+                 FROM paragraphs
+                WHERE id = :id";
+
+       $stmt = $dbh->prepare($sql);
+       $stmt->bindParam(':id', $_GET['paragraphId'], PDO::PARAM_INT);
+       $stmt->execute();
+       $stmt->bindColumn('page', $pageId);
+       $stmt->fetch(PDO::FETCH_ASSOC);
+
+       $cache = new Cache_Lite($GLOBALS['cacheOptions']);
+       $cache->clean('Toolbox');
+
+       echo (int) $res;
+       return $res;
+} catch (PDOException $e) {
+       Toolkit_Logger::logException('DB Error', $e);
+       return false;
+}
+?>
diff --git a/Toolkit/Toolbox/paragraphFileUpload.php b/Toolkit/Toolbox/paragraphFileUpload.php
new file mode 100644 (file)
index 0000000..2bc5553
--- /dev/null
@@ -0,0 +1,14 @@
+<?php
+require_once '../../setup.phtml';
+
+$fs = new Toolkit_FileServer_FileAdapter();
+try {
+       $res = $fs->upload('userfile');
+} catch (Toolkit_FileServer_Exception $e) {
+       Toolkit_Logger::logException('File Server', $e);
+       echo -1; // Don't return "false", it will mess up the JS plugin.
+       return;
+}
+
+echo json_encode($res);
+?>
diff --git a/Toolkit/Toolbox/paragraphMove.php b/Toolkit/Toolbox/paragraphMove.php
new file mode 100644 (file)
index 0000000..d53ebe7
--- /dev/null
@@ -0,0 +1,51 @@
+<?php
+if (!isset($_GET['kpass'])) {
+       die();
+}
+
+require_once '../../setup.phtml';
+
+$dbh = Toolkit_Database::getInstance();
+
+extract($_GET);
+
+try {
+       $dbh->beginTransaction();
+       if ($direction == 'down') {
+               //      Update paragraph nodes on this page who lie at the target node
+               //      position and the position just after it.
+               $sql = "
+            UPDATE paragraphs
+               SET pos  = CASE WHEN id = :id
+                                     THEN pos + 1 ELSE pos - 1 END
+             WHERE page = (SELECT page FROM paragraphs WHERE id = :id)
+               AND pos BETWEEN (SELECT pos FROM paragraphs WHERE id = :id)
+               AND (SELECT pos + 1 FROM paragraphs WHERE id = :id)";
+               $stmt = $dbh->prepare($sql);
+               $stmt->bindParam(':id', $id, PDO::PARAM_INT);
+               $stmt->execute();
+       } elseif ($direction == 'up') {
+               //      Update paragraph nodes on this page who lie at the target node
+               //      position and the position just before it.
+               $sql = "
+            UPDATE paragraphs
+               SET pos  = CASE WHEN id = :id
+                                     THEN pos - 1 ELSE pos + 1 END
+             WHERE page = (SELECT page FROM paragraphs WHERE id = :id)
+               AND pos BETWEEN (SELECT pos - 1 FROM paragraphs WHERE id = :id)
+               AND (SELECT pos FROM paragraphs WHERE id = :id)";
+               $stmt = $dbh->prepare($sql);
+               $stmt->bindParam(':id', $id, PDO::PARAM_INT);
+               $stmt->execute();
+       }
+
+       $dbh->commit();
+       die();
+} catch (PDOException $e) {
+       $dbh->rollback();
+       Toolkit_Logger::logException('DB Error', $e);
+       throw new Toolkit_Toolbox_Exception(
+               "unable to move paragraph `$id` `$direction`"
+       );
+}
+?>
diff --git a/Toolkit/Toolbox/styles.css b/Toolkit/Toolbox/styles.css
new file mode 100644 (file)
index 0000000..3be1ead
--- /dev/null
@@ -0,0 +1,132 @@
+.webform table { width: 742px; }
+.fieldcell { width: auto; }
+
+/* form section arrows */
+tr.hdr td {
+       background: #DDD url("//app.gaslightmedia.com/assets/icons/bullet_arrow_right.png") no-repeat;
+       padding-left: 15px;
+       cursor: pointer;
+       cursor: hand;
+}
+tr.hdr td:hover {
+       background-color: #e5e5e5;
+}
+tr.hdr td.open{
+       background-image: url("//app.gaslightmedia.com/assets/icons/bullet_arrow_down.png");
+}
+
+/* paragraph files */
+.files {
+       list-style-type: none;
+       margin: 0;
+       padding: 0;
+       width: 60%;
+}
+.files li {
+       margin: 0 3px 3px 3px;
+       padding: 0.4em;
+       padding-left: 1.5em;
+}
+.files li span {
+       position: absolute;
+       margin-left: -1.3em;
+}
+.files .ui-state-highlight {
+       height: 1.5em;
+       line-height: 1.2em;
+}
+#success_notification {
+       font-weight: bold;
+       color: #11a52f;
+}
+
+/* right tree element */
+.right-element {
+       float: left;
+       }
+.paragraph-line {
+       float: none !important;
+}
+.paragraph-line strong {
+        margin-left: 10px;
+        }
+/* highlight search matches in tree */
+.search {
+       color: orange;
+}
+
+/* Ove */
+#q {
+       border: 2px inset black;
+       height: 20px;
+       padding: 2px;
+       font-size: 16px;
+       width: 200px;
+       margin-right: 10px;
+       font-weight: bold}
+
+#tree li {
+       display: block;
+       line-height: 24px !important;
+}
+li.closed { background: url(assets/arrowClosed.png) no-repeat !important;}
+li.open { background: url(assets/arrowOpen.png) no-repeat !important;}
+/*.ltr>li {
+       border-bottom: 1px dashed #ccc;
+}*/
+#tree li.last {
+       float: none;
+}
+#tree {
+       width: 630px;
+}
+
+/*
+ * #tree ul {
+ *     background: #ece;
+ *     }
+ *     #tree ul ul {
+ *             background: #cee;
+ *     }
+ *     #tree ul ul ul {
+ *             background: #eec;
+ *     }
+ *     #tree ul ul ul ul {
+ *             background: #ede;
+ *     }
+ */
+.right-element a {
+       text-decoration: underline !important;
+}
+
+#tree .editPage, .editPage:hover,
+#tree .editParagraphs, .editParagraphs:hover,
+#tree .pagePreview, .pagePreview:hover {
+       height: 16px;
+       padding: 0 5px 0 0px !important;
+       border: 0;
+}
+
+/*
+#tree .editPage, .editPage:hover { background: url(//app.gaslightmedia.com/assets/icons/page_edit.png) no-repeat; }
+#tree .editParagraphs, .editParagraphs:hover {         background: url(//app.gaslightmedia.com/assets/icons/page_add.png) no-repeat; }
+#tree .pagePreview, .pagePreview:hover {       background: url(//app.gaslightmedia.com/assets/icons/page_go.png) no-repeat; }
+
+#tree a:hover { color: green;}
+*/
+/* Lines */
+#tree li {
+       /*      border-bottom: 1px dotted #666;
+        *      padding-bottom: 1px;
+        */
+}
+
+/* Mousetip */
+.moveUp, .moveDown {
+       cursor: hand;
+       cursor: pointer;
+}
+.tOff {
+       visibility: hidden;
+}
+.form-success {border:1px solid green;color:green;padding:10px;margin:5px;}
diff --git a/Toolkit/Tree.php b/Toolkit/Tree.php
new file mode 100644 (file)
index 0000000..e06db64
--- /dev/null
@@ -0,0 +1,145 @@
+<?php
+// vim:set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker syntax=php:
+
+/**
+ * DB tree structures
+ *
+ * PHP version 5
+ *
+ * @category Toolkit
+ * @package  Toolkit_Tree
+ * @author   Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @license     http://www.gaslightmedia.com Gaslightmedia
+ * @release     CVS: $Id: Tree.php,v 1.4 2009/06/18 14:12:50 jamie Exp $
+ * @link     http://demo.gaslightmedia.com
+ */
+
+/**
+ * Create tree structures of database entries
+ *
+ * Base class used to setup tree objects that will render
+ * database structures into iterable PHP objects.
+ *
+ * @category  Toolkit
+ * @package      Toolkit_Tree
+ * @author      Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @copyright 2008 Gaslight Media
+ * @license      http://www.gaslightmedia.com Gaslightmedia
+ * @link      http://demo.gaslightmedia.com
+ */
+abstract class Toolkit_Tree
+{
+    //    {{{ properties
+
+    /**
+     * Database ID of tuple
+     *
+     * @var int
+     * @access public
+     */
+    public $catid = 0;
+
+    /**
+     * Name of object
+     *
+     * @var string
+     * @access public
+     */
+    public $category;
+
+    /**
+     * Children of object
+     *
+     * @var array
+     * @access public
+     */
+    public $children = array();
+
+    /**
+     * Parent of this category
+     *
+     * @var array
+     * @access public
+     */
+    public $parentId;
+
+    /**
+     * Database connection handle
+     *
+     * @var PDO Object
+     * @access protected
+     */
+    protected $dbh;
+
+    //    }}}
+    //    {{{ __construct()
+
+    /**
+     * Setup the objects properties and call to setup children
+     *
+     * @param integer $catid    ID of the Database object
+     * @param string  $name     Name of the Database object
+     * @param PDO     $dbh      Database handler object
+     * @param integer $parentId Id of parent node
+     *
+     * @author    Jamie Kahgee <jamie.kahgee@gmail.com>
+     * @access    public
+     */
+    public function __construct($catid, $name, PDO $dbh, $parentId = 0)
+    {
+        $this->parentId = $parentId;
+        $this->catid    = $catid;
+        $this->category = $name;
+        $this->dbh      = $dbh;
+        $this->addChildren();
+    }
+
+    //    }}}
+    //    {{{ __get()
+
+    /**
+     * utilized for reading data from inaccessible members
+     *
+     * @param string $name property name
+     *
+     * @return mixed  void
+     * @access public
+     */
+    public function __get($name)
+    {
+        return $this->$name;
+    }
+
+    //    }}}
+    //    {{{ __set()
+
+
+    /**
+     * Run when writing data to inaccessible members
+     *
+     * @param string $name  property
+     * @param string $value new value
+     *
+     * @return void
+     * @access public
+     */
+    public function __set($name, $value)
+    {
+        $this->$name = $value;
+    }
+
+    //    }}}
+    //    {{{ addChildren()
+
+    /**
+     * Attaches children to tree object
+     *
+     * @author Jamie Kahgee <jamie.kahgee@gmail.com>
+     * @return void
+     * @access protected
+     */
+    abstract protected function addChildren();
+
+    //    }}}
+}
+?>
diff --git a/Toolkit/Videos/AdminEditVideoForm.php b/Toolkit/Videos/AdminEditVideoForm.php
new file mode 100644 (file)
index 0000000..0977087
--- /dev/null
@@ -0,0 +1,309 @@
+<?php
+
+/**
+ * AdminEditVideoForm.php
+ *
+ * PHP version 5
+ *
+ * @category Toolkit
+ * @package  Videos
+ * @author   Steve Sutton <steve@gaslightmedia.com>
+ * @license  http://www.gaslightmedia.com Gaslightmedia
+ * @link     http://demo.gaslightmedia.com
+ */
+
+/**
+ * Toolkit_Videos_AdminEditVideoForm
+ *
+ * Handles the form generation and processing of the Video Edit Page
+ *
+ * @category  Toolkit
+ * @package   Videos
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2008 Gaslight media
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @link      http://demo.gaslightmedia.com
+ */
+class Toolkit_Videos_AdminEditVideoForm
+    extends Toolkit_FormBuilder
+{
+       /**
+        * What do you want the success msg to be if the form validates successfully
+        *
+        * @var string
+        * @access protected
+        */
+       protected $successMsg
+               = '<div id="form-success-top">Coupon successfully updated.</div>';
+
+       /**
+        * The default rules to register for validating
+        *
+        * We have to register these rules, or any others we want, before
+        * we are able to use them in our forms.
+        *
+        * @var string
+        * @access protected
+        */
+       protected $registeredRules = array(
+               array(
+                       'checkURI',
+                       'callback',
+                       'uri',
+                       'Validate'
+               )
+       );
+    public $errorMsg = '
+        <div id="form-warning-top">There was a Problem with your form!</div>';
+
+    /**
+     * Setup Form Constants
+     *
+     * Constants are form values (hidden) fields most likely that cannot be
+     * changed by user input
+     *
+     * @return void
+     */
+    public function configureConstants()
+    {
+        $c = array();
+        $this->setConstants($c);
+    }
+
+    /**
+     * Setup Form Defaults
+     *
+     * if $_GET['id'] is numeric then it creates a Toolkit_Videos_Video
+     * class object and gets set as form element defaults
+     *
+     * @param PDO $dbh Database Connection
+     *
+     * @return void
+     */
+    public function configureDefaults(PDO $dbh)
+    {
+        $d = array();
+        if (is_numeric($_GET['id'])) {
+            $video = new Toolkit_Videos_VideoMapper($dbh);
+            $d = $video->getVideoById($_GET['id']);
+        }
+        $this->setDefaults($d);
+    }
+
+    /**
+     * Setup the Form elements
+     *
+     * @param PDO              $dbh Database Connection
+     * @param Config_Container $c   PEAR::Config_Container object
+     *
+     * @return void
+     */
+    public function configureElements(PDO $dbh, Config_Container $c)
+       {
+        $e = array();
+
+        //  get reference to [conf] section of config file
+        $appHasFeaturedVideos
+            = $c->getItem('section', 'conf')
+            ->getItem('directive', 'featuredVideos')
+            ->getContent();
+        // All Elements are created here.
+        // This includes group element definitions.
+               $e[] = array(
+            'type' => 'advcheckbox',
+            'req' => false,
+            'name' => 'active',
+                       'display' => 'Active',
+                       'val' => array(0, 1)
+        );
+        if ($appHasFeaturedVideos) {
+            $e[] = array(
+                'type' => 'advcheckbox',
+                'req' => false,
+                'name' => 'featured',
+                'display' => 'Featured',
+                'val' => array(0, 1)
+            );
+        } else {
+            $e[] = array(
+                'type' => 'hidden',
+                'req' => false,
+                'name' => 'featured'
+            );
+        }
+        $e[] = array(
+               'type' => 'text',
+               'req' => true,
+               'name' => 'video_url',
+               'display' => 'Video Link<br>
+                <span style="color:blue">
+                Click on the \'Share\' function on the YouTube video
+                <br>to get your link. If you\'re having problems click on "show options"
+                and select "long link"</span>',
+               'opts' => array('class' => 'text','size'=>50)
+        );
+               $e[] = array(
+            'type' => 'submit',
+            'req' => false,
+            'name' => 'submit_rmv',
+            'display' => 'Submit Video',
+            'opts' => array('id' => 'submit')
+        );
+        if (ctype_digit($_GET['id'])) {
+            $e[] = array(
+                'type' => 'submit',
+                'req' => false,
+                'name' => 'delete_rmv',
+                'display' => 'Delete Video',
+                'opts' => array('id' => 'delete')
+            );
+        }
+
+               $this->setupElements($e);
+       }
+
+    /**
+     * Setup Form filters
+     *
+     * @return void
+     */
+    public function configureFilters()
+       {
+        $f   = array();
+               $f[] = array(
+            'element' => '__ALL__',
+            'filter' => 'trim'
+        );
+        $f[] = array(
+            'element' => 'url',
+                       'filter' => array('Toolkit_Common', 'filterURI')
+        );
+
+               $this->setupFilters($f);
+       }
+
+    /**
+     * Runs all function to setup the Form
+     *
+     * @param PDO              $dbh Database Connection
+     * @param Config_Container $c   PEAR::Config_Container object
+     *
+     * @return void
+     */
+    public function configureForm(
+               PDO $dbh,
+               Config_Container $c
+       ) {
+               $this->configureElements($dbh, $c);
+               $this->configureFilters();
+               $this->configureRules();
+               $this->configureDefaults($dbh);
+               $this->configureConstants();
+       }
+
+    /**
+     * Setup the Form's rules
+     *
+     * @return void
+     */
+    public function configureRules()
+       {
+        $r = array();
+
+               $r[] = array(
+                       'element'    => 'video_url',
+                       'message'    => 'ERROR: Invalid URL format (http)',
+                       'type'       => 'checkURI',
+                       'format'     => array(
+                               'allowed_schemes' => array('http'),
+                               'strict' => false
+                       ),
+                       'validation' => $this->validationType,
+                       'reset'      => false,
+                       'force'      => false
+               );
+
+
+               $this->setupRules($r);
+       }
+
+    /**
+     * _processData function creates an video object Toolkit_Videos_Video
+     * if id exists it creates the object by id
+     * else it creates a new object
+     * saved is called and the object is either created or updated
+     *
+     * If the $values['id'] is given it creates a Toolkit_Videos_VideoMapper
+     * object and get the video object using the getVideoById method
+     * If not it then just creates an empty video object
+     * The object is then updated using it's accessor method and saved
+     *
+     * @param type $dbh    Database Connection
+     * @param type $values Form $values
+     *
+     * @return void
+     */
+    private function _processData($dbh, $values)
+    {
+        $videoMapper = new Toolkit_Videos_VideoMapper($dbh);
+        if ($_GET['id']) {
+            $video = $videoMapper->getVideoById($_GET['id'], false);
+        } else {
+            $video = new Toolkit_Videos_Video();
+        }
+        $video->setVideo_url($values['video_url'])
+            ->setFeatured($values['featured'])
+            ->setActive($values['active']);
+
+        $video->save($dbh);
+    }
+
+    /**
+     * Output HTML for the form
+     *
+     * @param PDO $dbh Database Connection
+     *
+     * @return string|boolean
+     */
+    public function toHtml(PDO $dbh)
+    {
+       //  Handle Deleting banner.
+        if (   $this->isSubmitted()
+            && ctype_digit($_GET['id'])
+        ) {
+            if ($this->getSubmitValue('delete_rmv')) {
+                $videoMapper = new Toolkit_Videos_VideoMapper($dbh);
+                $video = $videoMapper->getVideoById($_GET['id'], false);
+                if ($video instanceof Toolkit_Videos_Video) {
+                    if ($video->delete($dbh, $is)) {
+                        return 'Video successfully deleted.';
+                    }
+                } else {
+                    //  the coupon has already been deleted or doesn't exist.
+                    return "The video has already been deleted or doesn't exists.";
+                }
+                header ('Location: videos.php');
+                return false;
+            }
+        }
+
+               $this->setupRenderers();
+               if ($this->validate()) {
+                       $this->cleanForm();
+
+                       $submitValues = $this->getSubmitValues();
+                       if ($this->_processData($dbh, $submitValues)) {
+                               $this->freeze();
+                               $output = $this->successMsg;
+                       }
+            header('Location: videos.php');
+               } elseif ($this->isSubmitted()) {
+                       $output  = $this->errorMsg;
+                       $output .= parent::toHTML();
+               } else {
+                       $output = parent::toHTML();
+               }
+               return $output;
+       }
+
+}
diff --git a/Toolkit/Videos/Controller.php b/Toolkit/Videos/Controller.php
new file mode 100644 (file)
index 0000000..d33be54
--- /dev/null
@@ -0,0 +1,58 @@
+<?php
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+/**
+ * Description of Controller
+ *
+ * @author steve
+ */
+class Toolkit_Videos_Controller {
+
+    protected $flexyOptions;
+
+    public function getFlexyOptions()
+    {
+        return $this->flexyOptions;
+    }
+
+    public function setFlexyOptions($flexyOptions)
+    {
+        $this->flexyOptions = $flexyOptions;
+    }
+
+    function __construct()
+    {
+        $options = $GLOBALS['flexyOptions'];
+               $options['templateDir']
+                       = dirname(__FILE__) . '/templates';
+               $options['compileDir']
+                       = dirname(__FILE__) . '/templates/compiled';
+        $this->flexyOptions = $options;
+    }
+
+    public function toHtml(PDO $dbh, Config_Container $appConf)
+    {
+        switch ($_GET['module']) {
+        case 'editVideo':
+            $form = new Toolkit_Videos_AdminEditVideoForm(
+                'edit-video'
+            );
+            $form->configureForm($dbh, $appConf);
+            $ret = $form->toHtml($dbh);
+            break;
+        case 'listVideo':
+        default:
+            $videos = new Toolkit_Videos_VideosDataGrid($dbh);
+            $videos->setConfig($appConf);
+                       $videos->setQuery();
+            $videos->setDefaultSort(array('pos' => 'ASC'));
+                       $ret .= $videos->toHtml($appConf);
+            break;
+        }
+        return $ret;
+    }
+}
+?>
diff --git a/Toolkit/Videos/Database/application.sql b/Toolkit/Videos/Database/application.sql
new file mode 100644 (file)
index 0000000..178e2fd
--- /dev/null
@@ -0,0 +1,12 @@
+--
+-- setup schema
+--
+
+CREATE SCHEMA videos;
+GRANT ALL ON SCHEMA videos TO nobody;
+
+--
+-- Tables
+--
+
+\i ./table/videos.sql
diff --git a/Toolkit/Videos/Database/removeApplication.sql b/Toolkit/Videos/Database/removeApplication.sql
new file mode 100644 (file)
index 0000000..6c60d9c
--- /dev/null
@@ -0,0 +1,6 @@
+--
+-- Drops schema
+-- WARNING: CANNOT BE UNDONE
+--
+
+DROP SCHEMA IF EXISTS videos CASCADE;
\ No newline at end of file
diff --git a/Toolkit/Videos/Database/table/videos.sql b/Toolkit/Videos/Database/table/videos.sql
new file mode 100644 (file)
index 0000000..0411150
--- /dev/null
@@ -0,0 +1,12 @@
+CREATE TABLE videos.videos (
+    id SERIAL,
+    create_date DATE DEFAULT current_date,
+    video_url TEXT,
+    active BOOLEAN,
+    featured BOOLEAN,
+    pos INT DEFAULT 1,
+    PRIMARY KEY (id)
+);
+
+GRANT ALL ON videos.videos TO nobody;
+GRANT ALL ON videos.videos_id_seq TO nobody;
\ No newline at end of file
diff --git a/Toolkit/Videos/IDecorator.php b/Toolkit/Videos/IDecorator.php
new file mode 100644 (file)
index 0000000..1095b2d
--- /dev/null
@@ -0,0 +1,56 @@
+<?php
+
+/**
+ * IDecorator.php
+ *
+ * PHP version 5
+ *
+ * @category Toolkit
+ * @package  Videos
+ * @author   Steve Sutton <steve@gaslightmedia.com>
+ * @license  http://www.gaslightmedia.com Gaslightmedia
+ * @link     http://demo.gaslightmedia.com
+ */
+
+/**
+ * Toolkit_Videos_IDecorator
+ *
+ * Inteface class for the Toolkit_Videos_Decorators
+ * In future there may be more decorators here we're insurring that they have
+ * simular API's
+ *
+ * @category  Toolkit
+ * @package   Videos
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2008 Gaslight media
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @link      http://demo.gaslightmedia.com
+ */
+interface Toolkit_Videos_IDecorator
+{
+     /**
+     * Class Constructor
+     *
+     * @return void
+     */
+    public function __construct();
+
+    /**
+     * Handles setting up the PEAR::HTML_Template_Flexy options
+     *
+     * @return void
+     */
+    public function setFlexyOptions();
+    /**
+     * Handles creating HTML output
+     *
+     * @param PDO    $dbh     Database Connection
+     * @param object $gateway Gateway Object
+     *
+     * @return string
+     */
+    public function toHtml(
+        PDO $dbh,
+        $gateway = null
+    );
+}
diff --git a/Toolkit/Videos/Navigation.php b/Toolkit/Videos/Navigation.php
new file mode 100644 (file)
index 0000000..0428d7a
--- /dev/null
@@ -0,0 +1,123 @@
+<?php
+
+/**
+ * Navigation.php
+ *
+ * PHP version 5
+ *
+ * @category Toolkit
+ * @package  Videos
+ * @author   Steve Sutton <steve@gaslightmedia.com>
+ * @license  http://www.gaslightmedia.com Gaslightmedia
+ * @link     http://demo.gaslightmedia.com
+ */
+
+/**
+ * Toolkit_Videos_Navigation
+ *
+ * Handles the navigation for the Video admin side
+ *
+ * @category  Toolkit
+ * @package   Videos
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2008 Gaslight media
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @link      http://demo.gaslightmedia.com
+ */
+class Toolkit_Videos_Navigation
+    extends Toolkit_NavigationAbstract
+       implements Toolkit_INavigation
+{
+       //      {{{     __construct()
+
+    /**
+     * Class Constructor
+     *
+     * @param HTML_Menu          $menu    Menu to use
+     * @param HTML_Menu_Renderer $rEngine Render to use
+     *
+     * @return void
+     */
+       public function __construct(
+               HTML_Menu $menu,
+               HTML_Menu_Renderer $rEngine
+       ) {
+               $this->menu      = $menu;
+               $this->rEngine   = $rEngine;
+               $this->currIndex = 'listCoupons';
+       }
+
+    /**
+     * Create templates for the navigation
+     *
+     * @return void
+     */
+       protected function setNavTemplates()
+       {
+               $tpl = '<li><a href="%s" title="%s">{Title}</a></li>';
+               $this->rEngine->setEntryTemplate(
+                       HTML_MENU_ENTRY_INACTIVE,
+                       sprintf($tpl, '{url}', '{desc}', '{Title}')
+               );
+               $this->rEngine->setEntryTemplate(
+                       HTML_MENU_ENTRY_ACTIVE,
+                       sprintf($tpl, '{url}', '{desc}', '{Title}')
+               );
+               $this->rEngine->setEntryTemplate(
+                       HTML_MENU_ENTRY_ACTIVEPATH,
+                       sprintf($tpl, '{url}', '{desc}', '{Title}')
+               );
+               $this->rEngine->setMenuTemplate('', '');
+               $this->rEngine->setRowTemplate('<ul class="admin_nav">', '</ul>');
+       }
+
+    /**
+     * force the current index to $_GET['page']
+     *
+     * @return void
+     */
+       protected function setCurrentIndex()
+       {
+               $this->menu->forceCurrentIndex($_GET['page']);
+       }
+
+       //      }}}
+       //      {{{     getNavSructure()
+       //      @codeCoverageIgnoreStart
+
+    /**
+     * Sets up a multi dimensional array used for the nav structure
+        *
+        * @param Config_Container $c Application configuration
+     *
+     * @return array navigational array hash
+     * @access public
+     */
+       public function getNavStructure(Config_Container $c)
+       {
+        //  get reference to [listing type] section of config file
+        $singularType = $c->getItem('section', 'listing type')
+                         ->getItem('directive', 'singular')
+                         ->getContent();
+        $pluralType = $c->getItem('section', 'listing type')
+                         ->getItem('directive', 'plural')
+                         ->getContent();
+
+               $nav = array(
+                       'listVideos' => array(
+                               'Title' => "List {$pluralType}",
+                               'url' => MEDIA_BASE_URL . 'admin/videos.php?page=listVideos&amp;module=listVideos',
+                               'desc' => "Display all the {$pluralType}",
+                       ),
+                       'editVideo' => array(
+                               'Title' => "Add {$singularType}",
+                               'url' => MEDIA_BASE_URL . 'admin/videos.php?page=editVideo&amp;module=editVideo',
+                               'desc' => "Edit a {$singularType}"
+                       ),
+               );
+
+               return $nav;
+       }
+
+       //      @codeCoverageIgnoreEnd
+}
diff --git a/Toolkit/Videos/Video.php b/Toolkit/Videos/Video.php
new file mode 100644 (file)
index 0000000..8e5f6e3
--- /dev/null
@@ -0,0 +1,346 @@
+<?php
+
+/**
+ * Video.php
+ *
+ * PHP version 5
+ *
+ * @category Toolkit
+ * @package  Videos
+ * @author   Steve Sutton <steve@gaslightmedia.com>
+ * @license  http://www.gaslightmedia.com Gaslightmedia
+ * @link     http://demo.gaslightmedia.com
+ */
+
+/**
+ * Toolkit_Videos_Video
+ *
+ * Object Data pattern class for the Video PDO Table
+ *
+ * @category  Toolkit
+ * @package   Videos
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2008 Gaslight media
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @link      http://demo.gaslightmedia.com
+ */
+class Toolkit_Videos_Video
+{
+
+    protected $id;
+    protected $video_url;
+    protected $pos;
+    protected $featured;
+    protected $active;
+
+    /**
+     * Handles the work of Inserting a video record into the database
+     * returns the id for the new record
+     *
+     * @param PDO $dbh Database Connection
+     *
+     * @return int
+     */
+    private function _insert(PDO $dbh)
+    {
+        $classData = $this->getClassAsArray();
+        unset($classData['id']);
+        $pos = $this->getMaxPos();
+        $classData['pos'] = ++$pos;
+        $sql = Toolkit_Common::createSQLInsert(
+            'videos',
+            array_keys($classData)
+        );
+        $sql .= " RETURNING id";
+        $stmt = Toolkit_Common::prepareQuery(
+            $dbh,
+            'videos',
+            $sql,
+            $classData
+        );
+        $stmt->execute();
+        return $stmt->fetchColumn();
+    }
+
+    /**
+     * Handles the update of the record on the database record
+     *
+     * @param PDO $dbh Database Connection
+     *
+     * @return int
+     */
+    private function _update(PDO $dbh)
+    {
+        $classData = $this->getClassAsArray();
+        $sql = Toolkit_Common::createSQLUpdate(
+            'videos',
+            array_keys($classData),
+            array('id = :id')
+        );
+        return Toolkit_Common::processQuery(
+            $dbh,
+            'videos',
+            $sql,
+            $classData
+        );
+    }
+
+    /**
+     * Handles the deletion of the record in the database and reordering
+     * any other videos in the table
+     *
+     * @param PDO $dbh Database Connection
+     *
+     * @return void
+     */
+    public function delete(PDO $dbh)
+    {
+        try {
+            $dbh->beginTransaction();
+            $sql = "
+            DELETE
+              FROM videos
+             WHERE id = :id";
+            $stmt = $dbh->prepare($sql);
+            $stmt->bindParam(
+                ':id',
+                $this->getId(),
+                PDO::PARAM_INT
+            );
+            $stmt->execute();
+            // we have to reorder the positions for the rest of the videos
+            $sql = "
+            UPDATE videos
+               SET pos = pos - 1
+             WHERE pos >= :oldpos";
+            $stmt = $dbh->prepare($sql);
+            $stmt->bindParam(':oldpos', $this->getPos(), PDO::PARAM_INT);
+            $stmt->execute();
+            $dbh->commit();
+        } catch(PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+    }
+
+    /**
+     * active getter
+     *
+     * @return boolean
+     */
+    public function getActive()
+    {
+        return $this->active;
+    }
+
+    /**
+     * active setter the given values is cast to a boolean value
+     * so if you send in a string 'true' or 'false' both will be cast to true
+     *
+     * @param boolean $active Active flag
+     *
+     * @return Toolkit_Videos_Video
+     */
+    public function setActive($active)
+    {
+        $this->active = (bool)$active;
+        return $this;
+    }
+
+    /**
+     * id getter
+     *
+     * @return int
+     */
+    public function getId()
+    {
+        return $this->id;
+    }
+
+    /**
+     * id setter
+     *
+     * @param int $id integer for the record id
+     *
+     * @return Toolkit_Videos_Video
+     */
+    public function setId($id)
+    {
+        if (!$this->id) {
+            $this->id = $id;
+        }
+        return $this;
+    }
+
+    /**
+     * video_url getter
+     *
+     * @return string
+     */
+    public function getVideo_url()
+    {
+        return $this->video_url;
+    }
+
+    /**
+     * video_url settur
+     *
+     * @param string $video_url the youtube video url
+     *
+     * @return Toolkit_Videos_Video
+     */
+    public function setVideo_url($video_url)
+    {
+        $this->video_url = $video_url;
+        return $this;
+    }
+
+    /**
+     * pos getter
+     *
+     * @return int
+     */
+    public function getPos()
+    {
+        return $this->pos;
+    }
+
+    /**
+     * pos setter
+     *
+     * @param type $pos Position number for the video
+     *
+     * @return Toolkit_Videos_Video
+     */
+    public function setPos($pos)
+    {
+        $this->pos = $pos;
+        return $this;
+    }
+
+    /**
+     * featured getter
+     *
+     * @return boolean
+     */
+    public function getFeatured()
+    {
+        return $this->featured;
+    }
+
+    /**
+     * featured setter needs to be boolean not a string
+     *
+     * @param boolean $featured Featured flag
+     *
+     * @return Toolkit_Videos_Video
+     */
+    public function setFeatured($featured)
+    {
+        $this->featured = (bool)$featured;
+        return $this;
+    }
+
+    /**
+     * Handles the saving of the object to the database
+     *
+     * If the object has an id field set then update the record or else
+     * create a new one
+     *
+     * @param PDO $dbh Database Connection
+     *
+     * @return void
+     */
+    public function save(PDO $dbh)
+    {
+        if ($this->getId()) {
+            $this->_update($dbh);
+        } else {
+            $this->_insert($dbh);
+        }
+    }
+
+    /**
+     * Create an array of the class fields
+     *
+     * @return array
+     */
+    public function getClassAsArray()
+    {
+        $data = array();
+        $classData = get_class_vars(get_class($this));
+        foreach ($classData as $fieldName => $element) {
+            $getFunc = 'get' . ucfirst($fieldName);
+            $data[$fieldName] = $this->$getFunc();
+        }
+        return $data;
+    }
+
+    /**
+     * Get the max position for the videos
+     *
+     * @staticvar int $maxPos position of the video
+     * @return int
+     */
+    public function getMaxPos()
+    {
+        static $maxPos;
+        if (!$maxPos) {
+            $dbh = Toolkit_Database::getInstance();
+            $sql = "
+            SELECT count(id) as maxpos
+              FROM videos";
+            $ret = $dbh->query($sql);
+            $maxPos = $ret->fetchColumn();
+        }
+        return (int)$maxPos;
+    }
+
+    /**
+     * Create the youtube url
+     *
+     * Using preg_match on two pattern to return the video url portion
+     * first is /v=(.*)$/ older style
+     * second is /youtu.be\/(.*)/
+     * These may need to be updated if youtube creates newer embed codes
+     *
+     * @return boolean
+     */
+    public function getVideoCode()
+    {
+        $pattern  = '/v=(.*)$/';
+        $pattern2 = '/youtu.be\/(.*)/';
+        $vidUrl = $this->getVideo_url();
+        if (preg_match($pattern, $vidUrl, $match)) {
+            return $match[1];
+        } else if (preg_match($pattern2, $vidUrl, $match)) {
+            return $match[1];
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * Using the youtube API get the video title for the embed url
+     *
+     * @return string
+     */
+    public function getVideoTitle()
+    {
+        $url = "http://gdata.youtube.com/feeds/api/videos/". $this->getVideoCode();
+        $ch          = curl_init();
+        $curlOptions = array(
+                CURLOPT_URL            => $url,
+                CURLOPT_HEADER         => 0,
+                CURLOPT_RETURNTRANSFER => 1
+                );
+        curl_setopt_array($ch, $curlOptions);
+
+        $response = curl_exec($ch);
+        curl_close($ch);
+        $doc   = new DOMDocument;
+        $doc->loadXML($response);
+        $title = $doc->getElementsByTagName("title")->item(0)->nodeValue;
+        return $title;
+    }
+
+}
diff --git a/Toolkit/Videos/VideoMapper.php b/Toolkit/Videos/VideoMapper.php
new file mode 100644 (file)
index 0000000..2a00c87
--- /dev/null
@@ -0,0 +1,111 @@
+<?php
+
+/**
+ * Video.php
+ *
+ * PHP version 5
+ *
+ * @category Toolkit
+ * @package  Videos
+ * @author   Steve Sutton <steve@gaslightmedia.com>
+ * @license  http://www.gaslightmedia.com Gaslightmedia
+ * @link     http://demo.gaslightmedia.com
+ */
+
+/**
+ * Toolkit_Videos_Video
+ *
+ * Object Mapper pattern class for the Video PDO Table. Creates
+ * Toolkit_Video_Video objects by values or by id
+ *
+ * @category  Toolkit
+ * @package   Videos
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2008 Gaslight media
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @link      http://demo.gaslightmedia.com
+ */
+class Toolkit_Videos_VideoMapper
+{
+
+    /**
+     * Class Constructor
+     *
+     * @param PDO $pdo Database Connection
+     */
+    function __construct(PDO $pdo)
+    {
+        $this->dbh = $pdo;
+    }
+
+    /**
+     * Creates Video object by a given array of values
+     * values must be set with the Videos fields as keys
+     *
+     * @param array $values array of values
+     *
+     * @return Toolkit_Videos_Video
+     */
+    public function createByValues($values)
+    {
+        $video = new Toolkit_Videos_Video();
+        $video->setId($values['id'])
+            ->setVideo_url($values['video_url'])
+            ->setActive($values['active'])
+            ->setfeatured($values['featured'])
+            ->setPos($this->getMaxPos());
+        return $video;
+    }
+
+    public function getMaxPos()
+    {
+        $dbh = Toolkit_Database::getInstance();
+        $sql = "
+        SELECT count(id) as maxpos
+            FROM videos";
+        $ret = $dbh->query($sql);
+        $maxPos = $ret->fetchColumn();
+        if ((int) $maxPos == 0) {
+            $maxPos = 1;
+        }
+        return $maxPos;
+    }
+    /**
+     * Create Video object by a given id
+     * if the id is not found then it does not return
+     *
+     * @param int   $id          id for the video record
+     * @param array $returnArray Can return the array instead of object
+     *
+     * @return mixed
+     */
+    public function getVideoById($id, $returnArray = true)
+    {
+        try {
+            $sql = "
+            SELECT *
+              FROM videos
+             WHERE id = :id";
+            $stmt = $this->dbh->prepare($sql);
+            $stmt->bindParam(
+                ':id', $id, PDO::PARAM_INT
+            );
+            $stmt->execute();
+            $res = $stmt->fetch(PDO::FETCH_ASSOC);
+            if ($res) {
+                $video = new Toolkit_Videos_Video();
+                $video->setId($res['id'])
+                    ->setVideo_url($res['video_url'])
+                    ->setActive($res['active'])
+                    ->setfeatured($res['featured'])
+                    ->setPos($res['pos']);
+                return ($returnArray)
+                    ? $video->getClassAsArray()
+                    : $video;
+            }
+        } catch(PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+    }
+    
+}
diff --git a/Toolkit/Videos/VideosDataGrid.php b/Toolkit/Videos/VideosDataGrid.php
new file mode 100644 (file)
index 0000000..f9c5c93
--- /dev/null
@@ -0,0 +1,300 @@
+<?php
+
+/**
+ * VideosDataGrid.php
+ *
+ * PHP version 5
+ *
+ * @category Toolkit
+ * @package  Videos
+ * @author   Steve Sutton <steve@gaslightmedia.com>
+ * @license     http://www.gaslightmedia.com Gaslightmedia
+ * @link        http://demo.gaslightmedia.com
+ */
+
+/**
+ * Toolkit_Videos_VideosDataGrid
+ *
+ * Handles the listing of the videos using the PEAR::Structures_DataGrid
+ * extended from our class Toolkit_DataGridBuilder
+ *
+ * @category  Toolkit
+ * @package   Videos
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2008 Gaslight media
+ * @license      http://www.gaslightmedia.com Gaslightmedia
+ * @link      http://demo.gaslightmedia.com
+ */
+class Toolkit_Videos_VideosDataGrid
+    extends Toolkit_DataGridBuilder
+{
+
+    /**
+     * Description for protected
+     * @var    string
+     * @access protected
+     */
+       protected $noRecMessage = 'No Videos Found.';
+
+    protected $config;
+
+    /**
+     * Set the config
+     *
+     * @param Config_Container $config
+     */
+    public function setConfig(Config_Container $config)
+    {
+        $this->config = $config;
+    }
+
+    /**
+     * Configure the columns for the datagrid
+     *
+     * @param Config_Container $c PEAR::Config_Container object
+     *
+     * @return void
+     * @access protected
+     */
+       protected function configureColumns()
+       {
+        //  get reference to [conf] section of config file
+        $appHasFeaturedVideos
+            = $this->config->getItem('section', 'conf')
+            ->getItem('directive', 'featuredVideos')
+            ->getContent();
+
+        $this->addColumn(
+            new Structures_DataGrid_Column(
+                       'Function',
+            null,
+            null,
+            array('class' => 'editLink'),
+            null,
+            array(&$this, 'renderEditLink')
+            )
+        );
+
+        $this->addColumn(
+            new Structures_DataGrid_Column(
+            'Active',
+            null,
+            null,
+            null,
+            null,
+            array($this, 'renderActive')
+            )
+        );
+
+        if ($appHasFeaturedVideos) {
+            $this->addColumn(
+                new Structures_DataGrid_Column(
+                'Featured',
+                null,
+                null,
+                null,
+                null,
+                array($this, 'renderFeatured')
+                )
+            );
+        }
+
+        $this->addColumn(
+            new Structures_DataGrid_Column(
+            'Position',
+            null,
+            null,
+            null,
+            null,
+            array($this, 'renderPos')
+            )
+        );
+
+        $this->addColumn(
+            new Structures_DataGrid_Column(
+            'Video Url',
+            'video_url',
+            null
+            )
+        );
+
+        $this->addColumn(
+            new Structures_DataGrid_Column(
+            'Thumbnail',
+            null,
+            null,
+            null,
+            null,
+            array($this, 'renderThumbNail')
+            )
+        );
+       }
+
+    /**
+     * Get the max position for the videos for generating a drop down for
+     * reordering them in the datagrid
+     *
+     * @staticvar type $maxPos
+     * @return int
+     */
+    public function getMaxPos()
+    {
+        static $maxPos;
+        if (!$maxPos) {
+            $dbh = Toolkit_Database::getInstance();
+            $sql = "
+            SELECT count(id) as maxpos
+              FROM videos";
+            $ret = $dbh->query($sql);
+            $row = $ret->fetch(PDO::FETCH_ASSOC);
+            if ((int)$row['maxpos'] == 0) {
+                $maxPos = 1;
+            } else {
+                $maxPos = $row['maxpos'];
+            }
+        }
+        return (int)$maxPos;
+    }
+
+    /**
+     * Render the edit link for a category
+     *
+     * @param array $data DB record
+     *
+     * @return mixed Link to edit a category
+     * @access public
+     */
+       public function renderEditLink($data)
+       {
+               extract($data['record']);
+               $link = '<a href="%sadmin/videos.php?page=editVideo&amp;module=editVideo&amp;id=%s">Edit</a>';
+               return sprintf($link, MEDIA_BASE_URL, $id);
+       }
+
+    /**
+     * Handles rendering the pos column
+     *
+     * @param type $data Data array
+     *
+     * @return string
+     */
+    public function renderPos($data)
+    {
+        extract($data['record']);
+        $html = '<select name="position" rel="'.$id.'" class="posSelect">';
+        $maxPos = $this->getMaxPos();
+        for ($i = 1; $i <= $maxPos; ++$i) {
+            $html .= '<option value="'.$i.'"';
+            if ($pos == $i) {
+                $html .= ' selected="selected"';
+            }
+            $html .= '>'.$i.'</option>';
+        }
+        $html .= '</select>';
+        return $html;
+    }
+
+    /**
+     * Handles rendering the thumbnail column
+     *
+     * @param type $data Data array
+     *
+     * @return string
+     */
+    public function renderThumbNail($data)
+    {
+        extract($data['record']);
+        $pattern = '/v=(.*)/';
+        $pattern2 = '/youtu.be\/(.*)/';
+        if (preg_match($pattern, $video_url, $match)) {
+            $vidCode = $match[1];
+        } else if (preg_match($pattern2, $video_url, $match)) {
+            $vidCode = $match[1];
+        }
+        $thumb = ($vidCode)
+            ? '<a href="http://www.youtube.com/embed/'.$vidCode.'?rel=0" class="various fancybox.iframe vidimg"><img src="http://img.youtube.com/vi/'.$vidCode.'/default.jpg"></a>'
+            : '';
+        return $thumb;
+    }
+
+    /**
+     * Handles rendering the active column
+     *
+     * @param type $data Data array
+     *
+     * @return string
+     */
+    public function renderActive($data)
+    {
+        $active = $data['record']['active'];
+        $link   = MEDIA_BASE_URL . 'video-active-toggle/'.$data['record']['id'].'/';
+        $ball
+            = ($active)
+            ? 'grnball.gif'
+            : 'redball.gif';
+        $html = sprintf(
+            '<a href="%s"><img border="0" src="%sToolkit/Toolbox/assets/%s"></a>',
+            $link,
+            MEDIA_BASE_URL,
+            $ball
+        );
+        return $html;
+    }
+
+    /**
+     * Handles rendering the featured column
+     *
+     * @param type $data Data array
+     *
+     * @return type
+     */
+    public function renderFeatured($data)
+    {
+        $featured = $data['record']['featured'];
+        $link   = MEDIA_BASE_URL . 'video-featured-toggle/'.$data['record']['id'].'/';
+        $ball
+            = ($featured)
+            ? 'grnball.gif'
+            : 'redball.gif';
+        $html = sprintf(
+            '<a href="%s"><img border="0" src="%sToolkit/Toolbox/assets/%s"></a>',
+            $link,
+            MEDIA_BASE_URL,
+            $ball
+        );
+        return $html;
+    }
+
+    /**
+     * Handles setting the query for the datagrid
+     *
+     * @return void
+     */
+       public function setQuery()
+       {
+               $sql = "
+                       SELECT *
+              FROM videos";
+
+               parent::setQuery($sql);
+       }
+
+    /**
+     * Handles creation of the HTML for the datagrid
+     *
+     * @return string
+     */
+       public function toHtml()
+       {
+               $GLOBALS['styleSheets'][]
+            = MEDIA_BASE_URL . 'Toolkit/Videos/styles.css';
+        $GLOBALS['styleSheets'][]
+            = MEDIA_BASE_URL . 'fancybox/jquery.fancybox.css';
+        $GLOBALS['topScripts'][]
+            = MEDIA_BASE_URL . 'fancybox/jquery.fancybox.js';
+        $GLOBALS['bottomScripts'][]
+            = MEDIA_BASE_URL . 'Toolkit/Videos/libjs/videos.js';
+               return parent::toHTML();
+       }
+
+}
diff --git a/Toolkit/Videos/WebDecorator.php b/Toolkit/Videos/WebDecorator.php
new file mode 100644 (file)
index 0000000..c22d952
--- /dev/null
@@ -0,0 +1,102 @@
+<?php
+
+/**
+ * WebDecorator.php
+ *
+ * PHP version 5
+ *
+ * @category Toolkit
+ * @package  Videos
+ * @author   Steve Sutton <steve@gaslightmedia.com>
+ * @license  http://www.gaslightmedia.com Gaslightmedia
+ * @link     http://demo.gaslightmedia.com
+ */
+
+/**
+ * Toolkit_Videos_WebDecorator
+ *
+ * Handles the listing of the videos using the PEAR::Structures_DataGrid
+ * extended from our class Toolkit_DataGridBuilder
+ *
+ * @category  Toolkit
+ * @package   Videos
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2008 Gaslight media
+ * @license   http://www.gaslightmedia.com Gaslightmedia
+ * @link      http://demo.gaslightmedia.com
+ */
+class Toolkit_Videos_WebDecorator
+    implements Toolkit_Videos_IDecorator
+{
+
+    private $_flexyOptions = array();
+    private $_template = 'webDecorator.html';
+    
+    /**
+     * Class Constructor
+     *
+     * @return void
+     */
+    public function __construct()
+    {
+        $this->setFlexyOptions();
+    }
+
+    /**
+     * Handles setting the flexyOptions
+     *
+     * @return void
+     */
+    public function setFlexyOptions()
+    {
+        $this->_flexyOptions = $GLOBALS['flexyOptions'];
+        $this->_flexyOptions['templateDir']
+            = BASE . 'Toolkit/Videos/templates';
+        $this->_flexyOptions['compileDir']
+            = BASE . 'Toolkit/Videos/templates/compiled';
+    }
+
+    /**
+     * Handles HTML output
+     *
+     * @param PDO  $dbh     Database Connection
+     * @param type $gateway Gateway object
+     *
+     * @return boolean
+     */
+    public function toHtml(
+        PDO $dbh,
+        $gateway = null
+    ) {
+        try {
+            $sql = "
+              SELECT id
+                FROM videos
+               WHERE featured = true
+            ORDER BY pos
+               LIMIT 1
+              OFFSET 0";
+            $row = $dbh->query($sql)->fetch(PDO::FETCH_ASSOC);
+            if ($row) {
+                $tpl  = new HTML_Template_Flexy($this->_flexyOptions);
+                $page = new stdClass;
+                $page->videoUrl = Toolkit_Template_Page::getSeoUrl($gateway, 37);
+                $videoMapper
+                    = new Toolkit_Videos_VideoMapper($dbh);
+                $video = $videoMapper->getVideoById($row['id'], false);
+                if ($video) {
+                    $page->vCode = $video->getVideoCode();
+                    $page->title = $video->getVideoTitle();
+                }
+
+                $tpl->compile($this->_template);
+                return $tpl->bufferedOutputObject($page);
+            } else {
+                return false;
+            }
+        } catch(PDOException $e) {
+            Toolkit_Common::handle_error($e);
+        }
+    }
+
+}
diff --git a/Toolkit/Videos/WebPageDecorator.php b/Toolkit/Videos/WebPageDecorator.php
new file mode 100644 (file)
index 0000000..3a9cd6c
--- /dev/null
@@ -0,0 +1,98 @@
+<?php
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+/**
+ * WebPageDecorator
+ *
+ * PHP version 5
+ *
+ * @category  Category
+ * @package   Videos
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2009 Gaslight Media
+ * @license   Gaslight Media
+ * @version   CVS: $Id: Display.php,v 1.10 2010/07/04 23:55:12 jamie Exp $
+ * @link      <>
+ */
+
+/**
+ * WebPageDecorator
+ *
+ * Description of class
+ *
+ * @category  Category
+ * @package   Videos
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2009 Gaslight Media
+ * @license   Gaslight Media
+ * @link      <>
+ */
+class Toolkit_Videos_WebPageDecorator implements Toolkit_Videos_IDecorator
+{
+    private $_flexyOptions = array();
+    private $_template = 'webPageDecorator.html';
+    private $_perRow   = 5;
+
+    public function __construct()
+    {
+        $this->setFlexyOptions();
+    }
+
+    public function setFlexyOptions()
+    {
+        $this->_flexyOptions = $GLOBALS['flexyOptions'];
+        $this->_flexyOptions['templateDir']
+            = BASE . 'Toolkit/Videos/templates';
+        $this->_flexyOptions['compileDir']
+            = BASE . 'Toolkit/Videos/templates/compiled';
+    }
+
+    public function toHtml(
+        PDO $dbh,
+        $gateway = null
+    ) {
+        $GLOBALS['bottomScripts'][]
+            = MEDIA_BASE_URL . 'fancybox/jquery.fancybox.js';
+        $GLOBALS['bottomScripts'][]
+            = MEDIA_BASE_URL . 'Toolkit/Videos/libjs/videos.js';
+        $dbh = Toolkit_Database::getInstance();
+        $sql = "
+          SELECT id
+            FROM videos
+           WHERE active = true
+        ORDER BY pos";
+        $stmt = $dbh->query($sql);
+        $page = new stdClass;
+        $page->videos = array();
+        while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
+            $videoMapper
+                = new Toolkit_Videos_VideoMapper($dbh);
+            $video = $videoMapper->getVideoById($row['id'], false);
+            if ($video) {
+                $page->videos[] = array(
+                    'title' => $video->getVideoTitle(),
+                    'code'  => $video->getVideoCode()
+                );
+            }
+        }
+        $count = 1;
+        $trueCount = 1;
+        $num = count($page->videos);
+        foreach ($page->videos as &$row) {
+            $row['firstInRow'] = ($count == 1);
+            if ($count == $this->_perRow || $trueCount == $num) {
+                $row['lastInRow'] = true;
+                $count = 0;
+            } else {
+                $row['lastInRow'] = false;
+            }
+            ++$count;
+            ++$trueCount;
+        }
+        $tpl  = new HTML_Template_Flexy($this->_flexyOptions);
+        $tpl->compile($this->_template);
+        return $out  . $tpl->bufferedOutputObject($page);
+    }
+}
diff --git a/Toolkit/Videos/config.ini b/Toolkit/Videos/config.ini
new file mode 100644 (file)
index 0000000..ced83b7
--- /dev/null
@@ -0,0 +1,7 @@
+[conf]
+applicationName = "Videos"
+featuredVideos = Off
+
+[listing type]
+singular = "Video"
+plural = "Videos"
diff --git a/Toolkit/Videos/libjs/videos.js b/Toolkit/Videos/libjs/videos.js
new file mode 100644 (file)
index 0000000..600df7f
--- /dev/null
@@ -0,0 +1,29 @@
+var GLMColorBox = {
+    init: function() {
+        var width = $(window).width() * .85;
+        var height = $(window).height() * .8;
+        $('select.posSelect').each(function() {
+            $(this).change(function() {
+                var id = $(this).attr('rel');
+                var newpos = $(this).prop('value');
+                window.location.href = '../video-move/' + id + '/' + newpos + '/';
+            });
+        });
+    }
+};
+
+$(document).ready(GLMColorBox.init);
+
+$(document).ready(function() {
+    $(".various").fancybox({
+        maxWidth: 1200,
+        maxHeight: 800,
+        fitToView: false,
+        width: '100%',
+        height: '100%',
+        autoSize: false,
+        closeClick: false,
+        openEffect: 'none',
+        closeEffect: 'none'
+    });
+});
diff --git a/Toolkit/Videos/moveVideo.php b/Toolkit/Videos/moveVideo.php
new file mode 100644 (file)
index 0000000..74e4f49
--- /dev/null
@@ -0,0 +1,81 @@
+<?php
+
+/**
+ * VideosDataGrid.php
+ *
+ * Handles moving the video positions
+ *
+ * PHP version 5
+ *
+ * @category Toolkit
+ * @package  Videos
+ * @author   Steve Sutton <steve@gaslightmedia.com>
+ * @license  http://www.gaslightmedia.com Gaslightmedia
+ * @link     http://demo.gaslightmedia.com
+ */
+if (!isset($_GET['kpass'])) {
+       die();
+}
+require_once '../../setup.phtml';
+
+$dbh    = Toolkit_Database::getInstance();
+$newpos = $_REQUEST['newpos'];
+$id     = $_REQUEST['id'];
+$error  = false;
+try {
+    $dbh->beginTransaction();
+    $sql = "
+    SELECT pos
+      FROM videos
+     WHERE id = :id";
+    $stmt = $dbh->prepare($sql);
+    $stmt->bindParam(
+        ":id", $_REQUEST['id'], PDO::PARAM_INT
+    );
+    $stmt->execute();
+    $oldpos = $stmt->fetchColumn();
+    if ($oldpos < $newpos) {
+       $sql = "
+        UPDATE videos
+           SET pos = pos - 1
+         WHERE pos BETWEEN :oldpos AND :newpos";
+        $updateStmt = $dbh->prepare($sql);
+    } else if ($oldpos > $newpos) {
+        $sql = "
+        UPDATE videos
+           SET pos = pos + 1
+         WHERE pos BETWEEN :newpos AND :oldpos";
+        $updateStmt = $dbh->prepare($sql);
+    } else {
+        $error = true;
+    }
+
+    if (!$error) {
+        $updateStmt->bindParam(
+            ':newpos', $newpos, PDO::PARAM_INT
+        );
+        $updateStmt->bindParam(
+            ':oldpos', $oldpos, PDO::PARAM_INT
+        );
+        $updateStmt->execute();
+        // now change the real video to its position
+        $sql = "
+        UPDATE videos
+           SET pos = :pos
+         WHERE id = :id";
+        $stmt = $dbh->prepare($sql);
+        $stmt->bindParam(
+            ':pos', $newpos, PDO::PARAM_INT
+        );
+        $stmt->bindParam(
+            ':id', $id, PDO::PARAM_INT
+        );
+        $stmt->execute();
+    }
+    $dbh->commit();
+} catch (PDOException $e) {
+       $dbh->rollback();
+    Toolkit_Logger::logException('DB Error', $e);
+}
+
+header('Location: '.MEDIA_BASE_URL.'admin/videos.php');
diff --git a/Toolkit/Videos/styles.css b/Toolkit/Videos/styles.css
new file mode 100644 (file)
index 0000000..cc82e07
--- /dev/null
@@ -0,0 +1,154 @@
+#form-warning-top {
+       color: #FF0000;
+       font-size: 14px;
+       font-weight: bold;
+       margin-bottom: 0.5em;
+       margin-top: 1em;
+}
+.required, .req {
+       color: #FF0000;
+}
+.group {
+       display: -moz-inline-box;
+       width: 100%;
+}
+.group td {
+       width: 324px;
+}
+.requiredNote {
+       text-align: center;
+}
+#contact {
+       margin: 10px;
+}
+#contact table {
+       background-color: #FFFFFF;
+       border: 1px solid #EEEEEE;
+       border-collapse: collapse;
+}
+#contact td {
+       border: 1px solid #EEEEEE;
+       border-collapse: collapse;
+       color: #000000;
+       font-family: arial, helvetica, sans-serif;
+       padding: 3px;
+       font-size: 12px;
+}
+.labelcell {
+       background-color: transparent;
+       padding-right: 10px;
+       padding-top: 3px;
+       text-align: right;
+       white-space: nowrap;
+       width: 140px;
+}
+.fieldcell {
+       padding-left: 4px;
+       width: 320px;
+}
+.fieldcell .text {
+       width: 90%;
+}
+#contact table.group {
+       font-size: 10px;
+       border: none;
+       padding-top: 4px;
+}
+#contact table.group td {
+       border: none;
+}
+#contact .hdr {
+       background-color: #999999;
+       border: 1px solid #666666;
+       font-weight: bold;
+}
+.paging {
+       text-align: center;
+       background-color: #F6F6F6;
+       border-color: #E86a10;
+       border-color: #296DC0;
+       border-style: solid;
+       border-width: 1px 0;
+       margin: 1.0em 0;
+       padding: 8px 0;
+       text-align: center;
+       width: 100%;
+       font-size: 12px;
+       
+}
+.paging b {
+       border: 1px solid #b22c2c;
+       border: 1px solid #E86A10;
+       background: #FFF;
+       padding: 5px 7px;
+       margin: 0 5px;
+}
+.paging a {
+       background: #FFF;
+       border: 1px solid #CCC;
+       padding: 5px 7px;
+       text-decoration: none;
+       font-family: helvetica, times;
+       color: #000;
+       margin: 0 5px;
+}
+.paging a:hover {
+       border: 1px solid #999;
+       border: 1px solid #b22c2c;
+       border: 1px solid #E86A10;
+}
+#dataGrid {
+       margin: 10px auto;
+       border: 1px solid #296DC0;
+       width: 100%;
+       border-collapse: collapse;
+}
+#dataGrid thead th {
+       background: #296DC0;
+       border: 1px solid #1b4880;
+       color: #000;
+       font-weight: normal;
+}
+#dataGrid th a {
+       font-weight: bolder;
+       color: #000;
+       text-decoration: none;
+}
+#dataGrid th a:hover {
+       color: #E86A10;
+       text-decoration: underline;
+}
+#dataGrid tr {
+       border: 1px solid #296DC0;
+       border-collapse: collapse;
+}
+#dataGrid tbody tr td {
+       padding: 5px;
+}
+#dataGrid .editLink, #dataGrid .delLink,
+#dataGrid .mailLink, #dataGrid .dateLink,
+#dataGrid .posLink {
+       text-align: center;
+}
+img.status {
+       border: none;
+}
+.even {
+       background-color: #D9D9D9;
+}
+#gridSorter {
+       margin: 0 auto;
+       padding: 10px;
+       text-align: center;
+       border: 1px solid #296DC0;
+}
+#gridSorter table {
+       border: none;
+}
+#gridSorter td {
+       border: none;
+}
+.fieldcell textarea {
+       width: 90%;
+       height: 70px;
+}
diff --git a/Toolkit/Videos/templates/webDecorator.html b/Toolkit/Videos/templates/webDecorator.html
new file mode 100644 (file)
index 0000000..4e51e6b
--- /dev/null
@@ -0,0 +1,6 @@
+<div id="v" class="hBox">
+            <a href="http://www.youtube.com/embed/{vCode}?rel=0&hd=1" class="various fancybox.iframe vidimg">
+            <img src="<?php echo MEDIA_BASE_URL;?>assets/video_image.png">
+            <span class="vidoverlay"></span>
+       </a>
+</div>
diff --git a/Toolkit/Videos/templates/webPageDecorator.html b/Toolkit/Videos/templates/webPageDecorator.html
new file mode 100644 (file)
index 0000000..9f8b142
--- /dev/null
@@ -0,0 +1,12 @@
+<ul id="videoGallery" class="small-block-grid-1 medium-block-grid-3 large-block-grid-4">
+    {foreach:videos,v} 
+        <li class="vThumb">
+            <div class="imgBorderMe">
+                <a href="http://www.youtube.com/embed/{v[code]}?rel=0&hd=1" class="various fancybox.iframe vidimg">
+                    <img src="http://img.youtube.com/vi/{v[code]}/default.jpg">
+                <span class="vTitle">{v[title]:h}<span class="play"></span></span>
+                </a>
+            </div>
+        </li>
+    {end:}
+</ul>
\ No newline at end of file
diff --git a/Toolkit/Videos/toggleActive.php b/Toolkit/Videos/toggleActive.php
new file mode 100644 (file)
index 0000000..1b2eec6
--- /dev/null
@@ -0,0 +1,65 @@
+<?php
+
+/**
+ * VideosDataGrid.php
+ *
+ * Handles switching the active flag for the video
+ *
+ * PHP version 5
+ *
+ * @category Toolkit
+ * @package  Videos
+ * @author   Steve Sutton <steve@gaslightmedia.com>
+ * @license  http://www.gaslightmedia.com Gaslightmedia
+ * @link     http://demo.gaslightmedia.com
+ */
+if (!isset($_GET['kpass'])) {
+       die();
+}
+require_once '../../setup.phtml';
+
+$dbh    = Toolkit_Database::getInstance();
+$id     = $_REQUEST['id'];
+
+try {
+    $dbh->beginTransaction();
+    $sql = "
+    SELECT active
+      FROM videos
+     WHERE id = :id";
+    $stmt = $dbh->prepare($sql);
+    $stmt->bindParam(
+        ":id",
+        $_REQUEST['id'],
+        PDO::PARAM_INT
+    );
+    $stmt->execute();
+    $activeState = $stmt->fetchColumn();
+    
+    if ($activeState) {
+       $sql = "
+        UPDATE videos
+           SET active = false
+         WHERE id = :id";
+        $updateStmt = $dbh->prepare($sql);
+    } else {
+        $sql = "
+        UPDATE videos
+           SET active = true
+         WHERE id = :id";
+        $updateStmt = $dbh->prepare($sql);
+    }
+
+    $updateStmt->bindParam(
+        ':id',
+        $id,
+        PDO::PARAM_INT
+    );
+    $updateStmt->execute();
+    $dbh->commit();
+} catch (PDOException $e) {
+       $dbh->rollback();
+    Toolkit_Logger::logException('DB Error', $e);
+}
+
+header('Location: '.MEDIA_BASE_URL.'admin/videos.php');
diff --git a/Toolkit/Videos/toggleFeatured.php b/Toolkit/Videos/toggleFeatured.php
new file mode 100644 (file)
index 0000000..53b6294
--- /dev/null
@@ -0,0 +1,65 @@
+<?php
+
+/**
+ * VideosDataGrid.php
+ *
+ * Handles switching the featured flag for the video
+ *
+ * PHP version 5
+ *
+ * @category Toolkit
+ * @package  Videos
+ * @author   Steve Sutton <steve@gaslightmedia.com>
+ * @license  http://www.gaslightmedia.com Gaslightmedia
+ * @link     http://demo.gaslightmedia.com
+ */
+if (!isset($_GET['kpass'])) {
+       die();
+}
+require_once '../../setup.phtml';
+
+$dbh    = Toolkit_Database::getInstance();
+$id     = $_REQUEST['id'];
+
+try {
+    $dbh->beginTransaction();
+    $sql = "
+    SELECT featured
+      FROM videos
+     WHERE id = :id";
+    $stmt = $dbh->prepare($sql);
+    $stmt->bindParam(
+        ":id",
+        $_REQUEST['id'],
+        PDO::PARAM_INT
+    );
+    $stmt->execute();
+    $featuredState = $stmt->fetchColumn();
+    
+    if ($featuredState) {
+       $sql = "
+        UPDATE videos
+           SET featured = false
+         WHERE id = :id";
+        $updateStmt = $dbh->prepare($sql);
+    } else {
+        $sql = "
+        UPDATE videos
+           SET featured = true
+         WHERE id = :id";
+        $updateStmt = $dbh->prepare($sql);
+    }
+
+    $updateStmt->bindParam(
+        ':id',
+        $id,
+        PDO::PARAM_INT
+    );
+    $updateStmt->execute();
+    $dbh->commit();
+} catch (PDOException $e) {
+       $dbh->rollback();
+    Toolkit_Logger::logException('DB Error', $e);
+}
+
+header('Location: '.MEDIA_BASE_URL.'admin/videos.php');
diff --git a/Toolkit/qfcaptcha.php b/Toolkit/qfcaptcha.php
new file mode 100755 (executable)
index 0000000..8b789de
--- /dev/null
@@ -0,0 +1,39 @@
+<?php
+/**
+ * HTML_QuickForm_CAPTCHA example - Image generator
+ *
+ * PHP versions 4 and 5
+ *
+ * @category   HTML
+ * @package    HTML_QuickForm_CAPTCHA
+ * @subpackage Examples
+ * @author     Philippe Jausions <Philippe.Jausions@11abacus.com>
+ * @copyright  2006-2008 by Philippe Jausions / 11abacus
+ * @license    http://www.opensource.org/licenses/bsd-license.php New BSD
+ * @version    CVS: $Id: qfcaptcha.php,v 1.2 2009/10/01 16:00:02 jamie Exp $
+ * @filesource
+ * @link       http://pear.php.net/package/HTML_QuickForm_CAPTCHA
+ * @see        qfcaptcha_form_image.php
+ * @see        qfcaptcha_form_random.php
+ */
+
+// Require the class before opening the session
+// so the instance unserialize properly
+require_once '../setup.phtml';
+require_once 'Text/CAPTCHA.php';
+require_once 'Text/CAPTCHA/Driver/Image.php';
+
+HTTP_Session2::useCookies(false);
+HTTP_Session2::start();
+
+header('Content-Type: image/jpeg');
+
+$sessionVar = (empty($_REQUEST['var']))
+              ? '_HTML_QuickForm_CAPTCHA'
+              : $_REQUEST['var'];
+
+// Force a new CAPTCHA for each one displayed
+$_SESSION[$sessionVar]->setPhrase();
+
+echo $_SESSION[$sessionVar]->getCAPTCHAAsJPEG();
+?>
diff --git a/admin/.htaccess b/admin/.htaccess
new file mode 100644 (file)
index 0000000..38dcd05
--- /dev/null
@@ -0,0 +1 @@
+RewriteEngine Off
diff --git a/admin/CommonEvents/.htaccess b/admin/CommonEvents/.htaccess
new file mode 100644 (file)
index 0000000..7f2df55
--- /dev/null
@@ -0,0 +1,3 @@
+AddDefaultCharset utf-8
+php_flag register_globals off
+php_flag magic_quotes_gpc off
diff --git a/admin/CommonEvents/index.php b/admin/CommonEvents/index.php
new file mode 100644 (file)
index 0000000..36a4a66
--- /dev/null
@@ -0,0 +1,28 @@
+<?php
+try {
+    // look for a setup.phtml file in the directory one up from here
+    if (file_exists('../../setup.phtml')) {
+        include_once '../../setup.phtml';
+    } else {
+        throw new Exception('setup.phtml file not found!');
+    }
+    // set CommonApp define if not set
+    if (!defined('COMMON_APP_BASE')) {
+        define('COMMON_APP_BASE', '/var/www/server/CommonApps/');
+    }
+    // load the event application config files form CommonApps
+    define('COMMON_APP_NAME', 'EventCalendar');
+    define('COMMON_APP_VERSION', 'V1');
+    define('COMMON_APP_INI', 'application.ini');
+    define('COMMON_APP_SITE_INI', 'eventCalendar.ini');
+
+    define('COMMON_APP_CONTROLLER', 'AdminController');
+    $appPath = COMMON_APP_BASE . COMMON_APP_NAME . '/' . COMMON_APP_VERSION . '/';
+    define('COMMON_APP_PATH', $appPath);
+    // now only need to pull in the main file to run the app
+    // pull in admin.php file
+    require COMMON_APP_PATH . 'Bootstrap.php';
+
+} catch(Exception $e) {
+    die($e->getMessage());
+}
diff --git a/admin/Contact/contact_inquiry.phtml b/admin/Contact/contact_inquiry.phtml
new file mode 100755 (executable)
index 0000000..0b89a92
--- /dev/null
@@ -0,0 +1,125 @@
+<?php
+
+/**
+ * contact_inquiry.phtml
+ *
+ * Groups are now added to the contact inquiry types.  Groups is a
+ * required field.
+ *
+ * PHP versions 4 and 5
+ *
+ * @category  Toolkit
+ * @package   Contacts
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2009 Gaslight Media
+ * @license   Gaslight Media
+ * @version   CVS: $Id: contact_inquiry.phtml,v 1.3 2009/07/24 14:27:45 matrix Exp $
+ * @link      http://pear.php.net/package/Contacts
+ * @see       References to other sections (if any)...
+ */
+
+/**
+ * base setup file for all apps
+ */
+require_once '../../setup.phtml';
+
+/**
+ * base setup for contact admin section
+ */
+require_once "contact_setup.inc";
+GLM_TOOLBOX::top('Contact Inquiries', '');
+//  Adjust the nav for this page, add the Edit Groups page.
+if (is_array($nav) && array_key_exists('Contact Inquiry Fields', $nav)) {
+    $tmp = array('Inquiry Groups' => 'list_groups.php');
+    array_insert($nav, 'Contact Inquiry Fields', $tmp);
+}
+GLM_TOOLBOX::html_nav_table($nav, $navWidth);
+$qs = "
+ SELECT id, header, groupid, pos
+   FROM contact_inq
+  ORDER BY groupid, pos;";
+$result = $DB->db_exec($qs);
+$query = "
+    SELECT id, name
+      FROM inq_group
+     ORDER BY name;";
+if ($groupData = $DB->db_auto_get_data($query)) {
+    foreach ($groupData as $groupRow) {
+        $gRow[$groupRow['id']] = $groupRow['name'];
+    }
+}
+?>
+<table id="admin-list-table">
+    <tr>
+      <th colspan="4">
+        <form action="edit_inquiry.phtml" method="POST" name="edit_inquiry">
+            <input type="submit" name="Command" value="Add Item" />
+        </form>
+      </th>
+    </tr>
+    <tr>
+      <th>Options:</th>
+      <th>Pos:</th>
+      <th>Group:</th>
+      <th>Inquiry Name:</th>
+    </tr>
+<?php
+$num = $DB->db_numrows($result);
+if ($num > 0) {
+    for ($i = 0; $i < $num; $i++) {
+        $data = $DB->db_fetch_array($result, $i, PGSQL_ASSOC);
+        if ($data['groupid'] != $oldgroup && isset($oldgroup)) {
+            echo '<tr><td colspan="4">&nbsp;</td></tr>';
+        }
+        ?>
+        <tr>
+            <td nowrap>
+                <a href="edit_inquiry.phtml?id=<?php echo $data['id'];?>&amp;Command=Edit">[Edit / Delete]</a>
+            </td>
+            <td>
+        <?php
+        $qs = "
+         SELECT COUNT(id) AS maxpos
+           FROM contact_inq
+          WHERE groupid = ".$data['groupid'];
+
+        $maxresult = $DB->db_exec($qs);
+        $max_data = $DB->db_fetch_array($maxresult, 0, PGSQL_ASSOC);
+        $maxpos = $max_data['maxpos'];
+        $pos = '<select style="font-size: 10pt;" name="pos"
+            onChange="location.href=this[this.selectedIndex].value;"
+        size="1">';
+        for ($newpos = 1; $newpos <= $maxpos; $newpos++) {
+            $string = 'Command=Move&amp;groupid='.$data['groupid'].'&amp;id='.$data['id']."&amp;newpos=$newpos";
+            $pos .= "<option value=\"update_inquiry.phtml?$string\"";
+            if ($newpos == $data['pos']) {
+                $pos .= ' selected';
+            }
+            $pos .= ">$newpos</option>";
+        }
+        $pos .= '</select>';
+        echo $pos;
+        ?>
+            </td>
+            <td nowrap>
+                <?php echo $gRow[$data['groupid']];?>
+            </td>
+            <td align="left">
+                <?php echo $data['header'];?>
+            </td>
+        </tr>
+        <?php
+        $oldgroup = $data['groupid'];
+    }
+} else {
+    ?>
+    <tr>
+        <th colspan="2">Nothing in the database yet</th>
+    </tr>
+    <?php
+}
+?>
+</table>
+<?php
+GLM_TOOLBOX::footer();
+?>
diff --git a/admin/Contact/contact_setup.inc b/admin/Contact/contact_setup.inc
new file mode 100755 (executable)
index 0000000..29f7502
--- /dev/null
@@ -0,0 +1,475 @@
+<?php
+
+/**
+* contact_setup.phtml
+* 
+* Config setting for the admin contact application 
+* 
+* PHP versions 4 and 5
+* 
+* @category  Toolkit
+* @package   Contacts
+* @author    Steve Sutton <steve@gaslightmedia.com>
+* @copyright 2009 Steve Sutton
+* @license   Gaslight Media
+* @version   CVS: $Id: contact_setup.inc,v 1.7 2010/05/13 17:05:53 matrix Exp $
+* @link      <>
+*/
+extract($_REQUEST);
+// The Contact Type array is now is one location and is used by the 
+// Toolkit_Contacts_Admin_EditContact class
+$conf = new Config;
+$contactRoot =& $conf->parseConfig(
+       BASE . 'Toolkit/Contacts/config.ini',
+       'IniFile'
+);
+if (!defined("ENTRIES_PER_PAGE")) {
+
+    /**
+    * how many per page on list contacts
+    */
+    define("ENTRIES_PER_PAGE", 10);    // Entries per Page in list_contact.phtml
+}
+
+/**
+* define for customer table
+*/
+define("CUSTOMER_TABLE", "customer");    // name of customer table
+
+/**
+* define for contact table
+*/
+define("CONTACT_TABLE", "contact");        // name of contact table
+
+$table = $contactRoot->getItem('section', 'conf')
+    ->getItem('directive', 'table')
+    ->getContent();
+/**
+* Table to user in db
+*/
+define("TABLE", $table);            // which table to use
+
+/**
+* postgres date formate
+*/
+define("DATEFORMAT", "US");                // date format (for edit_contact.phmtl)
+
+/**
+* template for the newsletter
+*/
+define("NEWSLETTER_PROTOTYPE", "newsletter_template.html");                // date format (for edit_contact.phmtl)
+
+/**
+* class_template needed for GLM_TEMPLATE::get_seo_url()
+*/
+require_once BASE.'classes/class_template.inc';
+$DB = new GLM_DB();
+if (!defined("HTML_EMAIL")) {
+
+    /**
+    * HTML_EMAIL = ON or OFF 
+    */
+    define("HTML_EMAIL", "ON");
+}
+if (!defined("PRODUCTION_MODE")) {
+
+    /**
+    * PRODUCTION_MODE off/on  mail sent
+    */
+    define("PRODUCTION_MODE", "ON");
+}
+if (!defined("NEWSLETTER")) {
+
+    /**
+    * NEWSLETTER 1 or true will allow newsletters
+    */
+    define("NEWSLETTER", 1); //bool- does the contact database mail out a newsletter? 
+}
+
+if (!function_exists("template_read")) {
+
+    /**
+    * Short description for function
+    * 
+    * Long description (if any) ...
+    * 
+    * @param unknown $template Parameter description (if any) ...
+    * 
+    * @return string  Return description (if any) ...
+    */
+    function template_read($template)
+    {
+        $fp       = fopen($template, "r");
+        $contents = fread($fp, filesize($template));
+        fclose($fp);
+        if ($contents) {
+            return $contents;
+        } else {
+            return "";
+        }
+    }
+}
+// {{{ array_insert()
+
+
+/**
+* insert an array into another array after position.
+* You can specify an associative array key or index key
+* to dictact the positioning of the new array
+* 
+* @param array   &$array       array to be used
+* @param unknown $position     position to add array
+* @param unknown $insert_array array to be inserted
+* 
+* @return void   
+*/
+function array_insert(&$array, $position, $insert_array)
+{
+    if (!is_int($position)) {
+        $i = 0;
+        foreach ($array as $key => $value) {
+            if ($key == $position) {
+                $position = ++$i;
+                break;
+            }
+            ++$i;
+        }
+    }
+    $first_array = array_splice($array, 0, $position);
+    $array = array_merge($first_array, $insert_array, $array);
+}
+// }}}
+// {{{ explode_template()
+if (!function_exists("explode_template")) {
+
+    /**
+    * Short description for function
+    * 
+    * Long description (if any) ...
+    * 
+    * @param unknown $template Parameter description (if any) ...
+    * @param unknown $data     Parameter description (if any) ...
+    * 
+    * @return unknown Return description (if any) ...
+    */
+    function explode_template($template, $data)
+    {
+        $template = template_read($template);
+        $output   = template_replacement($template, $data);
+        $output   = wordwrap($output, 72);
+        return $output;
+
+    }
+}
+// }}}
+// {{{ template_replacement($template, $fieldarr)
+if (!function_exists("template_replacement")) {
+
+    /**
+    * Short description for function
+    * 
+    * Long description (if any) ...
+    * 
+    * @param unknown $template Parameter description (if any) ...
+    * @param array   $fieldarr Parameter description (if any) ...
+    * 
+    * @return unknown Return description (if any) ...
+    */
+    function template_replacement($template, $fieldarr)
+    {
+        if (is_array($fieldarr)) {
+            foreach ($fieldarr as $key => $value) {
+                $template = str_replace("<!-- ".$key." -->", $value, $template);
+            }
+        }
+        return $template;
+    }
+}
+// }}}
+// {{{ add_image($image, $align)
+if (!function_exists("add_image")) {
+
+    /**
+    * Short description for function
+    * 
+    * Long description (if any) ...
+    * 
+    * @param string $image Parameter description (if any) ...
+    * @param string $align Parameter description (if any) ...
+    * 
+    * @return string Return description (if any) ...
+    */
+    function add_image($image, $align)
+    {
+        if ($image != "") {
+            return '<div style="margin:5px;float:'.$align.';"><img src="'.MIDSIZED.$image.'"></div>';
+        }
+    }
+}
+// }}}
+// {{{ Navigation array
+$nav      = array(
+    "Report Builder"         => "index.phtml",
+    "Add Contact"            => "edit_contact.php",
+    "List Contacts"          => "list_contact.phtml",
+    "Contact Inquiry Fields" => "contact_inquiry.phtml",
+    "HTML Emails"            => "emails.php",
+    "Saved Reports"          => "list_query.phtml",
+);
+// }}}
+$navWidth = 7;
+$query    = "select * from contact_inq order by pos;";
+$cData    = $DB->db_auto_get_data($query);
+if (is_array($cData)) {
+    foreach ($cData as $key => $value) {
+        $int_array[$value['id']] = $value['header'];
+    }
+}
+$contactTypesSetup = 
+    $contactRoot->getItem('section', 'contact_types')
+       ->toArray();
+$cType = $contactTypesSetup['contact_types'];
+// {{{ search_where($name, $search_where)
+
+/**
+* Short description for function
+* 
+* Long description (if any) ...
+* 
+* @param string $name         Parameter description (if any) ...
+* @param string $search_where Parameter description (if any) ...
+* 
+* @return string Return description (if any) ...
+*/
+function search_where($name, $search_where)
+{
+    $out = '
+    <select name="'.$name.'">
+        <option value="1" '.( ( !isset( $search_type ) || $search_type == "0" ) ? 'selected' : '' ).'>Anywhere 
+        <option value="2" '.( ( $search_where == '1' ) ? 'selected' : '' ).'>Begining 
+        <option value="3" '.( ( $search_where == '2' ) ? 'selected' : '' ).'>Ending
+    </select>';
+    return $out;
+}
+// }}}
+// {{{ search_type($name, $search_type)
+
+/**
+* Short description for function
+* 
+* Long description (if any) ...
+* 
+* @param string $name        Parameter description (if any) ...
+* @param string $search_type Parameter description (if any) ...
+* 
+* @return string Return description (if any) ...
+*/
+function search_type($name, $search_type)
+{
+    $out = '
+    <select name="'.$name.'">
+        <option value="2" '.( ( !isset( $search_type ) || $search_type == '2' ) ? 'selected' : '' ).'>And 
+        <option value="3" '.( ( $search_type == '3' ) ? 'selected' : '' ).'>Or
+        <option value="4" '.( ( $search_type == '4' ) ? 'selected' : '' ).'>Not
+    </select>';
+    return $out;
+}
+// }}}
+// {{{ search_bool($name, $search_type)
+
+/**
+* Short description for function
+* 
+* Long description (if any) ...
+* 
+* @param string $name        Parameter description (if any) ...
+* @param string $search_type Parameter description (if any) ...
+* 
+* @return string Return description (if any) ...
+*/
+function search_bool($name, $search_type)
+{
+    $out = '
+    <select name="'.$name.'">
+        <option value="n" '.( !isset( $search_type ) ? 'selected' : '' ).'>Don\'t Care 
+        <option value="1" '.( ( $search_type == '1' ) ? 'selected' : '' ).'>Yes
+        <option value="0" '.( ( $search_type == '0' ) ? 'selected' : '' ).'>No
+    </select>';
+    return( $out );
+}
+// }}}
+// {{{ interest($field)
+
+/**
+* Short description for function
+* 
+* Long description (if any) ...
+* 
+* @param unknown $field Parameter description (if any) ...
+* 
+* @return boolean Return description (if any) ...
+*/
+function interest($field)
+{
+    if (!is_array($GLOBALS['int_array'])) {
+        return false;
+    }
+    echo "<table><tr>";
+    $count = 0;
+    foreach ($GLOBALS['int_array'] as $key => $value) {
+        if ($count==0) {
+            echo "<td>";
+        }
+        echo "<input type=\"checkbox\" name=\"interest[]\" value=\"$key\"";
+        if (strstr($field, ":".$key.":")) {
+            echo " checked";
+        }
+        echo ">$value<br>";
+        if ($count==5) {
+            echo "</td><td>";
+        }
+        if ($count==11) {
+            echo "</td>";
+        }
+        $count++;
+    }
+    echo "</tr></table>";
+}
+/**
+* Get a group of select elements to represent a date
+*
+* @param string $M Name attribute of the month select list
+* @param string $D Name attribute of the day select list
+* @param string $Y Name attribute of the year select list
+* @param string $m Selected value of the month select list
+* @param string $d Selected value of the day select list
+* @param string $y Selected value of the year select list
+*
+* @return string Html string of group select lists
+* @access public
+*/
+function dateSelector($M, $D, $Y, array $m = null, array $d = null, array $y = null)
+{
+    //  Get a Select element
+    $month = new HTML_Select($M);
+    //  Get a Range of months jan-dec
+    $monthArray = array_flip(range(1, 12));
+    //  Make the keys and values the same
+    foreach ($monthArray as $i => &$j) {
+        $j = $i;
+    }
+    $month->loadArray($monthArray);
+    if (is_null($m)) {
+        $month->setSelectedValues(array(date('m')));
+    } else {
+        $month->setSelectedValues($m);
+    }
+
+    //  Get a Select element
+    $day = new HTML_Select($D);
+    //  Get a Range of months jan-dec
+    $dayArray = array_flip(range(1, 31));
+    //  Make the keys and values the same
+    foreach ($dayArray as $i => &$j) {
+        $j = $i;
+    }
+    $day->loadArray($dayArray);
+    if (is_null($d)) {
+        $day->setSelectedValues(array(date('d')));
+    } else {
+        $day->setSelectedValues($d);
+    }
+
+    //  Get a Select element
+    $year = new HTML_Select($Y);
+    //  Get a Range of months jan-dec
+    $yearArray = array_flip(range(CONTACTS_FIRST_YEAR, date('Y')));
+    //  Make the keys and values the same
+    foreach ($yearArray as $i => &$j) {
+        $j = $i;
+    }
+    $year->loadArray($yearArray);
+    if (is_null($y)) {
+        $year->setSelectedValues(array(date('Y')));
+    } else {
+        $year->setSelectedValues($y);
+    }
+
+    return $month->toHtml() . $day->toHtml() . $year->toHtml();
+}
+
+// }}}
+// default query on create_date
+$c_date_from  = GLM_TOOLBOX::contact_date_entry("", "", "", "fc_month", "fc_day", "fc_year");
+$c_date_to    = GLM_TOOLBOX::contact_date_entry("", "", "", "tc_month", "tc_day", "tc_year");
+
+// The Following $DB_fields array is no longer used for the edit contact page
+// You must alter the class Toolkit_Contacts_Admin_EditContact
+// The following is only used for the search form and the listing pages
+
+$primaryKey = $contactRoot->getItem('section', 'conf')
+    ->getItem('directive', 'primarykey')
+    ->getContent();
+/**
+* Description for define
+*/
+define("ID", $primaryKey);
+
+/**
+* Description for define
+*/
+define("MAILOK", "mail_ok");
+
+$sequence = $contactRoot->getItem('section', 'conf')
+    ->getItem('directive', 'sequence')
+    ->getContent();
+/**
+* Description for define
+*/
+define("SEQUENCE", $sequence);
+
+/**
+* Description for define
+*/
+define("WHERE", ID." IS NOT NULL");
+// $DB_fields are used for edit and updating contacts
+$DB_fields[] = array("name" => "id",            "title" => "id",            "type" => "hide");
+$DB_fields[] = array("name" => "create_date",   "title" => "Create Date",   "type" => "static");
+$DB_fields[] = array("name" => "fname",         "title" => "First Name",    "type" => "text");
+$DB_fields[] = array("name" => "lname",         "title" => "Last Name",     "type" => "text");
+if (TABLE == 'customer') {
+    $DB_fields[] = array("name" => "add1",       "title" => "Address",       "type" => "text");
+    $DB_fields[] = array("name" => "add2",      "title" => "Address 2",     "type" => "text");
+} else {
+    $DB_fields[] = array("name" => "company",       "title" => "Company Name",  "type" => "text");
+    $DB_fields[] = array("name" => "address",       "title" => "Address",       "type" => "text");
+    $DB_fields[] = array("name" => "address2",      "title" => "Address 2",     "type" => "text");
+}
+$DB_fields[] = array("name" => "city",          "title" => "City",          "type" => "text");
+$DB_fields[] = array("name" => "state",         "title" => "State",         "type" => "state");
+$DB_fields[] = array("name" => "zip",           "title" => "Zip",           "type" => "text");
+$DB_fields[] = array("name" => "phone",         "title" => "Phone",         "type" => "text");
+$DB_fields[] = array("name" => "fax",           "title" => "Fax",           "type" => "text");
+$DB_fields[] = array("name" => "email",         "title" => "Email",         "type" => "text");
+$DB_fields[] = array("name" => "mail_ok",       "title" => "Mail Ok?",      "type" => "radio");
+$DB_fields[] = array("name" => "interest",      "title" => "Interest",      "type" => "interest");
+$DB_fields[] = array("name" => "contact_type", "title" => "Contact Type", "type" => "drop", "drop" => $cType);
+
+// $fields are used for building the query page
+foreach ($DB_fields as $key=>$value) {
+    if ($value['type'] == "text" || $value['type'] == "state") {
+        $fields[$value['name']] = $value['title'];
+    } elseif ( $value['type'] == "radio") {
+        $boolean[$value['name']] = $value['title'];
+    } elseif ( $value['type'] == "drop") {
+        $dropdowns[$value['name']] = array('title' => $value['title'], 'drop' => $value['drop']);
+    }
+}
+
+$data['bailout'] .= "You are receiving this message because you have expressed an interest in ";
+$data['bailout'] .= "receiving specials and information from ".SITENAME.". If you do not ";
+$data['bailout'] .= "wish to receive future items of this nature, please reply to this e-mail ";
+$data['bailout'] .= "with the word \"CANCEL\" on the subject line. You will then be removed ";
+$data['bailout'] .= "from future mailings.<br>";
+$data['bailout'] .= "<a href=\"mailto:".OWNER_EMAIL."?subject=CANCEL\">".OWNER_EMAIL."</a><br>";
+?>
diff --git a/admin/Contact/del_query.phtml b/admin/Contact/del_query.phtml
new file mode 100755 (executable)
index 0000000..0ad9363
--- /dev/null
@@ -0,0 +1,20 @@
+<?php
+//$Id: del_query.phtml,v 1.2 2009/05/20 20:27:27 matrix Exp $
+include "../../setup.phtml";
+include "contact_setup.inc";
+
+$qs = "DELETE
+       FROM     query_db
+       WHERE    id = $id";
+
+if(!$DB->db_auto_exec($qs)) {
+    GLM_TOOLBOX::html_error(DB_ERROR_MSG.$qs,1);
+}
+GLM_TOOLBOX::html_header("Admin","Deleted","");
+?>
+<script lang="javascript">
+document.onload=window.opener.location.reload(1);
+</script>
+Query <?php echo $id?> is Deleted
+<center><a href="" onClick="window.close();return(false);">Close This
+Window</a></center>
diff --git a/admin/Contact/download.phtml b/admin/Contact/download.phtml
new file mode 100755 (executable)
index 0000000..c60bd88
--- /dev/null
@@ -0,0 +1,228 @@
+<?php
+
+/**
+ * download.phtml
+ *
+ * gives admin user ability to download a csv file for import into
+ * another source.  Forces the save as dialog box.
+ * tested on ie6 ie7 and firefox.  $query_string is passed through to
+ * this page by a form.
+ *
+ * PHP versions 4 and 5
+ *
+ * @category  Toolkit
+ * @package   PackageName
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2009 Gaslight Media
+ * @license   Gaslight Media
+ * @version   CVS: $Id: download.phtml,v 1.3 2009/08/05 15:15:00 matrix Exp $
+ * @link      <>
+ */
+
+/**
+ * main site setup config file
+ */
+require_once "../../setup.phtml";
+
+/**
+ * main contact setup file
+ */
+require_once "contact_setup.inc";
+
+// check that the site sending this request is the same
+// as the base_url
+$base = str_replace("admin/Contact/list_contact.phtml",
+    "",
+    $_SERVER['HTTP_REFERER']
+);
+if ($base != MEDIA_BASE_URL) {
+    // redirect them
+    header("Location: index.phtml");
+}
+
+$delimiter = str_replace("comma", ",", $delimiter);
+$delimiter = str_replace("tab", "\t", $delimiter);
+$delimiter = str_replace("csv", ",", $delimiter);
+$delimiter = str_replace("pipe", "|", $delimiter);
+
+if ($_REQUEST['query_string']) {
+    /* Remove the old reports if they exsists */
+    if (is_file("report.csv")) {
+        unlink("report.csv");
+    }
+    if (is_file("report.tar.gz")) {
+        unlink("report.tar.gz");
+    }
+    if (is_file("report.zip")) {
+        unlink("report.zip");
+    }
+
+    if (!$fp = fopen("report.csv", "w")) {
+        html_error("Cant open report", 0);
+    }
+    $query_string = stripslashes($_REQUEST['query_string']);
+    $query_string = str_replace("SELECT " . ID . ",", "SELECT ", $query_string);
+    $query_string = str_replace(" FROM ",
+        ",contact_type,interest FROM ",
+        $query_string);
+
+    $dbh = Toolkit_Database::getInstance();
+
+    $res = $dbh->prepare($query_string);
+    $res->execute();
+    $total = $res->rowCount();
+    $headers =array();
+    $headerString = '';
+    if ($total > 0) {
+        for ($i = 0; $i < $total; ++$i) {
+            $result_string = "";
+            $row           = $res->fetch(PDO::FETCH_ASSOC);
+            $cConTypes     = array();
+            foreach ($row as $fieldName => $value) {
+                if ($i == 0) {
+                    if (in_array($fieldName, array('contact_type', 'interest'))) {
+                        switch ($fieldName) {
+                        case 'contact_type' :
+                            foreach ($cType as $contactTypeName) {
+                                $headers[] = $contactTypeName;
+                            }
+                            break;
+                        case 'interest' :
+                            if (   is_array($int_array)
+                            && !empty($int_array)
+                            ) {
+                            foreach ($int_array as $interesName) {
+                                $headers[] = $interesName;
+                                }
+                            }
+                            break;
+                        }
+                    } else {
+                        $headers[] = $fieldName;
+                    }
+                }
+                // this section creates the contact_type part of the rows
+                // need to make this one a set number of fields for the contact
+                // types needed for all contacts
+                // so create count($cType) number of columns for this one field
+                if ($fieldName == 'contact_type') {
+                    $cConTypes = array();
+                    $st        = array();
+                    if (strstr($value, ":")) {
+                        $cConTypesStr = preg_replace("/^:|:$/",
+                            "",
+                            $value
+                        );
+                        $cConTypes    = explode(":", $cConTypesStr);
+                    }
+                    if (is_array($cType) && !empty($cType)) {
+                        foreach ($cType as $contactTypeId => $contactTypeName) {
+                            $st[]
+                            = (   is_array($cConTypes)
+                               && in_array($contactTypeId, $cConTypes))
+                                ? $contactTypeName
+                                : '';
+                            }
+                        $result_string .= implode("|", $st);
+                    }
+                    $result_string .= '|';
+                }
+                // this section creates the interest part of the rows
+                // need to make this one a set number of fields for the contact
+                // types needed for all contacts
+                // so create count($int_array) number of columns for this one field
+                else if ($fieldName == 'interest') {
+                    $iIntTypes = array();
+                    $st        = array();
+                    if (strstr($value, ":")) {
+                        $iIntTypesStr = preg_replace("/^:|:$/",
+                            "",
+                            $value);
+                        $iIntTypes    = explode(":", $iIntTypesStr);
+                    }
+                    if (   is_array($int_array)
+                        && !empty($int_array)
+                        ) {
+                        foreach ($int_array as $interestId => $interestName) {
+                            $st[]
+                            = (   is_array($iIntTypes)
+                               && in_array($interestId, $iIntTypes))
+                                ? $interestName
+                                : '';
+                            }
+                        $result_string .= implode("|", $st);
+                    }
+                    $result_string .= '|';
+                }
+                // this one deals with al the other fields
+                else if (!in_array($fieldName, array('contact_type', 'interest'))) {
+                    $result_string .= $value.'|';
+                }
+            }
+            $result_string = substr($result_string, 0, strlen($result_string)-1);
+            if ($i == 0) {
+                if ($csv) {
+                    $headerString = '"'.implode('","', $headers)."\"\n";
+                } else {
+                    $headerString = implode($delimiter, $headers)."\n";
+                }
+                fputs($fp, $headerString, strlen($headerString));
+            }
+            if ($csv) {
+                $result_string = str_replace("|", "\",\"", $result_string);
+                $result_string = "\"".$result_string."\"\n";
+            } else {
+                $result_string = str_replace("|", $delimiter, $result_string);
+                $result_string = $result_string."\n";
+            }
+            fputs($fp, $result_string, strlen($result_string));
+        }
+    }
+    if (!fclose($fp)) {
+        html_error("Cant close filepointer", 0);
+    }
+    chmod("report.csv", 0660);
+    $output = "report.csv";
+
+    if ($file == "gz") {
+        $output = "report.tar.gz";
+        exec("tar -czvf report.tar.gz report.csv 2>&1", $result_array, $result);
+        if ($result != 0) {
+            echo $result_array[0];
+            exit;
+        }
+        chmod("report.tar.gz", 0660);
+    }
+
+    if ($file == "zip") {
+        $output = "report.zip";
+        exec("zip report report.csv 2>&1", $result_array, $result);
+        if ($result != 0) {
+            echo $result_array[0];
+            exit;
+        }
+        chmod("report.zip", 0660);
+    }
+    if ($file == "rpt") {
+        $output = "report.csv";
+        chmod("report.csv", 0660);
+    }
+    if (ini_get('zlib.output_compression')) {
+        ini_set('zlib.output_compression', 'Off');
+    }
+    header("Content-Type: application/force-download\n");
+    /* Correction for the stupid MSIE thing */
+    if (strstr(getenv('HTTP_USER_AGENT'), 'MSIE')) {
+        header("Content-Disposition: inline; filename=\"$output\"");
+    } else {
+        header("Content-Disposition: attachment; filename=\"$output\"");
+    }
+    //header("Location: $output");
+    $fn = fopen($output, "r");
+    fpassthru($fn);
+    @fclose($fn);
+    exit();
+} else {
+    header("Location: list_contact.phtml");
+}
+?>
diff --git a/admin/Contact/edit_autoresponse.phtml b/admin/Contact/edit_autoresponse.phtml
new file mode 100755 (executable)
index 0000000..3000ae6
--- /dev/null
@@ -0,0 +1,102 @@
+<?php
+//$Id: edit_autoresponse.phtml,v 1.12 2010/08/11 18:16:41 matrix Exp $
+include "../../setup.phtml";
+include "contact_setup.inc";
+GLM_TOOLBOX::top("AutoReponse for Newsletter", HELP_BASE."response.phtml?key=edit+section");
+
+GLM_TOOLBOX::html_nav_table($nav, $navWidth);
+
+if ($id) {
+    $qs = "SELECT   id,subject,response
+        FROM    news_response
+        WHERE   id = $id";
+
+    if (!$res = $DB->db_exec($qs)) {
+        GLM_TOOLBOX::html_error(DB_ERROR_MSG.__LINE__, 1);
+    }
+    $row = $DB->db_fetch_array($res,0, PGSQL_ASSOC);
+} else {
+    $row['subject']  = '';
+    $row['response'] = '';
+}
+?>
+<script src=<?php echo MEDIA_BASE_URL."admin/verify.js";?>></script>
+<script type="text/javascript" src="<?php echo CKEDITOR_JS;?>"></script>
+<script language="javascript">
+<!--// closed source
+function mySubmit(o){
+    o.response.optional = true;
+    o.response.r = 'Description';
+    o.subject.optional = false;
+    o.subject.r = 'Subject';
+    return(verify(o))
+}
+var Newsletter =
+{
+    init: function()
+    {
+        if ($('#response').length) {
+            //  Only try to replace the textarea if the
+            //  CKEditor is compatible w/ the browser.
+            if (CKEDITOR.env.isCompatible) {
+                CKEDITOR.replace('response',
+                    {
+                        toolbar : 'Default',
+                        width : 570,
+                        height : 400,
+                        filebrowserImageBrowseUrl : '../../Toolkit/CKImages/browser.php?folder=1',
+                        filebrowserImageUploadUrl : '../../Toolkit/CKImages/controller.php?command=Upload',
+                        filebrowserImageWindowWidth : '760',
+                        filebrowserImageWindowHeight : '500'
+                    });
+            }
+        }
+    }
+};
+
+$(document).ready(Newsletter.init);
+
+//-->
+</script>
+<form id="form1" name="form1" enctype="multipart/form-data" action="update_autoresponse.phtml" method="POST">
+<table id="admin-edit-table">
+<?
+foreach ($row as $key => $value) {
+    switch ($key) {
+
+    case "id":
+        echo "<input type=\"hidden\" name=\"id\" value=\"$value\">";
+        break;
+
+    case "subject":
+        echo "<tr><td class=\"navtd\" align=\"right\">Subject:</td>";
+        GLM_TOOLBOX::text_box("subject", $value);
+        echo "</tr>";
+        break;
+
+    case "response":
+        echo "<tr><td class=\"navtd\" align=\"right\">Response:</td>";
+        echo '<td><textarea name="response" id="response" cols="60" rows="60">'.$value.'</textarea></td>';
+        echo "</tr>";
+        break;
+
+    default:
+    break;
+    }
+}
+echo '<tr><td></td><td nowrap="nowrap">';
+if ($id) {
+    ?>
+    <input type="submit" name="Command" value="Update">
+    <?php
+} else {
+    ?>
+    <input type="submit" name="Command" value="Insert">
+    <?php
+}
+echo '
+</td></tr>
+</table>
+</form>';
+GLM_TOOLBOX::footer();
+?>
diff --git a/admin/Contact/edit_contact.php b/admin/Contact/edit_contact.php
new file mode 100644 (file)
index 0000000..01a7f2f
--- /dev/null
@@ -0,0 +1,25 @@
+<?php
+require_once '../../setup.phtml';
+include "contact_setup.inc";
+$form = new Toolkit_Contacts_Admin_EditContact(
+    Toolkit_Database::getInstance(), 
+    'edit_contact'
+);
+$form->setConfig($contactRoot);
+$form->setTableName(
+    $contactRoot->getItem('section', 'conf')
+    ->getItem('directive', 'table')
+    ->getContent()
+);
+$form->setPrimaryKey(
+    $contactRoot->getItem('section', 'conf')
+    ->getItem('directive', 'primarykey')
+    ->getContent()
+);
+
+$form->configureForm($contactRoot);
+$formOutput = $form->toHtml();
+GLM_TOOLBOX::top("Updatable Listings (Add/Edit)", "help/contact.phtml?key=Edit");
+GLM_TOOLBOX::html_nav_table($nav, $navWidth);
+echo $formOutput;
+GLM_TOOLBOX::footer();
diff --git a/admin/Contact/edit_group.php b/admin/Contact/edit_group.php
new file mode 100644 (file)
index 0000000..93c4751
--- /dev/null
@@ -0,0 +1,99 @@
+<?php
+include "../../setup.phtml";
+include "contact_setup.inc";
+if ($_POST) {
+       $sub_oktogo = true;
+       if (empty($_POST['group'])) {
+               $sub_oktogo = false;
+       }
+       switch ($_POST['Command']) {
+    case 'Add' :
+        $qs = "INSERT INTO inq_group(name) VALUES('".$_POST['group']."')";
+        break;
+
+    case 'Edit' :
+        $qs = "UPDATE inq_group SET name = '".$_POST['group']."' WHERE id = ".$_POST['id'];
+        break;
+
+    case 'Delete' :
+        $qs = "DELETE FROM inq_group WHERE id = ".$_POST['id'];
+        break;
+
+    default :
+        unset($qs);
+        break;
+       }
+       if ($_POST['Command'] == 'Delete') {
+               $DB->db_exec($qs);
+               header('Location: list_groups.php');
+       } else if ($sub_oktogo && isset($qs)) {
+               $DB->db_exec($qs);
+               header('Location: list_groups.php');
+       }
+}
+?>
+<script type="text/javascript">
+function form_sub_check() {
+       var sub_oktogo = true;
+       var group = document.getElementById('group').value;
+       var groupBox = document.getElementById('group_box');
+
+       if (group == '') {
+               groupBox.className = "problem";
+               sub_oktogo = false;     
+               problems = '-- Name\n';
+       } else {
+               groupBox.className = '';
+    }
+
+       if (!sub_oktogo)
+               alert('Please give your new group a name before continuing');
+       //      Return whether the form will submit or not!
+       return sub_oktogo ? true : false;
+}
+</script>
+<style type="text/css">
+.problem {
+       background-color: #FCA9A4 !important;
+}
+</style>
+<?php
+GLM_TOOLBOX::top('Edit/Add Inquiry','');
+//     Adjust the nav for this page, add the Edit Groups page.
+if (is_array($nav) && array_key_exists('Contact Inquiry Fields', $nav)) {
+       $tmp = array('Inquiry Groups' => 'list_groups.php');
+       array_insert($nav, 'Contact Inquiry Fields', $tmp); 
+}
+GLM_TOOLBOX::html_nav_table($nav,$navWidth);
+if (is_numeric($_GET['id'])) {
+       $query = 'SELECT * FROM inq_group WHERE id = '.$_GET['id'];
+       if ($data = $DB->db_auto_get_data($query)) {
+               $name = $data[0]['name'];
+               $id = $data[0]['id'];
+       }
+} else {
+       $name = '';
+}
+?>
+<form action="edit_group.php" method="POST" onSubmit="return form_sub_check();">
+       <table id="admin-edit-table">
+               <tr>
+                       <th>Group Name:</th>
+                       <td id="group_box" <?php echo ($_POST && !$sub_oktogo) ? 'class="problem"' : '';?>>
+                               <input type="text" name="group" id="group" value="<?php echo $name;?>">
+                               <input type="hidden" name="id" id="id" value="<?php echo $id;?>">
+                       </td>
+               </tr>
+               <tr>
+                       <td colspan="2" align="center">
+                               <?php if ($_GET['Command'] == "Edit") :?>
+                                       <input type="submit" name="Command" value="Edit">
+                                       <input type="submit" name="Command" value="Delete">
+                               <?php else :?>
+                                       <input type="submit" name="Command" value="Add">
+                               <?php endif;?>
+                       </td>
+               </tr>
+       </table>
+</form>
+<?php GLM_TOOLBOX::footer();?>
diff --git a/admin/Contact/edit_inquiry.phtml b/admin/Contact/edit_inquiry.phtml
new file mode 100755 (executable)
index 0000000..fbf65ae
--- /dev/null
@@ -0,0 +1,129 @@
+<?php
+require_once "../../setup.phtml";
+require_once "contact_setup.inc";
+GLM_TOOLBOX::top('Edit/Add Inquiry','');
+?>
+<script type="text/javascript">
+    $(document).ready(function(){
+        $('#deleteButton').click(function(){
+            $('input[name="xCommand"]').val('Delete');
+        });
+    });
+function form_sub_check()
+{
+    var submitButton = $('input[name="xCommand"]').val();
+    if (submitButton == 'Delete') {
+        return true;
+    }
+    var sub_oktogo = true;
+    var problems = '';
+    var header = $("#header").val();
+    var headerBox = $("#header_box");
+    var group = $("#groupid").val();
+    var groupBox = $("#group_box");
+    var newGroup = $("#new_group").val();
+
+    if (header == '') {
+        headerBox.addClass("problem");
+        sub_oktogo = false;
+        problems = '-- Name\n';
+    } else {
+        headerBox.removeClass("problem");
+    }
+    if ((group == undefined || group == 0) && newGroup == '') {
+        groupBox.addClass("problem");
+        sub_oktogo = false;
+        problems += '-- Group\n';
+    } else {
+        groupBox.removeClass("problem");
+    }
+    if (!sub_oktogo) {
+        alert('You have problems on your form!\nPlease carefully review the following and re-submit your form.\n\n' + problems);
+    }
+    //  Return whether the form will submit or not!
+    return sub_oktogo ? true : false;
+}
+</script>
+<style type="text/css">
+.problem {
+    background-color: #FCA9A4 !important;
+}
+</style>
+<?php
+//  Adjust the nav for this page, add the Edit Groups page.
+if (is_array($nav) && array_key_exists('Contact Inquiry Fields', $nav)) {
+    $tmp = array('Inquiry Groups' => 'list_groups.php');
+    array_insert($nav, 'Contact Inquiry Fields', $tmp);
+}
+GLM_TOOLBOX::html_nav_table($nav,$navWidth);
+if (is_numeric($_GET['id'])) {
+    $query = "SELECT * FROM contact_inq WHERE id = $id";
+    if ($data = $DB->db_auto_get_data($query)) {
+        $header         = $data[0]['header'];
+        $description    = $data[0]['description'];
+        $pos            = $data[0]['pos'];
+        $groupid        = $data[0]['groupid'];
+    }
+} else {
+    $header         = '';
+    $description    = '';
+    $groupid        = '';
+}
+$query = "SELECT * FROM inq_group ORDER BY name;";
+if ($gData = $DB->db_auto_get_data($query)) {
+    $group_sel = '<select id="groupid" name="groupid">';
+    $group_sel .= '<option value="">New Group - &gt;&gt;</option>';
+    foreach ($gData as $gRow) {
+        $group_sel .= '<option value="'.$gRow['id'].'"';
+        if( $groupid && $gRow['id'] == $groupid )
+        {
+            $group_sel .= ' selected';
+        }
+        $group_sel .= '>'.$gRow['name'];
+        $group_sel .= '</option>';
+    }
+    $group_sel .= '</select>';
+}
+?>
+<form action="update_inquiry.phtml" method="POST" onSubmit="return form_sub_check();">
+    <input type="hidden" name="old_groupid" value="<?php echo $groupid;?>" />
+    <table id="admin-edit-table">
+        <tr>
+            <th>Name:</th>
+            <td id="header_box">
+                <input type="text" name="header" id="header" size="50" value="<?php echo $header?>">
+                <input type="hidden" name="id" value="<?php echo $id?>">
+            </td>
+        </tr>
+        <?
+        echo '<input type="hidden" name="oldpos" value="'.$pos.'">';
+    ?>
+    <tr>
+        <th>Group:</th>
+        <td id="group_box">
+        <?php echo $group_sel;?>
+        <input type="text" id="new_group" name="new_group" value="" />
+        </td>
+    </tr>
+    <tr>
+        <td colspan=2 align=center>
+            <input type="hidden" name="xCommand" value="" />
+<?php
+if ($Command == "Edit") {
+    ?>
+    <input type="submit" name="Command" value="Edit">
+    <input type="submit" id="deleteButton" name="Command" value="Delete">
+    <?php
+} else {
+?>
+    <input type="submit" name="Command" value="Add">
+<?php
+}
+?>
+        </td>
+        </tr>
+    </table>
+</form>
+<?php
+GLM_TOOLBOX::footer();
+?>
diff --git a/admin/Contact/emails.php b/admin/Contact/emails.php
new file mode 100644 (file)
index 0000000..86695f7
--- /dev/null
@@ -0,0 +1,36 @@
+<?php
+require_once '../../setup.phtml';
+require_once BASE . 'classes/class_db.inc';
+require_once 'contact_setup.inc';
+$DB = new GLM_DB();
+$query = "select * from news_response order by id;"; 
+$data  = $DB->db_auto_get_data( $query );
+GLM_TOOLBOX::top( "Email HTML Newsletters", "");
+GLM_TOOLBOX::html_nav_table($nav,$navWidth);
+$out = '<table id="admin-list-table">
+       <tr><td colspan="2" align="center">
+                       <form name="add" action="edit_autoresponse.phtml" method="post">
+                               <button name="command" onClick="add.submit()">Add HTML EMAIL</button>
+       </td></tr>
+       <tr>
+               <th>Function:</th>
+               <th>Subject:</th>
+               <th>Last Update:</th>
+               <th> &nbsp; </th>
+       </tr>';
+       
+if (is_array($data)) {
+       foreach ($data as $key => $val) {
+               $out .= '<tr>
+                       <td><a href="edit_autoresponse.phtml?id='.$val["id"].'">[Edit]</a>&nbsp;
+                               <a href="view_newsletter.phtml?id='.$val["id"].'">[View]</a></td>
+                       <td>'.$val["subject"].'</td>
+                       <td>'.$val["last_update"].'</td>
+                       <td><a href="update_autoresponse.phtml?Command=Delete&amp;id='.$val["id"].'" onClick="return(confirm(\'This will delete the Email Newsletter!\n\nAre you sure?\'))">[Delete]</a></td>
+               </tr>';
+       }
+}
+$out .= '</table>';
+echo $out;
+GLM_TOOLBOX::footer();
+?>
diff --git a/admin/Contact/form.js b/admin/Contact/form.js
new file mode 100755 (executable)
index 0000000..0bbabf0
--- /dev/null
@@ -0,0 +1,42 @@
+function reshow(object) {
+    artist = object.options[object.selectedIndex].text;
+        for (var i = document.track.names.length;i > 0;i--)
+            document.track.names.options[0] = null;
+        reloading = true;
+        showlinks();
+        document.track.names.options[0].selected = true;
+    return false;
+}
+
+function load(object) {
+    alert('Just testing: ' + object.options[object.selectedIndex].value);
+    //window.location.href = object.options[object.selectedIndex].value;
+    return false;
+}
+
+function showlinks() {
+    if (artist == 'Chris Rea') {
+        opt('cr/one.zip','The Road To Hell');
+        opt('cr/two.zip','Let\'s Dance');
+    }
+
+    if (artist == 'Annie Lennox') {
+        opt('al/why.zip','Why');
+        opt('al/wobg.zip','Walking on Broken Glass');
+    }
+
+    if (artist == 'Dina Carrol') {
+        opt('dc/track1.zip','Escaping');
+        opt('dc/track2.zip','Only Human');
+    }
+}
+
+function opt(href,text) {
+    if (reloading)  {
+        var optionName = new Option(text, href, false, false)
+        var length = document.track.names.length;
+        document.track.names.options[length] = optionName;
+    }
+    else
+        document.write('<OPTION VALUE="',href,'">',text,'<\/OPTION>');
+}
diff --git a/admin/Contact/help/contact.phtml b/admin/Contact/help/contact.phtml
new file mode 100755 (executable)
index 0000000..bdec6d7
--- /dev/null
@@ -0,0 +1,146 @@
+<HTML>
+<HEAD>
+<TITLE>Help</TITLE>
+</HEAD>
+<BODY BGCOLOR="#FFFFFF" BACKGROUND="../../help/helpbg.gif" TEXT="#000000" LINK="#FF0000" VLINK="#800000" ALINK="#FF00FF" BACKGROUND="?">
+<FONT FACE="ms sans serif,arial,helvetica" SIZE=2 COLOR="#444444">
+<H4 align="center">Contact Help</H4>
+<hr>
+<?
+switch ($key) {
+       case "search":
+       ?>
+<h4 align="center">Contact Database Search</h4>
+
+<P>
+In this page you will start to build your query to the contact database.
+</p>
+<p>
+<big><b>Search:</b></big>
+</p>
+<p>
+<b>Search records for:</b>
+</p>
+<p>Here is where you will enter any keywords to use in the search.  You must
+enter in something in the "Search records for" box.  You may use more than one
+word (ie.) Sam Field.</p>
+<p><font color=red>HINT:</font>To help search use wildcards!</p>
+<p>? optional space<br>
+* optional spaces<br>
++ at least one space
+. will match anything<br>
+</p>
+<p><font color=green>NOTE:</font>Leaving this fields blank will select all
+contacts.  You can leave this blank and choose "Mail OK" true to get all
+contacts that allow emails.</p>
+<p><b>Search Where in fields:</b></p>
+<p>Tells the database to Search "Anywhere", "Beginning", or "Ending" of the
+fields to be searched.</p>
+<p><b>In Fields:</b></p>
+<p>Select from "In Fields" box.  This determines what fields to look in for
+this search.</p>
+<p><font color=red>HINT</font>
+If you want to select more than one field to search in hold down the 'Ctrl' key while clicking on the selection to select or
+deselect it from the list.</p>
+<p><font color=red>HINT</font>
+You can use the "All" and "None" buttons to help you save time.  This will
+select all or none of the fields in the boxes.</p>
+<p><b>Search Type:</b></p>
+<p>Select the type of search you want (ie.) an "Exact string" search will return
+only those fields which match the "Search records" for string exactly as compared
+to "Or" which will return any field that match any words you place into "Search
+records for"</p>
+<p><b>Case Sensitivity:</b></p>
+<p>This will turn "On" and "Off" the case sensitivity.
+(ie.)If you leave it "Off" and enter "bob" it will return anything like
+"bob","BOB","Bob","BOb","boB",or "BoB" turned "On" will match only "bob".</p>
+
+<p>
+<big><b>Output of records</b></big>
+</p>
+<p><b>Output Fields:</b></p>
+<p>Select from "Output Fields" box.  This determines what fields will be in the
+output of this search.</p>
+<p><font color=red>HINT</font>
+You can use the "All" and "None" buttons to help you save time.  This will
+select all or none of the fields in the boxes.</p>
+<p><font color=red>HINT</font>
+If you want to select more than
+one Output field hold down the 'Ctrl' key while clicking on the selection to select or
+deselect it from the list.</p>
+<p><b>File Output:</b></p>
+<p>Select from here if you wish to download a file with the results of this
+search.  The file will built "On the Fly" so you can download it.</p>
+<p><font color=green>NOTE:</font>The text file is output as report.doc.  This
+is only a text file.
+</p>
+<p><b>Delimiter:</b></p>
+<p>This determines what separates the fields in your file.</p>
+
+<?
+       break;
+
+       case "List":
+       ?>
+<h4 align="center">List Contacts</h4>
+<P>
+This page is for listing the results of your query.  You can download files if
+you have selected a file type or edit and delete the contact found.
+</p>
+<p><b>[Edit]</b></p>
+<p>Link to contact edit page.</p>
+
+<p><b>[Delete]</b></p>
+<p>Link to Delete Contact.</p>
+
+<p><big><b>Download Files</b></big></p>
+<p>If you see this then there is a file you can download.
+Click on the file and you can download it.</p>
+<?
+       break;
+
+       case "Edit":
+       ?>
+<h4 align="center">Edit a Contact</h4>
+<P>
+This page is for editing and modifying an existing Contact in the database.
+When editing is complete, click on the "Submit Query" button. The database will
+be updated, and you will be directed back to the "List Contacts" page.
+</p>
+<p>
+
+<p>
+<b>Submit Query</b>
+</p>
+<p>When you have made the changes you want to the Contact,
+you can click "Submit Query." This will update the information about the
+Contact in the database.
+</p>
+<?
+       break;
+
+       case "Add":
+       ?>
+<h4 align="center">Add an Contact</h4>
+<P>
+This page is for Adding Contacts in the database.
+When form is complete, click on the "Submit Query" button. The database will
+be updated, and you will be directed back to the "List Contacts" page.
+</p>
+
+<p>
+<b>Submit Query</b>
+</p>
+<p>When you have made the changes you want to the Contact,
+you can click "Submit Query." This will update the information about the
+Contact in the database.
+</p>
+<?
+       break;
+
+}
+?>
+<BR CLEAR=ALL>
+<CENTER><A HREF="" onClick = "window.close('self');"><IMG SRC="../../help/closewindow.gif" border=0></A></CENTER>
+</BODY>
+</HTML>
diff --git a/admin/Contact/htmlarea.css b/admin/Contact/htmlarea.css
new file mode 100644 (file)
index 0000000..23bdf7d
--- /dev/null
@@ -0,0 +1,180 @@
+.htmlarea { background: #fff; }
+
+.htmlarea .toolbar {
+  cursor: default;
+  background: ButtonFace;
+  padding: 1px 1px 2px 1px;
+  border: 1px solid;
+  border-color: ButtonHighlight ButtonShadow ButtonShadow ButtonHighlight;
+}
+.htmlarea .toolbar table { font-family: tahoma,verdana,sans-serif; font-size: 11px; }
+.htmlarea .toolbar img { border: none; }
+.htmlarea .toolbar .label { padding: 0px 3px; }
+
+.htmlarea .toolbar .button {
+  background: ButtonFace;
+  color: ButtonText;
+  border: 1px solid ButtonFace;
+  padding: 1px;
+  margin: 0px;
+}
+.htmlarea .toolbar .buttonHover {
+  border: 1px solid;
+  border-color: ButtonHighlight ButtonShadow ButtonShadow ButtonHighlight;
+}
+.htmlarea .toolbar .buttonActive, .htmlarea .toolbar .buttonPressed {
+  padding: 2px 0px 0px 2px;
+  border: 1px solid;
+  border-color: ButtonShadow ButtonHighlight ButtonHighlight ButtonShadow;
+}
+.htmlarea .toolbar .buttonPressed {
+  background: ButtonHighlight;
+}
+.htmlarea .toolbar .indicator {
+  padding: 0px 3px;
+  overflow: hidden;
+  width: 20px;
+  text-align: center;
+  cursor: default;
+  border: 1px solid ButtonShadow;
+}
+
+.htmlarea .toolbar .buttonDisabled { background-color: #aaa; }
+
+.htmlarea .toolbar .buttonDisabled img {
+  filter: alpha(opacity = 25);
+  -moz-opacity: 25%;
+}
+
+.htmlarea .toolbar .separator {
+  position: relative;
+  margin: 3px;
+  border-left: 1px solid ButtonShadow;
+  border-right: 1px solid ButtonHighlight;
+  width: 0px;
+  height: 16px;
+  padding: 0px;
+}
+
+.htmlarea .toolbar .space { width: 5px; }
+
+.htmlarea .toolbar select { font: 11px Tahoma,Verdana,sans-serif; }
+
+.htmlarea .toolbar select,
+.htmlarea .toolbar select:hover,
+.htmlarea .toolbar select:active { background: FieldFace; color: ButtonText; }
+
+.htmlarea .statusBar {
+  border: 1px solid;
+  border-color: ButtonShadow ButtonHighlight ButtonHighlight ButtonShadow;
+  padding: 2px 4px;
+  background-color: ButtonFace;
+  color: ButtonText;
+  font: 11px Tahoma,Verdana,sans-serif;
+}
+
+.htmlarea .statusBar .statusBarTree a {
+  padding: 2px 5px;
+  color: #00f;
+}
+
+.htmlarea .statusBar .statusBarTree a:visited { color: #00f; }
+.htmlarea .statusBar .statusBarTree a:hover {
+  background-color: Highlight;
+  color: HighlightText;
+  padding: 1px 4px;
+  border: 1px solid HighlightText;
+}
+
+
+/* Hidden DIV popup dialogs (PopupDiv) */
+
+.dialog {
+  color: ButtonText;
+  background: ButtonFace;
+}
+
+.dialog .content { padding: 2px; }
+
+.dialog, .dialog button, .dialog input, .dialog select, .dialog textarea, .dialog table {
+  font: 11px Tahoma,Verdana,sans-serif;
+}
+
+.dialog table { border-collapse: collapse; }
+
+.dialog .title {
+  background: #008;
+  color: #ff8;
+  border-bottom: 1px solid #000;
+  padding: 1px 0px 2px 5px;
+  font-size: 12px;
+  font-weight: bold;
+  cursor: default;
+}
+
+.dialog .title .button {
+  float: right;
+  border: 1px solid #66a;
+  padding: 0px 1px 0px 2px;
+  margin-right: 1px;
+  color: #fff;
+  text-align: center;
+}
+
+.dialog .title .button-hilite { border-color: #88f; background: #44c; }
+
+.dialog button {
+  width: 5em;
+  padding: 0px;
+}
+
+.dialog .buttonColor {
+  padding: 1px;
+  cursor: default;
+  border: 1px solid;
+  border-color: ButtonHighlight ButtonShadow ButtonShadow ButtonHighlight;
+}
+
+.dialog .buttonColor-hilite {
+  border-color: #000;
+}
+
+.dialog .buttonColor .chooser, .dialog .buttonColor .nocolor {
+  height: 0.6em;
+  border: 1px solid;
+  padding: 0px 1em;
+  border-color: ButtonShadow ButtonHighlight ButtonHighlight ButtonShadow;
+}
+
+.dialog .buttonColor .nocolor { padding: 0px; }
+.dialog .buttonColor .nocolor-hilite { background-color: #fff; color: #f00; }
+
+.dialog .label { text-align: right; width: 6em; }
+.dialog .value input { width: 100%; }
+.dialog .buttons { text-align: right; padding: 2px 4px 0px 4px; }
+
+.dialog legend { font-weight: bold; }
+.dialog fieldset table { margin: 2px 0px; }
+
+.popupdiv {
+  border: 2px solid;
+  border-color: ButtonHighlight ButtonShadow ButtonShadow ButtonHighlight;
+}
+
+.popupwin {
+  padding: 0px;
+  margin: 0px;
+}
+
+.popupwin .title {
+  background: #fff;
+  color: #000;
+  font-weight: bold;
+  font-size: 120%;
+  padding: 3px 10px;
+  margin-bottom: 10px;
+  border-bottom: 1px solid black;
+  letter-spacing: 2px;
+}
+
+form { margin: 0px; border: none; }
diff --git a/admin/Contact/index.phtml b/admin/Contact/index.phtml
new file mode 100755 (executable)
index 0000000..871b9c9
--- /dev/null
@@ -0,0 +1,334 @@
+<?php
+session_start();
+require_once "../../setup.phtml";
+require_once "contact_setup.inc";
+error_reporting();
+if (isset($mailout)) {
+    unset($_SESSION['mailout']);
+    //session_unregister("mailout");
+}
+if (isset($_SESSION['sess_vars'])) {
+    extract($_SESSION['sess_vars']);
+    unset($_SESSION['sess_vars']);
+    //session_unregister("sess_vars");
+}
+$qs = "SELECT   count(*) as total
+       FROM     ".TABLE;
+if (TABLE == CUSTOMER_TABLE) {
+    $qs .= " WHERE (fname != '-Guest-' OR fname is null)";
+}
+$GLOBALS['styleSheets'][]   = JQUERY_UI_CDN_CSS;
+$GLOBALS['bottomScripts'][] = JQUERY_UI_CDN_JS;
+$res = $DB->db_auto_get_data($qs);
+$total = $res[0]['total'];
+GLM_TOOLBOX::top("Contact Database","help/contact.phtml?key=search","SteveContactsDatabase_1.0");
+GLM_TOOLBOX::html_nav_table($nav,$navWidth);
+?>
+<table id="admin-list-table">
+  <tr>
+    <td colspan=4>
+    There
+    <?php
+    if ($total < 1 ) {
+        echo " No records";
+    } elseif ($total > 1) {
+        echo "are $total contacts";
+    } else {
+        echo "is $total contact";
+    }
+    ?> in the database.
+    </td>
+  </tr>
+  <tr>
+    <th colspan=4 class="theader">
+    Search:
+    </th>
+  </tr>
+  <tr>
+    <td colspan=4>
+    <b>Search records for:</b><br>
+    </td>
+  </tr>
+  <tr>
+  <td colspan=4>
+  <form name="search" action="query_contact.phtml" method="POST" onSubmit="
+    var msg = '';
+    var errors = '';
+    var ping = 0;
+    var all = 0;
+    this.fvalue.value = '';
+    this.rfvalue.value = '';
+    this.rdvalue.value = '';
+
+    if(this.search.value == '') {
+        all++;
+    }
+
+    for(var i = 0;i<4;i++) {
+        if(this.search_type.options[i].selected){
+            ping++;
+        }
+    }
+
+    if(all == 0) {
+        if(ping == 0) {
+            errors += '-You must select a search type\n';
+        }
+    }
+
+    for(var i=0;i<<?php echo count($fields)?>;i++) {
+        if(this.ifields.options[i].selected) {
+            this.fvalue.value += ':' + this.ifields.options[i].value;
+        }
+    }
+
+    for(var i=0;i<<?php echo count($fields)?>;i++) {
+        if(this.return_fields.options[i].selected) {
+            this.rfvalue.value += ':' + this.return_fields.options[i].value;
+        }
+    }
+
+    for(var i=0;i<1;i++) {
+        if(this.dates.options[i].selected) {
+            this.rdvalue.value += ':' + this.dates.options[i].value;
+        }
+    }
+
+    if(all == 0) {
+        if(this.fvalue.value == '') {
+            errors += '-You must select at least one field to search in\n';
+        }
+    }
+
+     if(this.rfvalue.value == '') {
+        errors += '-You must select at least one field for output\n';
+    }
+
+    if(all == 1) {
+        if(errors == '') {
+            return(true);
+        }
+    }
+
+    if(errors == '') {
+        return(true);
+    } else {
+        msg += '_______________________________________\n\n';
+        msg += 'The form was not submitted please check\n';
+        msg += 'the following and resubmit\n\n';
+        msg += errors + '\n\n';
+        msg += '_______________________________________\n\n';
+
+        alert(msg);
+        return(false);
+    }
+  ">
+  <input name="search" value="<?php echo stripslashes($search)?>" size=40>
+  <input type="submit" name="Submit Query">
+  </td>
+  </tr>
+  <tr>
+    <th colspan=4 class="theader">
+    Search Dates Ranges
+    </th>
+  </tr>
+  <tr>
+      <td align="left" colspan=4 nowrap>
+                 <div style="float:left;width:240px;height:35px;">Created From:<br>
+              <input id="contactFromDate" name="contact_from_date" <?php echo ($contact_from_date) ? 'value="'.$contact_from_date.'"': '';?>></div>
+                 <div style="float:left;width:240px;height:35px;">Created To:<br>
+              <input id="contactToDate" name="contact_to_date" <?php echo ($contact_to_date) ? 'value="'.$contact_to_date.'"': '';?>></div>
+      </td>
+  </tr>
+  <tr>
+    <td class="small" valign=top>
+    <b>In Fields:</b><br>
+    <select name="ifields" multiple size=8>
+    <?php foreach($fields as $key2=>$value2) {?>
+    <option value="<?php echo $key2?>" <?php echo (strstr($fvalue,$key2))?"selected":""?>><?php echo $value2?>
+    <?php }?>
+    </select>
+    <br>
+    <label for="in-all"><input type="radio" id="in-all" name="a" onClick="
+    for(var i=0;i<<?php echo count($fields)?>;i++) {
+        this.form.ifields.options[i].selected=1;
+    }
+    ">All</label>
+    <label for="in-none"><input type="radio" id="in-none" name="a" onClick="
+    for(var i=0;i<<?php echo count($fields)?>;i++) {
+        this.form.ifields.options[i].selected=0;
+    }
+    ">None</label>
+    </td>
+    <td valign=top class="small" nowrap>
+      <b>Search Where:</b><br>
+      <select name="alter">
+      <option value="0" <?php echo ($alter=="0")?"selected":""?>>Anywhere
+      <option value="1" <?php echo ($alter=="1")?"selected":""?>>Begining
+      <option value="2" <?php echo ($alter=="2")?"selected":""?>>Ending
+      </select><br>
+    <input type="hidden" name="fvalue">
+    <?php
+    if (is_array($boolean)) {
+        foreach ($boolean as $bool_key => $bool_val) {
+            echo '<br>
+                <b>'.$bool_val.'</b><br>';
+            echo search_bool($bool_key, ${$bool_key});
+        }
+    }
+    if (is_array($dropdowns)) {
+        foreach ($dropdowns as $drop_key => $drop_row) {
+            echo '<br>
+                <b>'.$drop_row['title'].'</b>';
+            echo '<br>';
+            if( is_array( $drop_row['drop'] ) ) {
+                echo GLM_TOOLBOX::build_picklist( $drop_key.'[]', $drop_row['drop'], ${$drop_key}, 'multiple', 0, null, 5 );
+            }
+        }
+    }
+    echo '</td><td valign=top width=25%>';
+?>
+    <br><b>Search Type:</b><br>
+  <select name="search_type" size=4>
+  <option value="1" <?php echo (!isset($search_type) || $search_type=="1")?"selected":""?>>Exact string
+  <option value="2" <?php echo ($search_type=="2")?"selected":""?>>And
+  <option value="3" <?php echo ($search_type=="3")?"selected":""?>>Or
+  <option value="4" <?php echo ($search_type=="4")?"selected":""?>>Not
+  </select>
+    </td>
+      <td valign=top class=small width=25%>
+      <b>Case Sensitivity:</b><br>
+      <select name="case">
+      <option value="ON" <?php echo ($case == "ON")?"selected":""?>>On
+      <option value="OFF" <?php echo (!isset($case) || $case == "OFF")?"selected":""?>>Off
+      </select><br>
+      </td>
+      </tr>
+      <tr>
+      <td colspan="4"> &nbsp;
+<?php
+    $sql = "
+      SELECT contact_inq.*, inq_group.name as group
+        FROM contact_inq
+             LEFT OUTER JOIN inq_group
+               ON (contact_inq.groupid = inq_group.id)
+       WHERE inq_group.id != 6
+    ORDER BY groupid, pos";
+
+    $interests = array();
+    $intData = $DB->db_auto_get_data($sql);
+    foreach ($intData as $row) {
+        $interests[$row['group']][$row['id']] = $row['header'];
+    }
+
+    foreach ($interests as $i => $j) {
+        echo '<fieldset>';
+        echo '<legend>';
+        echo $i;
+        echo '</legend>';
+        foreach ($j as $k => $v) {
+            echo '<label class="glmCheckBox">';
+            echo '<input type="checkbox" name="cols[]" value="'.$k.'" ';
+            if (   is_array($cols)
+                && in_array($data['id'], $cols)) {
+                echo " checked";
+            }
+            echo '>';
+            echo $v;
+            echo '</label>';
+                    }
+        echo '</fieldset>';
+    }
+?>
+      </td>
+    </tr>
+  <tr>
+    <th colspan=4>
+    Output of records:
+    </th>
+  </tr>
+  <tr>
+    <td class="small" valign=top>
+    <b>Output Fields:</b><br>
+    <select name="return_fields" multiple size=8>
+    <?php foreach($fields as $key2=>$value2) {?>
+    <option value="<?php echo $key2?>" <?php echo (strstr($rfvalue,$key2))?"selected":""?>><?php echo $value2?>
+    <?php }?>
+    </select>
+    <br>
+    <input type="hidden" name="rfvalue">
+    <label for="out-all"><input type="radio" id="out-all" name="a" onClick="
+    for(var i=0;i<<?php echo count($fields)?>;i++) {
+        this.form.return_fields.options[i].selected=1;
+    }
+    for(var i=0;i<<?php echo ($p_date_from)?"3":"1";?>;i++) {
+        this.form.dates.options[i].selected=1;
+    }
+    ">All</label>
+    <label for="out-none"><input id="out-none" type="radio" name="a" onClick="
+    for(var i=0;i<<?php echo count($fields)?>;i++) {
+        this.form.return_fields.options[i].selected=0;
+    }
+    for(var i=0;i<<?php echo ($p_date_from)?"3":"1";?>;i++) {
+        this.form.dates.options[i].selected=0;
+    }
+    ">None</label>
+  </td>
+  <td class="small" valign=top>
+    <input type="hidden" name="rdvalue" value="">
+    <b>Output fields (Dates):</b>
+    <select name="dates" multiple size=3>
+    <option value="create_date" <?php echo (strstr($dates,"create_date"))?"selected":""?>>Created Date
+    <?php if($p_date_from)
+    {?>
+  <option value="purch_date" <?php echo (strstr($dates,"purch_date"))?"selected":""?>>Last Purchase Date
+  <?php }
+  if($a_date_from)
+  {?>
+  <option value="access_date" <?php echo (strstr($dates,"access_date"))?"selected":""?>>Last Access Date
+  <?php }?>
+    </select>
+  </td>
+  <td class="small" valign=top width=25%>
+  <b>File output:</b><br>
+  <select name="file" size=4>
+  <option value="" <?php echo (!isset($file) || $file == "")?"selected":""?>>No File
+  <option value="zip" <?php echo ($file=="zip")?"selected":""?>>zip file
+  <option value="gz" <?php echo ($file=="gz")?"selected":""?>>tar.gz(tar ball)
+  <option value="rpt" <?php echo ($file=="rpt")?"selected":""?>>text file
+  </select>
+  </td>
+  <td valign=top class=small width=25%>
+  <b>Delimiter:</b><br>
+  <select name="delimiter" size=4>
+  <option value="tab" <?php echo ($delimiter=="tab")?"selected":""?>>TAB
+  <option value="comma" <?php echo ($delimiter=="comma")?"selected":""?>>Comma
+  <option value="csv" <?php echo ($delimiter=="csv")?"selected":""?>>CSV
+  <option value="pipe" <?php echo ($delimiter=="pipe")?"selected":""?>>Pipe
+  </select>
+  </td>
+  </tr>
+  <tr>
+    <td colspan="4" align="center">
+    <input type="submit" name="Submit Query">
+    </td>
+  </tr>
+  </table>
+<script>
+    $(function(){
+        $("#contactFromDate").datepicker({
+            altField: "#contactToDate",
+            showOn: "both",
+            dateFormat: "mm/dd/yy",
+            buttonText: "Calendar"
+        });
+        $("#contactToDate").datepicker({
+            showOn: "both",
+            dateFormat: "mm/dd/yy",
+            buttonText: "Calendar"
+        });
+    });
+</script>
+<?php
+GLM_TOOLBOX::footer();
+?>
diff --git a/admin/Contact/list_contact.phtml b/admin/Contact/list_contact.phtml
new file mode 100755 (executable)
index 0000000..b36b176
--- /dev/null
@@ -0,0 +1,287 @@
+<?php
+require_once '../../setup.phtml';
+require_once 'contact_setup.inc';
+if (!$start) {
+    $start = 0;
+}
+
+if ($postquery) {
+    $query_string = $postquery;
+}
+$checkqs = "SELECT  count(*) as contacts
+            FROM    ".TABLE;
+
+if (!$checkres = $DB->db_auto_get_data($checkqs)) {
+    GLM_TOOLBOX::html_error(DB_ERROR_MSG.__LINE__.$checkqs,1);
+}
+
+$numcontacts = $checkres[0]['contacts'];
+if ($numcontacts == 0) {
+    GLM_TOOLBOX::html_error("There are no contacts in the database",1);
+}
+//var_dump($query_string);
+//var_dump($_REQUEST);
+//exit;
+if(!isset($back) && !isset($query_string) && !isset($query_no)) {
+    $query = "SELECT    ".ID.",*
+              FROM      ".TABLE."
+              WHERE     ".WHERE."
+              ORDER BY  lname,fname";
+
+    $query = addslashes($query);
+    $qs = "SELECT   id
+           FROM     query_db
+           WHERE    query_name = '(current)'";
+
+    if (!$res = $DB->db_exec($qs)) {
+        GLM_TOOLBOX::html_error(DB_ERROR_MSG.__LINE__.$qs,1);
+    }
+
+    if ($DB->db_numrows($res)==0) {
+        $qs = "INSERT
+               INTO     query_db
+                        (query,query_name)
+               VALUES   ('$query','(current)')";
+    } else {
+        $id = $res->fetchColumn();
+        $qs = "UPDATE   query_db
+               SET      query = '$query',
+                        file = '',
+                        delimiter = ''
+               WHERE    id = $id";
+    }
+    if (!$res = $DB->db_exec($qs)) {
+        html_error(DB_ERROR_MSG.__LINE__.$qs,1);
+    }
+    unset($qs);
+}
+
+if ($delimiter == "csv") {
+    $csv = TRUE;
+}
+
+if (isset($query_string) && $query_string) {
+    $query_string = strtr($query_string,"\n"," ");
+    $query_string = strtr($query_string,"\t"," ");
+    $query_string = stripslashes($query_string);
+    $qs = $query_string;
+} elseif ($query_no) {
+    $qs = "select query from query_db where id = $query_no";
+    $queryres = $DB->db_exec($qs);
+    if ($queryres->rowCount() == 0) {
+        $qs = "SELECT   ".ID.",*
+              FROM      ".TABLE."
+              WHERE     ".WHERE."
+              ORDER BY  lname,fname";
+    } else {
+        $qs = $queryres->fetchColumn();
+    }
+} else {
+    $queryqs = "SELECT  query
+                FROM    query_db
+                WHERE   query_name LIKE '(current)'";
+    $queryres = $DB->db_exec($queryqs);
+    if ($queryres->rowCount() == 0) {
+        $qs = "SELECT   ".ID.",*
+              FROM      ".TABLE."
+              WHERE     ".WHERE."
+              ORDER BY  lname,fname";
+    } else {
+        $qs = $queryres->fetchColumn();
+    }
+}
+
+GLM_TOOLBOX::top("List Contacts","help/contact.phtml?key=List");
+?>
+<script src="wm.js"></script>
+<script src="msg.js"></script>
+<table id="admin-list-table">
+<?php
+GLM_TOOLBOX::html_nav_table($nav,$navWidth);
+if (NEWSLETTER) {
+$mquery = "select id,subject,last_update from news_response order by last_update desc;";
+    $mres = $DB->db_exec($mquery);
+    $mailout = array();
+    while ($mrow = $mres->fetch())
+    {
+        $mailout[] = $mrow;
+    }
+?>
+<script type="text/javascript">
+    var remind;
+    remind = 'This will mailout the Newsletter\n';
+</script>
+<table id="admin-edit-table" style="width:500px;">
+  <tr>
+    <th colspan=2>
+    <form action="mailout.phtml" method="POST" onSubmit="return(confirm(remind));">
+        <select name="mail_id" style="width:100%;"><?php
+    unset($mrow);
+    foreach ($mailout as $mrow) {
+        echo '<option value="'.$mrow["id"].'">';
+        echo ' '.htmlspecialchars(strip_tags($mrow["subject"]));
+        echo ' (Updated: '.$mrow["last_update"].')';
+        echo '</option>';
+    }
+    ?>
+      </select>
+      <input type="hidden" name="postmail" value="<?php echo $qs?>">
+      <input type="submit" value="Mail Out the Newsletter">
+    </form>
+    </th>
+</tr>
+<?php }
+$totalqs = substr_replace($qs," count(*) as total FROM ",strpos($qs,"SELECT")+7,strpos($qs,"FROM")-3);
+if (strpos($totalqs,"ORDER BY")!=0) {
+    $totalqs = substr_replace($totalqs,"",strpos($totalqs,"ORDER"));
+}
+if (!$totalres = $DB->db_exec($totalqs)) {
+    GLM_TOOLBOX::html_error(DB_ERROR_MSG.__LINE__.$totalqs,1);
+}
+if ($totalres->rowCount() == 0) {
+    $totalnum = 0;
+} else {
+    $totalnum = $totalres->fetchColumn();
+}
+$qs .= " LIMIT ".ENTRIES_PER_PAGE." OFFSET ".$start;
+$res = $DB->db_exec($qs);
+?>
+<tr>
+  <td colspan="2"><?php echo $totalnum?>Result(s)
+<?php
+if ($_REQUEST['Action']) {
+    echo '<div style="background-color:green;color:white;padding:5px;">'.$_REQUEST['Action'].' Successfully!</div>';
+}
+?></td>
+</tr>
+<?php
+if(!$res) GLM_TOOLBOX::html_error(DB_ERROR_MSG.__LINE__.$qs,1);
+// What page are you on?
+if ($start==0) {
+    $page == 1;
+} else {
+    $page = ($start / ENTRIES_PER_PAGE) + 1;
+}
+$totalpages = floor($totalnum / ENTRIES_PER_PAGE);
+$totalpages++;
+
+$result_string = "";
+$num = $DB->db_numrows($res);
+if (!$start) {
+    $start = 0;
+}
+$begin  = 0;
+$ending = $num;
+
+$stuff = "query_string=".urlencode($query_string)."&file=".$file."&delimiter=".$delimiter."&csv=".$csv;
+// for paging results get a first page and last page link also
+// first page is easy use 0
+$first = "<a href=\"list_contact.phtml?".$stuff."&start=0\">FIRST</a> - ";
+// last page use total pages
+$lastpage = " - <a href=\"list_contact.phtml?".$stuff."&start=".( ( $totalpages - 1 ) * ENTRIES_PER_PAGE )."\">Last</a>";
+if ($totalnum > ENTRIES_PER_PAGE && ( $page != $totalpages ) ) {
+    $end = ENTRIES_PER_PAGE + $start;
+} else {
+    $end = $totalnum;
+}
+$last = $start - ENTRIES_PER_PAGE;
+if (!$query_string) {
+    $query_string = $qs;
+    $query_string = str_replace(" LIMIT ".ENTRIES_PER_PAGE." OFFSET ".$start,"",$query_string);
+}
+
+if (($start - ENTRIES_PER_PAGE) < 0) {
+    $prev = "PREV";
+} else {
+    $prev = "<a href=\"list_contact.phtml?".$stuff."&start=".$last."\">PREV</a>";
+}
+if ($end < $totalnum) {
+    $next = "<a href=\"list_contact.phtml?".$stuff."&start=".$end."\">NEXT</a>";
+} else {
+    $next = "NEXT";
+}
+?>
+<tr>
+ <td colspan="2">
+ <?php
+ if($num!=0)
+ echo $first.$prev."-".($start+1)."-to-".$end."-".$next.$lastpage;
+ ?>
+ </td>
+</tr>
+<tr>
+  <td colspan="2">
+<?php
+if (count($res)>0) {
+    for ($i=$begin;$i<$ending;$i++) {
+        if (!$row = $DB->db_fetch_array($res,$i,PGSQL_ASSOC)) {
+            GLM_TOOLBOX::html_error(DB_ERROR_MSG.__LINE__,1);;
+        }
+        $col = 0;
+        foreach ($row as $fieldName => $fieldVal) {
+            $fields[$col] = $fieldName;
+            if ($i == $begin) {
+                $head_ar[] = $fieldName;
+            }
+            $body_ar[] = $fieldVal;
+            ++$col;
+        }
+        if ($i == $begin) {
+            $head_array = array_values($head_ar);
+        }
+        $contact_data[] = array_values($body_ar);
+        ?>
+        <tr <?php echo $background;
+    $id = ID;
+            ?>>
+                <td nowrap><a href="edit_contact.php?<?php echo ID . '=' . $row[$id]?>&start=<?php echo $start;?>">
+          [Edit]</a>
+          <a href="update_contact.phtml?Command=Delete&id=<?php echo $row[$id]?>&start=<?php echo $start;?>" onClick="
+            if(confirm('This will delete this record Are you sure?')) {
+                return(true);
+            }else {
+                return(false);
+            }
+            ">
+          [Delete]</a>
+          </td>
+          <td align=left>
+          <?php
+          foreach($fields as $key) {
+          if($key != "id" && $key != "cust_id"
+                  && $key != "userid" && $key != "usernum"
+                  && $key != "usergroup" && $key != "passwd")
+            echo $row[$key]." ";
+          }
+          ?>
+          </td>
+        </tr>
+        <?php
+    }
+}
+    ?>
+    </td>
+</tr>
+    </table>
+    <?php
+if(isset($file) && $file != "" && $DB->db_numrows($res) > 0) {
+?>
+<table>
+<tr>
+    <th colspan=2>Download files</th>
+</tr>
+<tr>
+  <td><form action="download.phtml">
+  <input type="hidden" name="query_string" value="<?php echo $query_string?>">
+  <input type="hidden" name="file" value="<?php echo $file?>">
+  <input type="hidden" name="delimiter" value="<?php echo $delimiter?>">
+  <input type="hidden" name="csv" value="<?php echo $csv?>">
+  <input type="submit" value="Download Report">
+  </form></td>
+</tr>
+</table>
+<?php
+}
+GLM_TOOLBOX::html_nav_table($nav,5);
+GLM_TOOLBOX::footer();
+?>
diff --git a/admin/Contact/list_groups.php b/admin/Contact/list_groups.php
new file mode 100644 (file)
index 0000000..ac03bb0
--- /dev/null
@@ -0,0 +1,56 @@
+<?php
+include_once '../../setup.phtml';
+include "contact_setup.inc";
+GLM_TOOLBOX::top('Contact Inquiries', '');
+//     Adjust the nav for this page, add the Edit Groups page.
+if (is_array($nav) && array_key_exists('Contact Inquiry Fields', $nav))
+{
+       $tmp = array('Inquiry Groups' => 'list_groups.php');
+       array_insert($nav, 'Contact Inquiry Fields', $tmp); 
+}
+GLM_TOOLBOX::html_nav_table($nav, $navWidth);
+$qs = "
+    SELECT id, name
+      FROM inq_group
+     ORDER BY name;";
+$result = $DB->db_exec( $qs );
+?>
+<table id="admin-list-table">
+       <tr>
+         <th colspan="2">
+          <form action="edit_group.php" method="POST" name="edit_group">
+                       <input type="submit" name="Command" value="Add Group" />
+               </form>
+         </th>
+       <tr>
+       <tr>
+         <th>Options:</th>
+         <th>Group:</th>
+       <tr>
+       <?php
+       if($DB->db_numrows($result) > 0) 
+       {
+               for($i=0 ;$i < $DB->db_numrows($result); $i++) 
+               {
+                       $data = $DB->db_fetch_array($result, $i, PGSQL_ASSOC);  
+                       ?>
+                       <tr>
+                               <td width="10" nowrap><a href="edit_group.php?id=<?php echo $data['id'];?>&amp;Command=Edit">[Edit / Delete]</a></td>
+                               <td width="80%" align="left"><?php echo $data['name'];?>
+                       </tr>
+                       <?php
+               }
+       }
+       else 
+       {
+               ?>
+               <tr>
+                       <th colspan="2">Nothing in the database yet</th>
+               </tr>
+               <?php
+       }
+       ?>
+</table>
+<?php
+GLM_TOOLBOX::footer();
+?>
diff --git a/admin/Contact/list_query.phtml b/admin/Contact/list_query.phtml
new file mode 100755 (executable)
index 0000000..ab6d24b
--- /dev/null
@@ -0,0 +1,57 @@
+<?php
+require_once '../../setup.phtml';
+require_once 'contact_setup.inc';
+
+GLM_TOOLBOX::top("Query DB","");
+
+GLM_TOOLBOX::html_nav_table($nav,$navWidth);
+?>
+<script src="<?php echo MEDIA_BASE_URL."admin/wm.js"?>"></script>
+<script src="<?php echo MEDIA_BASE_URL."admin/msg.js"?>"></script>
+<table id="admin-list-table">
+<tr bgcolor="#2f4f4f">
+  <th class="theader">
+  Functions:
+  </th>
+  <th class="theader">
+  Queries in database
+  </th>
+</tr>
+<?php
+//if(!$dbd = db_connect()) html_error(DB_ERROR_MSG,0);
+
+$qs = "SELECT   id,query_name
+       FROM     query_db";
+
+if(!$res = $DB->db_exec($qs)) GLM_TOOLBOX::html_error(DB_ERROR_MSG.$qs,0);
+
+for($i=0;$i<$DB->db_numrows($res);$i++) {
+    $row = $DB->db_fetch_array($res,$i,PGSQL_ASSOC);
+
+?>
+    <script lang="javascript">
+    var o<?php echo $i?> = new Object();
+    o<?php echo $i?>.msg = 'You are about to Permanently Delete this Query';
+    o<?php echo $i?>.url = 'del_query.phtml?id=<?php echo $row[id]?>';
+    o<?php echo $i?>.popup = '1';
+    o<?php echo $i?>.popup.name = "delwin";
+    o<?php echo $i?>.width = 630;
+    o<?php echo $i?>.height = 300;
+    </script>
+<tr>
+ <td>
+ <a href="query_contact.phtml?query_no=<?php echo $row[id]?>">[Recall]</a>
+ <?php if($row[query_name] != "(current)") {?>
+ <a href="del_query.phtml?id=<?php echo $row[id]?>" onClick="
+ glm_confirm(o<?php echo $i?>);
+ return(false);
+ ">[Delete]</a>
+ <?php }?>
+ </td>
+  <td><b><?php echo $row[query_name]?></b></td>
+</tr>
+<?php }?>
+</table>
+<?php
+GLM_TOOLBOX::footer();
+?>
diff --git a/admin/Contact/mailout.phtml b/admin/Contact/mailout.phtml
new file mode 100644 (file)
index 0000000..c87e197
--- /dev/null
@@ -0,0 +1,160 @@
+<html>
+<head>
+<title>Mailing out The Newsletter</title>
+</head>
+<body bgcolor="white">
+<?php
+require_once '../../setup.phtml';
+require_once 'contact_setup.inc';
+
+// File names for SPAMerizer
+$Filename = tempnam( "/var/spool/SPAMerizer", "MOUNP" );
+unlink($Filename);
+
+$HeadFilename = $Filename.".head";
+$BodyFilename = $Filename.".body";
+$ToFilename = $Filename.".to";
+$ReadyFilename = $Filename.".ready";
+
+$postmail = stripslashes($postmail);
+
+$pattern   = '/SELECT.*FROM/is';
+$replace   = "SELECT email INTO TEMPORARY temp_table FROM";
+$postmail  = preg_replace($pattern, $replace, $postmail);
+$postmail  = preg_replace("/ORDER BY.*/is","", $postmail);
+$postmail .= " AND ".MAILOK;
+
+if (!$mailres = $DB->db_exec($postmail)) {
+    html_error(DB_ERROR_MSG.__LINE__.$postmail, 1);
+}
+
+$mailqs = "SELECT
+           DISTINCT ON (email) email
+           FROM     temp_table
+           GROUP BY email;";
+flush();
+if (!$mailres = $DB->db_exec($mailqs)) {
+    html_error(DB_ERROR_MSG.__LINE__.$mailqs, 1);
+}
+
+if ($DB->db_numrows($mailres)>0) {
+    for($a=0;$a<$DB->db_numrows($mailres);$a++) {
+        $mvdata = $DB->db_fetch_array($mailres, $a, PGSQL_ASSOC);
+        $email = trim($mvdata["email"]);
+        if ($email) {
+            $mail[] = $email;
+        }
+    }
+}
+if (is_array($mail) && count($mail)>0) {
+    // write the temp.to file
+    $mail = implode("\n", $mail);
+    $fp = fopen($ToFilename, "w");
+    fputs($fp, $mail, strlen($mail));
+    fclose($fp);
+} else {
+    $mail = "";
+}
+
+
+if ($mail != "") {
+    // I am changing this to a two part mime type email
+    // html and text
+    // using class_html
+    $responseqs = "SELECT   *
+                   FROM     news_response
+                   WHERE    id = $mail_id";
+    if (!$resres = $DB->db_exec($responseqs)) {
+        html_error(DB_ERROR_MSG.$responseqs, 0);
+    }
+
+    $responserow = $DB->db_fetch_array($resres, 0, PGSQL_ASSOC);
+
+    $subject = trim($responserow['subject']);
+    $data['subject'] = &$subject;
+    $message = $responserow['response'];
+    // html part of email
+    //$data['response'] = stripslashes(nl2br($message));
+    $data['response'] = $message;
+    $data['url'] = MEDIA_BASE_URL;
+
+    $html = explode_template(NEWSLETTER_PROTOTYPE,$data);
+
+    // text part of email
+    $text = strip_tags($message);
+    $text .= "\n\n-------------------------------------------------------------------\n";
+    $text .= "You are receiving this message because you have expressed an interest in\n";
+    $text .= "receiving specials and information from ".SITENAME.". If you do not\n";
+    $text .= "wish to receive future items of this nature, please reply to this e-mail\n";
+    $text .= "with the word \"CANCEL\" on the subject line. You will then be removed \n";
+    $text .= "from future mailings.\n";
+    $text .= "-------------------------------------------------------------------\n";
+
+    // Write the temp.header file
+    $glm_headers = "NotifyAddr: ".OWNER_EMAIL."\n"
+        . "ProcessName: ".SITENAME."\n"
+        . "From: ".FROM_NEWS_EMAIL."\n"
+        . "ReportInterval: 2\n"
+        . "BlockSize: 1\n"
+        . "ProductionMode: ".PRODUCTION_MODE."\n";
+
+    $fp = fopen($HeadFilename, "w");
+    fputs($fp, $glm_headers, strlen($glm_headers));
+    fclose($fp);
+
+    $headers =  "From: ".FROM_NEWS_EMAIL."\n".
+                "To: ".OWNER_EMAIL."\n".
+                "Subject: $subject\n".
+                "Reply-to: ".REPLY_TO."\n".
+                "Mime-Version: 1.0\n".
+                "Content-Type: multipart/alternative; boundary=ContentBoundry\n\n";
+    $fp = fopen($BodyFilename,"w");
+    if(HTML_EMAIL=="ON"){
+        $body_html = '--ContentBoundry
+Content-Type: text/plain; charset="UTF-8"
+'.$text.'
+--ContentBoundry
+Content-Type: text/html; charset="UTF-8"
+
+'.$html.'
+
+--ContentBoundry--';
+        fputs($fp, $headers, strlen($headers));
+        fputs($fp, $body_html, strlen($body_html));
+    } else {
+        fputs($fp, $headers, strlen($headers));
+        fputs($fp, $text, strlen($text));
+    }
+    fclose($fp);
+    // write the temp.ready file and your done!
+    $fp = fopen($ReadyFilename, "w");
+    fclose($fp);
+?>
+<table>
+<tr>
+  <td>Mail the current <?php echo $subject?></td>
+</tr>
+<tr>
+  <td><?php echo (PRODUCTION_MODE == "ON")?"ProductionMode is ON, Mail is sent.":"ProductionMode is OFF, Mail is not sent."?></td>
+</tr>
+<tr>
+  <td><?php echo (HTML_EMAIL == "ON")?"HTML Email is ON, Mail is html encoded.":"HTML Email is OFF, Mail is plain text."?></td>
+</tr>
+<tr>
+  <td>You will recieve notification on the mailing task by email at <?php echo OWNER_EMAIL?>.</td>
+</tr>
+</table>
+<?php
+} else {
+?>
+<table width=500 bgcolor="#e0e0e0">
+<tr bgcolor="#2f4f4f">
+  <th><font color=white>Newsletter Not Sent!</th>
+  </tr>
+</table>
+<?php
+}
+
+?>
+</body>
+</html>
diff --git a/admin/Contact/main.css b/admin/Contact/main.css
new file mode 100755 (executable)
index 0000000..fef38df
--- /dev/null
@@ -0,0 +1,23 @@
+body {
+       background-color: #FFFFFF;
+}
+
+.navlink {
+       font-size: 80%;
+       font-family: arial;
+}
+
+td {
+       font-size: 80%;
+       font-family: arial,helvetica;
+}
+
+.theader {
+       font-size: 120%;
+       font-family: arial,helvetica;
+       color: #FFFFFF;
+}
+
+.theadertd {
+       background-color: #000080;
+}
diff --git a/admin/Contact/msg.js b/admin/Contact/msg.js
new file mode 100755 (executable)
index 0000000..8ed837d
--- /dev/null
@@ -0,0 +1,29 @@
+function glm_confirm(o) {
+       var p = o.msg.split("\n");
+       var k = 0;
+       for(i = 0;i < p.length;i++) {
+               if(k > p[i].length)
+                       continue;
+               else 
+                       k = p[i].length;
+       }       
+       
+       var bound = "";
+       for(i = 0; i < k; i++) {
+               bound = bound+'_';
+       }
+       var str = bound+"\n\n"+o.msg+"\n\n"+bound+"\n\nAre You Sure?";
+       if(confirm(str)) {
+               if(o.popup == '1') {
+                       var nw = new Object();
+                       nw.url = o.url;
+                       nw.name = o.popup.name;
+                       nw.width = o.width;
+                       nw.height = o.height;
+                       glm_open(nw);
+               }
+               else {
+                       location.replace(o.url);
+               }
+       }
+}
diff --git a/admin/Contact/newsletter_template.html b/admin/Contact/newsletter_template.html
new file mode 100755 (executable)
index 0000000..9a145a7
--- /dev/null
@@ -0,0 +1,42 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+       <meta http-equiv="content-type" content="text/html;charset=utf-8">
+</head>
+<body>
+<table cellspacing="0" cellpadding="0" border="0" width="100%">
+       <tr>
+               <td align="center">
+      <table cellspacing="0" cellpadding="0" border="0" width="550" bgcolor="#ffffff">
+       <tr>
+               <td>
+                                               <a href="<!-- url -->"><img src="<!-- url -->assets/nHeader.jpg" width="550" height="114" alt="" style="border: 0; display: block;"></a>
+                                       </td>
+                               </tr>
+                               <tr>
+                                       <td>
+            <table cellspacing="15" cellpadding="0" border="0">
+               <tr>
+                       <td>
+                                                                       <font face="arial, helvetica, sans-serif" size="2">
+                                                                               <!-- response -->
+                                                                       </font>
+                                                               </td>
+                                                       </tr>
+                                                       <tr>
+                                                               <td>
+                                                                       <font face="arial, helvetica, sans-serif" size="1">
+                                                                       <hr>
+                                                                               <!-- bailout -->
+                                                                       </font>
+                                                               </td>
+                                                       </tr>
+                                               </table>
+                                       </td>
+                               </tr>
+                       </table>
+               </td>
+       </tr>
+</table>                                                                       
+</body>
+</html>
diff --git a/admin/Contact/notes/ChangeLog b/admin/Contact/notes/ChangeLog
new file mode 100755 (executable)
index 0000000..fc6c567
--- /dev/null
@@ -0,0 +1,327 @@
+2002-05-07 13:47  matrix
+
+       * contact_setup.inc, del_query.phtml, download.phtml,
+       edit_contact.phtml, form.js, index.phtml, list_contact.phtml,
+       list_query.phtml, mailout.phtml, main.css, msg.js,
+       query_contact.phtml, query_db.phtml, query_save.phtml,
+       update_contact.phtml, verify.js, wm.js, help/contact.phtml,
+       notes/ChangeLog, notes/Contact, notes/adm2.sql, notes/contact.sql,
+       notes/guest.sql: "version 2.4"
+
+2002-05-07 13:45  matrix
+
+       * contact.sql, contact_setup.inc, edit_contact.phtml,
+       list_contact.phtml, update_contact.phtml, notes/ChangeLog,
+       notes/contact.sql, notes/Contact: adding ChangeLog file and moving
+       sql file into notes.  I have also set the insert part of
+       update_contact.phtml to use nextval to generate the PRIMEKEY so
+       this will work with previous version of th shop which don't have
+       the default set on cust_id
+
+2002-05-07 11:14  matrix
+
+       * contact.sql, contact_setup.inc, del_query.phtml, download.phtml,
+       edit_contact.phtml, form.js, index.phtml, list_contact.phtml,
+       list_query.phtml, mailout.phtml, main.css, msg.js,
+       query_contact.phtml, query_db.phtml, query_save.phtml,
+       update_contact.phtml, verify.js, wm.js, help/contact.phtml,
+       notes/adm2.sql, notes/guest.sql: testing out both contact and
+       customer table use
+
+2002-05-07 10:08  matrix
+
+       * form.js, msg.js, verify.js, wm.js: "putting javascript files in
+       dir"
+
+2002-05-07 09:57  matrix
+
+       * index.phtml: "all versions now 2.0"
+
+2002-05-07 09:57  matrix
+
+       * index.phtml: new file
+
+2002-05-07 09:44  matrix
+
+       * admin_constants.inc, contact.phtml, contact.sql,
+       contact_setup.inc, contact_test.sql, del_query.phtml,
+       download.phtml, edit_contact.phtml, index.html, list_contact.phtml,
+       list_cust_form.phtml, list_customers.phtml, list_query.phtml,
+       mailout.phtml, main.css, path.phtml, query_contact.phtml,
+       query_db.phtml, query_save.phtml, shopping_cart_setup.inc,
+       update_contact.phtml, help/contact.phtml, notes/adm2.sql,
+       notes/guest.sql: "merging final changes into one app"
+
+2002-03-14 11:23  matrix
+
+       * download.phtml: removed offending dot
+
+2002-03-12 10:32  matrix
+
+       * contact_setup.inc: file contact_setup.inc was initially added on
+       branch glm-Contact-2-0.
+
+2002-03-12 10:32  matrix
+
+       * download.phtml: file download.phtml was initially added on branch
+       glm-Contact-2-0.
+
+2002-03-12 10:32  matrix
+
+       * contact.phtml, contact_setup.inc, del_query.phtml,
+       download.phtml, edit_contact.phtml, list_contact.phtml,
+       list_query.phtml, mailout.phtml, query_contact.phtml,
+       query_db.phtml, update_contact.phtml: make it customer and ocntact
+
+2002-03-12 09:36  matrix
+
+       * list_cust_form.phtml, list_customers.phtml, path.phtml,
+       shopping_cart_setup.inc: updates
+
+2002-03-12 09:34  matrix
+
+       * contact.phtml, del_query.phtml, edit_contact.phtml,
+       list_contact.phtml, list_query.phtml, query_contact.phtml,
+       query_db.phtml, update_contact.phtml: prepare for merging
+
+2001-12-17 10:13  matrix
+
+       * list_contact.phtml, mailout.phtml: added ID
+
+2001-12-17 10:02  matrix
+
+       * list_contact.phtml, mailout.phtml: mail can't be sent by url
+
+2001-11-27 16:50  matrix
+
+       * contact.phtml, del_query.phtml, edit_contact.phtml,
+       list_contact.phtml, list_query.phtml, query_contact.phtml,
+       query_db.phtml, query_save.phtml, update_contact.phtml: needed to
+       update adding contacts to customer table as there is no default
+       value for cust_id
+
+2001-11-21 14:07  matrix
+
+       * contact.phtml, del_query.phtml, edit_contact.phtml,
+       list_contact.phtml, list_query.phtml, path.phtml,
+       query_contact.phtml, query_db.phtml, update_contact.phtml: using
+       setup.phtml not path.phtml
+
+2001-11-07 14:30  matrix
+
+       * list_contact.phtml: removed echo
+
+2001-11-07 14:27  matrix
+
+       * contact.phtml, del_query.phtml, edit_contact.phtml,
+       list_query.phtml, mailout.phtml, path.phtml, query_contact.phtml,
+       query_db.phtml, update_contact.phtml: updatng now using setup.phtml
+
+2001-11-07 14:24  matrix
+
+       * list_contact.phtml: correcting email out code
+
+2001-10-15 15:19  matrix
+
+       * contact.phtml, query_contact.phtml: adding date search
+
+2001-10-11 14:44  matrix
+
+       * list_contact.phtml: updating
+
+2001-10-11 14:34  matrix
+
+       * mailout.phtml: file mailout.phtml was initially added on branch
+       glm-Contact-2-0.
+
+2001-10-11 14:32  matrix
+
+       * list_contact.phtml, mailout.phtml: added autoresponder
+
+2001-09-25 10:14  matrix
+
+       * path.phtml: changed the path so we use one file
+
+2001-09-25 10:13  matrix
+
+       * contact.phtml: tr tag
+
+2001-07-02 14:29  matrix
+
+       * path.phtml: symplified the path files now this referes to the
+       main one in admin
+
+2001-06-22 08:55  matrix
+
+       * contact.phtml, contact.sql, edit_contact.phtml,
+       update_contact.phtml: adding field referred_by
+
+2001-06-19 08:50  matrix
+
+       * list_contact.phtml: no real change
+
+2001-06-19 08:49  matrix
+
+       * update_contact.phtml, edit_contact.phtml: modified for mailok
+
+2001-06-19 08:45  matrix
+
+       * list_contact.phtml: modified for errors on recalls
+
+2001-06-19 08:45  matrix
+
+       * edit_contact.phtml, update_contact.phtml: modified for mailok
+
+2001-06-18 10:08  matrix
+
+       * query_db.phtml: shop_query_db
+
+2001-06-18 10:08  matrix
+
+       * help/helpbg.gif: file helpbg.gif was initially added on branch
+       glm-Contact-shop-1-0.
+
+2001-06-18 10:08  matrix
+
+       * help/: closewindow.gif, contact.phtml, helpbg.gif: added images
+       to help folder
+
+2001-06-18 10:08  matrix
+
+       * help/closewindow.gif: file closewindow.gif was initially added on
+       branch glm-Contact-shop-1-0.
+
+2001-06-18 10:05  matrix
+
+       * query_contact.phtml: shop_query_db
+
+2001-06-18 10:04  matrix
+
+       * list_query.phtml: added nav links
+
+2001-06-18 10:03  matrix
+
+       * list_query.phtml: new shop query db
+
+2001-06-11 13:14  matrix
+
+       * list_contact.phtml: error correction
+
+2001-06-11 10:51  matrix
+
+       * list_contact.phtml: if there are no queries insert current
+
+2001-06-11 10:31  matrix
+
+       * list_contact.phtml: if there are no contacts html_error
+
+2001-06-11 10:18  matrix
+
+       * list_query.phtml: added nav to top of page
+
+2001-06-11 10:15  matrix
+
+       * help/contact.phtml: corrected paths to help images
+
+2001-06-08 09:17  matrix
+
+       * contact.sql: changing query table name to keep from messing up
+       other application
+
+2001-06-08 09:16  matrix
+
+       * help/contact.phtml: updateing help file
+
+2001-06-08 09:12  matrix
+
+       * contact.phtml: changed radio buttons on mail_ok to drop down
+
+2001-06-08 08:50  matrix
+
+       * list_contact.phtml: modified
+
+2001-06-08 08:46  matrix
+
+       * contact.phtml: made the mail_ok a drop down
+
+2001-06-07 14:54  matrix
+
+       * contact.phtml, list_contact.phtml, query_contact.phtml: updated
+       per gloriebe contactdb
+
+2001-06-07 14:06  matrix
+
+       * query_contact.phtml, help/contact.phtml: made changes for ereg
+       wildcards
+
+2001-06-06 15:51  matrix
+
+       * contact.phtml, contact.sql, edit_contact.phtml,
+       list_contact.phtml, query_contact.phtml, query_save.phtml,
+       update_contact.phtml: shop version
+
+2001-06-06 15:42  matrix
+
+       * main.css: added file
+
+2001-06-06 15:40  matrix
+
+       * report.rpt: "removed"
+
+2001-06-06 15:00  matrix
+
+       * contact.phtml, list_contact.phtml, query_contact.phtml,
+       update_contact.phtml, help/contact.phtml: worked out some bugs
+
+2001-06-06 13:41  matrix
+
+       * help/contact.phtml: changed path on images
+
+2001-06-06 13:38  matrix
+
+       * main.css: adding needed files
+
+2001-06-06 13:38  matrix
+
+       * main.css: file main.css was initially added on branch
+       glm-Contact-2-0.
+
+2001-06-05 11:17  matrix
+
+       * path.phtml: changed path to help
+
+2001-06-05 11:13  matrix
+
+       * path.phtml: changed path to help
+
+2001-06-05 10:45  matrix
+
+       * path.phtml: added path file
+
+2001-06-05 10:38  matrix
+
+       * contact.phtml, list_contact.phtml, query_contact.phtml: added
+       pipe and csv delimiter
+
+2001-05-31 12:43  matrix
+
+       * contact.phtml, contact.sql, contact_test.sql, del_query.phtml,
+       edit_contact.phtml, list_contact.phtml, list_query.phtml,
+       query_contact.phtml, query_db.phtml, query_save.phtml,
+       update_contact.phtml, help/contact.phtml: combining the contact
+       databases
+
+2001-04-04 13:42  matrix
+
+       * admin_constants.inc, index.html, list_cust_form.phtml,
+       list_customers.phtml, path.phtml, report.rpt,
+       shopping_cart_setup.inc, notes/adm2.sql, notes/guest.sql: Initial
+       revision
+
+2001-04-04 13:42  matrix
+
+       * admin_constants.inc, index.html, list_cust_form.phtml,
+       list_customers.phtml, path.phtml, report.rpt,
+       shopping_cart_setup.inc, notes/adm2.sql, notes/guest.sql: imported
+       sources
+
diff --git a/admin/Contact/notes/Contact b/admin/Contact/notes/Contact
new file mode 100755 (executable)
index 0000000..937f46f
--- /dev/null
@@ -0,0 +1,4 @@
+All application setup stuff will be in contact_setup.phtml
+1)     right now if you add to the $fields array you'll still have to change
+       edit_contact.phtml and update_contact.phtml
+2)     contact.sql - contains the query to build the contact table and query_db table
diff --git a/admin/Contact/notes/contact.sql b/admin/Contact/notes/contact.sql
new file mode 100755 (executable)
index 0000000..4fd960b
--- /dev/null
@@ -0,0 +1,100 @@
+\connect - postgres
+
+CREATE TABLE "contact" (
+       "id" SERIAL PRIMARY KEY, 
+       "create_date" date default current_date,
+       "fname" text,
+       "lname" text,
+       "company" text,
+       "address" text,
+       "address2" text,
+       "city" text,
+       "state" text,
+       "zip" text,
+       "country" text,
+       "phone" text,
+       "fax" text,
+       "email" text,
+       "user_agent" text,
+       "remote_addr" text,
+       "interest" text,
+       "mail_ok" boolean default 'f',
+    "contact_type" text
+);
+
+REVOKE ALL on "contact" from PUBLIC;
+GRANT ALL on "contact" to "nobody";
+GRANT ALL on "contact" to "postgres";
+
+REVOKE ALL on "contact_id_seq" from PUBLIC;
+GRANT ALL on "contact_id_seq" to "nobody";
+GRANT ALL on "contact_id_seq" to "postgres";
+
+CREATE TABLE "query_db" (
+       "id" SERIAL PRIMARY KEY, 
+       "query_name" text,
+       "query" text,
+       "file" text,
+       "delimiter" text
+);
+
+REVOKE ALL on "query_db" from PUBLIC;
+GRANT ALL on "query_db" to "nobody";
+GRANT ALL on "query_db" to "postgres";
+
+REVOKE ALL on "query_db_id_seq" from PUBLIC;
+GRANT ALL on "query_db_id_seq" to "nobody";
+GRANT ALL on "query_db_id_seq" to "postgres";
+
+CREATE TABLE "news_response" (
+       "id" SERIAL PRIMARY KEY, 
+       "subject" text,
+       "response" text,
+       "image" text,
+       "image2" text,
+       "image3" text,
+       "image_align" text,
+       "image2_align" text,
+       "image3_align" text,
+       "mailout" date default current_date,
+       "last_update" date default current_date
+);
+
+REVOKE ALL on "news_response" from PUBLIC;
+GRANT ALL on "news_response" to "postgres";
+GRANT ALL on "news_response" to "nobody";
+GRANT ALL on "news_response_id_seq" to "nobody";
+
+INSERT INTO news_response (subject,response) values ('subject','response');
+
+CREATE UNIQUE INDEX contact_id_indx ON contact(id);
+CREATE INDEX contact_email_indx ON contact(email);
+CREATE INDEX contact_fname_indx ON contact(fname);
+CREATE INDEX contact_lname_indx ON contact(lname);
+CREATE INDEX contact_create_date_indx ON contact(create_date);
+CREATE UNIQUE INDEX news_response_id_indx ON contact(id);
+CREATE UNIQUE INDEX query_db_id_indx ON contact(id);
+
+CREATE TABLE contact_inq (
+    id serial NOT NULL,
+    header text,
+       pos integer,
+    description text,
+    image text,
+    groupid integer
+);
+REVOKE ALL ON TABLE contact_inq FROM PUBLIC;
+GRANT ALL ON TABLE contact_inq TO nobody;
+REVOKE ALL ON TABLE contact_inq_id_seq FROM PUBLIC;
+GRANT ALL ON TABLE contact_inq_id_seq TO nobody;
+ALTER TABLE ONLY contact_inq
+    ADD CONSTRAINT contact_inq_pkey PRIMARY KEY (id);
+
+CREATE TABLE inq_group (
+    id SERIAL PRIMARY KEY,
+    name text
+);
+GRANT ALL ON inq_group TO nobody;
+GRANT ALL ON inq_group_id_seq TO nobody;
+
+INSERT INTO inq_group (name) VALUES ('Areas of Interest');
diff --git a/admin/Contact/preview.phtml b/admin/Contact/preview.phtml
new file mode 100755 (executable)
index 0000000..aec6264
--- /dev/null
@@ -0,0 +1,14 @@
+<?php
+require_once '../../setup.phtml';
+require_once 'contact_setup.inc';
+
+$query = "SELECT * FROM news_response WHERE id = {$_REQUEST['id']}";
+$res = $DB->db_auto_get_data($query);
+$data['url'] = MEDIA_BASE_URL;
+$data['subject'] = $res[0]["subject"];
+$response = $res[0]["response"];
+$data['response'] = $response;
+
+$page = GLM_TOOLBOX::explode_template(NEWSLETTER_PROTOTYPE,$data);
+echo $page;
+?>
diff --git a/admin/Contact/query_contact.phtml b/admin/Contact/query_contact.phtml
new file mode 100755 (executable)
index 0000000..48c536c
--- /dev/null
@@ -0,0 +1,445 @@
+<?php
+session_start();
+require_once '../../setup.phtml';
+require_once 'contact_setup.inc';
+/* Includes  */
+extract($_REQUEST);
+function clean_array(&$array){
+    if (is_array($array)){
+        $counter = 0;
+        foreach ($array as $key=>$value){
+            if ($value == ""){
+                unset($array[$counter]);
+            }
+            $counter++;
+        }
+    }
+}
+$sess_vars = $_POST;
+$_SESSION['sess_vars'] = $sess_vars;
+$query_no = filter_var($_REQUEST['query_no'], FILTER_VALIDATE_INT);
+if (!$query_no) {
+    /* The fields array is sent as a string
+        explode it out using : as delimiter */
+    $fvalue = preg_replace("/^:/","",$fvalue);
+    $fields = explode(":",$fvalue);
+    $rfvalue = preg_replace("/^:/","",$rfvalue);
+    $return_fields = explode(":",$rfvalue);
+    $dates = preg_replace("/^:/","",$rdvalue);
+    $dates = explode(":",$dates);
+
+    clean_array($return_fields);
+    clean_array($dates);
+    if (!isset($search)) {
+        header("Location: index.phtml");
+    }
+    /* Chop off whitespaces spaces */
+    $search = chop(trim($search));
+    $search = preg_replace("/\\\|\"/", "", $search);
+    if (!$search && (count ($fields) == 1 && $fields[0] == '')) {
+        $ALL = TRUE;
+    }
+
+    function getKeywords($keywords) {
+        /* Replace the whitespace with a , */
+        $keywords = str_replace(" ",",",$keywords);
+        $keywords = str_replace(",,",",",$keywords);
+        $seperated = explode(",",$keywords);
+        /* Return exploded string */
+        return $seperated;
+    }
+
+    switch($search_type) {
+    case "1":
+        $keywords = $search;
+        $compare = "OR";
+        break;
+
+    case "2":
+        $keywords = getKeywords($search);
+        $compare = "AND";
+        break;
+
+    case "3":
+        $keywords = getKeywords($search);
+        $compare = "OR";
+        break;
+
+    case "4":
+        $keywords = getKeywords($search);
+        $compare = "AND";
+        $NOT = TRUE;
+        break;
+
+    default:
+        echo "not valid";
+        break;
+    }
+
+    if (is_array($keywords)) {
+        for ($rip=0;$rip<count($keywords);$rip++) {
+            $keywords[$rip] = trim($keywords[$rip]);
+            /* if * is at the begging the replace with .* */
+            $keywords[$rip] = preg_replace("/[\x2a]/",".*",$keywords[$rip]);
+            $keywords[$rip] = preg_replace("/[\x3f]/",".?",$keywords[$rip]);
+            $keywords[$rip] = preg_replace("/[\x2b]/",".+",$keywords[$rip]);
+        }
+    } else {
+        $keywords = trim($keywords);
+        /* if * is at the begging the replace with .* */
+        $keywords = preg_replace("/[\x2a]/",".*",$keywords);
+        $keywords = preg_replace("/[\x3f]/",".?",$keywords);
+        $keywords = preg_replace("/[\x2b]/",".+",$keywords);
+    }
+
+    switch($alter) {
+    /* $alter defines where to look in fields */
+    case "1":
+        $begin = "^";
+        $end = "";
+        break;
+
+    case "2":
+        $begin = "";
+        $end = " *$";
+        break;
+
+    default:
+        $begin = "";
+        $end = "";
+        break;
+    }
+
+    $operator = " ";
+    if ($NOT) {
+        $operator .= "!~";
+    } else {
+        $operator .= "~";
+    }
+    if ($case == "OFF") {
+        $operator .= "*";
+    }
+    if ($search == "" && ! (count ($fields)  == 1 && $fields[0] == '') )  {
+        $operator = "=";
+    }
+    $operator .= " ";
+
+    /* finally, build the query string from string or array $keywords */
+    $query_string = "SELECT ".ID.",";
+    $totali = count($return_fields)-1;
+    for ($i=0;$i<count($return_fields);$i++) {
+        $query_string .= $return_fields[$i];
+        if ($i != $totali) {
+            $query_string .= ",";
+        }
+        if ($i == 8) {
+            $query_string .= "\n";
+        }
+    }
+    $totald = count($dates)-1;
+    for ($i=0;$i<count($dates);$i++) {
+        if ($dates[$i] != " " && $dates[$i] != "") {
+            if ($i == 0) {
+                $query_string .= ",";
+            }
+        }
+        $query_string .= $dates[$i];
+        if ($i != $totald) {
+            $query_string .= ",";
+        }
+    }
+    if (!$ALL) {
+        $query_string .= "\nFROM\t".TABLE." \nWHERE\t".WHERE."\nAND\t";
+        $query_string .= "(";
+        for ($b=0;$b<count($fields);$b++) {
+            $totalb = count($fields)-1;
+            if (is_array($keywords)) {
+                for ($c=0;$c<count($keywords);$c++) {
+                    $totalc = count($keywords)-1;
+                    $query_string .= $fields[$b].$operator."'".
+                        $begin.$keywords[$c].$end."'";
+                    if ($c != $totalc) {
+                        $query_string .= " \n$compare\t";
+                    }
+                }
+            } else {
+                $query_string .= $fields[$b].$operator."'".
+                    $begin.$keywords.$end."'";
+            }
+            if ($b != $totalb) {
+                $query_string .= " \n$compare\t";
+            }
+        }
+        $query_string .= ")";
+    } else {
+        $query_string .= "\nFROM\t".TABLE." \nWHERE\t".WHERE."\n";
+    }
+    if (is_array ($boolean) ) {
+        foreach ($boolean as $bool_key => $bool_val) {
+            if ($_POST[$bool_key] == "1") {
+                $bstring[] = " $bool_key = 't'";
+            } elseif ($_POST[$bool_key] == "0") {
+                $bstring[] = " $bool_key = 'f'";
+            }
+        }
+        if (is_array($bstring)) {
+            $query_string .= ' and ('.implode(" $compare ",$bstring) .')  ';
+        }
+    }
+    if( is_array( $dropdowns ) ) {
+        foreach( $dropdowns as $drop_key => $drop_val ) {
+            if( is_array( $_POST[$drop_key] ) ) {
+                foreach( $_POST[$drop_key] as $mdrop_key => $mdrop_val ){
+                    if( $mdrop_val != '' ){
+                        $dstring[] = " $drop_key $operator ':".$mdrop_val.":'";
+                    }
+                }
+            }elseif( !is_array( $_POST[$drop_key] ) ) {
+                if( $_POST[$drop_key] != '' ){
+                    $dstring[] = " $drop_key = '".$_POST[$drop_key]."'";
+                }
+            }
+        }
+        if( is_array($dstring) ) {
+            $query_string .= ' and ('.implode(" $compare ",$dstring).' ) ';
+        }
+    }
+    if (is_array ($cols) ) {
+        foreach ($cols as $ikey => $ival) {
+            $cstring[] = " interest $operator ':$ival:'";
+        }
+        if (is_array($cstring)) {
+            $query_string .= ' and ('.implode(" $compare ",$cstring).')  ';
+        }
+    }
+    $contactFromDate = filter_var(
+        $_REQUEST['contact_from_date'],
+        FILTER_VALIDATE_REGEXP,
+        array(
+            'options' =>
+            array(
+            'regexp' => '%[0-9]{2}/[0-9]{2}/[0-9]{4}%')
+        )
+    );
+    $contactToDate = filter_var(
+        $_REQUEST['contact_to_date'],
+        FILTER_VALIDATE_REGEXP,
+        array(
+            'options' =>
+            array(
+            'regexp' => '%[0-9]{2}/[0-9]{2}/[0-9]{4}%')
+        )
+    );
+    if ($contactFromDate && $contactToDate) {
+        $query_string .= " AND create_date >= '{$contactFromDate}'"
+        . " AND create_date <= '{$contactToDate}'";
+    } else if ($contactFromDate) {
+        $query_string .= " AND create_date >= '{$contactFromDate}'";
+    } else if ($contactToDate) {
+        $query_string .= " AND create_date <= '{$contactToDate}'";
+    }
+
+    if (isset($fp_month)) {
+        $fp_str = mktime(0,0,0,$fp_month,$fp_day,$fp_year);
+        $tp_str = mktime(0,0,0,$tp_month,$tp_day,$tp_year);
+        $fa_str = mktime(0,0,0,$fa_month,$fa_day,$fa_year);
+        $ta_str = mktime(0,0,0,$ta_month,$ta_day,$ta_year);
+
+        if ($fp_str<$tp_str) {
+            $fp_date = $fp_month."/".$fp_day."/".$fp_year;
+            $tp_date = $tp_month."/".$tp_day."/".$tp_year;
+            $query_string .= " AND purch_date >= '$fp_date'
+                               AND purch_date <= '$tp_date'";
+        }
+        if ($fa_str<$ta_str) {
+            $fa_date = $fa_month."/".$fa_day."/".$fa_year;
+            $ta_date = $ta_month."/".$ta_day."/".$ta_year;
+            $query_string .= " AND access_date >= '$fa_date'
+                               AND access_date <= '$ta_date'";
+        }
+        if ($fp_str>$tp_str) {
+            $fp_date = $fp_month."/".$fp_day."/".$fp_year;
+            $tp_date = $tp_month."/".$tp_day."/".$tp_year;
+            $query_string .= " AND purch_date <= '$tp_date'";
+        }
+        if ($fa_str>$ta_str) {
+            $fa_date = $fa_month."/".$fa_day."/".$fa_year;
+            $ta_date = $ta_month."/".$ta_day."/".$ta_year;
+            $query_string .= " AND access_date <= '$ta_date'";
+        }
+    }
+    if (isset($fc_month)) {
+        $fc_str = mktime(0,0,0,$fc_month,$fc_day,$fc_year);
+        $tc_str = mktime(0,0,0,$tc_month,$tc_day,$tc_year);
+
+        if ($fc_str<$tc_str) {
+            $fc_date = $fc_month."/".$fc_day."/".$fc_year;
+            $tc_date = $tc_month."/".$tc_day."/".$tc_year;
+            $query_string .= " AND create_date >= '$fc_date'
+                               AND create_date <= '$tc_date'";
+        }
+        if ($fc_str>$tc_str) {
+            $fc_date = $fc_month."/".$fc_day."/".$fc_year;
+            $tc_date = $tc_month."/".$tc_day."/".$tc_year;
+            $query_string .= " AND create_date <= '$tc_date'";
+        }
+    }
+} else {
+    $sql          = "
+    SELECT query_name,query,delimiter,file
+      FROM query_db
+     WHERE id = $query_no";
+    $stmt         = $DB->dbh->query($sql);
+    $row          = $stmt->fetch(PDO::FETCH_ASSOC);
+    $query_name   = $row['query_name'];
+    $file         = $row['file'];
+    $delimiter    = $row['delimiter'];
+    $query_string = $row['query'];
+    //var_dump($query_string);
+    //exit;
+}
+/* Thought the customer would like to see what's in the query */
+$showq = str_replace("SELECT","Return\n",$query_string);
+$showq = str_replace ("\nFROM\t".TABLE." \nWHERE\t".WHERE."\nAND\t",
+" \nfrom the contact database \nwhere ",$showq);
+$showq = str_replace ("\nFROM\t".TABLE." \nWHERE\t".WHERE."\n",
+" \nfrom the contact database",$showq);
+$showq = str_replace("fname","first name",$showq);
+$showq = str_replace("cust_id,","",$showq);
+$showq = str_replace("lname","last name",$showq);
+$showq = str_replace("!~*","does not contain",$showq);
+$showq = str_replace("!~","does not contain",$showq);
+$showq = str_replace("~*","contains",$showq);
+$showq = str_replace("~","is in",$showq);
+$showq = str_replace("does not contain '^"," does not start with ",$showq);
+$showq = str_replace("contains '^"," starts with ",$showq);
+$showq = str_replace("is in '^"," starts with ",$showq);
+$showq = str_replace("$"," in the ending ",$showq);
+$showq = str_replace("OR","or",$showq);
+$showq = str_replace("AND","and",$showq);
+$showq = str_replace("'","",$showq);
+if (!$ALL) {
+    if ($case == "OFF") {
+        $showq .= "\n(case insensitive match)";
+    } else {
+        $showq .= "\n(case sensitive match)";
+    }
+}
+if (isset($file) && $file != "") {
+    $showq .= "\noutput 1 file in ";
+    if ($file == "rpt") {
+        $showq .= "text";
+    }elseif ($file == "gz") {
+        $showq .= "tar ball";
+    }else {
+        $showq .= "zip";
+    }
+    if ($delimiter == "csv") {
+        $showq .= " format using ".$delimiter;
+    } else {
+        $showq .= " format using ".$delimiter." as delimiter";
+    }
+}
+$showq .= ".";
+$query = $query_string;
+
+GLM_TOOLBOX::top("QUERY BUILDER PAGE","");
+GLM_TOOLBOX::html_nav_table($nav,$navWidth);
+?>
+<script src="<?php echo MEDIA_BASE_URL."admin/wm.js"?>"></script>
+<script src="<?php echo MEDIA_BASE_URL."admin/msg.js"?>"></script>
+
+<table id="admin-list-table">
+<tr>
+  <th bgcolor="#2f4f4f" class="theader">
+    Submit Query
+  </th>
+  </tr>
+    <tr>
+    <td><a href="index.phtml">Go Back to Query page</a></td>
+    </tr>
+  <tr>
+  <td>
+    <?php echo nl2br($showq)?>
+    <br>
+    <?php if (isset($query_name)) {
+    echo "Query ".$query_name." Recalled";
+    }?>
+
+    <form action="list_contact.phtml" method="POST">
+    <input type="hidden" name="delimiter" value="<?php echo $delimiter?>">
+    <input type="hidden" name="file" value="<?php echo $file?>">
+    <?php
+    if (!$_GET['query_no']) {
+    ?>
+    <input type="hidden" name="query_string" value="<?php echo $query_string?>">
+    <?php
+    } else {
+    ?>
+    <input type="hidden" name="query_no" value="<?php echo $query_no;?>">
+    <?php
+    }
+    ?>
+    <input type="hidden" name="Submit" value="Submit Query">
+    <center>
+    <input type="submit" value="Send Query">
+    </form>
+    </center>
+    </td>
+  </tr>
+</table>
+<script lang="javascript">
+    var o_save = new Object();
+    o_save.url = 'query_save.phtml';
+    o_save.name = 'savewin';
+    o_save.width = 510;
+    o_save.height = 150;
+</script>
+<table>
+  <tr>
+  <th bgcolor="#2f4f4f" class="theader">
+    Do you wish to save this query for future use?
+  </th>
+  </tr>
+  <tr>
+    <td>
+    <a href="#" onClick="
+    glm_open(o_save);
+    return(false);
+    ">Save This Report</a>
+    </td>
+</tr>
+</table>
+
+<?php
+/* Save the query with (current) as query_name */
+$qs = "SELECT id
+         FROM query_db
+        WHERE query_name = '(current)'";
+$res = $DB->dbh->query($qs);
+if (!$rowId = $res->fetchColumn()) {
+    $sql = "
+    INSERT INTO query_db
+    (query_name, query, file, delimiter)
+    VALUES
+    ('(current)', :query, :file, :delimiter)";
+} else {
+    $sql = "
+    UPDATE query_db
+       SET query = :query,
+           file = :file,
+           delimiter = :delimiter
+     WHERE id = :id";
+}
+// save query
+$stmt = $DB->dbh->prepare($sql);
+$stmt->bindParam(':query', $query);
+$stmt->bindParam(':file', $file);
+$stmt->bindParam(':delimiter', $delimiter);
+if ($rowId) {
+    $stmt->bindParam(':id', $rowId);
+}
+$stmt->execute();
+
+GLM_TOOLBOX::footer();
+?>
diff --git a/admin/Contact/query_db.phtml b/admin/Contact/query_db.phtml
new file mode 100755 (executable)
index 0000000..97f9371
--- /dev/null
@@ -0,0 +1,19 @@
+<?php
+require_once '../../setup.phtml';
+require_once 'contact_setup.inc';
+
+if(!isset($file)) $file = "";
+if(!isset($delimiter)) $delimiter = "";
+
+$qs = "INSERT
+       INTO     query_db (query_name,query,file,delimiter)
+      (select '$query_name',query,file,delimiter from query_db where query_name = '(current)' limit 1 offset 0)";
+if(!$DB->db_auto_exec($qs))
+    {
+        html_error(DB_ERROR_MSG.$qs,1);
+    }
+GLM_TOOLBOX::html_header("Saving Query","Saved","");
+?>
+Query is saved as <?php echo $query_name?>
+<center><a href="#" onClick="window.close();return(false);">Close This
+Window</a></center>
diff --git a/admin/Contact/query_save.phtml b/admin/Contact/query_save.phtml
new file mode 100755 (executable)
index 0000000..596720c
--- /dev/null
@@ -0,0 +1,18 @@
+<html>
+<body bgcolor=white>
+<table bgcolor="#e0e0e0" width=500 cellpadding=4 cellspacing=0 border=0>
+  <tr>
+    <td>Name of Report
+
+    <form name="form2" action="query_db.phtml" method="POST">
+    <input type="hidden" name="query" value="<?php echo $query_string?>">
+    <input type="hidden" name="delimiter" value="<?php echo $delimiter?>">
+    <input type="hidden" name="file" value="<?php echo $file?>">
+    <input name="query_name">
+    <input type="submit" name="Submit" value="Save">
+    </form>
+    </td>
+</tr>
+</table>
+</body>
+</html>
diff --git a/admin/Contact/update_autoresponse.phtml b/admin/Contact/update_autoresponse.phtml
new file mode 100755 (executable)
index 0000000..4c2578e
--- /dev/null
@@ -0,0 +1,82 @@
+<?php
+require_once '../../setup.phtml';
+require_once 'contact_setup.inc';
+
+
+switch ($Command) {
+
+case "Update":// {{{
+    $location = "emails.php";
+    $dbh = Toolkit_Database::getInstance();
+    $id       = filter_input(INPUT_POST, 'id', FILTER_SANITIZE_NUMBER_INT);
+    if (!$id) {
+        break;
+    }
+
+    $sql = "
+    UPDATE news_response
+       SET subject = :subject,
+           response = :response,
+           last_update = current_date
+     WHERE id = :id";
+    $update = $dbh->prepare($sql);
+    $update->bindParam(
+        ':subject',
+        stripslashes($subject),
+        PDO::PARAM_STR
+    );
+    $update->bindParam(
+        ':response',
+        stripslashes($response),
+        PDO::PARAM_STR
+    );
+    $update->bindParam(
+        ':id',
+        $id,
+        PDO::PARAM_INT
+    );
+    $update->execute();
+    break;// }}}
+
+case "Insert":// {{{
+    $dbh = Toolkit_Database::getInstance();
+    $sql = "
+    INSERT INTO news_response
+        (last_update,subject,response)
+        VALUES
+    (current_date,:subject,:response)";
+    $insert = $dbh->prepare($sql);
+    $insert->bindParam(
+        ':subject',
+        stripslashes($subject),
+        PDO::PARAM_STR
+    );
+    $insert->bindParam(
+        ':response',
+        stripslashes($response),
+        PDO::PARAM_STR
+    );
+    $insert->execute();
+    $location = "emails.php";
+
+    break;// }}}
+
+case "Cancel":// {{{
+    $location = "emails.php";
+    break;// }}}
+
+case "Delete":// {{{
+    $query = "
+    DELETE FROM news_response
+    WHERE id = $id;";
+    $DB->db_exec( $query );
+    $location = "emails.php";
+    break;// }}}
+
+default:// {{{
+    GLM_TOOLBOX::html_error("incorrect value for Command",1);
+    break;// }}}
+}
+
+header("Location: $location");
+?>
diff --git a/admin/Contact/update_contact.phtml b/admin/Contact/update_contact.phtml
new file mode 100755 (executable)
index 0000000..a6c1ee0
--- /dev/null
@@ -0,0 +1,39 @@
+<?php
+require_once '../../setup.phtml';
+require_once 'contact_setup.inc';
+$location = "list_contact.phtml?back=1&start=" . $start;
+
+if (is_array($contact_type)) {
+    $contact_type = ":".implode(":",$contact_type).":";
+}
+if (is_array($interest)) {
+    $interest = ":".implode(":",$interest).":";
+}
+GLM_TOOLBOX::http_strip($url);
+
+$LAST = count($DB_fields)-1;
+if ($REQUEST_METHOD == "POST" || $Command == "Delete") {
+    switch ($Command) {
+    case "Delete":
+        $qs = "DELETE FROM ".TABLE."
+               WHERE ".ID." = $id";
+
+        if (!$DB->db_auto_exec($qs)) {
+            $ERRORS .= pg_errormessage($dbd).$qs;
+        }
+        $location .= '&Action=Contact+Deleted';
+
+        break;
+
+    case "Cancel":
+        break;
+
+    default:
+         $ERRORS .= "incorrect value for Command";
+        break;
+
+    }
+
+    header("Location: $location");
+}
+?>
diff --git a/admin/Contact/update_inquiry.phtml b/admin/Contact/update_inquiry.phtml
new file mode 100755 (executable)
index 0000000..975673a
--- /dev/null
@@ -0,0 +1,118 @@
+<?php
+include "../../setup.phtml";
+include "contact_setup.inc";
+switch ($Command) {
+case "Move":
+    $qs = "select pos,id from contact_inq where id = $id";
+    if (!$result = $DB->db_exec($qs)) {
+        GLM_TOOLBOX::html_error(DB_ERROR_MSG.$qs, 0);
+    }
+    $data = $DB->db_fetch_array($result, 0, PGSQL_ASSOC);
+    $pos = $data['pos'];
+    if ($newpos < $pos) {
+        $qs = "select id,pos from contact_inq where pos < $pos and pos >= $newpos and groupid = $groupid order by pos";
+        if(!$res = $DB->db_exec($qs)) {
+            GLM_TOOLBOX::html_error(DB_ERROR_MSG.$qs, 0);
+        }
+        $counter = ($newpos + 1);
+        $totalNum = $DB->db_numrows($res);
+        for ($i = 0;$i < $totalNum; $i++) {
+            $res_data = $DB->db_fetch_array($res, $i, PGSQL_ASSOC);
+            $res_id = $res_data['id'];
+            $res_pos = $res_data['pos'];
+            $qs = "update contact_inq set pos = $counter where id = $res_id";
+            if (!$DB->db_exec($qs)) {
+                GLM_TOOLBOX::html_error(DB_ERROR_MSG.$qs, 0);
+            }
+            $counter++;
+        }
+    } else {
+        $qs = "select pos,id from contact_inq where pos > $pos and pos <= $newpos and groupid = $groupid order by pos";
+        if (!$res = $DB->db_exec($qs)) {
+            GLM_TOOLBOX::html_error(DB_ERROR_MSG.$qs, 0);
+        }
+        $counter = ($pos);
+        $totalNum = $DB->db_numrows($res);
+        for ( $i = 0; $i < $totalNum; $i++) {
+            $res_data = $DB->db_fetch_array($res, $i, PGSQL_ASSOC);
+            $res_id = $res_data['id'];
+            $res_pos = $res_data['pos'];
+            $qs = "update contact_inq set pos = $counter where id = $res_id";
+            if (!$DB->db_exec($qs)) {
+                GLM_TOOLBOX::html_error(DB_ERROR_MSG.$qs, 0);
+            }
+            $counter++;
+        }
+    }
+    $qs = "update contact_inq set pos = $newpos where id = $id";
+    if (!$DB->db_exec($qs)) {
+        GLM_TOOLBOX::html_error(DB_ERROR_MSG.$qs, 0);
+    }
+    break;
+
+case "Edit":
+    if ($_POST['new_group']) {
+        $query = "select id from inq_group where name = '".$_POST['new_group']."'";
+        if ($newData = $DB->db_auto_get_data($query)) {
+            // already exists use the id returned
+            $groupid = $newData[0]['id'];
+        } else {
+            $query = "
+            INSERT INTO inq_group
+            (name)
+            VALUES
+            ('{$_POST['new_group']}')
+            RETURNING id";
+            if ($newData = $DB->db_auto_get_data($query)) {
+                $groupid = $newData[0]['id'];
+            }
+        }
+    }
+    $qs = "update contact_inq set header = '$header',image = '$image_name',description = '$description',groupid = $groupid where id = $id;";
+    $DB->db_exec($qs);
+    break;
+
+case "Add":
+    if ($_POST['new_group']) {
+        $query = "select id from inq_group where name = '".$_POST['new_group']."'";
+        if ($newData = $DB->db_auto_get_data($query)) {
+            // already exists use the id returned
+            $groupid = $newData[0]['id'];
+        } else {
+            $query = "
+            INSERT INTO inq_group
+            (name)
+            VALUES
+            ('{$_POST['new_group']}')
+            RETURNING id";
+            if ($newData = $DB->db_auto_get_data($query)) {
+                $groupid = $newData[0]['id'];
+            }
+        }
+    }
+    $qs = "select MAX(pos) as maxpos from contact_inq where groupid = $groupid";
+    $res = $DB->db_exec($qs);
+    $row = $DB->db_fetch_array($res,0,PGSQL_ASSOC);
+    $nextpos = $row[maxpos];
+    $nextpos++;
+    $qs = "insert into contact_inq (header,description,image,pos,groupid) values ('$header','$description','$image_name',$nextpos,$groupid);";
+    $DB->db_exec($qs);
+    break;
+
+case "Delete":
+    $qs = "delete from contact_inq where id = $id";
+    $DB->db_exec($qs);
+    if ($_REQUEST['old_groupid']) {
+       $qs = "select pos,id from contact_inq where pos > $oldpos and groupid = $old_groupid order by pos";
+        $res = $DB->db_exec($qs);
+        $oldcatid_counter = $oldpos;
+        $totalNum = $DB->db_numrows($res);
+        for ($i = 0; $i < $totalNum; $i++) {
+            $row = $DB->db_fetch_array($res, $i, PGSQL_ASSOC);
+            $qs = "update contact_inq set pos = $oldcatid_counter where id = $row[id]";
+        }
+        $DB->db_exec($qs);
+    }
+    break;
+}
+header("Location: contact_inquiry.phtml");
diff --git a/admin/Contact/verify.js b/admin/Contact/verify.js
new file mode 100755 (executable)
index 0000000..a254639
--- /dev/null
@@ -0,0 +1,72 @@
+function isblank(s) {
+       for(var i = 0; i < s.length; i++) {
+               var c = s.charAt(i);
+               if((c != ' ') && (c != '\n') && (c != '\t'))
+                       return(false);
+       }
+       return(true);
+}
+
+function verify(f) {
+       var msg;
+       var empty_fields = "";
+       var errors = "";
+
+       for(var i = 0; i < f.length; i++) {
+               var e = f.elements[i];
+               if(((e.type == "text") || (e.type == "textarea")) && !e.optional) {
+                       if((e.value == null) || (e.value == "") || isblank(e.value)) {
+                               empty_fields += "\n             " + e.r;
+                               continue;
+                       }
+
+                       if(e.d) {
+                               if(isNaN(Date.parse(e.value)))
+                                       errors += "- The field " +e.r+" must be formated like 01/17/2001\n";
+                       }
+                       if(e.numeric || (e.min != null) || (e.max != null)) {
+                               if(e.i) {
+                                       var v = parseInt(e.value);
+                                       if(v != e.value) {
+                                               errors += "- The field " +e.r + " must be a ";
+                                               errors += "number with no decimal\n";
+                                               continue;
+                                       }
+                               }
+                               else
+                                       var v = parseFloat(e.value);
+                               if(isNaN(v) ||
+                                       ((e.min != null) && (v < e.min)) ||
+                                       ((e.max != null) && (v > e.max))) {
+
+                                       errors += "- The field " + e.r + " must be a number";
+                                       if(e.min != null)
+                                               errors += " that is greater than " + e.min;
+                                       if(e.max != null && e.min != null)
+                                               errors += " and less than " + e.max;
+                                       else if (e.max != null)
+                                               errors += " that is less than " + e.max;
+                                       errors += ".\n";
+                               }
+                       }
+               }
+       }
+
+       if(!empty_fields && !errors)
+               return(true);
+
+       msg = "_____________________________________________________\n\n";
+       msg +="The form was not submitted because of the following error(s).\n";
+       msg +="Please correct these error(s) and re-submit.\n";
+       msg +="_____________________________________________________\n\n";
+
+       if(empty_fields) {
+               msg += "- The following required field(s) are empty:"
+                               + empty_fields + "\n";
+               if(errors)
+                       msg += "\n";
+       }
+       msg += errors;
+       alert(msg);
+       return(false);
+}
diff --git a/admin/Contact/view_newsletter.phtml b/admin/Contact/view_newsletter.phtml
new file mode 100755 (executable)
index 0000000..1c13387
--- /dev/null
@@ -0,0 +1,22 @@
+<?php
+require_once '../../setup.phtml';
+require_once 'contact_setup.inc';
+define("STYLE","main.css");
+if ($_REQUEST['id'] == '') {
+    $_REQUEST['id'] = 1;
+}
+
+GLM_TOOLBOX::top("AutoReponse for Newsletter", HELP_BASE."response.phtml?key=edit+section");
+GLM_TOOLBOX::html_nav_table($nav,$navWidth);   
+
+echo'<iframe src="preview.phtml?id='.$_REQUEST['id'].'"
+width="780" height="480"
+align="center">
+</iframe>
+
+  </td>
+</tr>
+</table>';
+
+GLM_TOOLBOX::footer();
+?>
diff --git a/admin/Contact/wm.js b/admin/Contact/wm.js
new file mode 100755 (executable)
index 0000000..7a7323e
--- /dev/null
@@ -0,0 +1,13 @@
+function glm_open(o) {
+       var x = (screen.width/2) - (o.width/2);
+       var y = (screen.height/2) - (o.height/2);
+       var args = "width="+o.width+",height="+o.height+",screenX="+x+",screenY="+y+",top="+y+",left="+x;
+       if(o.scroll == true)
+               args += ",scrollbars=1";
+       //args += "\'";
+       //alert(args);
+       pow=window.open(o.url,o.name,args);
+       //confirm(args);
+       if (pow.opener == null)
+               pow.opener = self;
+}
diff --git a/admin/Events/down.gif b/admin/Events/down.gif
new file mode 100755 (executable)
index 0000000..ea554f4
Binary files /dev/null and b/admin/Events/down.gif differ
diff --git a/admin/Events/editEvent.php b/admin/Events/editEvent.php
new file mode 100644 (file)
index 0000000..16f510c
--- /dev/null
@@ -0,0 +1,44 @@
+<?php
+require_once '../../setup.phtml';
+require_once 'event_setup.inc';
+$dbh = Toolkit_Database::getInstance();
+
+$pageUrl
+    = MEDIA_BASE_URL . 'admin/Events/editEvent.php';
+$params = array();
+if ($_GET['archived']) {
+    $params[] = 'archived=' . $_GET['archived'];
+}
+if ($_GET['pending']) {
+    $params[] = 'pending=' . $_GET['pending'];
+}
+if ($_GET['start']) {
+    $params[] = 'start=' . $_GET['start'];
+}
+if ($_GET['Query']) {
+    $params[] = 'Query=' . urlencode($_GET['Query']);
+}
+if ($_GET['date_search_to']) {
+    $params[] = 'date_search_to=' . $_GET['date_search_to'];
+}
+if ($_GET['date_search_from']) {
+    $params[] = 'date_search_from=' . $_GET['date_search_from'];
+}
+if (!empty($params)) {
+    $pageUrl
+        .= '?'
+        . implode('&', $params);
+}
+$form = new Toolkit_Events_Forms_Admin_EditEvent(
+    $dbh,
+    'edit_event',
+    'post',
+    $pageUrl
+);
+
+$form->configureForm($dbh);
+$formOut = $form->toHtml($dbh);
+echo GLM_TOOLBOX::top('Edit Event', '');
+echo event_nav($lnav);
+echo $formOut;
+echo GLM_TOOLBOX::footer();
diff --git a/admin/Events/editTopics.php b/admin/Events/editTopics.php
new file mode 100644 (file)
index 0000000..0da08d3
--- /dev/null
@@ -0,0 +1,23 @@
+<?php
+require_once '../../setup.phtml';
+require_once 'event_setup.inc';
+$dbh = Toolkit_Database::getInstance();
+
+$config = new Config;
+$root   =& $config->parseConfig(
+    BASE . 'Toolkit/Toolbox/config.ini',
+    'IniFile'
+);
+
+$form = new Toolkit_Events_Forms_Admin_EditTopics(
+    $dbh,
+    'edit_event',
+    'post',
+    MEDIA_BASE_URL . "admin/Events/editTopics.php"
+);
+
+$formOut = $form->toHtml($dbh);
+echo GLM_TOOLBOX::top('Edit Categories', '');
+echo event_nav($lnav);
+echo $formOut;
+echo GLM_TOOLBOX::footer();
diff --git a/admin/Events/event.css b/admin/Events/event.css
new file mode 100644 (file)
index 0000000..151bb3d
--- /dev/null
@@ -0,0 +1,142 @@
+/* temp*/
+body {padding-left: 50px;}
+.clearer {
+ height:1px;
+ overflow:hidden;
+ margin-top:-1px;
+ clear:left;
+}
+h1 {font-family: arial, helvetica, sans-serif; font-size: 18px; color: #777;}
+/* end temp */
+
+/* MAIN PAGE PROPERTIES */
+#topic-list,
+#event-list,
+#event-edit {font-family: arial, helvetica, sans-serif; padding: 20px;font-size: 12px; }
+#topic-list {width: 400px;}
+/*The Row*/
+.topic-row {clear: left;margin: 3px 0; padding: 3px; background-color: #eee; border: 1px solid #ddd; font-size: 12px;}
+.topic-row table {width: 100%;}
+
+/*The Color Picker Square*/
+.colorsquare {height: 18px; width: 18px; border: 1px solid #333; cursor: pointer;}
+/*Name, Edit, Delete*/
+.topic-name { margin: 3px 0 0 6px; font-size: 12px;}
+.topic-name input {width: 200px; }
+.topic-save { margin-left: 5px;}
+.topic-delete { margin-left: 5px;}
+/*Palette ID\'s*/
+/* The Color Palette*/
+.colorbox {top: -21px; left:-100px; clear: left;}
+.colorbox table {width: auto;}
+.colorbox table{
+       border: 1px solid #333; 
+       border-collapse: collapse;
+}
+.colorbox table td {
+       border: 1px solid #333;
+       border-collapse: collapse;
+       height: 19px;
+       width: 19px;
+       cursor: pointer;
+       padding: 0;
+}
+.color1 {background-color: #FFCCCC;}
+.color2 {background-color: #CC9999;}
+.color3 {background-color: #FF9999;}
+.color4 {background-color: #FFCCFF;}
+.color5 {background-color: #CC99CC;}
+
+.color6 {background-color: #FF99FF;}
+.color7 {background-color: #CCCCFF;}
+.color8 {background-color: #9999CC;}
+.color9 {background-color: #9999FF;}
+.color10 {background-color: #CCFFFF;}
+
+.color11 {background-color: #99CCCC;}
+.color12 {background-color: #99FFFF;}
+.color13 {background-color: #CCFFCC;}
+.color14 {background-color: #99CC99;}
+.color15 {background-color: #99FF99;}
+
+.color16 {background-color: #FFFFCC;}
+.color17 {background-color: #CCCC99;}
+.color18 {background-color: #FFFF99;}
+.color19 {background-color: #FFCC99;}
+.color20 {background-color: #FF99CC;}
+
+.color21 {background-color: #CC99FF;}
+.color22 {background-color: #99CCFF;}
+.color23 {background-color: #99FFCC;}
+.color24 {background-color: #CCFF99;}
+.color25 {background-color: #CCCCCC;}
+
+/* events admin */
+ul.event_nav 
+{
+margin: 0;
+padding: 0 0 20px 0px;
+border-bottom: 1px solid #000;
+font-family: arial, helvetica, sans-serif;
+margin-bottom: 20px;
+font-size: 12px;}
+
+ul.event_nav, ul.event_nav  li
+{
+margin: 0;
+padding: 0;
+display: inline;
+list-style-type: none;
+}
+
+ul.event_nav  a:link, ul.event_nav  a:visited {
+float: left;
+line-height: 12px;
+font-weight: bold;
+margin: 0 10px 4px 10px;
+text-decoration: none;
+color: #999;
+border-bottom: 2px solid #999;}
+
+ul.event_nav a:hover { border-bottom: 2px solid #000; color: #000; }
+
+/* event-list-table */
+#event-list-table {
+       clear:left;
+       font-family: arial, helvetica, sans-serif;
+       border: 2px solid #ccc;
+       border-collapse: collapse;
+       font-size: 12px;
+       }
+#event-list-table td {
+       border: 2px solid #ccc;
+       border-collapse: collapse;
+       background: #eee;
+       padding: 4px;}  
+#event-list-table img {border: 0; text-align: center; margin: 0 auto; display: block;}
+#event-list-table a:link {color: #666;}
+#event-list-table a:visited {color: #666;}
+#event-list-table a:active {color: #666;}
+#event-list-table a:hover {color: #000;}
+
+/*event-edit-table */
+#event-edit-table {
+       clear:left;
+       font-family: arial, helvetica, sans-serif;
+       border: 2px solid #ccc;
+       border-collapse: collapse;
+       font-size: 12px;
+       }
+#event-edit-table td {
+       border: 2px solid #ccc;
+       border-collapse: collapse;
+       background: #eee;       padding: 4px;}          
+#event-edit-table table td {border:none;margin:0;padding:0;}
+#event-edit-table td select,
+#event-edit-table td option,
+#event-edit-table td input {font-size: 8pt;}
+
+#event-list-table td select,
+#event-list-table td option,
+#event-list-table td input {font-size:8pt;}
+
diff --git a/admin/Events/event.js b/admin/Events/event.js
new file mode 100644 (file)
index 0000000..545bdb4
--- /dev/null
@@ -0,0 +1,11 @@
+function grabcolor( letter, newcolor )
+{
+       document.getElementById('square'+letter).style.backgroundColor= newcolor;
+       document.getElementById('colorbox-'+letter).style.display = 'none';
+       document.getElementById('topiccolor'+letter).value = newcolor;
+}
+function myDelete( myForm )
+{
+       myForm.Action.value = 'Delete Topic';
+       myForm.submit();
+}
diff --git a/admin/Events/event_setup.inc b/admin/Events/event_setup.inc
new file mode 100755 (executable)
index 0000000..f67ab48
--- /dev/null
@@ -0,0 +1,176 @@
+<?php
+extract($_REQUEST);
+/**
+ * About the $fields array
+ * Please only add the req part if you want javascript written for it
+ * Notice the hide,date,datetime,radio,and cat types  aren't req
+ * you'll have to right your own javascript for selects :(
+ *
+ * For the rest req => 1 will require the input of the field
+ * and req => 0 will not.
+ */
+$topinfo["idfield"] = "id";
+$topinfo["field"]   = "descr";
+$topinfo["table"]   = "topic";
+$topinfo["sfield"]  = "descr";
+
+$fields[] = array('name' => "id",      'title' => "id",              'type' => "hide");
+$fields[] = array('name' => "topicid", 'title' => "Topic",           'type' => "topic", 'info'=>$topinfo,'req' => 0);
+$fields[] = array('name' => "visable", 'title' => "Visible",         'type' => "radio", 'req' => 0);
+$fields[] = array('name' => "bdate",   'title' => "Begin Date",      'type' => "date",  'req' => 1);
+$fields[] = array('name' => "edate",   'title' => "End Date",        'type' => "date",  'req' => 1);
+$fields[] = array('name' => "",        'title' => "All Day Event",   'type' => "alldayevent");
+$fields[] = array('name' => "btime",   'title' => "Begin Time",      'type' => "time",  'req' => 0);
+$fields[] = array('name' => "etime",   'title' => "End Time",        'type' => "time",  'req' => 0);
+$fields[] = array('name' => "reacur",  'title' => "Recurring event", 'type' => "reacur");
+$fields[] = array('name' => "",        'title' => "Recurring Event", 'type' => "recurevent");
+$fields[] = array('name' => "loc",     'title' => "Location",        'type' => "text", 'req' => 0);
+$fields[] = array('name' => "header",  'title' => "Header",          'type' => "text", 'req' => 1);
+$fields[] = array('name' => "descr",   'title' => "Description",     'type' => "desc", 'req' => 0);
+$fields[] = array('name' => "img",     'title' => "Image 1",         'type' => "img",  'req' => 0);
+$fields[] = array('name' => "url",     'title' => "Url",             'type' => "text", 'req' => 0);
+$fields[] = array('name' => "email",   'title' => "Email",           'type' => "text", 'req' => 0);
+$fields[] = array('name' => "contact", 'title' => "Contact",         'type' => "text", 'req' => 0);
+$fields[] = array('name' => "phone",   'title' => "Phone",           'type' => "text", 'req' => 0);
+$fields[] = array('name' => "file",    'title' => "File",            'type' => "file", 'req' => 0);
+$fields[] = array('name' => "filename",'title' => "File Name",       'type' => "text", 'req' => 0);
+// navigation array
+$lnav = array(
+    "Edit Categories"  => "editTopics.php",
+    "Create New Event" => "editEvent.php",
+    "List Events"      => "list_events.phtml"
+);
+// set the cache option from $GLOBALS
+$cacheOptions = $GLOBALS['cacheOptions'];
+// redo the cachDir
+$cacheOptions['cacheDir'] = BASE . 'cache/';
+// create a new instance of Cache_Lite
+$cache = new Cache_Lite($cacheOptions);
+// Functions for the Events Admin
+/**
+ * Function add_topic
+ *
+ * Add a new topic to database checking first if it is already in topic table.
+ *
+ * @param string $topicname
+ * @return int $id of topic new or old with that name.
+ */
+function add_topic($topicname)
+{
+    $dbh = Toolkit_Database::getInstance();
+    $query = "
+    SELECT id
+      FROM topic
+     WHERE descr = '$topicname'";
+    $dres = $dbh->query($query);
+    if ($dres->columnCount == 0) {
+        $query = "
+        INSERT INTO topic
+        (descr)
+        VALUES
+        (:topicname)
+        RETURNING id";
+        $stmt = $dbh->prepare($query);
+        $stmt->bindParam(":topicname", $topicname, PDO::PARAM_STR);
+        $stmt->execuce();
+        return $stmt->fetchColumn();
+    } else {
+        return $dres->fetchColumn();
+    }
+}
+/**
+ * return html for navigation
+ *
+ * @param string $nav HTML of the event navigation
+ *
+ * @return string
+ */
+function event_nav($nav)
+{
+    if (is_array($nav)) {
+        $out = '<ul class="admin_nav">';
+        foreach ($nav as $link => $url) {
+            $out .= '<li><a href="'.$url.'">'.$link.'</a></li>';
+        }
+        $out .= '</ul>';
+    }
+    return $out;
+}
+/**
+ * return the input string of a date format to remove the year part
+ *
+ * @param string $date date string in MM/DD/YY format
+ *
+ * @return string
+ */
+function strip_year_part($date)
+{
+    $pattern = "/([0-9]*)[\/-]([0-9]*)[\/-]([0-9]*)/";
+    if (preg_match($pattern, $date, $temp)) {
+        $out = $temp[1].'/'.$temp[2].'/'.substr($temp[3],2,2);
+    }
+    return $out;
+}
+/**
+ * event_top
+ *
+ * @return string html heaoder for the event admin page.
+ */
+function event_top()
+{
+    $out = '
+        <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
+"http://www.w3.org/TR/html4/strict.dtd">
+<html>
+<head>
+<title>Untitled</title>
+<meta http-equiv="content-type" content="text/html;charset=utf-8">
+<link rel="stylesheet" type="text/css" href="../main.css">
+<link rel="stylesheet" type="text/css" href="./event.css">
+<script type="text/javascript" src="event.js"></script>
+<script type="text/javascript" src="'.MEDIA_APP_BASE_URL.'libjs/jquery/jquery-1.4.2.min.js"></script>
+<script type="text/javascript" src="'.CKEDITOR_JS.'"></script>
+<script>
+var Events =
+{
+    init: function()
+    {
+        if ($("#descr").length) {
+            //  Only try to replace the textarea if the
+            //  CKEditor is compatible w/ the browser.
+            if (CKEDITOR.env.isCompatible) {
+                CKEDITOR.replace("descr",
+                    {
+                        toolbar : "Default",
+                        width : 570,
+                        height : 400,
+                        filebrowserImageBrowseUrl : "../../Toolkit/CKImages/browser.php?folder=1",
+                        filebrowserImageUploadUrl : "../../Toolkit/CKImages/controller.php?command=Upload",
+                        filebrowserImageWindowWidth : "760",
+                        filebrowserImageWindowHeight : "500"
+                    });
+            }
+        }
+    }
+};
+
+$(document).ready(Events.init);
+</script>
+  </head>
+<body>
+';
+        return $out;
+}
+/**
+ * return html for the footer of the event page
+ *
+ * @return string
+ */
+function event_footer()
+{
+    $out = '
+        </body>
+        </html>
+        ';
+    return $out;
+}
diff --git a/admin/Events/events.sql b/admin/Events/events.sql
new file mode 100644 (file)
index 0000000..599dd49
--- /dev/null
@@ -0,0 +1,55 @@
+CREATE TABLE "event" (
+"id" SERIAL, 
+"topicid" int4 DEFAULT 0,
+"bdate" date,
+"edate" date,
+"loc" text,
+"header" text,
+"descr" text,
+"img" text,
+"url" text,
+"contact" text,
+"phone" text,
+"email" text,
+"btime" text,
+"etime" text,
+"visable" bool DEFAULT 'f',
+"home" bool DEFAULT 'f',
+"ds" timestamp,
+"daysow" int,
+"dayom" int,
+"reacur" bool DEFAULT 'f',
+"weekom" text,
+"all_day" bool DEFAULT 'f',
+"file" text,
+"filename" text,
+"member_id" int4);
+
+REVOKE ALL on "event" from PUBLIC;
+
+GRANT ALL on "event" to "nobody";
+                                                                                                                                                               
+CREATE TABLE "topic" (
+"id" SERIAL, 
+"descr" text,
+"topiccolor" text);
+
+REVOKE ALL on "topic" from PUBLIC;
+
+GRANT ALL on "topic" to "nobody";
+GRANT ALL on "topic_id_seq" to "nobody";
+GRANT ALL on "event_id_seq" to "nobody";
+
+CREATE UNIQUE INDEX event_id_indx on event(id);
+CREATE INDEX event_bdate_indx on event(bdate);
+CREATE INDEX event_edate_indx on event(edate);
+
+CREATE TABLE event_recur
+(id SERIAL,
+ event_id INTEGER
+   REFERENCES event(id)
+   ON DELETE CASCADE,
+ event_day DATE);
+
+GRANT ALL ON event_recur TO nobody;
+GRANT ALL ON event_recur_id_seq TO nobody;
diff --git a/admin/Events/importEvents.php b/admin/Events/importEvents.php
new file mode 100755 (executable)
index 0000000..fa45fd1
--- /dev/null
@@ -0,0 +1,142 @@
+<?php
+require_once '../../setup.phtml';
+class importEvents
+{
+    protected $db1h;
+    protected $db2h;
+    function __construct()// {{{
+    {
+        // db1h is devsys2 database that data will be imported into
+        $this->db1h = Toolkit_Database::getInstance();
+        // db2h is ds1 database that we're pulling data from (export)
+        try {
+            $dsn = 'pgsql:host=ds1.gaslightmedia.com dbname=stignace user=nobody';
+            $driverOptions = array(
+                PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
+            );
+            $this->db2h = new PDO($dsn, null, null, $driverOptions);
+            $this->db2h->setAttribute(
+                PDO::ATTR_ERRMODE,
+                PDO::ERRMODE_EXCEPTION
+            );
+        } catch (PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+        $this->getLiveData();
+        $this->importData();
+        echo '<pre>'.print_r($this->topicData, true).'</pre>';
+        echo '<pre>'.print_r($this->eventData[0], true).'</pre>';
+    }// }}}
+    function convertEncoding(&$var)// {{{
+    {
+        return iconv('windows-1251', 'utf-8', $var);
+    }// }}}
+    function getLiveData()// {{{
+    {
+        // getting the topics
+        try {
+            $sql = "
+              SELECT *
+                FROM topic
+            ORDER BY id";
+            $intData = $this->db2h->query($sql)->fetchAll();
+            if ($intData) {
+                $this->topicData =& $intData;
+            }
+        } catch (PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+        // getting the events
+        try {
+            $sql = "
+              SELECT *
+                FROM event
+               WHERE edate >= current_date
+            ORDER BY id";
+            $cData = $this->db2h->query($sql)->fetchAll();
+            if ($cData) {
+                $this->eventData =& $cData;
+            }
+        } catch (PDOException $e) {
+            Toolkit_Common::handleError($e);
+        }
+    }// }}}
+    function importData()// {{{
+    {
+        // start up transaction
+        $this->db1h->beginTransaction();
+        // build prepare statement for insert of interest types
+        $sql = "
+        INSERT INTO topic
+        (id,descr)
+        VALUES
+        (:id,:descr)";
+        $topicInsert = $this->db1h->prepare($sql);
+        if (is_array($this->topicData)) {
+            foreach ($this->topicData as $iRow) {
+                try {
+                    $topicInsert->bindParam(":id", $iRow['id'], PDO::PARAM_INT);
+                    $topicInsert->bindParam(":descr", $iRow['descr'], PDO::PARAM_STR);
+                    $topicInsert->execute();
+                } catch (PDOException $e) {
+                    Toolkit_Common::handleError($e);
+                }
+            }
+        }
+        // build prepare statement for insert of event
+        $sql = "
+        INSERT INTO event
+        (id,topicid,bdate,edate,loc,header,descr,img,url,contact,phone,email,
+        btime,etime,visable,home,ds,daysow,dayom,reacur,weekom)
+        VALUES
+        (:id,:topicid,:bdate,:edate,:loc,:header,:descr,:img,:url,:contact,:phone,:email,
+        :btime,:etime,:visable,:home,:ds,:daysow,:dayom,:reacur,:weekom)";
+        $eventInsert = $this->db1h->prepare($sql);
+        if (is_array($this->eventData)) {
+            foreach ($this->eventData as $cKey => &$cRow) {
+                try {
+                    $eventInsert->bindParam(":id",      $cRow['id'],           PDO::PARAM_INT);
+                    $eventInsert->bindParam(":topicid", $cRow['topicid'],      PDO::PARAM_INT);
+                    $eventInsert->bindParam(":bdate",   $cRow['bdate'],  PDO::PARAM_STR);
+                    $eventInsert->bindParam(":edate",   $cRow['edate'],  PDO::PARAM_STR);
+                    $eventInsert->bindParam(":loc",     $this->convertEncoding($cRow['loc']),     PDO::PARAM_STR);
+                    $eventInsert->bindParam(":header",  $this->convertEncoding($cRow['header']),  PDO::PARAM_STR);
+                    $eventInsert->bindParam(":descr",   $this->convertEncoding($cRow['descr']),   PDO::PARAM_STR);
+                    $eventInsert->bindParam(":img",     $this->convertEncoding($cRow['img']),     PDO::PARAM_STR);
+                    $eventInsert->bindParam(":url",     $this->convertEncoding($cRow['url']),     PDO::PARAM_STR);
+                    $eventInsert->bindParam(":contact", $this->convertEncoding($cRow['contact']), PDO::PARAM_STR);
+                    $eventInsert->bindParam(":phone",   $this->convertEncoding($cRow['phone']),   PDO::PARAM_STR);
+                    $eventInsert->bindParam(":email",   $this->convertEncoding($cRow['email']),   PDO::PARAM_STR);
+                    $eventInsert->bindParam(":btime",   $this->convertEncoding($cRow['btime']),   PDO::PARAM_STR);
+                    $eventInsert->bindParam(":etime",   $this->convertEncoding($cRow['etime']),   PDO::PARAM_STR);
+                    $visable = ($cRow['visable']) ? 1: 0;
+                    $eventInsert->bindParam(":visable", $visable,                                 PDO::PARAM_BOOL);
+                    $home = ($cRow['home']) ? 1: 0;
+                    $eventInsert->bindParam(":home",    $home,                                    PDO::PARAM_BOOL);
+                    $eventInsert->bindParam(":ds",      $cRow['ds'],                              PDO::PARAM_STR);
+                    $eventInsert->bindParam(":daysow",  $cRow['daysow'],                          PDO::PARAM_INT);
+                    $eventInsert->bindParam(":dayom",   $cRow['dayom'],                           PDO::PARAM_INT);
+                    $reacur = ($cRow['reacur']) ? 1: 0;
+                    $eventInsert->bindParam(":reacur",  $reacur,                                  PDO::PARAM_BOOL);
+                    $eventInsert->bindParam(":weekom",  $this->convertEncoding($cRow['weekom']),  PDO::PARAM_STR);
+
+                    $eventInsert->execute();
+                    //echo '<p>Importing to event ...</p>';
+                } catch (PDOException $e) {
+                    echo '<pre>';
+                    var_dump($cRow['zip']);
+                    var_dump($this->convertEncoding(&$cRow['zip']));
+                    echo '</pre>';
+                    echo '<pre>'.print_r($cRow, true).'</pre>';
+                    Toolkit_Common::handleError($e);
+                }
+            }
+        }
+        // end transaction
+        //$this->db1h->rollBack();
+        echo 'done';
+        $this->db1h->commit();
+    }// }}}
+}
+$importer = new importEvents();
+?>
diff --git a/admin/Events/index.phtml b/admin/Events/index.phtml
new file mode 100755 (executable)
index 0000000..924215d
--- /dev/null
@@ -0,0 +1 @@
+<?header("Location: list_events.phtml")?>
diff --git a/admin/Events/left.gif b/admin/Events/left.gif
new file mode 100755 (executable)
index 0000000..29d0ba1
Binary files /dev/null and b/admin/Events/left.gif differ
diff --git a/admin/Events/list_events.phtml b/admin/Events/list_events.phtml
new file mode 100755 (executable)
index 0000000..7b735ac
--- /dev/null
@@ -0,0 +1,326 @@
+<?php
+include_once '../../setup.phtml';
+include_once 'event_setup.inc';
+if (!$start) {
+    $start = 0;
+}
+$pagelength = 10;
+// days array
+$days[1]  = "Sun";
+$days[2]  = "Mon";
+$days[4]  = "Tue";
+$days[8]  = "Wed";
+$days[16] = "Thu";
+$days[32] = "Fri";
+$days[64] = "Sat";
+echo event_top("Events Calendar (List Events)", HELP_BASE."events.phtml?key=list");
+$colspan
+    = (defined('HOME_EVENTS') && HOME_EVENTS)
+    ? 9
+    : 8;
+$dbh = Toolkit_Database::getInstance();
+if ($_REQUEST['Command'] == 'Delete' && is_numeric($_REQUEST['id'])) {
+    try {
+        $sql = "
+        DELETE
+          FROM event
+         WHERE id = :id";
+        $del = $dbh->prepare($sql);
+        $del->bindParam(':id', $_REQUEST['id'], PDO::PARAM_INT);
+        $del->execute();
+    } catch(PDOException $e) {
+        die($e->getMessage($e));
+    }
+}
+?>
+<script type="text/javascript" src="../wm.js"></script>
+<script type="text/javascript" src="../msg.js"></script>
+<div id="event-list">
+<h1>Manage Events (list)</h1>
+<?php
+if (isset($_REQUEST['home'])) {
+    $nh = ($_REQUEST['home']) ? 0: 1;
+
+    $qs = "
+    UPDATE event
+       SET home = :home
+     WHERE id = :id";
+    $stmt = $dbh->prepare($qs);
+    $stmt->bindParam(":home", $nh, PDO::PARAM_BOOL);
+    $stmt->bindParam(":id", $id, PDO::PARAM_INT);
+    $stmt->execute();
+    $cache->clean('HomeEvents');
+}
+
+if (isset($_REQUEST['visable'])) {
+    $nv = ($_REQUEST['visable']) ? 0: 1;
+
+    $qs = "
+    UPDATE event
+       SET visable = :visable
+     WHERE id = :id";
+    $stmt = $dbh->prepare($qs);
+    $stmt->bindParam(":visable", $nv, PDO::PARAM_BOOL);
+    $stmt->bindParam(":id", $id, PDO::PARAM_INT);
+    $stmt->execute();
+    $cache->clean('HomeEvents');
+}
+function sortByField(&$params, $var)
+{
+    if ($GLOBALS[$var["field"]]) {
+        $params[] = $var["field"]."=".$GLOBALS[$var["field"]];
+        if ($GLOBALS[$var["field"]] == "D") {
+            $GLOBALS["order_by"] = $var["fname"]." ASC";
+            $GLOBALS[$var["field"]] = "A";
+            $GLOBALS[$var["image"]] = "down";
+        } else {
+            $GLOBALS["order_by"] = $var["fname"]." DESC";
+            $GLOBALS[$var["field"]] = "D";
+            $GLOBALS[$var["image"]] = "up";
+        }
+    } else {
+        $GLOBALS[$var["field"]] = "A";
+    }
+}
+function addUpdateParams($sort, $params)
+{
+    parse_str($params, $parsedParams);
+    if ($_GET[$sort['field']]) {
+        $pattern = "/&amp;".$sort['field']."=([A-Z]{1,2})/";
+        return preg_replace($pattern, '', $params);
+    } else {
+        $add = '&V=&HM=&T=&B=&E=&D=&C=&H=';
+        $pattern = "/&".$sort['field']."=/";
+        $add = preg_replace($pattern, '', $add);
+        return $params . $add;
+    }
+}
+// params to passthrough the urls
+$urlParamArray = array();
+// sorting fields
+$sortField[] = array("field"=>"V",  "image"=>"V_img",  "fname"=>"event.visable");
+$sortField[] = array("field"=>"HM", "image"=>"HM_img", "fname"=>"event.home");
+$sortField[] = array("field"=>"H",  "image"=>"H_img",  "fname"=>"event.header");
+$sortField[] = array("field"=>"T",  "image"=>"T_img",  "fname"=>"event.btime");
+$sortField[] = array("field"=>"B",  "image"=>"B_img",  "fname"=>"event.bdate");
+$sortField[] = array("field"=>"E",  "image"=>"E_img",  "fname"=>"event.edate");
+$sortField[] = array("field"=>"D",  "image"=>"D_img",  "fname"=>"event.daysow");
+$sortField[] = array("field"=>"C",  "image"=>"C_img",  "fname"=>"topic.descr");
+if (is_array($sortField)) {
+    foreach ($sortField as $var) {
+        sortByField($urlParamArray, $var);
+    }
+}
+
+if (!$order_by) {
+    $order_by = "event.bdate ASC,event.btime ASC";
+}
+$order = " ORDER BY $order_by";
+
+$select_part   = "event.id,event.bdate,event.edate,event.header,topic.descr as topic,
+    event.visable,event.home,event.btime,event.etime,event.daysow,event.all_day";
+$from_part     = "event LEFT OUTER JOIN topic ON (topic.id = event.topicid)";
+$where_part    = '';
+$Where         = 'where';
+$queryInput = filter_input(INPUT_GET, 'Query', FILTER_SANITIZE_STRING);
+if ($queryInput) {
+    $where_part      = " $Where header ilike '%$Query%'";
+    $Where           = 'and';
+    $urlParamArray[] = 'Query='.urlencode($Query);
+}
+// check to see if the dates are different
+if ($_REQUEST['date_search_from'] && $_REQUEST['date_search_to']) {
+    $date_search_to   = $_REQUEST['date_search_to'];
+       list($to_month, $to_day, $to_year) = explode("/", $date_search_to);
+    $date_search_from = $_REQUEST['date_search_from'];
+       list($from_month, $from_day, $from_year) = explode("/", $date_search_from);
+} else if ($_GET['to_month'] && $_GET['from_month']) {
+    $date_search_to_time   = mktime(0, 0, 1, $_GET['to_month'], $_GET['to_day'], $_GET['to_year']);
+    $date_search_from_time = mktime(0, 0, 1, $_GET['from_month'], $_GET['from_day'], $_GET['from_year']);
+    $date_search_to        = date("m/d/Y", $date_search_to_time);
+    $date_search_from      = date("m/d/Y", $date_search_from_time);
+}
+if ($date_search_to && $date_search_from) {
+    $urlParamArray[] = 'date_search_from='.urlencode($date_search_from);
+    $urlParamArray[] = 'date_search_to='.urlencode($date_search_to);
+    $where_part     .= " $Where edate >= '$date_search_from' and bdate <= '$date_search_to'";
+    $Where           = 'and';
+}
+if ($archived == 't') {
+    $urlParamArray[] = 'archived='.$archived;
+    $where_part .= " $Where edate < current_date";
+    $Where       = 'and';
+} elseif ($pending == 't') {
+    $urlParamArray[] = 'pending='.$pending;
+    $where_part .= " $Where visable = 'f'";
+    $Where       = 'and';
+} elseif (!isset($_GET['Query']) && !$_REQUEST['date_search_to'] && !$_REQUEST['to_month']) {
+    $where_part .= " $Where edate >= current_date";
+    $Where       = 'and';
+}
+$qs  =  "select $select_part    from $from_part $where_part";
+//echo '<p>'.$qs.$order.'</p>';
+
+$stmt  = $dbh->query($qs.$order." LIMIT $pagelength OFFSET $start");
+$data  = $stmt->fetchAll(PDO::FETCH_ASSOC);
+$num   = count($data);
+$qs1   = "SELECT count(*) as total FROM $from_part $where_part";
+$total = $dbh->query($qs1)->fetchColumn();
+$params
+    = (!empty($urlParamArray))
+    ? '&amp;'.implode("&amp;", $urlParamArray)
+    : '';
+$links = GLM_TOOLBOX::create_page_links($total,$num,$start,$params,$pagelength);
+
+echo event_nav($lnav);
+// from date
+$date_fields1 = GLM_TOOLBOX::date_entry($from_month, $from_day, $from_year,
+        'from_month', 'from_day', 'from_year');
+// to date
+$date_fields2 = GLM_TOOLBOX::date_entry($to_month, $to_day, $to_year,
+        'to_month', 'to_day', 'to_year');
+if ( $archived != 't' ) {
+    $archived_link = '<a href="list_events.phtml?archived=t">Archived Events</a>';
+} else {
+    $archived_link = '<a href="list_events.phtml">Current Events</a>';
+}
+echo '<table id="event-list-table">';
+    ?>
+        <tr>
+          <td colspan="<?php echo $colspan?>"><?php echo $links?>
+              <a href="list_events.phtml?pending=t">Pending Events</a>
+              <?php echo $archived_link;?>
+          </td>
+        </tr>
+        <tr>
+          <td colspan="<?php echo $colspan?>">
+          <form action="list_events.phtml">
+            <input type="" name="Query" value="<?php echo htmlspecialchars($queryInput);?>">
+            <input type="submit" value="Event Name Search">
+          </form>
+          </td>
+        </tr>
+        <tr>
+          <td colspan="<?php echo $colspan?>">
+          <form action="list_events.phtml">
+          <div style="display:inline;">
+          From:<?php echo $date_fields1;?>
+          </div>
+          <div style="display:inline;">
+          To:<?php echo $date_fields2;?>
+          </div>
+            <input type="submit" value="Event Date Search">
+          </form>
+          </td>
+        </tr>
+        <?php
+if ($num > 0) {
+    $parsedParams = array();
+    foreach ($sortField as $sort) {
+        $parsedParams[$sort['field']] = addUpdateParams($sort, $params);
+    }
+        ?>
+        <tr>
+          <th>Function</th>
+          <th>
+            <a class="headerlink" href="<?php echo $PHP_SELF.'?V='.$V.$parsedParams['V']?>">On/Off</a>
+            <?php if($V_img) echo '<img src="'.$V_img.'.gif">'?>
+          </th>
+          <?php if (defined('HOME_EVENTS') && HOME_EVENTS) {?>
+        <th>
+          <a class="headerlink" href="<?php echo $PHP_SELF.'?HM='.$HM.$parsedParams['HM']?>">Home.</a>
+        <?php if($HM_img) echo '<img src="'.$HM_img.'.gif">'?>
+        </th>
+        <?php }?>
+        <th>
+        <a class="headerlink" href="<?php echo $PHP_SELF.'?H='.$H.$parsedParams['H']?>">Title</a>
+        <?php if($H_img) echo '<img src="'.$H_img.'.gif">'?>
+        </th>
+        <th>
+        <a class="headerlink" href="<?php echo $PHP_SELF.'?C='.$C.$parsedParams['C']?>">Category</a>
+        <?php if($C_img) echo '<img src="'.$C_img.'.gif">'?>
+        </th>
+        <th>
+        <a class="headerlink" href="<?php echo $PHP_SELF.'?T='.$T.$parsedParams['T']?>">Times</a>
+        <?if($T_img) echo '<img src="'.$T_img.'.gif">'?>
+        </th>
+        <th>
+        <a class="headerlink" href="<?php echo $PHP_SELF.'?D='.$D.$parsedParams['D']?>">Days</a>
+        <?if($D_img) echo '<img src="'.$D_img.'.gif">'?>
+        </th>
+        <th>
+        <a class="headerlink" href="<?php echo $PHP_SELF.'?B='.$B.$parsedParams['B']?>">Start</a>
+        <?if($B_img) echo '<img src="'.$B_img.'.gif">'?>
+        </th>
+        <th >
+        <a class="headerlink" href="<?php echo $PHP_SELF.'?E='.$E.$parsedParams['E']?>">End</a>
+        <?if($E_img) echo '<img src="'.$E_img.'.gif">'?>
+        </th>
+        </tr>
+        <tbody>
+        <?php
+}
+for ($i = 0; $i < $num; $i++) {
+    $row = $data[$i];
+    if (!$row['id']) {
+        html_error(DB_ERROR_MSG, 1);
+    }
+
+    $btime = $row["btime"];
+    $etime = $row["etime"];
+    ?>
+        <tr>
+        <td >
+            <a href="editEvent.php?id=<?php echo $row['id']?>&amp;edit=1&amp;start=<?php echo $start;?><?php echo $params;?>">Edit</a>&nbsp;
+        <a href="list_events.phtml?Command=Delete&amp;id=<?php echo $row['id'];?>&amp;start=<?php echo $start;?><?php echo $params;?>" onClick="
+        return(confirm('You are about to delete this listing!'));
+    ">Delete</a>
+        </td>
+        <td >
+        <a href="<?php echo $PHP_SELF."?visable=$row[visable]&amp;id=$row[id]"?>"><?php
+        if($row[visable] == "t") {
+            echo "<img src=\"".MEDIA_BASE_URL
+                ."images/grnball.gif\" alt=\"On\"></a>";
+        } else {
+            echo "<img src=\"".MEDIA_BASE_URL
+                ."images/redball.gif\" alt=\"Off\"></a>";
+        }
+
+    ?>
+
+        </td>
+        <?php if (defined('HOME_EVENTS') && HOME_EVENTS) {?>
+        <td>
+        <a href="<?php echo $PHP_SELF."?home=$row[home]&amp;id=$row[id]"?>"><?php
+        if($row['home'] == "t") {
+            echo "<img src=\"".MEDIA_BASE_URL
+                ."images/grnball.gif\" alt=\"Don't display on Home Page\"></a>";
+        } else {
+            echo "<img src=\"".MEDIA_BASE_URL
+                ."images/redball.gif\" alt=\"Display on Home Page\"></a>";
+        }
+    ?>
+</td><?php }?>
+        <td><?php echo $row['header']?></td>
+        <td><?php echo $row['topic']?></td>
+        <td><?php echo (($row['all_day'] == 't')?'All Day':$btime." - ".$etime);?></td>
+        <td><?php
+        if ($row['daysow']) {
+            $ri = 1;
+            for ($r=1;$r<8;$r++) {
+                if ($row["daysow"]&$ri)
+                    echo $days[$ri];
+                $ri = $ri << 1;
+            }
+        }
+        else
+            echo "&nbsp;";
+    ?></td>
+        <td><?php echo strip_year_part($row['bdate']);?></td>
+        <td><?php echo strip_year_part($row['edate']);?></td>
+        </tr>
+        <?php
+}
+echo "</tbody></table></div>\n";
+echo event_footer();
+?>
diff --git a/admin/Events/setupEventsRecur.php b/admin/Events/setupEventsRecur.php
new file mode 100644 (file)
index 0000000..04f0ff3
--- /dev/null
@@ -0,0 +1,175 @@
+<?php
+
+require_once '../../setup.phtml';
+
+function ordinalDay($ord, $day, $month, $year)
+{
+    $firstOfMonth = mktime(0, 0, 30, $month, 1, $year);
+    $lastOfMonth  = $firstOfMonth + date("t", $firstOfMonth) * 86400;
+    $dayOccurs    = 0;
+
+    for ($i = $firstOfMonth; $i < $lastOfMonth; $i += 86400) {
+        if (date("w", $i) == $day) {
+            $dayOccurs++;
+            if ($dayOccurs == $ord) {
+                $ordDay = $i;
+            }
+        }
+    }
+    return $ordDay;
+}
+
+function getEventDates($starttime, $endtime, $recur, $format = 'm/d/Y')
+{
+    if (!is_array($recur)) {
+        return;
+    }
+    if ($starttime == $endtime) {
+        return;
+    }
+    if (is_array($recur['dow'])) {
+        $daysow = array_sum($recur['dow']);
+    } else {
+        $daysow = $recur['dow'];
+    }
+    if ($recur['recur_week'] == 9) {
+        $fWeekNum = date("W", $starttime);
+        if (date('w', $starttime) == 0) {
+            $fWeekNum++;
+        }
+        $lWeekNum = date("W", $endtime);
+        if (date('w', $endtime) == 0) {
+            $lWeekNum++;
+        }
+        for ($fi = $fWeekNum; $fi <= $lWeekNum; $fi = $fi + 2) {
+            $eWeeks[] = $fi;
+        }
+    }
+    for ($i        = $starttime; $i <= $endtime; $i += 86400) {
+        if ($recur['recur_week'] != '') {
+            if ($daysow) {
+                $ri = 1;
+                for ($r  = 0; $r < 7; $r++) {
+                    if ($daysow & $ri) {
+                        $ord = ordinalDay($recur['recur_week'], $r, date('n', $i), date('Y', $i));
+                    }
+                    $ri  = $ri << 1;
+                }
+            } else {
+                $ord = null;
+            }
+        } else {
+            $ord = null;
+        }
+        if ($recur['dom']) {
+            if (date("j", $i) == $recur['dom']) {
+                $events[] = date($format, $i);
+            }
+        } elseif ($daysow) {
+            $cur_dow = date("w", $i);
+            switch ($cur_dow) {
+                case 0:
+                    $cur_dow = 1;
+                    break;
+                case 1:
+                    $cur_dow = 2;
+                    break;
+                case 2:
+                    $cur_dow = 4;
+                    break;
+                case 3:
+                    $cur_dow = 8;
+                    break;
+                case 4:
+                    $cur_dow = 16;
+                    break;
+                case 5:
+                    $cur_dow = 32;
+                    break;
+                case 6:
+                    $cur_dow = 64;
+                    break;
+            }
+            if ((int) $cur_dow & $daysow) {
+                $cDateWeek = date("W", $i);
+                if (date('w', $i) == 0) {
+                    $cDateWeek++;
+                }
+                if ($recur['recur_week'] == 9) {
+                    if (in_array($cDateWeek, $eWeeks)) {
+                        $events[] = date($format, $i);
+                    }
+                } elseif ($recur['recur_week'] != '') {
+                    if ($recur['recur_week'] && $i && $ord) {
+                        $day1 = date('z', $i);
+                        $day2 = date('z', $ord);
+                        if ($day1 == $day2) {
+                            $events[] = date($format, $i);
+                        }
+                    }
+                } else {
+                    $events[] = date($format, $i);
+                }
+            }
+        }
+    }
+    return $events;
+}
+
+$dbh = Toolkit_Database::getInstance();
+$dbh->query(
+    "DELETE FROM event_recur"
+);
+$sql = "
+SELECT *
+  FROM event
+ WHERE edate >= current_date
+   AND bdate <= current_date
+   AND reacur = true";
+try {
+    $stmt = $dbh->query($sql);
+    while ($row  = $stmt->fetch(PDO::FETCH_ASSOC)) {
+        var_dump($row);
+        if ($row['reacur']) {
+            $recur['dow'] = $row['daysow'];
+            $recur['dom'] = $row['dayom'];
+            if ($recur['dow']) {
+                $recur['recur_week'] = ($row['weekom'])
+                    ? $row['weekom']
+                    : null;
+            } else {
+                $recur['recur_week'] = null;
+            }
+        }
+        $eventDates = getEventDates(
+            strtotime($row['bdate']),
+            strtotime($row['edate']),
+            $recur
+        );
+        $fields = array('event_id', 'event_day');
+        $eventSql = Toolkit_Common::createSQLInsert(
+            'event_recur',
+            $fields
+        );
+        var_dump($eventDates);
+        var_dump($eventSql);
+        if (isset($eventDates) && is_array($eventDates)) {
+            foreach ($eventDates as $eventDay) {
+                $eventValues = array(
+                    'event_id'  => $row['id'],
+                    'event_day' => $eventDay
+                );
+                $stmt2 = Toolkit_Common::prepareQuery(
+                    $dbh,
+                    'event_recur',
+                    $eventSql,
+                    $eventValues
+                );
+                $stmt2->execute();
+            }
+        }
+    }
+} catch (PDOException $e) {
+    Toolkit_Common::handleError($e);
+}
+
diff --git a/admin/Events/topic_action.php b/admin/Events/topic_action.php
new file mode 100644 (file)
index 0000000..92792c4
--- /dev/null
@@ -0,0 +1,38 @@
+<?php
+require_once '../../setup.phtml';
+$dbh = Toolkit_Database::getInstance();
+switch ($Action) {
+case "Add Topic":
+    $query = "
+    SELECT id 
+      FROM topic 
+     WHERE descr = '$topic';";
+    if (!$data = $dbh->query($query)->fetchAll(PDO::FETCH_ASSOC)) {
+        $query = "
+        INSERT INTO topic 
+        (descr) 
+        VALUES 
+        ('$topic');";
+        $dbh->query($query);
+    }
+    break;
+
+case "Delete Topic":
+    $query = "
+    DELETE FROM topic 
+    WHERE id = ".$_POST['id'];
+    $dbh->query($query);
+    break;
+
+case "Update Topic":
+    $query = "
+    UPDATE topic 
+       SET topiccolor = '".$_POST['topiccolor']."',
+           descr = '".$_POST['descr']."' 
+     WHERE id = ".$_POST['id'].";";
+    $dbh->query($query);
+    break;
+
+}
+header('Location: edit_topic.phtml');
+?>
\ No newline at end of file
diff --git a/admin/Events/up.gif b/admin/Events/up.gif
new file mode 100755 (executable)
index 0000000..b1f0eb8
Binary files /dev/null and b/admin/Events/up.gif differ
diff --git a/admin/blocks.php b/admin/blocks.php
new file mode 100644 (file)
index 0000000..e9eef22
--- /dev/null
@@ -0,0 +1,44 @@
+<?php
+/**
+ * blocks.php
+ *
+ * PHP version 5.2
+ *
+ * @category  Admin
+ * @package   Blocks
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2012 Gaslight Media
+ * @license   Gaslight Media
+ * @version   SVN: (0.1)
+ * @link      <>
+ */
+/**
+ * required files
+ */
+require_once '../setup.phtml';
+// get Application config file loaded into Zend_Config
+$config = new Zend_Config_Ini(
+    BASE . 'Toolkit/Blocks/application.ini',
+    strtolower($_ENV['GLM_HOST_ID'])
+);
+// add our custom jquery ui js file to all areas of this admin side application
+$jQueryVersion = $config->jqueryui->version;
+$jQueryTheme   = $config->jqueryui->theme;
+$GLOBALS['bottomScripts'][] = JQUERY_UI_CDN_JS;
+$GLOBALS['styleSheets'][]   = JQUERY_UI_CDN_CSS;
+// create Registry
+$registry         = new Toolkit_Registry;
+$registry->dbh    = Toolkit_Database::getInstance();
+$registry->logger = Toolkit_Logger::getLogger();
+$registry->router = new Toolkit_Router($registry);
+$registry->router->setPath($config->router->path);
+$registry->router->setApplication($config->router->application);
+$registry->appConfig = $config;
+
+// call loader()
+$html = $registry->router->loader();
+
+
+GLM_TOOLBOX::top($config->application->name, '');
+echo $html;
+GLM_TOOLBOX::footer();
diff --git a/admin/coupons.php b/admin/coupons.php
new file mode 100755 (executable)
index 0000000..eb3b262
--- /dev/null
@@ -0,0 +1,34 @@
+<?php
+require_once '../setup.phtml';
+
+//  application configuration
+$conf = new Config;
+$confRoot=& $conf->parseConfig(
+       BASE . 'Toolkit/Coupons/config.ini',
+       'IniFile'
+);
+
+//  get reference to [conf] section of config file
+$appName
+       = $confRoot->getItem('section', 'conf')
+       ->getItem('directive', 'applicationName')
+       ->getContent();
+
+$navigation = new Toolkit_Coupons_Navigation(
+       new HTML_Menu(),
+       new HTML_Menu_DirectRenderer()
+);
+
+$navArray = $navigation->getNavStructure($confRoot);
+$navHtml = $navigation->renderPageNav($navArray, 'rows');
+$c = new Toolkit_Coupons_Controller();
+$coupons = $c->toHtml(
+       Toolkit_Database::getInstance(),
+       $confRoot
+);
+
+GLM_TOOLBOX::top($appName, '');
+echo $navHtml;
+echo $coupons;
+GLM_TOOLBOX::footer();
+?>
diff --git a/admin/index.phtml b/admin/index.phtml
new file mode 100755 (executable)
index 0000000..708d02e
--- /dev/null
@@ -0,0 +1,31 @@
+<?php
+require_once '../setup.phtml';
+$page = filter_var($_REQUEST['page'], FILTER_SANITIZE_STRING);
+$page = $page ? $page : 'splash.phtml';
+?>
+<!DOCTYPE HTML>
+<html lang="en">
+<head>
+    <title><?php echo SITENAME;?> Administration</title>
+    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+</head>
+<frameset cols="160,*">
+    <frame name="Nav" src="nav.phtml" frameborder="0">
+    <frame name="Main" src="<?php echo $page;?>" frameborder="0">
+    <noframes>
+        <p>Admin Requires Frames Capable Browser</p>
+        You can get a Standard Compliant browser from:
+        <ul>
+            <li>Google
+                <a href="http://www.google.com/chrome">Chrome</a>
+            </li>
+            <li>Mozilla's
+                <a href="http://www.mozilla.org/products/firefox/">Firefox</a>
+            </li>
+            <li>Microsoft
+                <a href="http://www.microsoft.com/windows/ie/">Internet Explorer</a>
+            </li>
+        </ul>
+    </noframes>
+</frameset>
+</html>
diff --git a/admin/main.css b/admin/main.css
new file mode 100755 (executable)
index 0000000..e3346d5
--- /dev/null
@@ -0,0 +1,132 @@
+body {
+       padding-left: 50px;
+       font-family: arial, helvetica, sans-serif;
+       font-size: 12px;
+       }
+.clearer {
+ height:1px;
+ overflow:hidden;
+ margin-top:-1px;
+ clear:left;
+}
+h1 {font-size: 18px; color: #777;}
+form {margin: 0; padding: 0;}
+/* TOOLBOX NAV */
+ul#toolbox {list-style-position:inside;list-style-type:circle;}
+ul#toolbox li {list-style-type:circle}
+ul#toolbox li.toolboxArrow {list-style-type:none;padding-left:0;margin-left:-7px;}
+* html ul#toolbox li.toolboxArrow {margin-left:-20px;} /*style for IE*/
+/* APP  Nav*/
+ul.admin_nav 
+{
+margin: 0;
+list-style-type: none;
+padding: 5px 0;
+}
+ul.admin_nav li { display: inline; }
+ul.admin_nav li a
+{
+border-top: 1px solid #eee;
+border-right: 1px solid #ccc;
+border-bottom: 1px solid #ccc;
+border-left: 1px solid #eee;
+text-decoration: none;
+background-color: #ddd;
+color: #000;
+padding: 2px 6px;
+margin: 0 1px;
+font-weight: bold;
+font-size: 12px;
+}
+ul.admin_nav.current { color: #111 }
+#admin-list-table {clear:left;width:500px;}
+#admin-list-table td {
+       border: 2px solid #ccc;
+       border-collapse: collapse;
+       background: #eee;
+       padding: 4px;}  
+/*#admin-list-table img {border: 0; text-align: center; margin: 0 auto; display: block;}*/
+#admin-list-table a:link {color: #666;}
+#admin-list-table a:visited {color: #666;}
+#admin-list-table a:active {color: #666;}
+#admin-list-table a:hover {color: #000;}
+
+/*event-edit-table */
+#admin-edit-table {
+       clear:left;
+       font-family: arial, helvetica, sans-serif;
+       border: 2px solid #ccc;
+       border-collapse: collapse;
+       font-size: 12px;
+       }
+#admin-edit-table td {
+       border: 2px solid #ccc;
+       border-collapse: collapse;
+       background: #eee;       padding: 4px;}          
+#admin-list-table table td {border:none;padding:0;margin:0;}
+#admin-edit-table table td {border:none;padding:0;margin:0;}
+#admin-edit-table select,
+#admin-edit-table input,
+#admin-edit-table option {font-size: 12px;}
+#glm-manual {position:absolute;right:200px;top:10px;width:300px;}
+#glm-manual a {padding:5px 2px;width:149px;background-color:#DDDDDD;color:#000;}
+div.fileupload { border:1px solid black;float:left;margin:5px; padding:5px;background-color:white; color:black; }
+div.fileupload p { margin:0; padding:0;float:left; }
+div.fileupload span { background-color:#c0c0c0;  }
+.level-0 {
+        font-weight: bold;
+        padding-left: 0;
+        background-color: #ccc;
+}
+.level-1 {
+        padding-left: 20px;
+        background-color: #ddd;
+        }
+.level-2 {padding-left: 40px;}
+.level-3 {padding-left: 60px;}
+.level-4 {padding-left: 80px;}
+.level-5 {padding-left: 100px;}
+.level-6 {padding-left: 120px;}
+.pager {
+       text-align: center;
+       background: #F6F6F6;
+       border-color: #DDD;
+       border-style: solid;
+       border-width: 1px 0;
+       margin: 1.0em 0;
+       padding: 8px 0;
+       text-align: center;
+       width: 100%;
+       font-size: 12px;
+
+}
+.pager b {
+       border: 1px solid #CCC;
+       border: 1px solid #17186A;
+       background: #FFF;
+       padding: 5px 7px;
+}
+.pager a {
+       background: #FFF;
+       border: 1px solid #CCC;
+       padding: 5px 7px;
+       text-decoration: none;
+       color: #000;
+}
+.pager a:hover {
+       border: 1px solid #999;
+       border: 1px solid #17186A;
+}
+#form-wrap legend {
+       color: #fff;
+       background: #1D58A5;
+       border: 1px solid #17186A;
+       padding: 2px 6px;
+}
+#form-wrap p {margin:1px;}
+#form-wrap label {
+       float: none;
+       margin-right: 0;
+       display: inline;
+       clear: left;
+}
\ No newline at end of file
diff --git a/admin/members.php b/admin/members.php
new file mode 100755 (executable)
index 0000000..17d2f66
--- /dev/null
@@ -0,0 +1,39 @@
+<?php
+require_once '../setup.phtml';
+
+HTTP_Session2::useCookies(false);
+HTTP_Session2::start('MemberDB');
+
+$GLOBALS['styleSheets'][] = MEDIA_BASE_URL . 'Toolkit/Members/css/member-admin.css';
+$GLOBALS['topScripts'][] = MEDIA_APP_BASE_URL
+    . 'libjs/jqueryui/1.8.13/js/jquery-1.5.1.min.js';
+
+$controllerObject = new stdClass();
+$dbh = Toolkit_Database::getInstance();
+$membersConf = new Config;
+$membersRoot =& $membersConf->parseConfig(
+    BASE . 'Toolkit/Members/config.ini',
+    'IniFile'
+);
+
+$navigation = new Toolkit_Members_Admin_Navigation(
+    new HTML_Menu(),
+    new HTML_Menu_DirectRenderer()
+);
+$navArray = $navigation->getNavStructure($dbh, $membersRoot);
+$controllerObject->nav = $navigation->renderPageNav($navArray, 'rows');
+
+$registry = new Toolkit_Registry;
+$registry->dbh = $dbh;
+$registry->logger = Toolkit_Logger::getLogger();
+$registry->controllerObject = $controllerObject;
+$registry->tEngine = new HTML_Template_Flexy(Toolkit_Members::getFlexyOptions());
+$registry->config = $membersRoot;
+$registry->router = new Toolkit_Router($registry);
+$registry->router->setPath(BASE . 'Toolkit/Members/Admin');
+$registry->router->setApplication('Members/Admin');
+
+$html = $registry->router->loader();
+
+echo $navHtml;
+echo $html;
diff --git a/admin/nav.phtml b/admin/nav.phtml
new file mode 100644 (file)
index 0000000..2f5d44e
--- /dev/null
@@ -0,0 +1,167 @@
+<?php require_once '../setup.phtml';?>
+<!DOCTYPE HTML>
+<html lang="en">
+<head>
+<title>Admin Navigation</title>
+<style type="text/css">
+<?php
+$logoAdminSmall
+    = (file_exists('../images/logoAdminSmall.gif'))
+    ? 'background: url(../images/logoAdminSmall.gif) no-repeat;'
+    : '';
+?>
+body {
+    margin: 2px;
+    font-family: arial, sans-serif;
+    font-size: 12px;
+    background-color: #fff;}
+a:link {color: #004C64;}
+a:visited {color: #004C64;}
+a:hover {color: #2C788F;}
+a:active {color: #004C64;}
+
+h1 {
+    font-weight: bold;
+    text-align: center;
+    font-size: 14px;
+    <?php echo $logoAdminSmall;?>
+    text-indent: -3000px;
+    width: 130px;
+    height: 53px;
+    margin-left: 10px;
+    }
+ul { margin: 0; padding: 0; list-style-type: none; }
+li { margin: 0; padding: 0; display: block;    }
+li a {
+    text-decoration: none;
+    display: block;
+    margin: 0;
+    padding: 4px 8px;
+    background-color: #004C64;
+    border-bottom: 1px solid #eee;
+    width: 136px;
+    }
+li a:link, li a:visited, li a:active { color: #EEE; }
+li a:hover { background-color: #2C788F; color: #fff; }
+</style>
+</head>
+<body>
+    <h1><a href="<?php echo MEDIA_BASE_URL.$url;?>" target="_top">
+        <?php echo SITENAME;?></a>
+    </h1>
+<ul>
+<?php
+
+$conf = new Config;
+
+$nav['Home']    = MEDIA_BASE_URL.'admin/splash.phtml';
+$nav['Toolbox'] = MEDIA_BASE_URL.'admin/toolbox.php';
+if (defined('GLM_BLOCKS') && GLM_BLOCKS) {
+    $blocksConfig = new Zend_Config_Ini(
+        BASE . 'Toolkit/Blocks/application.ini',
+        strtolower($_ENV['GLM_HOST_ID'])
+    );
+    $nav[$blocksConfig->application->name] = $blocksConfig->application->path;
+}
+if (defined('ROTATING_IMAGES') && ROTATING_IMAGES) {
+    $dbh = Toolkit_Database::getInstance();
+    $sql = "
+      SELECT id,name
+        FROM rotatingimages.application
+    ORDER BY id";
+    $stmt = $dbh->query($sql);
+    while ($app = $stmt->fetch(PDO::FETCH_ASSOC)) {
+        $nav[$app['name']] = MEDIA_BASE_URL . 'admin/rotatingImages.php?app=' . $app['id'];
+    }
+}
+if (defined('SEASONS') && SEASONS) {
+    $seasonConfig = new Zend_Config_Ini(
+        BASE . 'Toolkit/Seasons/application.ini',
+        strtolower($_ENV['GLM_HOST_ID'])
+    );
+    $nav[$seasonConfig->application->name] = MEDIA_BASE_URL.'admin/seasons.php';
+}
+if (defined('BANNERS') && BANNERS) {
+    $nav['Banners'] = MEDIA_BASE_URL.'admin/banners.php';
+}
+if (defined('CONTACT_DB') && CONTACT_DB) {
+    $nav['Contacts'] = MEDIA_BASE_URL.'admin/Contact/';
+}
+if (defined('EVENT_DB') && EVENT_DB) {
+    $nav['Events']
+        = (defined('COMMON_EVENTS') && COMMON_EVENTS)
+        ? MEDIA_BASE_URL.'admin/CommonEvents/'
+        : MEDIA_BASE_URL.'admin/Events/';
+}
+if (defined('COUPONS') && COUPONS) {
+    $couponsRoot =& $conf->parseConfig(
+        BASE . 'Toolkit/Coupons/config.ini',
+        'IniFile'
+    );
+    $couponsName
+        = $couponsRoot->getItem('section', 'conf')
+        ->getItem('directive', 'applicationName')
+        ->getContent();
+    $nav[$couponsName] = MEDIA_BASE_URL.'admin/coupons.php';
+}
+if (defined('MEMBERS_DB') && MEMBERS_DB) {
+    $memberRoot =& $conf->parseConfig(
+        BASE . 'Toolkit/Members/config.ini',
+        'IniFile'
+    );
+    $memberName
+        = $memberRoot->getItem('section', 'listing type')
+        ->getItem('directive', 'plural')
+        ->getContent();
+    $nav[$memberName] = MEDIA_BASE_URL.'admin/members.php';
+}
+if (defined('EMPLOYMENT') && EMPLOYMENT) {
+    $empConfig = new Zend_Config_Ini(
+            BASE . 'Toolkit/Employment/config.ini',
+            strtolower($_ENV['GLM_HOST_ID'])
+        );
+    $empName
+        = $empConfig->applicationName;
+    $nav[$empName] = MEDIA_BASE_URL.'admin/employment/index.php';
+}
+if (defined('PHOTO_GALLERY') && PHOTO_GALLERY) {
+    $nav['Image Library'] = MEDIA_BASE_URL.'admin/photos.php';
+}
+if (defined('VIDEOS') && VIDEOS) {
+    $nav['Video Gallery'] = MEDIA_BASE_URL.'admin/videos.php';
+}
+if (defined('PRESS_DB') && PRESS_DB) {
+    $nav['Press'] = MEDIA_BASE_URL.'admin/Press/';
+}
+if (defined('TICKETING') && TICKETING) {
+    $nav['Ticketing'] = MEDIA_BASE_URL.'admin/ticketing/';
+}
+$nav['Support Form'] = MEDIA_APP_BASE_URL .
+    'supportForm.php?sitename='.urlencode(SITENAME);
+if (defined('EVENT_MANAGEMENT') && EVENT_MANAGEMENT) {
+    $nav['Event Management']
+        = MEDIA_BASE_URL.'admin/EventManagement/EventManagement.php';
+}
+$nav['Server Statistics'] = MEDIA_BASE_URL.'admin/logs/';
+
+foreach ($nav as $name => $url) {
+    echo '
+        <li>
+            <a href="'.$url.'" target="Main">'.$name.'</a>
+        </li>';
+}
+?>
+</ul>
+
+<a
+    style="display: block; margin: 10px auto; text-align: center;"
+    href="http://www.gaslightmedia.com/"
+    target="_blank">
+    <img
+        alt=""
+        src="https://app.gaslightmedia.com/assets/poweredby.gif"
+        border="0"
+        title="Gaslight Media Website">
+</a>
+</body>
+</html>
diff --git a/admin/photos.php b/admin/photos.php
new file mode 100644 (file)
index 0000000..9ee553f
--- /dev/null
@@ -0,0 +1,21 @@
+<?php
+require_once '../setup.phtml';
+// Registry Object to hold global objects and setting for application
+$registry = new Toolkit_Registry;
+// create a Zend Config Object and store into Registry
+$config = new Zend_Config_Ini(
+    BASE . 'Toolkit/Photos/application.ini',
+    strtolower($_ENV['GLM_HOST_ID'])
+);
+$registry->config = $config;
+$registry->page   = MEDIA_BASE_URL . 'admin/photos.php';
+$appConfig        = $config->application->toArray();
+$registry->dbh    = Toolkit_Database::getInstance();
+$registry->logger = Toolkit_Logger::getLogger();
+$registry->router = new Toolkit_Router($registry);
+$registry->router->setPath($appConfig['path']);
+$registry->router->setApplication($appConfig['application']);
+
+$html = $registry->router->loader();
+
+echo $html;
\ No newline at end of file
diff --git a/admin/splash.phtml b/admin/splash.phtml
new file mode 100755 (executable)
index 0000000..2186bbd
--- /dev/null
@@ -0,0 +1,23 @@
+<?php require_once '../setup.phtml';?>
+<!DOCTYPE HTML>
+<html lang="en">
+<head>
+<link
+    type="text/css"
+    rel=stylesheet
+    href="<?php echo MEDIA_BASE_URL.'admin/main.css';?>">
+<title>Welcome to GLM Toolbox</TITLE>
+</head>
+<body>
+
+<div style="text-align: center;">
+<p>
+    <strong>Welcome To The <?php echo SITENAME;?> Administration Area</strong>
+</p>
+<p>Please Choose The Area You Wish To Update.</p>
+<img src="<?php echo MEDIA_BASE_URL.'images/logo.gif';?>">
+</div>
+
+</body>
+</html>
+
diff --git a/admin/toolbox.php b/admin/toolbox.php
new file mode 100644 (file)
index 0000000..cfb8559
--- /dev/null
@@ -0,0 +1,37 @@
+<?php
+require_once '../setup.phtml';
+$registry = new Toolkit_Registry;
+$registry->dbh = Toolkit_Database::getInstance();
+$registry->logger = Toolkit_Logger::getLogger();
+$registry->router = new Toolkit_Router($registry);
+$registry->router->setPath(BASE . 'Toolkit/Toolbox');
+$registry->router->setApplication('Toolbox');
+
+$tlbConf = new Config;
+$tbxRoot
+       =& $tlbConf->parseConfig(BASE . 'Toolkit/Toolbox/config.ini', 'IniFile');
+
+$navigation = new Toolkit_Toolbox_Navigation(
+       new HTML_Menu(),
+       new HTML_Menu_DirectRenderer()
+);
+
+$navArray = $navigation->getNavStructure($tbxRoot);
+$navHtml  = $navigation->renderPageNav($navArray, 'rows');
+
+$html = $registry->router->loader();
+
+$appName
+       = $tbxRoot->getItem('section', 'conf')
+       ->getItem('directive', 'applicationName')
+       ->getContent();
+
+
+GLM_TOOLBOX::top($appName, '');
+echo '<div 
+    style="position:absolute;top:2px;right:5px;padding:5px;float:right;width:165px;background-color:#dddddd;"><a style="color:#000000;"
+    href="http://app.gaslightmedia.com/docs/ToolboxHelpGuide.pdf" target="_blank">Printable Toolbox Help Guide</a></div>';
+echo $navHtml;
+echo $html;
+GLM_TOOLBOX::footer();
+?>
diff --git a/admin/videos.php b/admin/videos.php
new file mode 100644 (file)
index 0000000..1fd8de6
--- /dev/null
@@ -0,0 +1,33 @@
+<?php
+require_once '../setup.phtml';
+
+//  application configuration
+$conf = new Config;
+$confRoot=& $conf->parseConfig(
+       BASE . 'Toolkit/Videos/config.ini',
+       'IniFile'
+);
+
+//  get reference to [conf] section of config file
+$appName
+       = $confRoot->getItem('section', 'conf')
+       ->getItem('directive', 'applicationName')
+       ->getContent();
+
+$navigation = new Toolkit_Videos_Navigation(
+       new HTML_Menu(),
+       new HTML_Menu_DirectRenderer()
+);
+
+$navArray = $navigation->getNavStructure($confRoot);
+$navHtml = $navigation->renderPageNav($navArray, 'rows');
+$c = new Toolkit_Videos_Controller();
+$videos = $c->toHtml(
+       Toolkit_Database::getInstance(),
+       $confRoot
+);
+
+GLM_TOOLBOX::top($appName, '');
+echo $navHtml;
+echo $videos;
+GLM_TOOLBOX::footer();
\ No newline at end of file
diff --git a/assets/accreditationsBg.png b/assets/accreditationsBg.png
new file mode 100644 (file)
index 0000000..c8a76c7
Binary files /dev/null and b/assets/accreditationsBg.png differ
diff --git a/assets/bg.jpg b/assets/bg.jpg
new file mode 100644 (file)
index 0000000..6bf55f5
Binary files /dev/null and b/assets/bg.jpg differ
diff --git a/assets/bgInside.jpg b/assets/bgInside.jpg
new file mode 100644 (file)
index 0000000..9d239fe
Binary files /dev/null and b/assets/bgInside.jpg differ
diff --git a/assets/bgRepeat.png b/assets/bgRepeat.png
new file mode 100644 (file)
index 0000000..416405f
Binary files /dev/null and b/assets/bgRepeat.png differ
diff --git a/assets/blue.png b/assets/blue.png
new file mode 100755 (executable)
index 0000000..b2e934c
Binary files /dev/null and b/assets/blue.png differ
diff --git a/assets/divider.png b/assets/divider.png
new file mode 100644 (file)
index 0000000..80301d3
Binary files /dev/null and b/assets/divider.png differ
diff --git a/assets/footerBg.png b/assets/footerBg.png
new file mode 100644 (file)
index 0000000..1deb12f
Binary files /dev/null and b/assets/footerBg.png differ
diff --git a/assets/logo.png b/assets/logo.png
new file mode 100644 (file)
index 0000000..649b00f
Binary files /dev/null and b/assets/logo.png differ
diff --git a/assets/navbgHover.png b/assets/navbgHover.png
new file mode 100644 (file)
index 0000000..ab80fca
Binary files /dev/null and b/assets/navbgHover.png differ
diff --git a/assets/newsletter/header.jpg b/assets/newsletter/header.jpg
new file mode 100644 (file)
index 0000000..bd855c6
Binary files /dev/null and b/assets/newsletter/header.jpg differ
diff --git a/assets/searchResultOn.gif b/assets/searchResultOn.gif
new file mode 100755 (executable)
index 0000000..f5576ea
Binary files /dev/null and b/assets/searchResultOn.gif differ
diff --git a/assets/subnavBg.jpg b/assets/subnavBg.jpg
new file mode 100644 (file)
index 0000000..9bbb610
Binary files /dev/null and b/assets/subnavBg.jpg differ
diff --git a/assets/toolboxTMP.gif b/assets/toolboxTMP.gif
new file mode 100644 (file)
index 0000000..db97616
Binary files /dev/null and b/assets/toolboxTMP.gif differ
diff --git a/classes/class_db.inc b/classes/class_db.inc
new file mode 100755 (executable)
index 0000000..a84eb0c
--- /dev/null
@@ -0,0 +1,846 @@
+<?php
+
+/**
+ * class_db.inc
+ *
+ * Class build for providing postgres function to facilitate
+ * database abstraction.
+ * We're moving on to using PHP Data Objects (PDO) now
+ *
+ * PHP version 5
+ *
+ * @category  Classes
+ * @package   GLM_DB
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2009 Gaslight Media
+ * @license   Gaslight Media
+ * @version   CVS: $Id: class_db.inc,v 1.8 2010/05/25 14:15:15 jamie Exp $
+ * @link      <>
+ */
+
+/**
+ * Short description for class
+ *
+ * Long description (if any) ...
+ *
+ * @category  Classes
+ * @package   GLM_DB
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2009 Gaslight Media
+ * @license   Gaslight Media
+ * @version   Release: @package_version@
+ * @link      <>
+ */
+class GLM_DB
+{
+    // {{{ Properties
+
+    /**
+     * host
+     * string host database host server name
+     *
+     * @var    mixed
+     * @access public
+     */
+    protected $host;
+    /**
+     * dbname
+     * string dbname name of the database
+     *
+     * @var    mixed
+     * @access public
+     */
+    protected $dbname;
+    /**
+     * user
+     * string user The user to connect as
+     *
+     * @var    mixed
+     * @access public
+     */
+    protected $user;
+    /**
+     * password
+     * string password The users password if any
+     *
+     * @var    mixed
+     * @access public
+     */
+    protected $password;
+    /**
+     * dbd
+     * string dbd Database connection result ID#
+     *
+     * @var    mixed
+     * @access public
+     */
+    protected $dbd;
+    /**
+     * conn
+     * string conn string postgres connection string default = CONN_STR
+     *
+     * @var    mixed
+     * @access public
+     */
+    protected $conn;
+    /**
+     * trans
+     * boolean trans bool if true a transaction is in process
+     *
+     * @var    mixed
+     * @access public
+     */
+    protected $trans;
+    /**
+     * Result
+     *
+     * @var    mixed
+     * @access public
+     */
+    protected $Result;
+
+    /**
+     * Description for public
+     * @var    unknown
+     * @access public
+     */
+    public $dbh;
+    // }}}
+    // {{{ GLM_DB()
+
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @param unknown $conn Parameter description (if any) ...
+     *
+     * @return void
+     * @access public
+     */
+    function GLM_DB($conn = CONN_STR)
+    {
+        $this->dbh      = Toolkit_Database::getInstance();
+        $this->host     = "";
+        $this->dbname   = "";
+        $this->user     = "nobody";
+        $this->password = "";
+        $this->conn     = $conn;
+        $this->trans    = 0;
+        $this->dbd      = "";
+    }
+
+    // }}}
+    // {{{ db_auto_array()
+
+    /** db_auto_get_array
+    *
+    *  The auto function for retrieving an array based soley on a query
+    *  string. This function makes the connection, does the exec, fetches
+    *  the array, closes the connection, frees memory used by the result,
+    *  and then returns the array
+    *
+    * {@source }
+    * @param  $qs    SQL           query       string
+    * @param  $i     row           number
+    * @param  $type  PGSQL_ASSOC or PGSQL_BOTH or PSQL_NUM
+    *
+    * @returns array - Returns an associative array of key-value pairs
+    * @access  public
+    */
+
+    function db_auto_array($qs, $i, $type)
+    {
+        if ($this->dbh) {
+            return $this->dbh->query($qs)->fetch(PDO::FETCH_ASSOC);
+        }
+        if (!$this->dbd) {
+            $this->dbd =& $this->db_connect();
+        }
+        $res = $this->db_exec($qs);
+        if (!$res) {
+            return 0;
+        }
+        if ($this->db_numrows($res) == 0) {
+            return 0;
+        }
+
+        $row = $this->db_fetch_array($res, $i, $type);
+
+        if(!$this->db_freeresult($res)) {
+            return 0;
+        }
+
+        return $row;
+    }
+
+    // }}}
+    // {{{ db_auto_exec()
+
+    /** db_auto_exec
+    *
+    *  The auto function for executing a query.
+    *  This function makes the connection, does the exec, fetches
+    *  the array, closes the connection, frees memory used by the result,
+    *  and then returns success (not a valid result index)
+    *
+    * {@source }
+    * @param   $qs    SQL query string
+    * @returns int - Returns 1 for success 0 for failure
+    * @access  public
+    */
+
+    function db_auto_exec($qs)
+    {
+        if ($this->dbh) {
+            return $this->dbh->query($qs);
+        }
+        $this->db_connect();
+        if (!$this->dbd) {
+            return 0;
+        }
+        if (!$this->db_exec( $qs)) {
+            return 0;
+        } else {
+            return 1;
+        }
+    }
+
+    // }}}
+    // {{{ db_auto_get_data()
+
+    /** db_auto_get_data
+    *
+    *  <p>The auto function for retrieving an array based soley on a query
+    *  string. This function makes the connection, does the exec, fetches
+    *  the array, closes the connection, frees memory used by the result,
+    *  and then returns the array.</p>
+    *
+    * {@source }
+    * @param string $qs    SQL query string
+    *
+    * @returns mixed
+    * @access  public
+    */
+    function db_auto_get_data($qs)
+    {
+        if ($this->dbh) {
+            return $this->dbh->query($qs)->fetchAll(PDO::FETCH_ASSOC);
+        }
+        if (!$this->dbd) {
+            $this->db_connect();
+        }
+        if (!( $res = $this->db_exec( $qs))) {
+            return false;
+        }
+        $totalrows = pg_NumRows( $res);
+        for ($i = 0 ; $i < $totalrows ; $i++) {
+            $data[$i] = $this->db_fetch_array ($res, $i, PGSQL_ASSOC);
+        }
+        if (isset( $data) && $data != "")    {
+            return $data;
+        } else {
+            return 0;
+        }
+    }
+
+    // }}}
+    // {{{ db_close()
+
+    /** db_close
+      *
+      * Closes the connection to database specified by the handle dbd
+      * returns a boolean for success
+      *
+      * {@source }
+      * @returns bool - Returns 1 on success 0 if dbd is not a valid connection
+    * @access  public
+     */
+
+    function db_close()
+    {
+        switch (DB_TYPE) {
+            case "postgres":
+                pg_close($this->dbd);
+            break;
+            default:
+            return false;
+        }
+    }
+
+    // }}}
+    // {{{ db_connect()
+
+   /**
+    * db_connect
+    *
+    * Creates a connection to database specified $conn_str,
+    * and returns a boolean for success.
+    *
+    * @uses    GLM_DB::$dbd
+    * @uses    GLM_DB::$conn
+    * @uses    GLM_DB::$dbname
+    * @uses    GLM_DB::$host
+    * @uses    GLM_DB::$user
+    * @uses    GLM_DB::$password
+    *          {@source }
+    * @returns int
+    * @access  public
+    */
+
+    function db_connect()
+    {
+        if (isset( $this->dbd) && $this->dbd != "") {
+            return $this->dbd;
+        }
+        switch (DB_TYPE) {
+        case "postgres":
+            if ($this->host == '' && $this->dbname == '') {
+                $conn = $this->conn;// CONN_STR;
+            } else {
+                $conn .= ($this->host != '') ? 'host='.$this->host.' ' : '';
+                $conn .= ($this->dbname != '') ? 'dbname='.$this->dbname.' ' : '';
+                $conn .= ($this->user != '') ? 'user='.$this->user." " : '';
+                $conn .= ($this->password != '') ? "password=".$this->password : '';
+            }
+            if (!$this->dbd = pg_connect($conn)) {
+                echo pg_errormessage($conn);
+                       }
+                       $this->_setSearchPath();
+            break;
+
+        default:
+            return 0;
+            break;
+        }
+        return $this->dbd;
+    }
+
+    // }}}
+
+       //      {{{     _setSearchPath()
+
+       private function _setSearchPath()
+       {
+               //      Add schemas to search path.
+               $format = "
+                       SELECT set_config(
+                               'search_path',
+                               current_setting('search_path') || ',%s',
+                               false
+                       )";
+
+               $sql = sprintf($format, 'toolbox');
+               $ret = $this->db_exec($sql);
+
+               $sql = sprintf($format, 'ckimages');
+               $this->db_exec($sql);
+
+               if (defined('CONTACT_DB') && CONTACT_DB) {
+                       //      define banner search path
+                       $sql = sprintf($format, 'contacts');
+                       $this->db_exec($sql);
+               }
+
+               if (defined('BANNERS') && BANNERS) {
+                       //      define banner search path
+                       $sql = sprintf($format, 'banners');
+                       $this->db_exec($sql);
+               }
+
+               if (defined('ROTATING_IMAGES') && ROTATING_IMAGES) {
+                       //      define rotating images search path
+                       $sql = sprintf($format, 'rotatingImages');
+                       $this->db_exec($sql);
+               }
+
+               if (defined('PHOTO_GALLERY') && PHOTO_GALLERY) {
+                       //      define phot gallery search path
+                       $sql = sprintf($format, 'photos');
+                       $this->db_exec($sql);
+               }
+
+               if (defined('EVENT_DB') && EVENT_DB) {
+                       //      define members search path
+                       $sql = sprintf($format, 'events');
+                       $this->db_exec($sql);
+               }
+
+               if (defined('POSTCARD_DB') && POSTCARD_DB) {
+                       //      define members search path
+                       $sql = sprintf($format, 'postcards');
+                       $this->db_exec($sql);
+               }
+
+               if (defined('MEMBERS_DB') && MEMBERS_DB) {
+                       //      define members search path
+                       $sql = sprintf($format, 'members');
+                       $this->db_exec($sql);
+               }
+
+               if (defined('COUPONS') && COUPONS) {
+                       //      define coupon search path
+                       $sql = sprintf($format, 'coupons');
+                       $this->db_exec($sql);
+               }
+       }
+
+       //      }}}
+    // {{{ db_exec()
+
+    /** db_exec
+    *
+    *  Execute an SQL query, * returning a valid result index or zero(0) on
+    *  failure.
+    *
+    * {@source }
+    * @param   $qs    -- SQL query string
+    * @returns int Returns a valid result index on success 0 on failure
+    * @access  public
+    */
+    function db_exec($qs)
+    {
+        if ($this->dbh) {
+            try {
+                $stmt = $this->dbh->query($qs);
+                $stmt->setFetchMode(PDO::FETCH_ASSOC);
+                return $stmt;
+            } catch(PDOEXception $e) {
+                Toolkit_Common::handleError($e);
+            }
+        }
+        if (!$this->dbd) {
+            $this->dbd = $this->db_connect();
+        }
+        switch (DB_TYPE) {
+            case "postgres":
+                if(!$ret = pg_exec($this->dbd, $qs)) {
+                    echo "<font color=red>".$qs."</font>";
+                }
+            break;
+            default:
+            return false;
+        }
+        return $ret;
+    }
+
+    // }}}
+    // {{{ db_fetch_array()
+
+    /** db_fetch_array
+    *
+    *  Stores the data in associative indices, using the field names as
+    *  keys.
+    *
+    * {@source }
+    * @param   $res   -- valid database result index
+    * @param   $i     -- row number
+    * @param   $type  -- PGSQL_ASSOC,PGSQL_BOTH,PGSQL_NUM
+    * @returns array Returns an associative array of key-value pairs
+    * @access  public
+    */
+
+    function db_fetch_array($res, $i = 0, $type = 'PGSQL_ASSOC')
+    {
+        if ($this->dbh) {
+            return $res->fetch(PDO::FETCH_ASSOC);
+        }
+        switch (DB_TYPE) {
+        case "postgres":
+            $row = pg_fetch_array($res, $i, $type);
+            break;
+
+        default:
+            return false;
+        }
+        return $row;
+    }
+
+    // }}}
+    // {{{ db_freeresult()
+
+    /** db_freeresult
+    *
+    *  Free result memory.
+    *
+    * {@source }
+    * @param   $res   -- valid database result index
+    * @returns bool - Returns 1 for success 0 for failure
+    * @access  public
+    */
+
+    function db_freeresult($res)
+    {
+        switch (DB_TYPE) {
+        case "postgres":
+            $ret = pg_freeresult($res);
+            break;
+
+        default:
+            return false;
+        }
+        return $ret;
+    }
+
+    // }}}
+    // {{{ db_numrows()
+
+    /** db_numrows
+    *
+    *  Determine number of rows in a result index
+    *
+    * {@source }
+    * @param   $res   -- valid database result index
+    * @returns int - Returns number of rows
+    * @access  public
+    */
+
+    function db_numrows($res)
+    {
+        if ($this->dbh) {
+            return $res->rowCount();
+        }
+        switch (DB_TYPE) {
+        case "postgres":
+            $ret = pg_numrows($res);
+            break;
+
+        default:
+            return -1;
+        }
+        return $ret;
+    }
+
+    // }}}
+    // {{{ delete()
+
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @param  unknown $table     Parameter description (if any) ...
+     * @param  unknown $condition Parameter description (if any) ...
+     * @return unknown Return description (if any) ...
+     * @access public
+     */
+    function delete($table, $condition)
+    {
+        return $this->pgsql_delete($table, $condition);
+    }
+
+    // }}}
+    // {{{ insert()
+
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @param  unknown $table       Parameter description (if any) ...
+     * @param  unknown $data        Parameter description (if any) ...
+     * @param  unknown $primary_key Parameter description (if any) ...
+     * @param  unknown $sequence    Parameter description (if any) ...
+     * @return unknown Return description (if any) ...
+     * @access public
+     */
+    function insert($table, $data, $primary_key, $sequence)
+    {
+        return $this->pgsql_insert($table, $data, $primary_key, $sequence);
+    }
+
+    // }}}
+    // {{{ pgsql_convert()
+
+    /**
+     * pgsql_convert
+     *
+     * converts an array (like _POST) and verifies field types to use in insert or update of postgres table
+     *
+     * @param  mixed  $table
+     * @param  mixed  $data
+     * @access public
+     * @return string
+     */
+    function pgsql_convert($table, $data)
+    {
+        $query = "select a.attname, format_type(a.atttypid, a.atttypmod)
+                from pg_class c, pg_attribute a
+                where c.relname = '$table'
+                and a.attnum > 0 and a.attrelid = c.oid
+                order by a.attnum";
+        if ($mData = $this->db_auto_get_data($query)) {
+            foreach ($mData as $mRow) {
+                $meta_data[$mRow['attname']] = $mRow['format_type'];
+            }
+        }
+        if (is_array($data)) {
+            foreach ($data as $field => $val) {
+                if ($meta_data[$field]) {
+                    switch ($meta_data[$field]) {
+                    case "integer":
+                    case "double precision":
+                        if ($val == '') {
+                            $proc_data[$field] = 'NULL';
+                        } elseif (is_numeric( $val)) {
+                            $proc_data[$field] = $val;
+                        } else{
+                            die('value for field (int)'.$field.' is not a number');
+                        }
+                        break;
+                    case "boolean":
+                        if ($val == '') {
+                            $proc_data[$field] = 'NULL';
+                        } elseif ($val == 'f' || $val == 't') {
+                            $proc_data[$field] = "'".$val."'";
+                        } else{
+                            die('value for field (boolean)'.$field.' is not a boolean');
+                        }
+                        break;
+                    case "text":
+                        // maybe check to see that the text is being slashed if not then prepare it for postgres
+                        $text = addslashes( stripslashes( trim( $val)));
+                        $proc_data[$field] = "'$text'";
+                        break;
+                    case "date":
+                    if (preg_match( "/([0-9]{1,2})[\/-]?([0-9]{1,2})[\/-]?([0-9]{2,4})|/", $val)) {
+                            $proc_data[$field] = "'".$val."'";
+                    } else{
+                            die('value for field (date)'.$field.' is not a date');
+                    }
+                        break;
+                    default:
+                        die('need case for this name:'.$field.' type: '.$meta_data[$field]);
+                        break;
+                    }
+                } else{
+                    die('Error no field named '.$field.' exist in '.$table);
+                }
+            }
+        } else{
+            return false;
+        }
+        return $proc_data;
+    }
+
+    // }}}
+    // {{{ pgsql_delete()
+
+    /**
+     * pgsql_delete
+     *
+     * @param  mixed  $table
+     * @param  mixed  $condition
+     * @access public
+     * @return string
+     */
+    function pgsql_delete($table, $condition)
+    {
+        reset($condition);
+        $cKey   = key($condition);
+        $query  = "delete from $table where ";
+        $query .= $cKey;
+        $query .= " = ".$condition[$cKey];
+        return $this->db_exec( $query);
+    }
+
+    // }}}
+    // {{{ pgsql_insert()
+
+    /**
+     * pgsql_insert
+     *
+     * @param  mixed  $table
+     * @param  mixed  $data
+     * @param  mixed  $primary_key
+     * @param  mixed  $sequence
+     * @access public
+     * @return string
+     */
+    function pgsql_insert($table, $data, $primary_key, $sequence)
+    {
+        $converted = $this->pgsql_convert($table, $data);
+        if ($res = $this->db_exec("select nextval('$sequence') as $primary_key")) {
+            $insert_data = $this->db_fetch_array( $res, 0, PGSQL_ASSOC);
+            $insert_id = $insert_data[$primary_key];
+        } else {
+            die('returned no insert_id');
+        }
+        $query = "INSERT INTO $table ($primary_key,".implode(",",array_keys( $converted)).") values ($insert_id,".implode(",",array_values( $converted)).")";
+        if ($res = $this->db_exec($query)) {
+            $this->insert_id = $insert_id;
+            return $insert_id;
+        } else {
+            echo $query;
+            die('error');
+        }
+    }
+
+    // }}}
+    // {{{ pgsql_select()
+
+    /**
+     * pgsql_select
+     *
+     * @param  mixed  $query
+     * @access public
+     * @return string
+     */
+    function pgsql_select($query)
+    {
+        return $this->db_auto_get_data( $query);
+    }
+
+    // }}}
+    // {{{ pgsql_update()
+
+    /**
+     * pgsql_update
+     *
+     * @param  mixed  $table
+     * @param  mixed  $data
+     * @param  mixed  $condition
+     * @access public
+     * @return string
+     */
+    function pgsql_update($table, $data, $condition)
+    {
+        $converted = $this->pgsql_convert($table, $data);
+        foreach ($converted as $key => $value) {
+            $q_parts[] = "$key = $value";
+        }
+        reset($condition);
+        $cKey = key($condition);
+        if (is_array($converted)) {
+            $query = "update $table set ";
+            if (is_array($q_parts)) {
+                $query .= implode(",",$q_parts);
+            }
+            $query .= " where ";
+            $query .= $cKey;
+            $query .= " = ".$condition[$cKey];
+        }
+        $this->db_exec($query);
+    }
+
+    // }}}
+    // {{{ select()
+
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @param  unknown $query Parameter description (if any) ...
+     * @return unknown Return description (if any) ...
+     * @access public
+     */
+    function select($query)
+    {
+        return $this->db_auto_get_data($query);
+    }
+
+    // }}}
+    // {{{ trans_end()
+
+    /** trans_end
+     *
+     * Commit the postgres transaction
+     *
+     * {@source }
+     * @returns bool true if successful
+    * @access  public
+     */
+    function trans_end()
+    {
+        if (!$this->trans) {
+            if(!$this->db_exec("COMMIT WORK;")) {
+                return false;
+            } else{
+                return true;
+            }
+        } else{
+            return false;
+        }
+    }
+
+    // }}}
+    // {{{ trans_exec()
+
+    /** trans_exec
+    *
+    *  exec a postgres query in a
+    *  postgres transaction
+    *
+    * {@source }
+    * @param  string query
+    * @access public
+    */
+    function trans_exec($query)
+    {
+        if ($query != "") {
+            if(!$ret = $this->db_exec($query)) {
+                $this->db_exec("ABORT WORK;");
+                return false;
+            } else {
+                return $ret;
+            }
+        } else {
+            return false;
+        }
+    }
+
+    // }}}
+    // {{{ trans_start()
+
+    /** trans_start
+    *
+    * Start a postgres transaction
+    *
+    * {@source }
+    * @returns bool true if sucessful
+    * @access  public
+    */
+    function trans_start()
+    {
+        if (!$this->trans) {
+            if (!$this->dbd = $this->db_connect()) {
+                $this->trans = false;
+                return false;
+            } else {
+                $this->db_exec("BEGIN WORK;");
+                $this->trans = true;
+                return true;
+            }
+        } else {
+            return true;
+        }
+    }
+
+    // }}}
+    // {{{ update()
+
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @param  unknown $table     Parameter description (if any) ...
+     * @param  unknown $data      Parameter description (if any) ...
+     * @param  unknown $condition Parameter description (if any) ...
+     * @return unknown Return description (if any) ...
+     * @access public
+     */
+    function update($table, $data, $condition)
+    {
+        return $this->pgsql_update($table, $data, $condition);
+    }
+
+    // }}}
+}
+?>
diff --git a/classes/class_template.inc b/classes/class_template.inc
new file mode 100644 (file)
index 0000000..bb27145
--- /dev/null
@@ -0,0 +1,2983 @@
+<?php
+/**
+ * Toolbox Classes :)
+ *
+ * @package Toolbox Library
+ * @subpackage Template Library
+ * @filesource
+ */
+
+require_once BASE."classes/class_db.inc";
+require_once BASE."classes/class_toolbox.inc";
+
+/**
+ *     Template Class :)
+ *
+ * <p>
+ * $Id: class_template.inc,v 1.52 2010/07/04 23:53:44 jamie Exp $
+ * NOTE: for the search engine freindly url's use .htaccess file.
+ * need to make sure .htaccess is enabled or this work work
+ * to turn off seo url's set define SEO_URL to 0 in setup file
+ * NOTE: make sure you redo the www.domain.com lines in the .htaccess file
+ * need to set it up before testing with it.
+ * </p>
+ * <p>
+ * For the page title and meta tags make a $title and $meta vars depending on
+ * weather or not it is the home page.
+ * </p>
+ *
+ * @package Toolbox Library
+ * @subpackage Template Library
+ * @category Template
+ * @author Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2007
+ * @version $Revision: 1.52 $
+ * @since $Date: 2010/07/04 23:53:44 $
+ * @todo update the phpdocs for all classes
+ * @todo add title for all the navigation links if and only if they have page title
+ * @todo some tutorial in the docs for how to set things up.
+ */
+class GLM_TEMPLATE
+{
+       //      {{{ properties
+
+       /**
+        * The category id for the page
+        * @var integer
+        * @access public
+        */
+       public $catid;
+
+       /**
+        * The postgres database handler
+        * @var object
+        * @access public
+        */
+       public $DB;
+
+       /**
+        * The starting style for the header
+        *
+        * class="content" should not be used anymore
+        *
+        * @var string
+        * @access protected
+        */
+       protected $header_begin = '<h1>';
+
+       /**
+        * The ending style for the header
+        * @var string header_end
+        * @access protected
+        */
+       protected $header_end = '</h1>';
+
+       /**
+        * The starting style for the sub header
+        * @var string
+        * @access protected
+        */
+       protected $subheader_begin = '<h2>';
+
+       /**
+        * The ending style for the sub header
+        * @var string
+        * @access protected
+        */
+       protected $subheader_end = '</h2>';
+
+       /**
+        * The starting position for images
+        *
+        * @var string
+        * @access protected
+        */
+       protected $img_align = 'left';
+
+       /**
+        * Whether to alternate the images or not
+        *
+        * for alternating images set to 1 else leave alone
+        *
+        * @var string
+        * @access protected
+        */
+       protected $img_alternate = 1;
+
+       /**
+        * The path to the image directory
+        * @var string
+        * @access protected
+        */
+       protected $img_size;
+
+       /**
+        * The category array
+        * @var array
+        * @access protected
+        */
+       protected $data;
+
+       /**
+        * The items array
+        * @var array
+        * @access protected
+        */
+       protected $items;
+
+       /**
+        * @var string type The type
+        */
+       protected $type;
+
+       /**
+        * Used for menu generation
+        * @var string whole_thread The thread string
+        */
+       protected $whole_thread = null;
+
+       /**
+        * Used for menu generation
+        * @var integer thread_count The thread count
+        */
+       protected $thread_count = 1;
+
+       /**
+        * @var array $pages
+        * @access protected
+        */
+       protected $pages;
+
+       /**
+        * adds active = 't' to queries only if ACTIVE_FLAG is set to true
+        * @var string
+        * @access protected
+        */
+       protected $active_query;
+
+       /**
+        * determines page layout
+        * @var integer
+        * @access protected
+        */
+       protected $template;
+
+       /**
+        * Page extension for php pages .php or .phtml
+        * @var string
+        * @access protected
+        */
+       protected $php_ext = '.php';
+
+       //      }}}
+       //      {{{ __construct()
+
+       /**
+        * GLM_TEMPLATE
+        *
+        * @param mixed $catid current page category id
+        * @param mixed $DB Object passed from GLM_DB if done
+        * @access public
+        * @return string
+        */
+       function __construct($catid, $DB = NULL)
+       {
+        // set the cache option from $GLOBALS
+        $this->cacheOptions = $GLOBALS['cacheOptions'];
+        // redo the cachDir
+        $this->cacheOptions['cacheDir'] = BASE . 'cache/';
+        // create a new instance of Cache_Lite
+        $this->cache = new Cache_Lite($this->cacheOptions);
+               $this->catid = $this->get_catid($catid);
+               // using a reference to $DB (should be started on setup.phtml
+               $this->set_DB($DB);
+        // switching to $GLOBALS['dbh']
+        $this->dbh = Toolkit_Database::getInstance();
+               // used for cvb's
+               $this->Member = $this->set_member();
+               // img_size are RESIZED, MIDSIZED, THUMB do not use ORIGINAL
+               $this->img_size = RESIZED;
+
+               $this->set_body_tag();
+               // Uses the PAGE array set in setup.phtml
+               $this->set_pages($GLOBALS['PAGES']);
+               // tell if page is active, deleted or inactive
+               $this->page_status($catid);
+               // set active query string
+               $this->set_active_query();
+       }
+
+       //      }}}
+
+       //      {{{ build_picklist()
+
+       /**
+        * build_picklist:
+        * @param string $fieldname:
+        * @param array $data:
+        * @param mixed $selected:
+        * @param string $type = "standard":
+        * @param boolean $auto = 0:
+        * @param integer $width = NULL :
+        *
+        * @uses GLM_TOOLBOX::build_picklist()
+        *
+        * @return string
+        * @access  public
+        */
+       function build_picklist( $fieldname, $data, $selected, $type = "standard",$auto = 0,$width = NULL )
+       {
+               return GLM_TOOLBOX::build_picklist( $fieldname, $data, $selected, $type = "standard",$auto = 0,$width = NULL );
+       }
+
+       //      }}}
+
+       //      {{{ clean_text()
+
+       /**
+       * clean_text:get rid of single br or p br tags left from htmlarea when textarea is empty.
+       * @param string $output:
+       *
+       * @return string text cleaned
+       * @access  public
+       **/
+       function clean_text($output)
+       {
+               $output = str_replace("<br />","<br>",$output);
+               $output = str_replace("<p><br></p>","",$output);
+               return $output;
+       }
+
+       //      }}}
+       //      {{{     convert_to_thread()
+
+       /**
+        * convert_to_thread:
+        *
+        * @param array $threads:
+        * @param array $thread:
+        * @uses GLM_TEMPLATE::$thread_count
+        * @uses GLM_TEMPLATE::$whole_thread
+        * @uses GLM_TEMPLATE::get_seo_url()
+        * @uses GLM_TEMPLATE::convert_to_thread()
+        *
+        * @return string
+        * @access public
+        */
+       function convert_to_thread($threads, $thread)
+       {
+               foreach($thread as $parent=>$value) {
+                       $this->whole_thread .= str_repeat(".",$this->thread_count);
+                       $this->whole_thread .= "|".htmlentities($value['category'],ENT_QUOTES,'UTF-8');
+                       $url = $this->get_seo_url( $value['id'] );
+                       $this->whole_thread .= "|$url";
+                       $this->whole_thread .= "\n";
+                       if ($threads[$parent]) {
+                               $this->thread_count++;
+                               GLM_TEMPLATE::convert_to_thread($threads, $threads[$parent]);
+                       }
+               }
+               $this->thread_count--;
+               return $this->whole_thread;
+       }
+
+       //      }}}
+
+       //      {{{     has_children()
+
+       /**
+        * has_children: return true or false if this category has sub categories under it.
+        *
+        * @param integer $catid:
+        * @uses GLM_DB::db_auto_get_data()
+        *
+        * @return  string
+        * @access  public
+        */
+       function has_children($catid)
+       {
+               // returns number of children that $catid has
+               $qs = "
+            SELECT count(*)
+              FROM bus_category
+             WHERE parent = $catid {$this-> active_query}";
+               $row=$this->DB->db_auto_get_data($qs);
+               return $row[0]['count'];
+       }
+
+       //      }}}
+       //      {{{ has_subs()
+
+       /**
+        * has_subs:
+        * @param integer $catid:
+        * @param object $DB:
+        *
+        * @uses GLM_TEMPLATE::$active_query
+        * @uses GLM_DB::db_auto_get_data()
+        *
+        * @return  string
+        * @access  public
+        */
+       function has_subs($catid, &$DB)
+       {
+               $sql = "
+            SELECT id
+              FROM bus_category
+             WHERE parent = $catid {$this->active_query}
+             ORDER BY pos";
+               $data = $DB->db_auto_get_data($sql);
+               return is_array($data);
+       }
+
+       //      }}}
+
+       //      {{{ get_all()
+
+       /**
+       * get_all:Does the query and set_data calls boths arrays
+       *
+       * @uses GLM_TEMPLATE::set_data()
+       * @uses GLM_TEMPLATE::$data
+       * @uses GLM_TEMPLATE::$items
+       * @uses GLM_DB::db_auto_get_data()
+       *
+       * @return void
+       * @access public
+       **/
+       function get_all($type = NULL)
+       {
+               $catid = $this->catid;
+               if ($type == 1 || !$type) {
+            $cat_query = "
+            SELECT *
+             FROM bus_category
+            WHERE id = $catid
+         ORDER BY pos";
+            try {
+                $res = $this->set_data($this->dbh->query($cat_query)->fetchAll());
+                $this->data = $res[0];
+            } catch(PDOException $e) {
+                echo '<pre>'.print_r($e, true).'</pre>';
+                die($e->getMessage());
+            }
+               }
+               //$this->img_size = MIDSIZED;
+               //$this->img_size = RESIZED;
+               if ($type == 2 || !$type) {
+            $item_query = "
+            SELECT b.*
+              FROM bus b
+                LEFT OUTER JOIN bus_category_bus bcb ON (bcb.busid = b.id)
+             WHERE bcb.catid = $catid
+          ORDER BY bcb.pos";
+            try {
+                $this->items = $this->set_data($this->dbh->query($item_query)->fetchAll());
+            } catch(PDOException $e) {
+                echo '<pre>'.print_r($e, true).'</pre>';
+                die($e->getMessage());
+            }
+            $file_query = "
+            SELECT *
+             FROM files
+            WHERE bus_id IN ( SELECT bus_id FROM bus_category_bus
+            WHERE catid = ".$this->catid." )
+         ORDER BY bus_id,pos;";
+            try {
+                $file_data = $this->DB->db_auto_get_data($file_query);
+            } catch(PDOException $e) {
+                echo '<pre>'.print_r($e, true).'</pre>';
+                die($e->getMessage());
+            }
+                       if (is_array($file_data)) {
+                               foreach ($file_data as $file_row) {
+                                       $this->item_files[$file_row['bus_id']][] = GLM_TEMPLATE::set_file( $file_row['filename'],$file_row['urltext'] );
+                               }
+                       }
+               }
+       }
+
+       //      }}}
+       //      {{{ get_ancestors()
+
+       /**
+        * get_ancestors:get the ancestors for this category
+        *
+        * @param integer $catid: catid
+        * @param integer $count: starting counter
+        * @uses GLM_DB::db_auto_get_data()
+        *
+        * @return array
+        * @access  public
+        */
+       function get_ancestors($catid, $count)
+       {
+               if ($count == 0 ) {
+                       unset ($this->ancestors);
+               }
+
+               if ($catid) {
+                       $query = "
+                               SELECT id,category,parent
+                             FROM bus_category
+                            WHERE id = ".$catid."
+                            ".$this->active_query;
+
+                       $res = $this->DB->db_auto_get_data($query);
+                       $id = $res[0]['id'];
+                       $parent = $res[0]['parent'];
+                       $category = $res[0]['category'];
+                       $this->ancestors[$count]['id'] = $id;
+                       $this->ancestors[$count]['label'] = $category;
+
+                       $url = $this->get_seo_url( $id );
+                       $this->ancestors[$count]['link'] = $url;
+                       GLM_TEMPLATE::get_ancestors($parent,$count+1);
+
+                       return array_reverse($this->ancestors);
+               }
+       }
+
+       //      }}}
+       //      {{{ get_base_url()
+
+       /**
+        * get_base_url: generate the base of the url for the given category id
+        * @param integer $id :
+        *
+        * @return string
+        * @access public
+        */
+       function get_base_url($id)
+       {
+               if ($this->pages[$id]) {
+                       $page = $this->pages[$id];
+               } else {
+                       $page = $this->pages['default'];
+               }
+               if ($GLOBALS['GLM_SERVER_ID'] == 'ws1.gaslightmedia.com' &&
+                       $this->catid == 1) {
+                       $page = '';
+               }
+               return $page;
+       }
+
+       //      }}}
+       //      {{{     get_bottom_nav()
+
+       /**
+        * get_bottom_nav: generate a top level only bottom navigation for the pages.
+        *
+        * @param integer $parent=0:
+        * @uses GLM_DB::db_auto_get_data()
+        *
+        * @return  string
+        * @access public
+        */
+       function get_bottom_nav($parent=0)
+       {
+               $out = '';
+               $query = "select id,category,intro from bus_category where id != 1 and parent = $parent ".$this->active_query." order by pos asc";
+               $row = $this->DB->db_auto_get_data( $query );
+               $url = $this->get_seo_url( HOME_ID );
+               $links[] = '<a href="'.$url.'">Home</a>';
+               if (!is_array( $row ) ) {
+                       return false;
+               } else {
+                       $out .= '<div id="nav-bottom">';
+                       for ($i = 0; $i < sizeof($row); $i++) {
+                               $category = htmlentities(strip_tags($row[$i]['category']),ENT_QUOTES,'UTF-8');
+                               $url = $this->get_seo_url( $row[$i]['id'] );
+                               $links[] = '<a href="'.$url.'">'.$category."</a>\n";
+                       }
+                       if (is_array($links)) {
+                               $out .= implode(" | ",$links);
+                       }
+                       $out .= '</div>';
+                       return $out;
+               }
+       }
+
+       //      }}}
+       //      {{{ get_bread_crumbs()
+
+       /**
+        * get_bread_crumbs
+        *
+        * @param mixed $catid
+        * @access public
+        * @return string
+        */
+       function get_bread_crumbs($catid)
+       {
+               $string = $this->get_ancestors($catid, 0);
+               $isMemberProfilePage = (   isset($_GET['member_id'])
+                                                               && ctype_digit((string)$_GET['member_id']));
+               if (is_array($string)) {
+                       //      adjust end if we have a profile page so we can last search
+                       //      page as link.
+                       $end = $isMemberProfilePage ? count($string) : count($string) - 1;
+                       if ($this->catid != HOME_ID) {
+                               $outarray[] = '<a href="'.BASE_URL.'">Home</a>';
+                       }
+                       for($i = 0; $i < $end; ++$i) {
+                               $outarray[] = '<a href="'.$string[$i]["link"].'">'.$string[$i]["label"].'</a>';
+                       }
+                       if (  isset($_GET['photo_catid'])
+                               && defined('PHOTO_GALLERY')
+                               && PHOTO_GALLERY
+                               && ctype_digit($_GET['photo_catid'])
+                       ) {
+                               //      user is now in a photo gallery albumn.
+                               //      need to update the breadcrumbs for this.
+                               $currPageUri = $this->get_seo_url($catid);
+                               $currPageLabel = $this->get_catheader($catid, $this->DB);
+
+                               $outarray[] = '<a href="'.$currPageUri.'">'.$currPageLabel.'</a>';
+
+                               $sql = "
+                                       SELECT category
+                                         FROM photo_category
+                                        WHERE id = :id";
+
+                               $stmt = $this->dbh->prepare($sql);
+                               $stmt->bindParam(':id', $_GET['photo_catid'], PDO::PARAM_INT);
+                               $stmt->execute();
+                               $stmt->bindColumn('category', $photoCategory);
+                               $stmt->fetch();
+
+                               $outarray[] = $photoCategory;
+                       } else {
+                               if (!$isMemberProfilePage) {
+                                       $outarray[] = $this->get_catheader($catid, $this->DB);
+                               } else {
+                                       $memberBreadCrumbs = new Toolkit_Members_BreadCrumbs();
+                                       $memberData = $memberBreadCrumbs->getArray(
+                                               $this->dbh,
+                                               $_GET['member_id']
+                                       );
+
+                                       $outarray = array_merge($outarray, $memberData);
+                               }
+                       }
+                       $out = '';
+                       if (is_array($outarray) && count($outarray) > 1) {
+                               $out .= implode(" &gt; ", $outarray);
+                       }
+                       if ($out) {
+                               $return = '<div id="breadcrumbs">'.$out.'</div>';
+                       }
+               }
+               return $return;
+       }
+
+       //      }}}
+       //      {{{ get_category()
+
+       /**
+       * get_category: grab just category contents
+       * @param integer $catid: id of bus_category
+       * @param boolean $showimg=1: weather or not to show category image
+       * @uses DELUXE_TOOLBOX
+       * @uses HOME_PAGE_EVENTS
+       * @uses GLM_TEMPLATE::clean_text()
+       * @uses GLM_TEMPLATE::get_home_events()
+       *
+       * @return string $output
+       * @access public
+       **/
+       function get_category($showimg = 1, $showdiv = 1)
+       {
+        if ($pageContent = $this->cache->get('page-'.$this->catid, 'Toolbox')) {
+            $sql = "
+            SELECT intro
+              FROM bus_category
+             WHERE id = :id";
+            try {
+                $stmt = $this->dbh->prepare($sql);
+                $stmt->bindParam(":id", $this->catid, PDO::PARAM_INT);
+                $stmt->execute();
+                $this->data['intro'] = $this->set_header($stmt->fetchColumn());
+            } catch(PDOException $e) {
+                Toolkit_Common::handleError($e);
+            }
+            return $pageContent;
+        } else {
+            if (DELUXE_TOOLBOX) {
+                $this->get_template("cat");
+            }
+            if (!$this->data) {
+                $this->get_all(1);
+            }
+            $data = $this->data;
+            // Setup the section_links boolean var so section links only
+            // show up on correct pages.
+            $this->section_links = ($data['section_links'] == 't');
+            if (   !$data["image"]
+               && !$data["description"]
+               && !$data["intro"] &&
+                ($this->catid != 1 && HOME_PAGE_EVENTS != true)
+            ) {
+                return false;
+            }
+
+            $output = '';
+            if ($showdiv == 1) {
+                $output .= '<div id="category">';
+            }
+
+            if ($this->catid == 1 && HOME_PAGE_EVENTS ) {
+                $output .= $this->get_home_events();
+            }
+
+            if ($data["image"] || $data["description"] || $data["intro"]) {
+            // $output .= $data["intro"] . ' ';
+                if ($showimg == 1) {
+                    $output .= $data["image"] . ' ';
+                }
+                $output .= $data["description"] . ' ';
+            }
+
+            if ($showdiv == 1) {
+                $output .= '</div>';
+            }
+            $output = GLM_TEMPLATE::clean_text($output);
+            $this->cache->save($output, 'page-'.$this->catid, 'Toolbox');
+            return $output;
+        }
+
+       }
+
+       //      }}}
+       //      {{{ get_category_name()
+
+       /**
+        * getCategoryName:
+        * used mostly for getting category name for search engine friendly url's
+        *
+        * @param integer $id:
+        * @param string $table:
+        * @param object &$DB :
+        * @uses GLM_DB::db_auto_get_data()
+        *
+        * @return void
+        * @access public
+        */
+       function get_category_name($id, $table, &$DB)
+       {
+               if (!is_numeric($id)) {
+                       return false;
+               }
+               if ($table == "class_category") {
+                       $category = "name";
+               } else {
+                       $category = "category";
+               }
+        $query = "
+        SELECT {$category}
+          FROM {$table}
+         WHERE id = :id";
+        try {
+            $stmt = $this->dbh->prepare($query);
+            $stmt->bindParam(":id", $id, PDO::PARAM_INT);
+            $stmt->execute();
+            $data = $stmt->fetchAll();;
+        } catch(PDOException $e) {
+            echo '<pre>'.print_r($e, true).'</pre>';
+            die($e->getMessage());
+        }
+               if (is_array($data)) {
+
+                       $add = '-'.$id;
+                       if ($data[0]['category']) {
+                               $category = $data[0]['category'].$add;
+                       } elseif ($data[0]['name']) {
+                               $category = $data[0]['name'].$add;
+                       } else {
+                               $category = $add;
+                       }
+                       return htmlspecialchars($category);
+               }
+       }
+
+       //      }}}
+       //      {{{ get_catheader()
+
+       /**
+        * get_catheader:output the category name.
+        *
+        * @param integer $catid: The catid for the page
+        * @param object $DB: db obj
+        * @uses GLM_DB::db_auto_get_data()
+        *
+        * @return void
+        * @access  public
+        */
+       function get_catheader($catid, &$DB)
+       {
+               $query = "SELECT category FROM bus_category WHERE id = $catid";
+               $data = $DB->db_auto_get_data($query);
+               if ($data[0]['category']!="") {
+                       $header = strip_tags($data[0]['category']);
+               } else {
+                       $header = '';
+               }
+               return $header;
+       }
+
+       //      }}}
+       //      {{{ get_catid()
+
+       /**
+        * get_catid: setting catid for class
+     *
+        * We should be using the $_POST or $_GET globals here
+        *
+        * @return int catid
+        * @access public
+        **/
+       function get_catid($catid)
+       {
+               if (is_numeric($_GET['catid'])) {
+                       return $_GET['catid'];
+               } elseif (is_numeric($_POST['catid'])) {
+                       return $_POST['catid'];
+               } else {
+                       return $this->catid = $catid;
+               }
+       }
+
+       //      }}}
+       //      {{{     get_catintro()
+
+       /**
+        * get_catintro: return the category page name.
+        *
+        * @param integer $catid: The catid for the page
+        * @param object $DB: db obj
+        * @uses GLM_DB::db_auto_get_data()
+        *
+        * @return void
+        * @access  public
+        */
+       function get_catintro($catid)
+       {
+               $query = "SELECT intro FROM bus_category WHERE id = $catid";
+               $data = $this->DB->db_auto_get_data($query);
+               if ($data[0]['intro']!="") {
+                       $header = strip_tags($data[0]['intro']);
+               } else {
+                       $header = '';
+               }
+               return $header;
+       }
+
+       //      }}}
+       //      {{{ get_distance_from_traverse()
+
+       function get_distance_from_traverse( $zipcode ){
+               if (is_numeric( $zipcode ) ){
+                       // zipcode for search is 49684
+                       $tlat = (float)44.77329;
+                       $tlon = (float)-85.70123;
+                       $zipDB = new GLM_DB();
+                       $zipDB->host = 'ds1';
+                       $zipDB->dbname = 'zip';
+                       $zipDB->pgsql_select( "BEGIN WORK;" );
+                       $query = "
+                SELECT lat, lon, city, state_name
+                  FROM zip
+                 WHERE zipcode = '$zipcode'
+                 order by city_type desc limit 1 offset 0;";
+                       if ($data = $zipDB->pgsql_select( $query ) ){
+                               $lat = (float)$data[0]['lat'];
+                               $lon = (float)$data[0]['lon'];
+                               $city = $data[0]['city'];
+                               $state = $data[0]['state_name'];
+                       }
+
+                       $temp = "(pow(sin( ( ( $tlat * pi()/180.0 ) - ( $lat * pi()/180.0 )) /2.0),2) + cos( ( $lat * pi()/180.0 )) * cos( ( $tlat * pi()/180.0 ) ) * pow(sin( ( ( $tlon * pi()/180.0 ) - ( $lon * pi()/180.0 )) /2.0),2))";
+                       $query = "select ceil(3956 * 2 * atan2(sqrt($temp ),sqrt(1- ($temp) ))) as distance";
+                       if ($zip_data = $zipDB->pgsql_select( $query ) ){
+                               $zipDB->pgsql_select( "ABORT WORK;" );
+                               $zdata['remote_addr'] = $_SERVER['REMOTE_ADDR'];
+                               $zdata['zipcode'] = $zipcode;
+                               $zdata['city'] = $city;
+                               $zdata['state'] = $state;
+                               $this->DB->pgsql_insert( 'zip_dist_form', $zdata, 'zip_dist_form_id', 'zip_dist_form_zip_dist_form_id_seq', true );
+                               return $zip_data[0]['distance'];
+                       }
+               }
+       }
+
+       //      }}}
+       //      {{{ get_event_date()
+
+       /**
+        * get_event_date: make the event date human readable
+     *
+        * @param string  $sdate       start date
+        * @param string  $edate       end date
+        * @param string  $dateType    dateType Postgres,etc
+     * @param boolean $microFormat use microformating on output
+        *
+        * @return string
+        * @access public
+        */
+       function get_event_date(
+        $sdate,
+        $edate,
+        $dateType,
+        $microFormat = false
+    ) {
+               switch($dateType) {
+                       case "Postgres":
+            $postgresPattern = "/([0-9]{1,2})[/-]([0-9]{1,2})[/-]([0-9]{4})/";
+            if (preg_match($postgresPattern, $sdate, $spt)) {
+                                       $mon = $spt[1];
+                                       $day = $spt[2];
+                                       $yr = $spt[3];
+                               }
+
+            if (preg_match($postgresPattern, $edate, $ept)) {
+                                       $mon2 = $ept[1];
+                                       $day2 = $ept[2];
+                                       $yr2 = $ept[3];
+                               }
+                               break;
+        case "eventstamp":
+                       case "timestamp":
+                               $mon = date("m",$sdate);
+                               $day = date("d",$sdate);
+                               $yr = date("Y",$sdate);
+                               $mon2 = date("m",$edate);
+                               $day2 = date("d",$edate);
+                               $yr2 = date("Y",$edate);
+                               break;
+               }
+
+               $start = mktime(0,0,0,$mon,$day,$yr);
+               $end = mktime(0,0,0,$mon2,$day2,$yr2);
+               if ($day == $day2 && $mon == $mon2 && $yr == $yr2) {
+                       $dateparam = "M j, Y";
+                       $dateBegin = date($dateparam, $start) ;
+                       $dateEnd   = "";
+               } elseif ($day == $day2 AND $mon == $mon2 AND $yr != $yr2) {
+                       $dateparam1 = "M j, Y -";
+                       $dateparam2 = "Y";
+                       $dateBegin  = date($dateparam1, $start);
+                       $dateEnd    = date($dateparam2, $end);
+               } elseif ($day != $day2 AND $mon == $mon2 AND $yr == $yr2) {
+                       $dateparam1 = "M j -";
+                       $dateparam2 = "j, Y";
+                       $dateBegin  = date($dateparam1, $start);
+                       $dateEnd    = date($dateparam2, $end);
+               } elseif ($day != $day2 AND $mon == $mon2 AND $yr != $yr2) {
+                       $dateparam1 = "M j, Y -";
+                       $dateparam2 = "M j, Y";
+                       $dateBegin = date($dateparam1, $start);
+                       $dateEnd = date($dateparam2, $end);
+               } elseif ($yr == $yr2) {
+                       $dateparam1 = "M j -";
+                       $dateparam2 = "M j, Y";
+                       $dateBegin  = date($dateparam1, $start);
+                       $dateEnd    = date($dateparam2, $end);
+               } else {
+                       $dateparam1 = "M j, Y -";
+                       $dateparam2 = "M j, Y";
+                       $dateBegin  = date($dateparam1, $start);
+                       $dateEnd    = date($dateparam2, $end);
+               }
+        if ($microFormat) {
+            $dateBegin = '<span class="dtstart">
+                <span class="value" title="'.date('Y-m-d', $start).'">' .
+                $dateBegin . '</span>
+                </span>';
+            $dateEnd
+                = ($dateEnd)
+                ? '<span class="dtend">
+                <span class="value" title="'.date('Y-m-d', $end).'">' .
+                $dateEnd . '</span>
+                </span>'
+                : '';
+        }
+               return $dateBegin." ".$dateEnd;
+       }
+
+       //      }}}
+       //  {{{ get_headlines()
+
+       function get_headlines()
+    {
+               $headlines = array();
+               $query = "
+          SELECT id,intro,feature_intro,description,image
+            FROM bus_category
+           WHERE featured = 't'
+             AND active = 't'
+           ORDER BY parent,pos;";
+               if( $data = $this->DB->pgsql_select( $query ) ){
+                       foreach( $data as $row ){
+                               $headlines[] = array(
+                    'href'   => $this->get_seo_url($row['id']),
+                    'img'    => ($row['image']) ? THUMB . $row['image']: '',
+                    'header' => $row['feature_intro'],
+                    'descr'  => GLM_TOOLBOX::make_teaser($row['description'], 150, true));
+                       }
+               }
+               return $headlines;
+       }// }}}
+       // {{{ get_home_events()
+       /**
+       * get_home_events: get events flaged as home events
+       *
+       * @uses GLM_TEMPLATE::get_event_date()
+       * @uses GLM_DB::db_auto_get_data()
+       *
+       * @return void
+       * @access public
+       **/
+       public function get_home_events($limit = 3)
+       {
+               $query = "
+                 SELECT id, header, descr, bdate, edate, img,
+                                to_char(bdate, 'Mon - DDth') AS sdate
+                   FROM event
+                  WHERE visable='t'
+                    AND edate >= current_date
+             AND home='t'
+        ORDER BY bdate asc, edate asc";
+               $data = $this->DB->db_auto_get_data($query);
+               if(is_array($data)) {
+                       foreach($data as $key => $value) {
+                               $id = $value['id'];
+                               $header = $value['header'];
+                               $title = strip_tags(addslashes($header));
+                               $descr = GLM_TOOLBOX::make_teaser($value['descr'], 250, true);
+                               $sdate = strtotime($value['bdate']);
+                               $edate = strtotime($value['edate']);
+                               $month = date('n',$sdate);
+                               $year = date('Y',$sdate);
+                               $dates = GLM_TEMPLATE::get_event_date($sdate,$edate,"timestamp");
+                               $href = BASE_URL.'index.php?catid='.EVENT_PAGE."&amp;month={$month}&amp;year={$year}&amp;eventid={$id}";
+                               $area_events[] = array(
+                                       'href'   => $href,
+                                       'bdate'  => $value['sdate'],
+                                       'dates'  => $dates,
+                                       'header' => $header,
+                                       'descr'  => $descr,
+                               );
+                       }
+                       return $area_events;
+               } else {
+                       return null;
+               }
+       }// }}}
+       //      {{{ get_hotspecials()
+
+       function get_hotspecials(){
+               $query = "
+            SELECT id, intro, feature_intro
+              FROM bus_category
+             WHERE featured = 't'
+             order by parent, pos;";
+               if ($data = $this->DB->pgsql_select( $query ) ){
+                       $out = '<div id="h-hotspecials">
+                               <span class="h-all">Hot specials</span>';
+                       foreach ($data as $row) {
+                               $intro = htmlspecialchars(strip_tags($row['intro']));
+                               $intro_text = '<p>'.htmlspecialchars(strip_tags($row['feature_intro'] ) ).'</p>';
+                               $out .= '<div class="h-hotspecial">
+                                       <a href="'.$this->get_seo_url( $row['id'] ).'">'.$intro.'</a>
+                                       '.$intro_text.'
+                                       </div>';
+                       }
+                       $out .= '</div>';
+               }
+               return $out;
+       }
+
+       //      }}}
+       //      {{{ get_main_nav()
+
+       function get_main_nav()
+       {
+               $query = "select id,category
+                       from bus_category
+                       where parent = 0
+                       and id not in (".HOME_ID.",".MEMBERS_CATEGORY.",7,8,9)
+                       and active = 't'
+                       order by pos";
+               if ($data = $this->DB->pgsql_select( $query ) ){
+                       $out = '<ul id="nav">';
+                       foreach( $data as $row ){
+                               $out .= '<li><a href="'.$this->get_seo_url( $row['id'] ).'">'.$row['category'].'</a></li>';
+                       }
+                       $out .= '</ul>';
+               }
+               return $out;
+       }
+
+       //      }}}
+       //      {{{     get_menu_array()
+
+
+       /**
+        * get_menu_array: like get_menu_string but returns an array
+        *
+        * @uses GLM_TEMPLATE::sort_childs()
+        * @uses GLM_DB::db_auto_get_data()
+        *
+        * @return  string
+        * @access  public
+        */
+       function get_menu_array()
+       {
+               $query = "
+            SELECT id, parent, category
+              FROM bus_category
+             WHERE id != 0 {$this->active_query}
+             order by parent, pos";
+               $data = $this->DB->db_auto_get_data($query);
+               $newdata = GLM_TEMPLATE::sort_childs($data);
+               return $newdata;
+       }
+
+       //      }}}
+       //      {{{ get_main_cats()
+
+       /**
+        * get_main_cats
+        *
+        * @access public
+        * @return string
+        */
+       function get_main_cats()
+       {
+               static $main_cats_array;
+               if (!is_array($main_cats_array)) {
+                       $query = "
+                SELECT id, category
+                  FROM bus_category
+                 WHERE parent = 0
+                 order by pos;";
+                       if ($data = $this->DB->db_auto_get_data($query)) {
+                               foreach ($data as $row) {
+                                       $main_cats_array[$row['id']] = htmlentities(strip_tags($row['category']),ENT_QUOTES,'UTF-8');
+                               }
+                       }
+               }
+               return $main_cats_array;
+       }
+
+       //      }}}
+       //      {{{ get_menu_string()
+
+       /**
+        * get_menu_string:get categories for the phplayermenu
+        *
+        * @uses GLM_TEMPLATE::sort_childs()
+        * @uses GLM_TEMPLATE::convert_to_thread()
+        * @uses GLM_DB::db_auto_get_data()
+        *
+        * @return string
+        * @access  public
+        **/
+       function get_menu_string()
+       {
+               $query = "
+            SELECT id, parent, category
+              FROM bus_category
+             WHERE id != 0 {$this->active_query}
+             ORDER BY parent, pos";
+               $data = $this->DB->db_auto_get_data($query);
+               $newdata = GLM_TEMPLATE::sort_childs($data);
+               $string = GLM_TEMPLATE::convert_to_thread($newdata,$newdata[0]);
+               return $string;
+       }
+
+       //      }}}
+       //      {{{ get_id_from_name()
+
+       /**
+        * getIdFromName:
+        *
+        * @param string $name:
+        * @param string $table:
+        * @param object $DB:
+        * @uses GLM_DB::db_auto_get_data()
+        *
+        * @deprecated Don't use this!  THIS IS A WARNING (this funcion will be gone next time)
+        * @return string
+        * @access  public
+        */
+       function get_id_from_name( $name, $table, &$DB)
+       {
+               if ($name == "") {
+                       return 0;
+               }
+               if (is_numeric($name)) {
+                       return $name;
+               }
+        $pattern = "/(.*)/$/";
+               if (preg_match($pattern, $name, $tmp)) {
+                       $name = $tmp[1];
+               }
+               $category = "category";
+        $pattern = "/-([0-9]*)$/";
+               if (preg_match($pattern, $name, $tmp)) {
+                       $id = $tmp[1];
+                       return $id;
+               }
+               // should already be returning id at this point
+               // putting the _id on the end of all url's as
+               // the other way is very inifiecent for the database.
+               $name = str_replace( "-"," ",$name );
+               $query = "select id from $table where trim(lower(replace(replace(replace(replace(replace(replace($category,'\'',''),'/',''),'-',' '),'#',''),'&',''),'?',''))) = '".trim(strtolower($name))."'";
+               $data = $DB->db_auto_get_data($query);
+               if (is_array($data)) {
+                       foreach ($data as $key => $val) {
+                               $catid = $val['id'];
+                       }
+                       return $catid;
+               } else {
+                       return 0;
+               }
+       }
+
+       //      }}}
+       //      {{{ get_id_from_path_info()
+
+       /**
+        * get_id_from_path_info: takes the path_info and gets a catid from bus_category table
+        * NOTE: not used.
+        *
+        * @deprecated using .htaccess file for this when using seo url's
+        * @return int catid
+        * @access public
+        **/
+       function get_id_from_path_info()
+       {
+               return false;
+       }
+
+       //      }}}
+       //      {{{ get_image_path()
+
+       /**
+        * get_image_path: get image path from the size used
+        *
+        * @uses MIDSIZED_PATH
+        * @uses RESIZED_PATH
+        * @uses THUMB_PATH
+        *
+        * @return path for images
+        * @access public
+        **/
+       function get_image_path()
+       {
+               if (strstr($this->img_size, 'midsized/')) {
+                       return MIDSIZED_PATH;
+               }
+               if (strstr($this->img_size,'resized/')) {
+                       return RESIZED_PATH;
+               }
+               if (strstr($this->img_size,'thumb/')) {
+                       return THUMB_PATH;
+               }
+       }
+
+       //      }}}
+       //      {{{ get_listings()
+
+       /**
+        * template_parser:This function creates data
+        * and items arrays and does the output for the page.
+        *
+        * @uses DELUXE_TOOLBOX
+        * @uses GLM_TEMPLATE::$items
+        * @uses GLM_TEMPLATE::get_template()
+        * @uses GLM_TEMPLATE::get_all()
+        * @uses GLM_TEMPLATE::load_static_page()
+        * @uses GLM_TEMPLATE::clean_text()
+        * @uses GLM_TEMPLATE::$item_files
+        *
+        * @return void
+        * @access public
+        */
+       function get_listings()
+       {
+        if ($paragraphContent = $this->cache->get('paragraphs-'.$this->catid, 'Toolbox')) {
+            return $paragraphContent;
+        } else {
+            // grab category and items into data and items respectfully
+            if (DELUXE_TOOLBOX ) {
+                $this->get_template( "list" );
+            }
+
+            if (!is_array($this->items)) {
+                $this->get_all(2);
+            }
+
+            $output = '';
+            // load any static category page from the static directory
+            // hard codded content would have $catid.phtml page for it
+            //$output .= $this->load_static_page();
+            if (is_array($this->items)) {
+               $anchors = array();
+                foreach($this->items as $key=>$val) {
+                    // building array with links to each paragraph
+                    $name = trim(strip_tags($val["name"]));
+                    if ($name && $this->section_links) {
+                        $anchors[] = '<a href="#sect-'.$val['id'].'">'.strip_tags($val["name"]).'</a>';
+                    }
+                    // items can be moved around as needed
+                    $output .= '<div class="listing" id="sect-'.$val['id'].'">'."\n";
+                    $output .= $val["name"];
+                    $output .= $val["image"];
+                    $output .= $val["address"];
+                    $output .= $val["description"];
+                    $output .= $val["contactname"];
+                    $output .= $val["email"];
+                    $output .= $val["phone"];
+                    $output .= $val["fax"];
+                    $output .= $val["url"];
+                    if (   isset($this->item_files[$val['id']])
+                       && is_array($this->item_files[$val['id']])
+                    ) {
+                        $output .= implode('', $this->item_files[$val['id']]);
+                    }
+                    if ($val['back_to_top'] == 't') {
+                        $output .= '<a href="#toolbox">Back to Top</a>';
+                    }
+                    $output .= "</div>"."\n";
+                }
+                // assign array of links to $this->sectionAnchors
+                $this->sectionAnchors = $anchors;
+            }
+            $output = GLM_TEMPLATE::clean_text($output);
+            $this->cache->save($output, 'paragraphs-'.$this->catid, 'Toolbox');
+            return $output;
+        }
+       }
+
+       //      }}}
+       //      {{{ get_page()
+
+       /**
+        * get_page: replacing template_parser with get_page function
+        *
+        * @uses GLM_TEMPLATE::get_category() For building the main page section
+        * @uses GLM_TEMPLATE::get_listings() For building out the paragraph sections
+        *
+        * @return  string $out NEED to echo results of this function
+        * @access  public
+        */
+       function get_page($showimg = 1, $showdiv = 1)
+       {
+               if (isset($_REQUEST['sitemap']) && $_REQUEST['sitemap'] == 1) {
+                       return $this->get_sitemap();
+               } elseif (   isset($_REQUEST['zipcode'])
+                                 && is_numeric($_REQUEST['zipcode'])
+               ) {
+                       $this->distance = '<div>'.$this->get_distance_from_traverse($_REQUEST['zipcode']).' miles from '.$_REQUEST['zipcode'].'</div>';
+               }
+
+               if ($this->page_status != 'Good') {
+                       return '<h1>Sorry this page is Down!</h1>';
+               }
+
+               if (   defined('GOOGLE_SEARCH')
+                   && GOOGLE_SEARCH
+                   && isset($_REQUEST['query'])
+                   && $_REQUEST['query']
+               ) {
+                       $out = '<div id="searchcontrol"></div>';
+               } elseif (   isset($_REQUEST['member_id'])
+                                 && is_numeric($_REQUEST['member_id'])
+                                 && !$this->Member->memberSections[$this->catid]
+               ) {
+                       $out = $this->load_static_page();
+               } elseif (   defined('MEMBERS_DB')
+                                 && MEMBERS_DB
+                                 && isset($this->Member->memberSections)
+                                 && $this->Member->memberSections[$this->catid]
+               ) {
+            $GLOBALS['bottomScripts'][]
+                = BASE_URL . 'Toolkit/Members/libjs/travel-list.js';
+            $out = $this->get_bread_crumbs($this->catid);
+                       if (   !isset($_REQUEST['member_id'])
+                && !isset($_REQUEST['search'])
+                && !isset($_REQUEST['start'])
+               ) {
+                               $out .= $this->get_category($showimg,0);
+                $pageHeader  = $this->data['intro'];
+                $out .= '<div id="category">';
+                $out .= $pageHeader;
+              //  $out .= $category;
+                $out .= '</div>';
+                       }
+
+            //  application configuration
+            $conf = new Config;
+            $root = $conf->parseConfig(
+                               BASE . 'Toolkit/Members/config.ini',
+                               'IniFile'
+                       );
+
+            if (isset($_GET['member_id']) && ctype_digit($_GET['member_id'])) {
+                try {
+                    $profile = new Toolkit_Members_ProfilePage(
+                                               Toolkit_Database::getInstance(),
+                        new Toolkit_Image_Server(),
+                        $_GET['member_id']
+                    );
+                    $profile->setCatId($this->catid);
+                    $profile->setConfig($root);
+                    $flexy = new HTML_Template_Flexy(
+                                               Toolkit_Members::getFlexyOptions()
+                                       );
+                    $cache = new Cache_Lite(Toolkit_Members::getCacheOptions());
+                    $out .= $profile->toHtml($flexy, $cache, MEMBER_RESIZED);
+                } catch (PEAR_Exception $e) {
+                    return Toolkit_Common::handleError($e);
+                }
+            } elseif (   isset($_GET['search'])
+                                         || $this->Member->hideUserSearchForm()
+                       ) {
+                               if ($this->Member->includeMemberMap()) {
+                                       $googleMap = new Toolkit_Members_Map();
+                                       $out .= $googleMap->toHtml();
+                               }
+
+                               $searchQuery = new Toolkit_Members_SearchQueryGenerator(
+                                       true,
+                                       $root
+                               );
+                               $sql = $searchQuery->getQuery($this->dbh);
+                $searchList = new Toolkit_Members_SearchList(
+                                       $this->dbh,
+                    50,
+                    null,
+                    null,
+                    true
+                );
+
+                $searchList->setConfig($root);
+                $searchList->setQuery($sql);
+                $searchList->setDefaultSort(array('member_name' => 'ASC'));
+                //  rendering engine to use
+                $rEngine = new Structures_DataGrid_Renderer_Flexy();
+                //  template options to use for template engine
+                $tplOpts  = Toolkit_Members::getFlexyOptions();
+                //  templating  engine to use
+                $tEngine = new HTML_Template_Flexy($tplOpts);
+                $rEngine->setContainer($tEngine);
+
+                $out .= $searchList->toHtml($rEngine);
+            } else {
+                               if ($this->Member->includeMemberMap()) {
+                                       $googleMap = new Toolkit_Members_Map();
+                                       $out .= $googleMap->toHtml();
+                               }
+
+                $action = BASE_URL . "index.php?catid={$this->catid}";
+                $form = new Toolkit_Members_UserSearchForm(
+                    'SearchForm',
+                    'get',
+                    $action,
+                    null,
+                    null,
+                    true
+                );
+                $res = $form->setCatId($this->catid);
+                if (!PEAR::isError($res)) {
+                    $form->setPageMemberCategories($this->dbh);
+                    $form->configureForm($this->dbh, $root);
+                    $out .= $form->toHtml(Toolkit_Members::getFlexyOptions());
+                } else {
+                    return Toolkit_Common::handleError($res);
+                }
+            }
+
+                       if (   !isset($_REQUEST['member_id'])
+                && !isset($_REQUEST['search'])
+                && !isset($_REQUEST['start'])
+            ) {
+                $out .= $this->load_static_page();
+                               $out .= $this->get_listings();
+                       }
+
+                       if (PHOTO_GALLERY) {
+                               $out .= $this->photo_module();
+                       }
+               } else {
+            $breadCrumbs = $this->get_bread_crumbs($this->catid);
+                       $category    = $this->get_category($showimg, 0);
+            $pageHeader  = $this->data['intro'];
+            $static      = $this->load_static_page();
+                       $listings    = $this->get_listings();
+            if (!$links = $this->cache->get('sectionLinks-'.$this->catid, 'Toolbox')) {
+                // generation of the section links to each paragraph
+                if (is_array($this->sectionAnchors)) {
+                    $links = '<ul id="paragraphLinks">';
+                    foreach ($this->sectionAnchors as $anchors) {
+                        $links .= '<li>'.$anchors.'</li>';
+                    }
+                    $links .= '</ul>';
+                $this->cache->save($links, 'sectionLinks-'.$this->catid, 'Toolbox');
+                }
+            }
+            $out  = $breadCrumbs;
+            $out .= '<div id="category">';
+            $out .= $pageHeader;
+            $out .= $links;
+            $out .= $category;
+            $out .= '</div>';
+            $out .= $static;
+            $out .= $listings;
+
+                       if ($this->_hasCouponsAssignedToPage()) {
+                               $out .= $this->_getCouponWidget();
+                       }
+
+                       if (PHOTO_GALLERY) {
+                               $out .= $this->photo_module();
+                       }
+               }
+               return $out;
+       }
+
+       //      }}}
+       //      {{{ get_page_header_image()
+
+       function get_page_header_image(){
+               $headers[5] = 'accommodations';
+               $headers[21] = 'arts';
+               $headers[22] = 'beaches';
+               $headers[16] = 'golf';
+               $headers[20] = 'guest';
+               $headers[4] = 'outdoor';
+               $headers[43] = 'reservations';
+               $headers[3] = 'things';
+               $headers[18] = 'winaries';
+               $headers[7] = 'meeting';
+               $headers[8] = 'tour';
+               $headers[9] = 'media';
+               $headers[33] = 'events';
+               $this->header_images = $headers;
+               $top_parent = $this->get_top_parent($this->catid);
+               if ($top_parent && $this->catid != HOME_ID) {
+                       if ($_SERVER['HTTPS'] == "on") {
+                               $base_url = BASE_SECURE_URL;
+                       } else {
+                               $base_url = BASE_URL;
+                       }
+
+                       if ($_SERVER['HTTPS'] != "on") {
+                       return( '
+<div id="topimg">
+       <object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=8,0,0,0" width="565" height="95" id="accommodations" align="middle">
+               <param name="allowScriptAccess" value="sameDomain">
+               <param value="transparent" name="wmode">
+               <param name="movie" value="'.$base_url.'assets/headers/headers.swf?XMLfile='.$base_url.'assets/headers/'.$headers[$top_parent].'.xml">
+               <param name="quality" value="high">
+               <param name="bgcolor" value="#3D8D3A">
+               <embed wmode="transparent" src="'.$base_url.'assets/headers/headers.swf?XMLfile='.$base_url.'assets/headers/'.$headers[$top_parent].'.xml" quality="high" bgcolor="#ffffff" width="565" height="95" name="accommodations" align="middle" allowScriptAccess="sameDomain" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer">
+       </object>
+</div>
+' );
+                       } else {
+                               return false;
+                       }
+               } else {
+                       return false;
+               }
+       }
+
+       //      }}}
+       //      {{{ get_parent()
+
+       /**
+       * get_parent: get parent for this category
+       *
+       * @param integer $catid: id
+       * @param object $DB: database obj
+       * @uses GLM_DB::db_auto_get_data()
+       *
+       * @return int $parent
+       * @access public
+       **/
+       function get_parent($catid,&$DB)
+       {
+               $query = "
+            SELECT parent
+              FROM bus_category
+             WHERE id = $catid
+             ORDER BY pos";
+               $data = $DB->db_auto_get_data($query);
+               return $data[0]["parent"];
+       }
+
+       //      }}}
+       //      {{{ get_parent_id ()
+
+       function get_parent_id($id)
+       {
+               if (is_numeric($id)) {
+                       $query = "select parent from bus_category where id = $id";
+                       if ($data = $this->DB->pgsql_select($query)) {
+                               return $data[0]['parent'];
+                       }
+               }
+       }
+
+       //      }}}
+       //      {{{ get_seo_url()
+
+       /**
+        * get_seo_url:
+        * grab category part of the search engine friendly url
+        * looks at define for seo_url to see weather to use the seach engine friendly url's or not
+        *
+        * @param integer $id:
+        * @param boolean $slash = 1 : to put a slash on end or not
+        * @uses BASE_URL
+        * @uses SEO_URL
+        * @uses HOME_ID
+        * @uses GLM_TEMPLATE::$php_ext
+        * @uses GLM_TEMPLATE::set_name_url()
+        * @uses GLM_TEMPLATE::get_category_name()
+        * @uses GLM_TEMPLATE::get_base_url()
+        *
+        * @return string $url for page.
+        * @access public
+        **/
+       function get_seo_url($id, $slash = 1)
+       {
+               if ($id == HOME_ID &&
+                               $GLOBALS['GLM_SERVER_ID'] != 'devsys.gaslightmedia.com') {
+                       return BASE_URL;
+               } elseif ($id == HOME_ID &&
+                               $GLOBALS['GLM_SERVER_ID'] == 'devsys.gaslightmedia.com') {
+                       return BASE_URL.'index.php';
+               }
+               if (   isset($this->category_toolbox)
+                       && is_array($this->category_toolbox)
+                       && in_array($id, $this->category_toolbox)
+               ) {
+                       $category_id = array_search($id, $this->category_toolbox);
+                       $url = BASE_URL.'member.php?category_id='.$category_id;
+                       return $url;
+               }
+        if (in_array($id, array(
+            MEMBERS_PROFILE_FORM_PAGE,
+            MEMBERS_COUPONS_PAGE,
+            MEMBERS_EVENTS_PAGE,
+            MEMBERS_REPORTS_PAGE
+        ))) {
+            switch ($id) {
+            case MEMBERS_PROFILE_FORM_PAGE :
+                return $baseurl . 'memberdb/index.php?Option=Member&amp;Action=Edit';
+            break;
+            case MEMBERS_COUPONS_PAGE :
+                return $baseurl . 'memberdb/index.php?Option=Coupons&amp;Action=List';
+            break;
+            case MEMBERS_EVENTS_PAGE :
+                return $baseurl . 'memberdb/index.php?Option=Events&amp;Action=List';
+            break;
+            case MEMBERS_REPORTS_PAGE :
+                return $baseurl . 'memberdb/index.php?Option=Reports&amp;Action=List';
+            break;
+            }
+        }
+               if (SEO_URL) {
+                       if ($id == 142) {
+                               $base_url = BASE_SECURE_URL;
+                       } else {
+                               $base_url = BASE_URL;
+                       }
+            $url = $base_url;
+                       if (defined("SHORT_URLS") && SHORT_URLS) {
+                static $ShortURL;
+                if (!$ShortURL) {
+                    $ShortURL   = new Toolkit_ShortURL($this->dbh);
+                }
+                $short_url = $ShortURL->getShortUrl($id);
+                if ($short_url) {
+                    return $base_url.$short_url . '/';
+                }
+                       }
+                       $url .= GLM_TEMPLATE::set_name_url(GLM_TEMPLATE::get_category_name($id, "bus_category", $this->DB));
+                       $url = htmlspecialchars(strip_tags($url));
+                       if ($slash) {
+                               $url .= '/';
+                       }
+               } else {
+                       $url = $this->get_base_url($id);
+                       if ($url) {
+                               if ($id == 142) {
+                                       $url = BASE_SECURE_URL.$url;
+                               }else{
+                                       $url = BASE_URL.$url;
+                               }
+                               $url .= $this->php_ext.'?catid='.$id;
+                       } else {
+                               $url = BASE_URL;
+                       }
+               }
+
+               return $url;
+       }
+
+       //      }}}
+       // {{{ get_side_nav()
+
+       function get_side_nav($parent = 0)
+       {
+        if ($navContent = $this->cache->get($this->catid, 'Nav')) {
+            return $navContent;
+        } else {
+            $qs = "
+                SELECT id, category, intro
+                  FROM bus_category
+                 WHERE parent = :parent {$this->active_query}
+                 ORDER BY pos";
+            try {
+                $stmt = $this->dbh->prepare($qs);
+                $stmt->bindParam(":parent", $parent, PDO::PARAM_INT);
+                $stmt->execute();
+                $data = $stmt->fetchAll(PDO::FETCH_ASSOC);
+            } catch(PDOException $e) {
+                return Toolkit_Common::handleError($e);
+            }
+            if (!is_array($data)) {
+                return false;
+            } else {
+               $return = '';
+                foreach ($data as $key => $row) {
+                    $url = $this->get_seo_url($row['id']);
+                    $title = strip_tags(addslashes($row['intro']));
+                    $return .= '<li><a title="' .
+                            htmlentities($title,ENT_QUOTES,'UTF-8') .
+                            '" href="'.$url.'"';
+                    if ($this->catid == $row['id']) {
+                        $return .= ' id="current"';
+                    }
+                    $return .= '>' .
+                        htmlentities($row['category'],ENT_QUOTES,'UTF-8')."</a>";
+                    $hasSubs = $this->has_subs($row['id'], $this->DB);
+                    $isSubId = $this->is_sub_id($this->catid, $row['id'], $this->DB);
+                    $isPage  = $this->catid == $row['id'];
+
+                    if ($hasSubs && ($isSubId || $isPage)) {
+                        $ret2 = $this->get_side_nav($row['id']);
+                        $return .= $ret2;
+                    }
+                    $return .= '</li>';
+                }
+                $main_cats = $this->get_main_cats();
+                if ($main_cats[$parent]) {
+                    $return = '<h2>'.$this->get_catheader($parent, $this->DB).'</h2>
+                    <ul>'.$return.'</ul>';
+                    $this->cache->save($return, $this->catid, 'Nav');
+                    return $return;
+                } else {
+                    $return = "\n<ul>$return</ul>\n";
+                    return $return;
+                }
+            }
+        }
+       }
+
+       // }}}
+       //      {{{ get_sitemap()
+
+       function get_sitemap(){
+               // sitemap.inc goes here
+               // maybe make a sitemap class
+       }
+
+       //      }}}
+       //      {{{ get_sub_nav()
+
+       /**
+       * get_sub_nav:
+       * @param integer $catid:
+       * @param object $DB:
+       *
+       * @uses GLM_TEMPLATE::get_parent()
+       * @uses GLM_TEMPLATE::get_seo_url()
+       * @uses GLM_TEMPLATE::is_sub_id()
+       * @uses GLM_TEMPLATE::get_sub_nav()
+       * @uses GLM_TEMPLATE::has_subs()
+       * @uses GLM_DB::db_auto_get_data()
+       *
+       * @return  string
+       * @access  public
+       **/
+       function get_sub_nav($catid,&$DB)
+       {
+               $parentid = GLM_TEMPLATE::get_parent($catid, $DB);
+               //echo $parentid.'<br>';
+               $query = "SELECT id,category,intro FROM bus_category WHERE parent = $parentid ".$this->active_query." ORDER BY pos";
+               $data = $DB->db_auto_get_data($query);
+               if (is_array($data)) {
+                       $output = '<div id="subnav">';
+                               $counter = 1;
+                               foreach($data as $key=>$val) {
+                                       $url = $this->get_seo_url( $val['id'] );
+                                       $title = strip_tags(addslashes($val['intro']));
+                                       if (   GLM_TEMPLATE::is_sub_id($catid, $parentid, $DB)
+                                               && (   GLM_TEMPLATE::is_sub_id($catid, $val['id'], $DB)
+                                                       || $val['id'] == $catid)
+                                       ) {
+                                               $output .=  '<a title="'.$title.'" class="current" href="'.$url.'">';
+                                       } else {
+                                               $output .=  '<a title="'.$title.'" href="'.$url.'">';
+                                       }
+                                       $output .=  $val["category"];
+                                       $output .=  '</a>';
+                                       if (   GLM_TEMPLATE::is_sub_id($catid, $val['id'], $DB)
+                                               && GLM_TEMPLATE::has_subs($val['id'], $DB)
+                                       ) {
+                                               $output .= GLM_TEMPLATE::get_sub_nav(
+                                                       $val["id"],
+                                                       $DB,
+                                                       $catid
+                                               );
+                                       }
+                               }
+                               $output .= '</div>';
+                       $output = GLM_TEMPLATE::clean_text($output);
+                       echo $output;
+               }
+               return false;
+       }
+
+       //      }}}
+       //      {{{ get_template()
+
+       /**
+        * get_template: get the template type of the bus_category
+        * @param mixed $type : 'cat' or 'list'
+        *
+        * @uses GLM_DB::db_auto_get_data()
+        * @uses GLM_TEMLATE::img_align()
+        * @uses GLM_TEMLATE::img_alternate()
+        * @uses GLM_TEMLATE::$template
+        *
+        * @return void
+        * @access public
+        */
+       function get_template($type)
+       {
+               $query = "select template from bus_category where id = ".$this->catid;
+               $data = $this->DB->db_auto_get_data($query);
+               switch ($type) {
+               case "cat" :
+                       switch ($data[0]['template']) {
+                       case "5" :
+                       case "4" :
+                       case "2" :
+                               $this->img_align = "left";
+                               break;
+
+                       default:
+                               $this->img_align = "right";
+                               break;
+                       }
+                       break;
+
+               case "list" :
+                       switch( $data[0]['template'] ) {
+                               case "6" :
+                               case "2" :
+                                       $this->img_align = "left";
+                                       $this->img_alternate = 0;
+                                       break;
+
+                               case "5" :
+                               case "1" :
+                                       $this->img_align = "right";
+                                       $this->img_alternate = 0;
+                                       break;
+
+                               case "4" :
+                                       $this->img_align = "right";
+                                       $this->img_alternate = 1;
+                                       break;
+
+                               case "3":
+                                       $this->img_align = "left";
+                                       $this->img_alternate = 1;
+                                       break;
+
+                               default:
+                                       break;
+                       }
+                       break;
+               }
+               return $this->template = $data[0]['template'];
+       }
+
+       //      }}}
+       //      {{{ get_top_parent()
+
+       /**
+        * get_top_parent:Get the highest level parent id (not 0 )for the category.
+        *
+        *      <p><b>NOTICE:</b> This is ment to get the top level parent not the parent of the id given.</p>
+        *
+        * @param integer $id: The catid for the page.
+        * @param object $DB: obj
+        * @uses GLM_TEMPLATE::get_top_parent()
+        * @uses GLM_DB::db_auto_get_data()
+        *
+        * @return int $parent
+        * @access public
+        */
+       function get_top_parent($id)
+       {
+               if ($id == 0 ){
+                       return 0;
+               }
+               $qs = "select parent from bus_category where id = $id";
+               $parentrow = $this->DB->db_auto_get_data( $qs );
+               if ($parentrow[0]['parent'] == 0 ){
+                       return $id;
+               } else {
+                       return $this->get_top_parent($parentrow[0]['parent']);
+               }
+       }
+
+       //      }}}
+
+       //      {{{     _getCouponWidget()
+
+       private function _getCouponWidget()
+       {
+               $page = new Toolkit_Coupons_PageWidget(
+                       new Toolkit_Coupons_WebCouponFactory(),
+                       new Toolkit_Coupons_Coupons()
+               );
+
+               $flexyOpts = Toolkit_Coupons_Controller::getFlexyOptions();
+               $tEngine = new HTML_Template_Flexy($flexyOpts);
+
+               $page->addPageCategories($this->dbh, $this->catid);
+
+               $searchForm = new Toolkit_Coupons_UserSearch(
+                       'coupon_search',
+                       'get',
+                       BASE_URL . 'index.php'
+               );
+               $searchForm->configureForm($this->dbh, $this->catid);
+
+               $page->setSearchForm($searchForm);
+
+               $couponForm = new HTML_QuickForm(
+                       'coupon_form',
+                       'post',
+                       BASE_URL . 'Toolkit/Coupons/print.php'
+               );
+               $renderer = $couponForm->defaultRenderer();
+               $renderer->clearAllTemplates();
+               return $page->toHtml($couponForm, $tEngine, 'coupons.html');
+       }
+
+       //      }}}
+       //      {{{     _hasCouponsAssignedToPage()
+
+       private function _hasCouponsAssignedToPage()
+       {
+               try {
+                       $sql = "
+                SELECT count(*) AS total
+                  FROM coupon_categories2toolbox_pages
+                 WHERE toolbox_catid = :catid";
+
+                       $stmt = $this->dbh->prepare($sql);
+                       $stmt->bindParam(':catid', $this->catid, PDO::PARAM_INT);
+                       $stmt->execute();
+
+                       $row = $stmt->fetch();
+
+                       return (bool) $row['total'];
+               } catch (PDOException $e) {
+                       Toolkit_Logger::logException('DB Error', $e);
+                       return false;
+               }
+       }
+
+       //      }}}
+
+       //      {{{ is_sub_id()
+
+       /**
+        * is_sub_id:Check to see if catid is sub of category
+        *
+        * @param integer $catid: the catid looking at
+        * @param integer $category: to see if it is in category
+        * @param object $DB: Db object reference
+        * @uses GLM_DB::db_auto_get_data()
+        *
+        * @return bool
+        * @access public
+        */
+       function is_sub_id($catid,$category,&$DB)
+       {
+               if (!is_numeric($catid)) {
+                       return false;
+               }
+
+               if ($category == $catid)
+               {
+                       return true;
+               }
+
+               $query = "select id,parent from bus_category where id = $catid";
+               $data = $DB->db_auto_get_data($query);
+               $parent = $data[0]['parent'];
+               if ($parent == 0) {
+                       return false;
+               } else {
+                       return GLM_TEMPLATE::is_sub_id($parent, $category, $DB);
+               }
+       }
+
+       //      }}}
+
+       //      {{{ keyword_replace()
+
+       /**
+        * keyword_replace:
+        *
+        * @param string $string:
+        * @uses GLM_DB::db_auto_get_data()
+        *
+        * @return string
+        * @access public
+        */
+       function keyword_replace($string)
+       {
+               //return $string;
+               if ($search = strstr($string,"{")) {
+                       if (preg_match("/\{([A-Z0-9\&\-\,\'\" ]*)\}/i",$string,$needle)) {
+                               if ($needle[0] != "") {
+                                       $qs = "
+                        SELECT id, category
+                          FROM bus_category
+                         WHERE trim(keyword) = '".trim($needle[1])."'";
+
+                                       $keyres = $this->DB->db_auto_get_data($qs);
+                                       $parent = $this->get_top_parent($keyres[0]['id']);
+                                       $url = $this->get_seo_url( $keyres[0]['id'] );
+                                       $replacement = "<a href=\"".$url."\">".htmlspecialchars($keyres[0]['category'])."</a>";
+                                       $string = str_replace($needle[0],$replacement,$string);
+                               }
+                       } else {
+                               return $string;
+                       }
+                       if ($search = strstr($string,"{")) {
+                               return $this->keyword_replace($string);
+                       }
+               }
+               return $string;
+       }
+
+       //      }}}
+
+       //      {{{ load_static_page()
+
+       /**
+       * load_static_page:using object buffer include the page $catid.phtml from static dir
+       and return it as string
+       *
+       * @return string $text
+       * @access  public
+       **/
+       function load_static_page()
+       {
+               if (file_exists(BASE . "static/{$this->catid}.phtml")) {
+                       ob_start();
+                       include BASE . "static/{$this->catid}.phtml";
+                       $text = ob_get_contents();
+                       ob_end_clean();
+                       return $text;
+               }
+       }
+
+       //      }}}
+
+       //      {{{ make_ul_menu()
+
+       /**
+        * make_ul_menu: create url list of categories
+        *
+        * @param integer $parent parent to start from
+        * @uses GLM_DB::db_auto_get_data()
+        *
+        * @return string
+        * @access public
+        */
+       function make_ul_menu($parent = 0)
+       {
+               $qs = "
+            SELECT id, category, intro
+              FROM bus_category
+             WHERE parent =  $parent
+               AND id <> ".MEMBERS_ONLY_CATEGORY."
+                        {$this->active_query}
+             order by pos";
+               $data = $this->DB->db_auto_get_data($qs);
+
+               if (!is_array($data)) {
+                       return false;
+               } else {
+                       $return = '';
+                       foreach ( $data as $key => $row ) {
+                               $url = $this->get_seo_url( $row['id'] );
+                               $title = strip_tags(addslashes($row['intro']));
+                               $return .= "\t<li><a title=\"".htmlentities($title, ENT_QUOTES, 'UTF-8').'" href="'.$url.'"';
+                               if ($this->catid == $row['id']) {
+                                       $return .= ' id="current"';
+                               }
+                               $return .= '>' . htmlentities($row['category'], ENT_QUOTES, 'UTF-8') . '</a>';
+                $hasSubs = $this->has_subs($row['id'], $this->DB);
+                $isSubId = $this->is_sub_id($this->catid, $row['id'], $this->DB);
+                $isPage  = $this->catid == $row['id'];
+                               if ($hasSubs && ($isSubId || $isPage)) {
+                    $ret2 = $this->make_ul_menu($row['id']);
+                    $return .= $ret2;
+                               }
+                               $return .= "</li>\n";
+                       }
+                       $main_cats = $this->get_main_cats();
+                       if (   (isset($parent) && $parent != 0)
+                               || (   isset($main_cats)
+                                       && isset($main_cats[$parent])
+                                       && $main_cats[$parent]
+                                  )
+                       ) {
+                               return "<ul>\n\t$return</ul>\n";
+                       } else {
+                               return "<ul id=\"nav\">\n\t" . $return . "</ul>\n";
+                       }
+               }
+       }
+
+       //      }}}
+       //      {{{ meta_tags()
+
+       /**
+        * meta_tags: create the meta description content for this page.
+        * this is taken from the category description.
+        * this should be only done for all but the home page.
+        * $meta = ( $catid != 1 ) ? $toolbox->meta_tags() : '';
+        *
+        * @uses GLM_DB::db_auto_get_data()
+        *
+        * @return  string
+        * @access  public
+        */
+       function meta_tags()
+       {
+        $query = "
+        SELECT meta_descr,description
+          FROM bus_category
+         WHERE id = :id";
+        try {
+            $stmt = $this->dbh->prepare($query);
+            $stmt->bindParam(":id", $this->catid, PDO::PARAM_INT);
+            $stmt->execute();
+            $data = $stmt->fetch();;
+        } catch(PDOException $e) {
+            echo '<pre>'.print_r($e, true).'</pre>';
+            die($e->getMessage());
+        }
+        if ($data['meta_descr']) {
+            $description = htmlentities(trim(strip_tags($data['meta_descr'])), ENT_QUOTES, 'UTF-8');
+        } else {
+                   $description = htmlentities(substr(trim(strip_tags($data['description'])), 0, 250),ENT_QUOTES,'UTF-8');
+        }
+               return $description;
+       }
+
+       //      }}}
+
+       //      {{{ page_status()
+
+       /**
+        * page_status
+        *
+        * tell if page is active, deleted or inactive
+        * this will redirect to the index.php page if the
+        * id has been deleted or not active
+        * it will display message about page not found.
+        *
+        * @param mixed $id
+        * @access public
+        * @return string
+        */
+       function page_status($id) {
+               if ($id == 9999) {
+                       $this->page_status = 'Good';
+                       return true;
+               }
+
+               if (empty($id)) {
+                       return false;
+               }
+               if ($id == HOME_ID) {
+                       $this->page_status = 'Good';
+               }
+               $query = "
+            SELECT id, active
+              FROM bus_category
+             WHERE id = :id";
+        try {
+            $stmt = $this->dbh->prepare($query);
+            $stmt->bindParam(":id", $id, PDO::PARAM_INT);
+            $stmt->execute();
+            $data = $stmt->fetch();
+            //echo '<pre>'.print_r($data, true).'</pre>';
+        } catch(PDOException $e) {
+            echo '<pre>'.print_r($e, true).'</pre>';
+            die($e->getMessage());
+        }
+
+               if (is_array($data)) {
+                       if (!$data['active'] &&
+                                       !strstr($_SERVER['HTTP_REFERER'], 'admin/')) {
+                // page was deleted and no longer avail.
+                // give 404 redirect and go back to index page
+                header('HTTP/1.1 404 Not Found');
+                               $this->page_status = 'Bad';
+                       } else {
+                               $this->page_status = 'Good';
+                       }
+               } else {
+                       // page was deleted and no longer avail.
+                       // give 301 redirect and go back to index page
+                       header('HTTP/1.1 301 Moved Permanently');
+                       header('Location: '.BASE_URL);
+                       exit();
+               }
+       }
+
+       //      }}}
+       //      {{{ photo_module()
+
+       /**
+        * photo_module
+        * Load the photo gallery into page
+        *
+        * @access public
+        * @return string
+        */
+       function photo_module()
+       {
+        $query = "
+            SELECT photocat_id
+            FROM photo_category_bus
+            WHERE buscat_id = $this->catid;";
+        $out = '';
+               if ($pData = $this->DB->db_auto_get_data($query)) {
+                       if (count($pData) > 1) {
+                               foreach ($pData as $pKey => $pVal) {
+                                       $photocatid[] = $pVal['photocat_id'];
+                               }
+                               $SINGLE_GALLERY = false;
+                       } else {
+                               $SINGLE_GALLERY = true;
+                               $photocatid = $pData[0]['photocat_id'];
+                       }
+                       if (is_numeric($photocatid) || is_array($photocatid)) {
+                               if (!$_REQUEST['photo_catid']) {
+                                       $_REQUEST['photo_catid'] = $photocatid;
+                               }
+                $photoApp = new Toolkit_Photos_Display();
+                $out = $photoApp->toHTML();
+                       }
+               }
+               return $out;
+       }
+
+       //      }}}
+       //      {{{ print_ancestors()
+
+       /**
+        * print_ancestors:print out the ancestors
+        * @param integer $catid: the id to start at.
+        *
+        * @return string
+        * @access public
+        */
+       function print_ancestors($catid)
+       {
+               return $this->get_bread_crumbs($catid);
+       }
+
+       //      }}}
+
+       //      {{{ set_active_query()
+
+       /**
+        * set_active_query: some toolboxes have an active flag some do not
+        * so this is to allow both with and without a flag.
+        *
+        * @uses ACTIVE_FLAG
+        *
+        * @return void
+        * @access public
+        **/
+       function set_active_query()
+       {
+               if (ACTIVE_FLAG) {
+                       $this->active_query = " and active = 't'";
+               }
+               return $this->active_query;
+       }
+
+       //      }}}
+       //      {{{ set_address()
+
+       /**
+       * set_address:set_address
+       * <code><p>{$address}<br>{$city},{$state} {$zip}</p></code>
+       * @param array $data: data contain the address info for display.
+       *
+       * @return string $address
+       * @access public
+       **/
+       function set_address($data)
+       {
+               $address = "";
+               if ($data["address"]) {
+                       $address .= $data["address"];
+               }
+               if ($data["city"] && $data["state"] && $data["zip"]) {
+                       $address .= '<br>'.$data["city"].', '.$data["state"].' '.$data["zip"];
+               } elseif ($data["city"] && $data["state"]) {
+                       $address .= '<br>'.$data["city"].', '.$data["state"];
+               } elseif ($data["city"]) {
+                       $address .= '<br>'.$data["city"];
+               }
+
+               if ($address != "") {
+                       return '<p>'.$address.'<br></p>';
+               }
+       }
+
+       //      }}}
+       //      {{{ set_body_tag()
+
+       /**
+        * @deprecated
+        */
+       function set_body_tag()
+       {
+               if ($this->catid != HOME_ID) {
+                       $this->body_tag = ' id="inside"';
+               }
+               if (isset($_REQUEST['query']) && $_REQUEST['query']) {
+                       switch (GLM_HOST_ID) {
+                       case "devsys.gaslightmedia.com":
+                       case "DEVELOPMENT":
+                               $apikey = "ABQIAAAA4LuqJozzD0jiTLPhI0tT7xQUAAYHl_Rab4aEI5hGyHxlqR-rKxQMGKAdHLOEIFLI9wcDJjjSkJ7qng";
+                               break;
+
+                       case "ws1.gaslightmedia.com":
+                       case "PRODUCTION":
+                               $apikey = "ABQIAAAAWqyv9sBAgUBdsdOdgo7LsRQRzeqzQXKdvmJb4FZzpdF0AtrabhSiNxG27kD8OcNt7Ae6sNRUH1VXCA";
+                               break;
+
+                       default :
+                               break;
+                       }
+                       $this->body_tag .= ' onload="OnLoad();"';
+                       $this->scripts .= '
+                               <script type="text/javascript" src="http://www.google.com/uds/api?file=uds.js&amp;v=1.0&amp;key='.$apikey.'"></script>';
+                       $this->scripts .= '<link rel="stylesheet" type="text/css" href="'.BASE_URL.'gsearch.css">';
+                       $this->scripts .= '<script type="text/javascript" src="'.BASE_URL.'libjs/gsearch.php?query='.urlencode($_REQUEST['query']).'"></script>';
+               }
+       }
+
+       //      }}}
+       //      {{{ set_catid()
+
+       /**
+       * set_catid:Set the class catid var
+       * @param integer $catid: $catid
+       *
+       * @deprecated using get_catid
+       * @return void
+       * @access public
+       **/
+       function set_catid($catid)
+       {
+               if (is_numeric($catid)) {
+                       $this->catid = $catid;
+               } else {
+                       $this->catid = 1;
+               }
+       }
+
+       //      }}}
+       //      {{{     set_contact()
+
+       /**
+       * set_contact:Set the contact string
+       * <code><p><strong>Contact Name:</strong>&nbsp;{$text}</p></code>
+       *
+       * @param string $text: The text as string
+       * @param string $email: email if givin
+       *
+       * @return string $text
+       * @access public
+       **/
+       function set_contact($text, $email)
+       {
+               if ($email != "") {
+                       $text = "";
+               } else {
+                       $text = '<p><strong>Contact Name:</strong>&nbsp;'.$text.'</p>';
+               }
+               return $text;
+       }
+
+       //      }}}
+       //      {{{ set_DB()
+
+       /**
+        * set_DB: set the DB up to be that of the global one if it exists
+        *
+        * @param object $DB : the DB object
+        * @uses GLM_DB
+        *
+        * @return void
+        * @access public
+        **/
+       function set_DB(&$DB) {
+               if (isset($DB)) {
+                       $this->DB = $DB;
+               } else {
+                       $this->DB = new GLM_DB();
+               }
+       }
+
+       //      }}}
+       //      {{{ set_data()
+
+       /**
+       * call all class methods to set tho data elements
+       *
+       * <p>This is hightly dependant on the three tables of bus bus_category and bus_category_bus
+       * set_data:Calls each function of the class
+       * based on the key af the array $data[0][$key]</p>
+       * @todo Really need to look at enhancing this function for different datasetups.
+       * @param array $data: The input array from db query
+       * @uses GLM_TEMPLATE::$img_size
+       * @uses GLM_TEMPLATE::$img_align
+       * @uses GLM_TEMPLATE::$img_alternate
+       * @uses GLM_TEMPLATE::set_text()
+       * @uses GLM_TEMPLATE::set_header()
+       * @uses GLM_TEMPLATE::set_subheader()
+       * @uses GLM_TEMPLATE::set_url()
+       * @uses GLM_TEMPLATE::set_address()
+       * @uses GLM_TEMPLATE::set_img()
+       * @uses GLM_TEMPLATE::set_email()
+       * @uses GLM_TEMPLATE::set_phone()
+       *
+       * @return array data The finished array
+       * @access  public
+       **/
+       function set_data($data)
+       {
+               if (is_array($data)) {
+                       foreach ($data as $k => $val) {
+                               foreach ($val as $key => $value) {
+                                       if (   strstr($key, "image")
+                        && !strstr($key, "name")
+                        && $value != ""
+                    ) {
+                                               $titletag = $data[$k]['category']
+                            ? $data[$k]['category']
+                            : $data[$k]['name'];
+                                               //$data[$k][$key."_name"] = $value;
+                                               $data[$k][$key] = $this->set_img(
+                                                       $value,
+                                                       $this->img_size,
+                                                       $this->img_align,
+                                                       $titletag,
+                                                       $data[$k][$key."name"]
+                                               );
+
+                                               if (!strstr($key, "name")) {
+                                                       if (   $this->img_align == "right"
+                                                               && $this->img_alternate
+                                                       ) {
+                                                               $this->img_align = "left";
+                                                       } elseif ($this->img_alternate) {
+                                                               $this->img_align = "right";
+                                                       }
+                                               }
+                                       } elseif (   strstr($key, "file")
+                                                         && strstr($key, "name")
+                                                         && $value != ""
+                                       ) {
+                                       } elseif (   strstr($key,"url")
+                                                         && strstr($key,"name")
+                                                         && $value != ""
+                                       ) {
+                                       } elseif (strstr($key,"descr") && $value != "") {
+                                               $data[$k][$key] = GLM_TEMPLATE::set_text($value);
+                                       } elseif (isset($data[$k]['email'])
+                                                         && $key == "contactname"
+                                                         && $value != ""
+                                       ) {
+                                               $data[$k][$key] = GLM_TEMPLATE::set_contact(
+                                                       $value,
+                                                       $data[$k]['email']
+                                               );
+                                       } elseif ($key == "name" && $value != "") {
+                                               $data[$k][$key] = GLM_TEMPLATE::set_subheader($value);
+                                       } elseif (strstr( $key, "header" ) && $value != "") {
+                                               $data[$k][$key] = GLM_TEMPLATE::set_subheader($value);
+                                       } elseif ($key == "intro" && $value != "") {
+                                               $data[$k][$key] = GLM_TEMPLATE::set_header($value);
+                                       } elseif ($key == "category" && $value != "") {
+                                               $data[$k][$key] = GLM_TEMPLATE::set_header($value);
+                                       } elseif ($key == "url" && $value != "") {
+                                               $data[$k][$key] = GLM_TEMPLATE::set_url(
+                            $value,
+                                                       $data[$k]["urlname"]
+                        );
+                                       } elseif ($key == "email" && $value != "") {
+                                               $data[$k][$key] = GLM_TEMPLATE::set_email(
+                            $value,
+                                                       $data[$k]["contactname"]
+                        );
+                                       } elseif ($key == "phone" && $value != "") {
+                                               $data[$k][$key] = GLM_TEMPLATE::set_phone($value);
+                                       } elseif ($key == "fax" && $value != "") {
+                                               $data[$k][$key] = GLM_TEMPLATE::set_fax($value);
+                                       } elseif (strstr($key, "file") && $value != "") {
+                                               $data[$k][$key] = GLM_TEMPLATE::set_file(
+                                                       $value,
+                                                       $data[$k][$key.'name']
+                                               );
+                                       } elseif ($key == "address") {
+                                               $data[$k][$key] =  GLM_TEMPLATE::set_address($data[$k]);
+                                       } elseif ($key == "id") {
+                                               $data[$k][$key] = $value;
+                                       } else {
+                                               $data[$k][$key] = GLM_TEMPLATE::set_text($value);
+                                       }
+                               }
+                       }
+                       return $data;
+               }
+               return false;
+       }
+
+       //      }}}
+       //      {{{     set_email()
+
+       /**
+       * set_email:Set the email string
+       * <code><p><strong>Contact:</strong>&nbsp;&nbsp;<a href="mailto:{$email}" target="_blank">{$text}</a></p></code>
+       * @param string $email: The email as string
+       * @param string $contact: The contactname this is used as the link text
+       *
+       * @return string $text
+       * @access public
+       **/
+       function set_email($email, $contact)
+       {
+               if ($email != "") {
+                       if ($contact != "") {
+                               $email = '<p><strong>Contact:</strong>&nbsp;&nbsp;<a href="mailto:'.$email.'" target="_blank">'.htmlspecialchars($contact).'</a></p>';
+                       } else {
+                               $email = '<p><strong>Email:</strong>&nbsp;&nbsp;<a href="mailto:'.$email.'" target="_blank">'.htmlspecialchars($email).'</a></p>';
+                       }
+               }
+               return $email;
+       }
+
+       //      }}}
+       //      {{{ set_fax()
+
+       /**
+       * set_fax:Set the fax string
+       * <code><p><strong>Fax:</strong>&nbsp;&nbsp;{$text}</p></code>
+       * @param string $text: The text as string
+       *
+       * @return string $text
+       * @access public
+       **/
+       function set_fax($text)
+       {
+               if ($text != "") {
+                       $text = '<p><strong>Fax:</strong>&nbsp;&nbsp;'.htmlspecialchars($text).'</p>';
+               }
+               return $text;
+       }
+
+       //      }}}
+    // {{{ set_file()
+
+       /**
+       * set_file:Set the file string
+       * <code><p><strong>Contact Name:</strong>&nbsp;{$text}</p></code>
+       * @param string $text: The text as string
+       * @param string $name: The file name displayed
+       * @uses MEDIA_BASE_URL
+       *
+       * @return string $text
+       * @access public
+       **/
+       function set_file($text, $name)
+       {
+               if ($text != "") {
+                       $outtext = '<p><a';
+                       if (preg_match("/[.]([A-Z0-9]{3}$)/i", $text, $tmp)) {
+                               $outtext .= ' class="file-download '.strtolower($tmp[1]).'"';
+                       }
+                       $outtext .= ' href="'.MEDIA_BASE_URL.'uploads/'.$text.'" target="_blank">';
+                       if ($name) {
+                               $outtext .= htmlspecialchars($name);
+                       } else {
+                               $outtext .= htmlspecialchars($text);
+                       }
+                       $outtext .= '</a>';
+                       if (preg_match("/[.]([A-Z0-9]{3}$)/i", $text, $tmp)) {
+                // don't output second link
+                               //$outtext .= '<a href="'.BASE_URL.'download.php?file='.$text.'" class="download">Click here to Download&nbsp;</a>';
+                       }
+                       $outtext .= '</p>';
+               }
+               return $outtext;
+       }
+
+       //      }}}
+       //      {{{ set_header()
+
+       /**
+       * set_header:Set the header string
+       * @param string $text: The text as string
+       * @uses GLM_TEMPLATE::header_begin()
+       * @uses GLM_TEMPLATE::header_end()
+       *
+       * @return string $text
+       * @access public
+       **/
+       function set_header($text)
+       {
+               if ($text != "") {
+                       $text = $this->header_begin.htmlspecialchars($text).$this->header_end;
+               }
+               return $text;
+       }
+
+       //      }}}
+       //      {{{ set_img()
+
+       /**
+       * set_img:Set the image string
+       * <p><code>
+       * <div class="image{$align}" style="width: {$width}px" src="{$size}{$image}" {$titletag}>
+       * <div class="imagecaption">{$caption}</div>
+       * </div>
+       * </code>
+       * </p>
+       *
+       * @param string $image: The image
+       * @param string $size: The path
+       * @param string $align: The alignment
+       * @param string $name: The image_name (displayed under image)
+       * @param string $alt_title text for use in alt and title tags
+       * @param string $caption Text for image caption if given
+       * @uses GLM_TEMPLATE::get_image_path()
+       *
+       * @return void
+       * @access public
+       **/
+       function set_img($image, $size, $align, $alt_title = NULL, $caption = NULL)
+       {
+               if ($image != "") {
+                       if ($caption != '') {
+                               $caption = str_replace('&amp;','&',$caption);
+                               $titletag = 'title="'.htmlspecialchars(strip_tags($caption)).'"';
+                               $titletag .= ' alt="'.htmlspecialchars(strip_tags($image)).'"';
+                       } elseif ($alt_title != '') {
+                               $alt_title = str_replace('&amp;','&',$alt_title);
+                               $titletag = 'title="'.htmlspecialchars(strip_tags($alt_title)).'"';
+                               $titletag .= ' alt="'.htmlspecialchars(strip_tags($image)).'"';
+                       } else {
+                               $titletag = 'title="'.htmlspecialchars(strip_tags($image)).'"';
+                               $titletag .= ' alt="'.htmlspecialchars(strip_tags($image)).'"';
+                       }
+
+                       if ($align != "") {
+                               $img_align = 'class="image'.$align.'"';
+                       }
+                       $path = $this->get_image_path();
+                       if (is_file($path.$image)) {
+                               $image_size = getimagesize($path.$image);
+                               $img_attr = $image_size[3];
+                       } else {
+                $imServer = new Toolkit_Image_Server();
+                $image_size = $imServer->getImageSize($size.$image);
+                //$image_size[0] = $ret['width'];
+                //$image_size[1] = $ret['height'];
+            }
+                       $img = '<div '.$img_align.' style="width: '.$image_size[0].'px">';
+                       $img .= '<img ';
+                       if (isset($img_attr)) {
+                               $img .= $img_attr;
+                       }
+                       $img .= ' src="'.$size.$image.'" '.$titletag.'>';
+                       if ($caption) {
+                               $img .= '<div class="imagecaption">'.$caption.'</div>';
+                       }
+                       $img .= '</div>';
+                       return $img;
+               }
+       }
+
+       //      }}}
+       //      {{{ set_member()
+
+       /**
+        * set_member
+        *
+        * @access public
+        * @return string
+        */
+       function set_member()
+       {
+               if (MEMBERS_DB && $this->catid) {
+                       $member = new Toolkit_Members_Display($this->dbh);
+            try {
+                $res = $member->setCatId($this->catid);
+            } catch (InvalidArgumentException $e) {
+                return Toolkit_Common::handleError($e);
+            }
+            $member->setMemberSections();
+            return $member;
+               }
+       }
+
+       //      }}}
+       //      {{{ set_name_url()
+
+       /**
+        * setNameUrl:
+        * @param string $name :
+        *
+        * @return string
+        * @access public
+        */
+       function set_name_url( $name )
+       {
+               $name = str_replace(" ","-",$name);
+               $name = preg_replace("/[\/#&?'\"]|amp;/", "", strip_tags(strtolower(trim($name))));
+               return htmlspecialchars( $name );
+       }
+
+       //      }}}
+       //      {{{ set_pages()
+
+       /**
+        * set_pages: grab the globals for the pages an use this for
+        *      the pages array for the class
+        *
+        * @return void
+        * @access public
+        **/
+       function set_pages(&$pages)
+       {
+        $this->pages[1] = 'index';
+       }
+
+       //      }}}
+       //      {{{ set_phone()
+
+       /**
+       * set_phone:Set the phone string
+       * <code><p><strong>Phone:</strong>&nbsp;&nbsp;{$text}</p></code>
+       * @param string $text: The text as string
+       *
+       * @return string $text
+       * @access public
+       **/
+       function set_phone($text)
+       {
+               if ($text != "") {
+                       $text = '<p><strong>Phone:</strong>&nbsp;&nbsp;'.$text.'</p>';
+               }
+               return $text;
+       }
+
+       //      }}}
+       //      {{{ set_subheader()
+
+       /**
+       * set_subheader:Set the subheader string
+       * @param string $text: The text as string
+       * @uses GLM_TEMPLATE::subheader_begin()
+       * @uses GLM_TEMPLATE::subheader_end()
+       *
+       *
+       * @return string $text
+       * @access public
+       **/
+       function set_subheader($text)
+       {
+               if ($text != "") {
+                       $text = $this->subheader_begin.htmlspecialchars($text).$this->subheader_end;
+               }
+               return $text;
+       }
+
+       //      }}}
+       //      {{{ set_text()
+
+       /**
+       * set_text:Set the contact string
+       * <code><p>{$text}</code>
+       * @param string $text: The text as string
+       *
+       * @return string $text
+       * @access public
+       **/
+       function set_text($text)
+       {
+               if ("" == str_replace("<br />", "", trim($text))) {
+                       return false;
+               }
+               if ($text != "") {
+                       $text = $this->keyword_replace($text);
+                       $text = $text;
+               }
+               return $text;
+       }
+
+       //      }}}
+       //      {{{ set_toolbox_to_category_array()
+
+       function set_toolbox_to_category_array(){
+               $category_toolbox[1] = 70;// Places to Stay
+               $category_toolbox[20] = 71;// Things to Do
+               $category_toolbox[19] = 72;// Food & Spirits
+               $category_toolbox[24] = 73;// Camping
+               $category_toolbox[40] = 74;// Travel Services
+               $category_toolbox[25] = 75;// Attractions
+               $category_toolbox[41] = 76;// Golf
+               $category_toolbox[42] = 77;// Real Estate
+               $category_toolbox[43] = 78;// Shopping
+               $category_toolbox[44] = 79;// Recreation
+               $this->category_toolbox = $category_toolbox;
+       }
+
+       //      }}}
+       //      {{{     set_url()
+
+       /**
+       * set_url:Set the url string
+       * <p>
+       * <code><p><a href="http://{$url}" target="_blank">{$text}</a></code>
+       * </p>
+       *
+       * @param string $url: The url
+       * @param string $text: The text as string
+       *
+       * @return string $text
+       * @access public
+       **/
+       function set_url($url, $text)
+       {
+               if ($url != "") {
+                       if (!$text) {
+                               $text = $url;
+                       }
+                       if (strtolower(substr($url, 0, 7) ) == "https://") {
+                               $url = '<p><a href="'.$url.'" target="_blank">'.htmlspecialchars($text).'</a></p>';
+                       } else {
+                               $url = '<p><a href="http://'.$url.'" target="_blank">'.htmlspecialchars($text).'</a></p>';
+                       }
+               }
+               return $url;
+       }
+
+       //      }}}
+       //      {{{ show_catheader()
+
+       /**
+        * show_catheader:
+        *
+        * @param integer $catid:
+        * @uses GLM_DB::db_auto_get_data()
+        *
+        * @return  string
+        * @access  public
+        **/
+       function show_catheader($catid)
+       {
+               $query = "SELECT category FROM bus_category WHERE id = $catid";
+               $data = $this->DB->db_auto_get_data($query);
+               if ($data[0][category]!="") {
+                       $header = $data[0][category];
+               } else {
+                       $header = '&nbsp;';
+               }
+               echo $header;
+       }
+
+       //      }}}
+       //      {{{ show_catimg()
+
+       /**
+        * show_catimg:output the category image.
+        *
+        * @param integer $catid: The catid for the page.
+        * @uses GLM_TEMPLATE::MIDSIZED
+        * @uses GLM_DB::db_auto_get_data()
+        *
+        * @return void
+        * @access  public
+        */
+       function show_catimg($catid)
+       {
+               $query = "SELECT image FROM bus_category WHERE id = $catid";
+               $data = $this->DB->db_auto_get_data($query);
+               if ($data[0]["image"]!="") {
+                       $img = '<img src="'.MIDSIZED.$data[0]["image"].'" border="0" vspace="30" hspace="0">';
+               } else {
+                       $img = '<img src="assets/logo_small.gif" width="150" height="85" vspace="0" hspace="0" border="0" alt="Birchwood Construction"><BR>';
+               }
+               echo $img;
+               echo '<BR><img src="assets/clear.gif" height="30" width="1">';
+       }
+
+       //      }}}
+       //      {{{ sort_childs()
+
+       /**
+        * sort_childs:
+        * @param array $threads:
+        *
+        * @return  string
+        * @access  public
+        */
+       function sort_childs($threads)
+       {
+               foreach($threads as $var=>$value) {
+                       $childs[$value["parent"]][$value["id"]] = $value;
+               }
+               return $childs;
+       }
+
+       //      }}}
+       //      {{{ sub_nav()
+
+       /**
+        * sub_nav:Create a sub navigation 4 across
+        *
+        * @param integer $catid: The catid for the page
+        * @uses GLM_DB::db_auto_get_data()
+        *
+        * @return void
+        * @access public
+        */
+       function sub_nav($catid)
+       {
+               $newcatid = $this->get_parent_id($catid);
+               if ($newcatid != 0) {
+                       $catid = $newcatid;
+               }
+               $query1 = "SELECT category FROM bus_category WHERE id = $catid";
+               $data1 = $this->DB->db_auto_get_data( $query1 );
+               $out = '<div id="subnav"> ';
+
+               $query = "SELECT id,category,intro FROM bus_category WHERE parent = $catid ".$this->active_query." ORDER BY pos";
+               $data = $this->DB->db_auto_get_data($query);
+               if (is_array($data)) {
+                       $counter = 1;
+                       foreach ($data as $key => $val) {
+                               $url = $this->get_seo_url($val['id']);
+                               $title = strip_tags(addslashes($val['intro']));
+                               //GLM_TEMPLATE::set_name_url( GLM_TEMPLATE::get_category_Name( $val['id'],"bus_category",$this->DB ) );
+                               $out .=  '<a title="'.$title.'" href="'.$url.'">';
+                               $out .= $val["category"];
+                               $out .= '</a>';
+                       }
+               }
+               $out .= '</div>';
+               return $out;
+       }
+
+       //      }}}
+
+       //      {{{ title()
+
+       /**
+        * title: create the title for the page.
+        * this should be only done for all but the home page.
+        * $title = ( $catid != 1 ) ? $toolbox->title() : '';
+        *
+        * @uses GLM_DB::db_auto_get_data()
+        *
+        * @return  string
+        * @access public
+        */
+       function title()
+       {
+        $query = "
+        SELECT category,intro,title
+          FROM bus_category
+         WHERE id = :id";
+        try {
+            $stmt = $this->dbh->prepare($query);
+            $stmt->bindParam(":id", $this->catid, PDO::PARAM_INT);
+            $stmt->execute();
+            $data = $stmt->fetchAll();;
+        } catch(PDOException $e) {
+            echo '<pre>'.print_r($e, true).'</pre>';
+            die($e->getMessage());
+        }
+
+               if ($data[0]['title']) {
+                       $title = strip_tags($data[0]['title']);
+        } else if ($data[0]['intro']) {
+                       $title = strip_tags($data[0]['intro']);
+               }       else {
+                       $title = strip_tags($data[0]['category']);
+               }
+               return htmlentities($title.' - ',ENT_QUOTES,'UTF-8');
+       }
+
+       //      }}}
+
+       //      {{{ valid_email()
+
+       /**  valid email
+        *
+        * <p>Checks for a valid format and good (mx check)
+        * email address.</p>
+        * @deprecated using GLM_TOOLBOX::valid_email()
+        * @uses GLM_TOOLBOX::valid_email()
+        *
+        * @param string email the email address as string.
+        * @return boolean
+        */
+       function valid_email( $email )
+       {
+               return GLM_TOOLBOX::valid_email( $email );
+       }
+
+       //      }}}
+}
+?>
diff --git a/classes/class_toolbox.inc b/classes/class_toolbox.inc
new file mode 100755 (executable)
index 0000000..ba79b11
--- /dev/null
@@ -0,0 +1,1709 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
+
+/**
+ *  Toolbox Classes :)
+ *
+ *  PHP version 5
+ *
+ *  <p>
+ *  These classes and any code is not licensed for anyone but gaslight to use
+ *  </p>
+ *
+ * @category  Toolbox
+ * @package   GLM_TOOLBOX
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2005-2008 Gaslight Media
+ * @license   http://app.gaslightmedia.com/license.php Gaslight Media
+ * @link      http://www.gaslightmedia.com www.gaslightmedia.com
+ * @since     $Date: 2010/07/25 17:22:09 $
+ */
+/**
+ *    Toolbox Class :)
+ *
+ * <p><b>NOTICE</b>
+ *  Im going to be moving all functions from the setup.phtml
+ *  file and putting it into this</p>
+ *  <p>class.  This is so we can eventially move
+ *  everything to one siteinfo.inc file.</p>
+ *  <kbd>matrix@devsys Does this work?</kbd>
+ *
+ * @todo move all functions from setup.phtml into here
+ */
+define('GLM_TOOLBOX', true);
+
+/**
+ * Short description for class
+ *
+ * Long description (if any) ...
+ *
+ * @category  Toolbox
+ * @package   GLM_TOOLBOX
+ * @author    Steve Sutton <steve@gaslightmedia.com>
+ * @copyright 2005-2008 Gaslight Media
+ * @license   http://app.gaslightmedia.com/license.php Gaslight Media
+ * @link      http://www.gaslightmedia.com www.gaslightmedia.com
+ * @see       References to other sections (if any)...
+ */
+class GLM_TOOLBOX
+{
+    /** @var array php_version */
+    var $php_version;
+    /** @var boolean true if php is version < 4.2 */
+    var $php_old_pg;
+    // {{{ __construct()
+
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @return void
+     * @access public
+     */
+    function __construct()
+    {
+        $this->php_version = $this->php_version_check();
+        $this->php_old_pg  = $this->php_old_pg();
+    }// }}}
+    // {{{ CC_date_entry($month, $year, $month_name, $year_name)
+
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @param string $month      Parameter description (if any) ...
+     * @param string $year       Parameter description (if any) ...
+     * @param string $month_name Parameter description (if any) ...
+     * @param string $year_name  Parameter description (if any) ...
+     *
+     * @return string Return description (if any) ...
+     * @access public
+     */
+    function CC_date_entry($month, $year, $month_name, $year_name)
+    {
+        $cur_date = getdate();
+        if ($month == "") {
+            $month = $cur_date['mon'];
+        }
+        if ($year == "") {
+            $year = $cur_date['year'];
+        }
+        $date = '<select name="'.$month_name.'">';
+        for ($i = 1; $i < 13; $i++) {
+            $date .= '<option value="';
+            if ($i < 10) {
+                $date .= "0";
+            }
+            $date .= $i.'"';
+            if ($i == $month) {
+                $date .= ' selected';
+            }
+            $date .= '>'.$i.'</option>';
+        }
+        $date .= '</select>';
+        $date .= '<select name="'.$year_name.'">';
+        for ($i = $year; $i < $year + 15; $i++) {
+            $date .= '<option value="'.$i.'"';
+            if ($i == $year) {
+                $date .= ' selected';
+            }
+            $date .= '>'.$i.'</option>';
+        }
+        $date .= '</select>';
+        return $date;
+    }    // }}}
+    // {{{ CreditVal($Num, $Name = '', $Accepted='')
+
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @param unknown $Num      Parameter description (if any) ...
+     * @param string  $Name     Parameter description (if any) ...
+     * @param string  $Accepted Parameter description (if any) ...
+     *
+     * @return boolean Return description (if any) ...
+     * @access public
+     */
+    function CreditVal($Num, $Name = '', $Accepted='')
+    {
+        $Name     = strtolower($Name);
+        $Accepted = strtolower($Accepted);
+        $GoodCard = 1;
+        $Num      = preg_replace("/[^[:digit:]]/", "", $Num);
+        switch ($Name) {
+
+        case "mastercard" :
+            $GoodCard = preg_match("/^5[1-5].{14}$/", $Num);
+            break;
+
+        case "visa" :
+            $GoodCard = preg_match("/^4.{15}$|^4.{12}$/", $Num);
+            break;
+
+        case "americanexpress" :
+            $GoodCard = preg_match("/^3[47].{13}$/", $Num);
+            break;
+
+        case "discover" :
+            $GoodCard = preg_match("/^6011.{12}$/", $Num);
+            break;
+
+        case "dinerscard" :
+            $GoodCard = preg_match("/^30[0-5].{11}$|^3[68].{12}$/", $Num);
+            break;
+
+        default:
+            if (preg_match("/^5[1-5].{14}$/", $Num)) {
+                $Name = "mastercard";
+            }
+            if (preg_match("/^4.{15}$|^4.{12}$/", $Num)) {
+                $Name = "visa";
+            }
+            if (preg_match("/^3[47].{13}$/", $Num)) {
+                $Name = "americanexpress";
+            }
+            if (preg_match("/^6011.{12}$/", $Num)) {
+                $Name = "discover";
+            }
+            if (preg_match("/^30[0-5].{11}$|^3[68].{12}$/", $Num)) {
+                $Name ="dinerscard";
+            }
+            break;
+        }
+
+        // If there's a limit on card types we accept, check for it here.
+        if ($Accepted) {
+            $type_verified = false;
+            $brands        = explode(",", $Accepted);
+            foreach ($brands as $brand) {
+                if ($Name == $brand) {
+                    $type_verified = true;
+                }
+            }
+            if (!$type_verified) {
+                return false;
+            }
+        }
+        $Num   = strrev($Num);
+        $Total = 0;
+        for ($x=0; $x<strlen($Num); $x++) {
+            $digit = substr($Num, $x, 1);
+            if ($x/2 != floor($x/2)) {
+                $digit *= 2;
+                if (strlen($digit) == 2) {
+                    $digit = substr($digit, 0, 1) + substr($digit, 1, 1);
+                }
+            }
+            $Total += $digit;
+        }
+        if ($GoodCard && $Total % 10 == 0) {
+            return true;
+        } else {
+            return false;
+        }
+    }// }}}
+    // {{{ build_checklist($name, $data)
+
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @param string $name Parameter description (if any) ...
+     * @param array  $data Parameter description (if any) ...
+     *
+     * @return mixed  Return description (if any) ...
+     * @access public
+     */
+    function build_checklist($name, $data)
+    {
+        if (!is_array($selected)) {
+            $sel[0] = $selected;
+        } else {
+            $sel = $selected;
+        }
+        if (!is_array($data)) {
+            return false;
+        }
+        foreach ($data as $field => $title) {
+            $out .= '<label for="'.$name.'['.$field.']">
+                <input type="checkbox" id="'.$name.'['.$field.']"
+                name="'.$name.'['.$field.']" value="t"';
+            if ($_POST[$field] == 't') {
+                $out .= ' checked';
+            }
+            $out .= '>'.$title.'</label>';
+        }
+        return $out;
+    }// }}}
+    // {{{ build_picklist($fieldname, $data, $selected, $type = "standard",$auto = 0,$width = null)
+
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @param string  $fieldname Parameter description (if any) ...
+     * @param array   $data      Parameter description (if any) ...
+     * @param unknown $selected  Parameter description (if any) ...
+     * @param string  $type      Parameter description (if any) ...
+     * @param integer $auto      Parameter description (if any) ...
+     * @param string  $width     Parameter description (if any) ...
+     *
+     * @return string  Return description (if any) ...
+     * @access public
+     */
+    function build_picklist($fieldname, $data, $selected, $type = "standard",$auto = 0,$width = null)
+    {
+        if (!is_array($selected)) {
+            $sel[0] = $selected;
+        } else {
+            $sel = $selected;
+        }
+        if ($auto == 1) {
+            $autosubmit = "onChange=\"form.submit()\"";
+        }
+        if ($width) {
+            $autosubmit .= "style=\"width:".$width."px;\"";
+        }
+        switch($type) {
+        case "multiple":
+            $str = "<select name=\"".$fieldname."\" multiple size=\"10\" ".$autosubmit.">\n";
+            while (list($key, $val) = each($data)) {
+                if (in_array($key, $sel)) {
+                    $select = " selected ";
+                } else {
+                    $select = "";
+                }
+                $str .= "    <option value=\"$key\"".$select.">$val\n";
+            }
+            break;
+        case "simple":
+            $str = "<select name=\"$fieldname\" ".$autosubmit.">\n";
+            for ($i = 0; $i < count($data); $i++) {
+                $select = (in_array($data[$i], $sel)) ? " selected ":"";
+                $str   .= "    <option value=\"".$data[$i]."\"".$select.">".$data[$i]."\n";
+            }
+            break;
+
+        case "standard":
+        default:
+            $str = "<select name=\"$fieldname\" ".$autosubmit.">\n";
+            while (list($key, $val) = each($data)) {
+                $select = (in_array($key, $sel)) ? " selected ":"";
+                $str   .= "    <option value=\"$key\"".$select.">$val\n";
+            }
+            break;
+        }
+        $str .= "</select>\n";
+        return $str;
+    }// }}}
+    // {{{ build_radios($name, $data)
+
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @param string $name Parameter description (if any) ...
+     * @param array  $data Parameter description (if any) ...
+     *
+     * @return mixed  Return description (if any) ...
+     * @access public
+     */
+    function build_radios($name, $data)
+    {
+        if (!is_array($selected)) {
+            $sel[0] = $selected;
+        } else {
+            $sel = $selected;
+        }
+        if (!is_array($data)) {
+            return false;
+        }
+        $count = 1;
+        foreach ($data as $field => $title) {
+            $out .= '<label for="'.$name.'-'.$count.'">
+                <input type="radio" id="'.$name.'-'.$count.'"
+                name="'.$name.'" value="'.$field.'"';
+            if ($_POST[$name] == $field) {
+                $out .= ' checked';
+            }
+            $out .= '>'.$title.'</label><br>';
+            $count++;
+        }
+        return $out;
+    }// }}}
+    // {{{ calendar_entry_input($name, $value, $icon_path)
+
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @param string  $name      Parameter description (if any) ...
+     * @param unknown $value     Parameter description (if any) ...
+     * @param string  $icon_path Parameter description (if any) ...
+     *
+     * @return string  Return description (if any) ...
+     * @access public
+     */
+    function calendar_entry_input($name, $value, $icon_path)
+    {
+        $out = '
+        <input value="'.(($value)?$value:'Enter Date').'"
+        style="width:100px;float:left;" type="text" name="'.$name.'"
+        id="'.$name.'" readonly="1">
+        <img src="'.$icon_path.'img.gif" id="'.$name.'_trigger"
+        style="cursor: pointer; border: 1px solid red;" title="Date selector"
+        onmouseover="this.style.background=\'red\';"
+        onmouseout="this.style.background=\'\'">
+            ';
+        return $out;
+    }// }}}
+    // {{{ calendar_entry_javascript($name, $value)
+
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @param string $name  Parameter description (if any) ...
+     * @param string $value Parameter description (if any) ...
+     *
+     * @return string Return description (if any) ...
+     * @access public
+     */
+    function calendar_entry_javascript($name, $value)
+    {
+        $out .= '
+            <script type="text/javascript">
+    Calendar.setup({
+        inputField     :    "'.$name.'",        // id of the input field
+        ifFormat       :    "%m/%e/%Y",         // format of the input field
+        button         :    "'.$name.'_trigger",// trigger for the calendar (button ID)
+        align          :    "Bl",               // alignment (defaults to "Bl")
+        singleClick    :    true,
+        step           :    1
+
+        ';
+        if ($value && $value != 'Enter Date') {
+            $out .= ',date : "'.$value.'"';
+        }
+        $out .= '
+    });
+</script>';
+        return $out;
+    }// }}}
+    // {{{ contact_date_entry($month,$day,$year,$month_name,$day_name,$year_name)
+
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @param string $month      Month
+     * @param string $day        Day
+     * @param string $year       Year
+     * @param string $month_name id and name value of month
+     * @param string $day_name   id and name value of day
+     * @param string $year_name  id and name value of year
+     * @param string $onChange   must be onChange="javzascript;"
+     *
+     * @return string Return description (if any) ...
+     * @access public
+     */
+    function contact_date_entry($month,$day,$year,$month_name,$day_name,$year_name,$onChange = null)
+    {
+        $cur_date = getdate();
+
+        if ($month == "") {
+            $month = $cur_date['mon'];
+        }
+        if ($day == "") {
+            $day = $cur_date['mday'];
+        }
+        if ($year == "") {
+            $year = $cur_date['year'];
+        }
+        $date = '<select id="'.$month_name.'" name="'.$month_name.'" '.$onChange.'>';
+        for ($i=1;$i<13;$i++) {
+            $date .= '<option value="';
+            if ($i < 10) {
+                $date .= "0";
+            }
+            $date .= $i.'"';
+            if ($i == $month) {
+                $date .= ' selected';
+            }
+            $date .= '>'.$i.'</option>';
+        }
+        $date .= '</select>';
+        $date .= '<select id="'.$day_name.'" name="'.$day_name.'" '.$onChange.'>';
+        for ($i=1;$i<32;$i++) {
+            $date .= '<option value="';
+            if ($i < 10) {
+                $date .= "0";
+            }
+            $date .= $i.'"';
+            if ($i == $day) {
+                $date .= ' selected';
+            }
+            $date .= '>'.$i.'</option>';
+        }
+        $date  .= '</select>';
+        $date  .= '<select id="'.$year_name.'" name="'.$year_name.'" '.$onChange.'>';
+        $ystart = $cur_date['year'] - 10;
+        for ($i=$ystart;$i<=$year;$i++) {
+            $date .= '<option value="'.$i.'"';
+            if ($i == $year) {
+                $date .= ' selected';
+            }
+            $date .= '>'.$i.'</option>';
+        }
+        $date .= '</select>';
+        return $date;
+    }// }}}
+    // {{{ cp1252_to_utf8($str)
+
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @param unknown $str Parameter description (if any) ...
+     *
+     * @return array   Return description (if any) ...
+     * @access public
+     */
+    function cp1252_to_utf8($str)
+    {
+        $cp1252_map = array(
+            "\xc2\x80" => "\xe2\x82\xac", /* EURO SIGN */
+            "\xc2\x82" => "\xe2\x80\x9a", /* SINGLE LOW-9 QUOTATION MARK */
+            "\xc2\x83" => "\xc6\x92",    /* LATIN SMALL LETTER F WITH HOOK */
+            "\xc2\x84" => "\xe2\x80\x9e", /* DOUBLE LOW-9 QUOTATION MARK */
+            "\xc2\x85" => "\xe2\x80\xa6", /* HORIZONTAL ELLIPSIS */
+            "\xc2\x86" => "\xe2\x80\xa0", /* DAGGER */
+            "\xc2\x87" => "\xe2\x80\xa1", /* DOUBLE DAGGER */
+            "\xc2\x88" => "\xcb\x86",    /* MODIFIER LETTER CIRCUMFLEX ACCENT */
+            "\xc2\x89" => "\xe2\x80\xb0", /* PER MILLE SIGN */
+            "\xc2\x8a" => "\xc5\xa0",    /* LATIN CAPITAL LETTER S WITH CARON */
+            "\xc2\x8b" => "\xe2\x80\xb9", /* SINGLE LEFT-POINTING ANGLE QUOTATION */
+            "\xc2\x8c" => "\xc5\x92",    /* LATIN CAPITAL LIGATURE OE */
+            "\xc2\x8e" => "\xc5\xbd",    /* LATIN CAPITAL LETTER Z WITH CARON */
+            "\xc2\x91" => "\xe2\x80\x98", /* LEFT SINGLE QUOTATION MARK */
+            "\xc2\x92" => "\xe2\x80\x99", /* RIGHT SINGLE QUOTATION MARK */
+            "\xc2\x93" => "\xe2\x80\x9c", /* LEFT DOUBLE QUOTATION MARK */
+            "\xc2\x94" => "\xe2\x80\x9d", /* RIGHT DOUBLE QUOTATION MARK */
+            "\xc2\x95" => "\xe2\x80\xa2", /* BULLET */
+            "\xc2\x96" => "\xe2\x80\x93", /* EN DASH */
+            "\xc2\x97" => "\xe2\x80\x94", /* EM DASH */
+
+            "\xc2\x98" => "\xcb\x9c",    /* SMALL TILDE */
+            "\xc2\x99" => "\xe2\x84\xa2", /* TRADE MARK SIGN */
+            "\xc2\x9a" => "\xc5\xa1",    /* LATIN SMALL LETTER S WITH CARON */
+            "\xc2\x9b" => "\xe2\x80\xba", /* SINGLE RIGHT-POINTING ANGLE QUOTATION*/
+            "\xc2\x9c" => "\xc5\x93",    /* LATIN SMALL LIGATURE OE */
+            "\xc2\x9e" => "\xc5\xbe",    /* LATIN SMALL LETTER Z WITH CARON */
+            "\xc2\x9f" => "\xc5\xb8"      /* LATIN CAPITAL LETTER Y WITH DIAERESIS*/
+               );
+        return  strtr(utf8_encode($str), $cp1252_map);
+    }// }}}
+    // {{{ create_href($options)
+
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @param array $options Parameter description (if any) ...
+     *
+     * @return mixed  Return description (if any) ...
+     * @access public
+     */
+    function create_href($options)
+    {
+        if (!is_array($options)) {
+            return false;
+        } else {
+            $id          = ($options['id'] && $options['id'] != '')
+                ? ' id="'.$options["id"].'"': '';
+            $title       = ($options['title'] && $options['title'] != '')
+                ? ' title="'.$options["title"].'"': '';
+            $class       = ($options['class'] && $options['class'] != '')
+                ? ' class="'.$options["class"].'"': '';
+            $text        = ($options['text'] && $options['text'] != '')
+                ? strip_tags($options["text"]) : '';
+            $onClick     = ($options['onClick'] && $options['onClick'] != '')
+                ? ' onClick="'.$options["onClick"].'"': '';
+            $onMouseover = ($options['onMouseover'] && $options['onMouseover'] != '')
+                ? ' onMouseover="'.$options["onMouseover"].'"': '';
+            $onMouseout  = ($options['onMouseout'] && $options['onMouseout'] != '')
+                ? ' onMouseout="'.$options["onMouseout"].'"': '';
+            $out         = '<a'.$class.$title.$onClick.$onMouseout.$id.'
+                href="'.$options["href"].'">'.$text.'</a>';
+            return $out;
+        }
+    }// }}}
+    // {{{ create_page_links($totalnum,$num,$start=0,$params,$page_length=ENTRIES_PER_PAGE)
+
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @param mixed   $totalnum    Parameter description (if any) ...
+     * @param unknown $num         Parameter description (if any) ...
+     * @param number  $start       Parameter description (if any) ...
+     * @param unknown $params      Parameter description (if any) ...
+     * @param number  $page_length Parameter description (if any) ...
+     *
+     * @return string  Return description (if any) ...
+     * @access public
+     */
+    function create_page_links($totalnum,$num,$start=0,$params=null,
+            $page_length=ENTRIES_PER_PAGE)
+    {
+        // FIND out which page we're on.
+        if ($totalnum!=0) {
+            $total_pages = floor($totalnum / $page_length);        // total pages = the total result divided by page length rounded down
+            $total_pages++;                                        // then add one
+            if ($start == 0) {                                     // if start is 0 then page is one {
+                $page = 1;
+            } else {
+                $page = ($start / $page_length) + 1;
+            }
+        }
+
+        if ($totalnum > $page_length && ($page != $totalpages)) {
+            $end = $page_length + $start;
+        } else {
+            $end = $totalnum;
+        }
+        $last = $start - $page_length;
+        if (($start - $page_length) < 0) {
+            $prev = "";
+        } else {
+            $prev = "[<a href=\"$GLOBALS[PHP_SELF]?start=".$last
+                ."&$params\">PREVIOUS PAGE</a>]";
+        }
+        if ($end < $totalnum) {
+            $next = "[<a href=\"$GLOBALS[PHP_SELF]?start=".$end
+                ."&$params\">NEXT PAGE</a>]";
+        } else {
+            $next = "";
+        }
+        $starting = $start + 1;
+        $last_c   = $start + $num;
+        $links    = '<div class="page-links">Showing: '.$starting.' to '
+            .$last_c.' of '.$totalnum.'<div> '.$prev. ' &nbsp;  '
+            .$next.'</div></div>';
+        return $links;
+    }// }}}
+    // {{{ create_sitemap()
+
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @return void
+     * @access public
+     */
+    function create_sitemap()
+    {
+        $page = new Toolkit_Page(new GLM_TEMPLATE(9999));
+        // 404 pages must not contain any links (including css)
+        //$page->createErrorDocument();
+    }// }}}
+    // {{{ date_entry($month,$day,$year,$month_name,$day_name,$year_name)
+
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @param string $month      Month
+     * @param string $day        Day
+     * @param string $year       Year
+     * @param string $month_name id and name value of month
+     * @param string $day_name   id and name value of day
+     * @param string $year_name  id and name value of year
+     * @param string $onChange   must be onChange="javzascript;"
+     *
+     * @return string Return description (if any) ...
+     * @access public
+     */
+    function date_entry($month,$day,$year,$month_name,$day_name,$year_name,$onChange = null)
+    {
+        $cur_date = getdate();
+        if ($month == "") {
+            $month = $cur_date['mon'];
+        }
+        if ($day == "") {
+            $day = $cur_date['mday'];
+        }
+        if ($year == "") {
+            $year = $cur_date['year'];
+        }
+        $date = '<select id="'.$month_name.'" name="'.$month_name.'" '.$onChange.'>';
+        for ($i=1;$i<13;$i++) {
+            $date .= '<option value="';
+            if ($i < 10) {
+                $date .= "0";
+            }
+            $date .= $i.'"';
+            if ($i == $month) {
+                $date .= ' selected';
+            }
+            $date .= '>'.$i.'</option>';
+        }
+        $date .= '</select>';
+        $date .= '<select id="'.$day_name.'" name="'.$day_name.'" '.$onChange.'>';
+        for ($i=1;$i<32;$i++) {
+            $date .= '<option value="';
+            if ($i < 10) {
+                $date .= "0";
+            }
+            $date .= $i.'"';
+            if ($i == $day) {
+                $date .= ' selected';
+            }
+            $date .= '>'.$i.'</option>';
+        }
+        $date .= '</select>';
+        $date .= '<select id="'.$year_name.'" name="'.$year_name.'" '.$onChange.'>';
+        for ($i=2000;$i<2023;$i++) {
+            $date .= '<option value="'.$i.'"';
+            if ($i == $year) {
+                $date .= ' selected';
+            }
+            $date .= '>'.$i.'</option>';
+        }
+        $date .= '</select>';
+        return $date;
+    }    // }}}
+    // {{{ explode_template($template,$data)
+
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @param boolean $template Parameter description (if any) ...
+     * @param unknown $data     Parameter description (if any) ...
+     *
+     * @return boolean Return description (if any) ...
+     * @access public
+     */
+    function explode_template($template,$data)
+    {
+        $template = GLM_TOOLBOX::template_read($template);
+        $output   = GLM_TOOLBOX::template_replacement($template, $data);
+        return $output;
+
+    }// }}}
+    // {{{ file_upload($form_field, $file_name, $destination_path, $restricted = false)
+
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @param unknown $form_field       Parameter description (if any) ...
+     * @param string  $file_name        Parameter description (if any) ...
+     * @param string  $destination_path Parameter description (if any) ...
+     * @param boolean $restricted       Parameter description (if any) ...
+     *
+     * @return array   Return description (if any) ...
+     * @access public
+     */
+    function file_upload($form_field, $file_name, $destination_path, $restricted = false)
+    {
+        foreach ($_FILES as $fieldName => $file) {
+            if ($file['tmp_name'] == $form_field) {
+                $fs = new Toolkit_FileServer_FileAdapter();
+                try {
+                    $res = $fs->upload($fieldName);
+                    return $res['name'];
+                } catch(Toolkit_FileServer_Exception $e) {
+                       Toolkit_Logger::logException('File Server', $e);
+                    return false;
+                }
+            }
+        }
+        return false;
+
+    }// }}}
+    // {{{ footer()
+
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @return void
+     * @access public
+     */
+    function footer()
+    {
+               $bottomScripts = Toolkit_Common::getScripts($GLOBALS['bottomScripts']);
+
+        echo "$bottomScripts </body></html>";
+    }
+
+       // }}}
+    // {{{ form_footer($name, $suppress = 0, $cs)
+
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @param unknown $name     Parameter description (if any) ...
+     * @param integer $suppress Parameter description (if any) ...
+     * @param unknown $cs       Parameter description (if any) ...
+     *
+     * @return void
+     * @access public
+     */
+    function form_footer($name, $suppress = 0, $cs = 2)
+    {
+        echo "<tr><td colspan=\"$cs\" align=center>
+            <input type=\"SUBMIT\" name=\"Command\" value=\"$name\">";
+        if ($suppress == 1) {
+            echo '<input type="SUBMIT" name="Command" value="Delete" onclick="return confirm(\'This will be Deleted! Are you sure?\');">';
+        }
+        /*        echo "<input type=\"SUBMIT\" name=\"Command\" value=\"Cancel\">";*/
+        echo "</td>";
+    }// }}}
+    // {{{ form_header($action, $method, $hidden = "")
+
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @param unknown $action Parameter description (if any) ...
+     * @param unknown $method Parameter description (if any) ...
+     * @param mixed   $hidden Parameter description (if any) ...
+     *
+     * @return void
+     * @access public
+     */
+    function form_header($action, $method, $hidden = "")
+    {
+        echo "<form action=\"$action\" method=\"$method\"
+            enctype=\"multipart/form-data\">";
+        if ($hidden != "" && is_array($hidden)) {
+            foreach ($hidden as $key=>$value) {
+                echo "<input type=\"hidden\" name=\"$key\" value=\"$value\">";
+            }
+        }
+    }// }}}
+    // {{{ get_parentid($id)
+
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @param integer $id Parameter description (if any) ...
+     *
+     * @return mixed   Return description (if any) ...
+     * @access public
+     */
+    function get_parentid($id)
+    {
+        static $parentshow;
+        if ($id == 0) {
+            return 0;
+        }
+        if (!is_array($parentshow)) {
+            $qs        = "select parent from bus_category where id = $id";
+            $parentrow = GLM_TOOLBOX::db_auto_get_data($qs);
+        }
+        if ($parentrow[0]['parent'] == 0) {
+            return $id;
+        } else {
+            return GLM_TOOLBOX::get_parentid($parentrow[0]['parent']);
+        }
+    }// }}}
+    // {{{ get_season()
+
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @return string Return description (if any) ...
+     * @access public
+     */
+    function get_season()
+    {
+        /*
+           The seasons
+           Spring     March 21st     to     June 20
+           Summer     June 21     to     Sept 20
+           Fall        Sept 21        to  Dec 20
+           Winter        Dec  21        to    March 20
+         */
+        $Spring = mktime(0, 0, 0, 3, 21, date("Y"));
+        $Summer = mktime(0, 0, 0, 6, 21, date("Y"));
+        $Fall   = mktime(0, 0, 0, 9, 21, date("Y"));
+        $Winter = mktime(0, 0, 0, 12, 21, date("Y"));
+        $time   = mktime();
+        if ($time >= $Spring && $time < $Summer) {
+            $season = "spring";
+        } elseif ($time >= $Summer && $time < $Fall) {
+            $season = "summer";
+        } elseif ($time >= $Fall && $time < $Winter) {
+            $season = "fall";
+        } else {
+            $season = "winter";
+        }
+        return $season;
+    }// }}}
+    // {{{ get_size($file)
+
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @param unknown $file Parameter description (if any) ...
+     *
+     * @return mixed   Return description (if any) ...
+     * @access public
+     */
+    function get_size($file)
+    {
+        $size = filesize($file) / 1024;
+        if ($size >= 1000) {
+            $size  = (float) number_format($size / 1000, 2);
+            $size .= 'MB';
+        } elseif ($size < 1000) {
+            $size  = ceil($size);
+            $size .= 'KB';
+        }
+        return $size;
+    }// }}}
+    // {{{ html_error($msg, $bail)
+
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @param string  $msg  Parameter description (if any) ...
+     * @param unknown $bail Parameter description (if any) ...
+     *
+     * @return void
+     * @access public
+     */
+    function html_error($msg, $bail)
+    {
+        echo '<div style="color:red;"><pre>'.$msg.'</pre></div>';
+        if ($bail) {
+            GLM_TOOLBOX::footer();
+        }
+    }// }}}
+    // {{{ html_header($title, $msg, $img)
+
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @param unknown $title Parameter description (if any) ...
+     * @param unknown $msg   Parameter description (if any) ...
+     * @param unknown $img   Parameter description (if any) ...
+     *
+     * @return void
+     * @access public
+     */
+    function html_header($title, $msg, $img)
+    {
+        $header_table_width = "400";
+        $header_table_align = "center";
+        echo '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+            "http://www.w3.org/TR/html4/loose.dtd">
+            <html>
+            <head>
+            <title><?echo $title?></title>
+            <link type="text/css" rel=stylesheet href="<?echo STYLE?>">
+            </head>
+            <body>
+            <table width="<?echo $header_table_width?>" align="<?echo $header_table_align?>" summary="Header Information" class="headertable" cellspacing="0" cellpadding="3">
+            <tr class="headertr">
+            <td class="headertd">';
+        if ($img) {
+            echo '<img src="<?echo IMG_BASE.$img?>" alt="<?echo HEAD?>" border="0">';
+        }
+        echo '</td>
+            </tr>
+            <tr>
+            <td class="headertd2" align="center">
+            <div class="headerh2" align="center"><?echo "$msg"?></div>
+            </td>
+            </tr>
+            </table>';
+    }// }}}
+    // {{{ html_nav_table($nav, $w)
+
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @param array   $nav Parameter description (if any) ...
+     * @param unknown $w   Parameter description (if any) ...
+     *
+     * @return void
+     * @access public
+     */
+    function html_nav_table($nav, $w)
+    {
+        if (is_array($nav)) {
+            $out = '<ul class="admin_nav">';
+            foreach ($nav as $link => $url) {
+                if (is_array($url)) {
+                    $out .= '<li><a href="'.$url[0].'" '.$url[1].'>'.$link.'</a></li>';
+                } else {
+                    $out .= '<li><a href="'.$url.'">'.$link.'</a></li>';
+                }
+            }
+            $out .= '</ul>';
+        }
+        echo $out;
+    }// }}}
+    // {{{ http_strip(&$string)
+
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @param unknown &$string Parameter description (if any) ...
+     *
+     * @return void
+     * @access public
+     */
+    function http_strip(&$string)
+    {
+        $test_string = strtolower($string);
+        if (substr($test_string, 0, 7) == "http://") {
+            $string = substr($string, 7);
+        }
+    }// }}}
+    // {{{ img_resize($path2image, $path2thumb, $size)
+
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @param unknown $path2image Parameter description (if any) ...
+     * @param unknown $path2thumb Parameter description (if any) ...
+     * @param unknown $size       Parameter description (if any) ...
+     *
+     * @return mixed   Return description (if any) ...
+     * @access public
+     */
+    function img_resize($path2image, $path2thumb, $size)
+    {
+        exec("which convert", $output, $return);
+        if ($return == 0) {
+            $CONVERT = $output[0];
+        } else {
+            return $error = array('convert path uknown');
+        }
+        $imageName = basename($path2image);
+        $thumbName = basename($path2thumb);
+
+        exec("$CONVERT -quality 100 -scale $size $path2image $path2thumb");
+        //chmod($path2thumb,660);
+
+        $img_resize_array = array("$imageName","$path2image","$thumbName","$path2thumb");
+        return $img_resize_array;
+    }// }}}
+    // {{{ img_upload($form_field, $img_name, $destination_path)
+
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @param unknown $form_field       Parameter description (if any) ...
+     * @param unknown $img_name         Parameter description (if any) ...
+     * @param unknown $destination_path Parameter description (if any) ...
+     *
+     * @return boolean Return description (if any) ...
+     * @access public
+     */
+    function img_upload($form_field, $img_name, $destination_path)
+    {
+        return GLM_TOOLBOX::file_upload($form_field, $img_name, $destination_path, true);
+    }// }}}
+    // {{{ is_utf8($string)
+
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @param unknown $string Parameter description (if any) ...
+     *
+     * @return mixed   Return description (if any) ...
+     * @access public
+     */
+    function is_utf8($string)
+    {
+        return preg_match('/^([\x00-\x7f]|[\xc2-\xdf][\x80-\xbf]|\xe0[\xa0-\xbf][\x80-\xbf]|[\xe1-\xec][\x80-\xbf]{2}|\xed[\x80-\x9f][\x80-\xbf]|[\xee-\xef][\x80-\xbf]{2}|f0[\x90-\xbf][\x80-\xbf]{2}|[\xf1-\xf3][\x80-\xbf]{3}|\xf4[\x80-\x8f][\x80-\xbf]{2})*$/', $string) === 1;
+    }// }}}
+    // {{{ make_data_table($data, $tableId = null)
+
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @param array  $data    Parameter description (if any) ...
+     * @param string $tableId Parameter description (if any) ...
+     *
+     * @return mixed  Return description (if any) ...
+     * @access public
+     */
+    function make_data_table($data, $tableId = null)
+    {
+        if (is_array($data)) {
+            $count = 0;
+            foreach ($data as $row) {
+                if (!is_array($row)) {
+                    return false;
+                }
+                $table_body .= '<tr>
+                    ';
+                $col         = 1;
+                foreach ($row as $field_name => $field) {
+                    if ($count == 0) {
+                        $table_head .= '<th>'.$field_name.'</th>';
+                    }
+                    $table_body .= '<td class="c'.$col.'">'.$field.'</td>';
+                    $col++;
+                }
+                $count++;
+                $table_body .= '</tr>
+                    ';
+            }
+            $table = '<table'.(($tableId)?' id="'.$tableId.'"':'').'>
+                <tr>'.$table_head.'</tr>
+                '.$table_body.'
+                </table>';
+        }
+        return $table;
+    }// }}}
+    // {{{ make_teaser($text, $maxlength, $strip_tags=false)
+
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @param unknown $text       Parameter description (if any) ...
+     * @param unknown $maxlength  Parameter description (if any) ...
+     * @param boolean $strip_tags Parameter description (if any) ...
+     *
+     * @return unknown Return description (if any) ...
+     * @access public
+     */
+    function make_teaser($text, $maxlength, $strip_tags=false)
+    {
+        if ($strip_tags) {
+            $text = strip_tags($text);
+        }
+        if (strlen($text) > $maxlength) {
+            if (!($pos = strpos($text, ' ', $maxlength))) {
+                return $text;
+            }
+            $text = substr($text, 0, $pos);
+        }
+        return $text;
+    }// }}}
+    // {{{ myEncode($string)
+
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @param unknown $string Parameter description (if any) ...
+     *
+     * @return boolean Return description (if any) ...
+     * @access public
+     */
+    function myEncode($string)
+    {
+        if (GLM_TOOLBOX::is_utf8($string)) {
+            return $string;
+        } else {
+            return GLM_TOOLBOX::cp1252_to_utf8($string);
+        }
+    }// }}}
+    // {{{ php_old_pg()
+
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @return boolean Return description (if any) ...
+     * @access public
+     */
+    function php_old_pg()
+    {
+        switch($this->php_version[0]) {
+        case 5:
+            return false;
+            break;
+
+        case 4:
+            switch($php_version[1]) {
+            case 2:
+                return false;
+                break;
+            case 1:
+                return true;
+                break;
+            }
+            break;
+        }
+    }// }}}
+    // {{{ php_version_check()
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @return array  Return description (if any) ...
+     * @access public
+     */
+    function php_version_check()
+    {
+        $this->php_version = explode(".", phpversion());
+        return $this->php_version;
+    }// }}}
+    // {{{ process_image($image, $image_name)
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @param unknown $image      Parameter description (if any) ...
+     * @param unknown $image_name Parameter description (if any) ...
+     *
+     * @return unknown Return description (if any) ...
+     * @access public
+     */
+    function process_image($image)
+    {
+        require_once BASE.'Toolkit/Image/Server.php';
+        $imServer = new Toolkit_Image_Server();
+        $image_name = $imServer->imageUpload($image);
+        return $image_name;
+    }// }}}
+    // {{{ send_html_email($to, $subject, $body, $headers, $charset = 'utf-8')
+
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @param unknown $to      Parameter description (if any) ...
+     * @param unknown $subject Parameter description (if any) ...
+     * @param unknown $body    Parameter description (if any) ...
+     * @param string  $headers Parameter description (if any) ...
+     * @param string  $charset Parameter description (if any) ...
+     *
+     * @return void
+     * @access public
+     */
+    function send_html_email($to, $subject, $body, $headers, $charset = 'utf-8')
+    {
+        if ($headers) {
+            $headers .= "\n";
+        }
+        $headers .= "MIME-Version: 1.0\n"
+                 .  "Content-type: text/html; charset=".$charset;
+        mail($to, $subject, $body, $headers);
+    }// }}}
+    // {{{ template_read($template)
+
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @param nknown $template Parameter description (if any) ...
+     *
+     * @return string  Return description (if any) ...
+     * @access public
+     */
+    function template_read($template)
+    {
+        $fp       = fopen($template, "r");
+        $contents = fread($fp, filesize($template));
+        fclose($fp);
+        if ($contents) {
+            return $contents;
+        } else {
+            return "";
+        }
+    }// }}}
+    // {{{ template_replacement($template, $fieldarr)
+
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @param unknown $template Parameter description (if any) ...
+     * @param array   $fieldarr Parameter description (if any) ...
+     *
+     * @return unknown Return description (if any) ...
+     * @access public
+     */
+    function template_replacement($template, $fieldarr)
+    {
+        if (is_array($fieldarr)) {
+            $fieldarr['BASE_URL'] = BASE_URL;
+            foreach ($fieldarr as $key => $value) {
+                $p = "/<!-- loop:$key -->(.*?)<!-- endloop:$key -->/s";
+                if (preg_match($p, $template, $match) && is_array($value)) {
+                    // ok we have found a loop for this array
+                    // now lets add into this template the correct
+                    // number we'll need to do the replacement
+                    // replace template loop section with {loop:$key}
+                    $p        = "/<!-- loop:$key -->(.*?)<!-- endloop:$key -->/s";
+                    $template = preg_replace($p, "{loop:$key}", $template);
+                    // saving the actual template with match run through
+                    // the array to build a tempory section
+                    // then switch it out for {loop:$key}
+                    $template_temp = '';
+                    foreach ($value as $srow) {
+                        $tpl = $match[1];
+                        foreach ($srow as $field => $val) {
+                            $p = "/<!-- if:$key:$field -->(.*?)<!-- endif:$key:$field -->/s";
+                            if (preg_match($p, $template) && $val == '') {
+                                $p   = "/<!-- if:$key:$field -->(.*?)<!-- endif:$key:$field -->/s";
+                                $tpl = preg_replace($p, '', $tpl);
+                            } else {
+                                $tpl = str_replace("<!-- $key:$field -->", $val, $tpl);
+                            }
+                        }
+                        $template_temp .= $tpl;
+                    }
+                    $template = str_replace("{loop:$key}", $template_temp, $template);
+
+                } else {
+                    $p = "/<!-- if:$key -->(.*?)<!-- endif:$key -->/s";
+                    if (preg_match($p, $template) && $value == '') {
+                        $p        = "/<!-- if:$key -->(.*?)<!-- endif:$key -->/s";
+                        $template = preg_replace($p, '', $template);
+                    } else {
+                        $template = str_replace("<!-- $key -->", $value, $template);
+                    }
+                }
+            }
+        }
+
+        return $template;
+    }// }}}
+    // {{{ text_area($name, $value, $rows = 15, $cols = 50, $wrap = "virtual")
+
+
+    /**
+     * create td element with textarea element inside
+     * Creates the textarea field with souround ing td tags
+     *
+     * @deprecated
+     *
+     * @param unknown $name  name attr
+     * @param unknown $value text node
+     * @param integer $rows  rows attr
+     * @param integer $cols  cols attr
+     * @param string  $wrap  wrap attr
+     *
+     * @return void
+     * @access public
+     */
+    function text_area($name, $value, $rows = 15, $cols = 50, $wrap = "virtual")
+    {
+        $html = '
+            <td>
+            <textarea id="%s" name="%s" rows="%s" cols="%s" wrap="%s">%s</textarea>
+            </td>';
+        return printf(
+            $html,
+            $name,
+            $name,
+            $rows,
+            $cols,
+            $wrap,
+            htmlspecialchars($value)
+        );
+    }// }}}
+    // {{{ text_box($name, $value, $size = 35)
+
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @param unknown $name  Parameter description (if any) ...
+     * @param unknown $value Parameter description (if any) ...
+     * @param integer $size  Parameter description (if any) ...
+     *
+     * @return void
+     * @access public
+     */
+    function text_box($name, $value, $size = 35)
+    {
+        echo "<td class=\"navtd2\"><input type=\"text\" name=\"$name\"
+            value=\"".htmlspecialchars($value)."\" size=\"$size\"></td>";
+    }// }}}
+    // {{{ time_entry($H, $m, $F, $H_name, $m_name, $F_name)
+
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @param mixed   $H      Parameter description (if any) ...
+     * @param string  $m      Parameter description (if any) ...
+     * @param string  $F      Parameter description (if any) ...
+     * @param unknown $H_name Parameter description (if any) ...
+     * @param unknown $m_name Parameter description (if any) ...
+     * @param unknown $F_name Parameter description (if any) ...
+     *
+     * @return string  Return description (if any) ...
+     * @access public
+     */
+    function time_entry($H, $m, $F, $H_name, $m_name, $F_name)
+    {
+        $cur_date = getdate();
+        if ($H == "") {
+            $H = $cur_date['hours'];
+        }
+        if ($m == "") {
+            $m = $cur_date['minutes'];
+        }
+        if ($H>12) {
+            $F = "PM";
+            $H = $H - 12;
+        }
+        $time = "Hr:<select name=\"$H_name\" size=\"1\">";
+        for ($i=1;$i<=12;$i++) {
+            $time .= "<option value=\"";
+            if ($i < 10) {
+                $time .= "0";
+            }
+            $time .= "$i\"";
+            if ($i == $H) {
+                $time .= " selected";
+            }
+            $time .= ">$i\n";
+        }
+        $time .= "</select>\n";
+        $time .= "Min:<select name=\"$m_name\" size=\"1\">";
+        for ($i=0;$i<60;$i=$i+15) {
+            $time .= "<Option value=\"";
+            if ($i < 10) {
+                $time .= "0";
+            }
+            $time .= "$i\"";
+            if ($i == $m) {
+                $time .= " selected";
+            }
+            $time .= ">";
+            if ($i < 10) {
+                $time .= "0";
+            }
+            $time .= "$i\n";
+        }
+        $time .= "</select>";
+        $time .= "<select name=\"$F_name\" size=\"1\">";
+        $time .= "<option value=\"AM\"";
+        if ($F == "AM") {
+            $time .= " selected";
+        }
+        $time .= ">AM\n";
+        $time .= "<option value=\"PM\"";
+        if ($F == "PM") {
+            $time .= " selected";
+        }
+        $time .= ">PM\n";
+        $time .= "</select>\n";
+        return $time;
+    }// }}}
+    // {{{ top($message, $hp,$hp2 = null)
+
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @param string  $message Parameter description (if any) ...
+     * @param unknown $hp      Parameter description (if any) ...
+     * @param string  $hp2     Parameter description (if any) ...
+     *
+     * @return void
+     * @access public
+     */
+    function top($message, $hp, $hp2 = null, $includeMainStyleSheet = true)
+    {
+        if ($hp2 != "") {
+            $help_guide = '<div id="glm-manual">';
+            /*
+               $help_guide = '<div id="glm-manual"><a id="manual-html"
+               href="http://www.gaslightmedia.com/manuals/html/'.$hp2.'.html"
+               target="_blank">Online Help Guide</a>&nbsp;';
+             */
+            $help_guide .= '<a id="manual-pdf"
+                href="http://www.gaslightmedia.com/manuals/pdf/'.$hp2.'.pdf"
+                target="_blank">Printable Help Guide</a></div>';
+        }
+               //      Default resources for admin pages.
+        if (is_array($GLOBALS['topScripts'])) {
+            // see if they have vrs 1.4.2  for jquery
+            // if so then don't load other jquery
+            $foundKey = array_search(
+                MEDIA_APP_BASE_URL . 'libjs/jquery/jquery-1.4.2.min.js',
+                $GLOBALS['topScripts']
+            );
+
+            if ($foundKey === false || $foundKey === null) {
+                $topScripts = $GLOBALS['topScripts'];
+                array_reverse($topScripts);
+                array_push($topScripts, JQUERY_CDN_JS);
+                array_reverse($topScripts);
+
+                $GLOBALS['topScripts'] = $topScripts;
+            }
+        } else {
+            $GLOBALS['topScripts'] = array(
+                JQUERY_CDN_JS
+            );
+        }
+               if ($includeMainStyleSheet) {
+                       $GLOBALS['styleSheets'][] = MEDIA_BASE_URL . 'admin/main.css';
+               }
+
+               $topScripts = Toolkit_Common::getScripts($GLOBALS['topScripts']);
+               $styles     = Toolkit_Common::getStyleSheets($GLOBALS['styleSheets']);
+
+               if (!empty($message)) {
+                       $header = "<h1>$message</h1>";
+               }
+
+        $out = '
+            <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
+            "http://www.w3.org/TR/html4/strict.dtd">
+            <html>
+            <head>
+            <title>Untitled</title>
+            <meta http-equiv="content-type" content="text/html;charset=utf-8">
+                       ' . $styles . $topScripts .'
+            </head>
+            <body>' .
+                               $header . ' ' .
+                               $help_guide;
+        echo $out;
+    }
+
+       // }}}
+    function topUserArea($message, $hp, $hp2 = null, $includeMainStyleSheet = true)
+    {
+        if ($hp2 != "") {
+            $help_guide = '<div id="glm-manual">';
+            /*
+               $help_guide = '<div id="glm-manual"><a id="manual-html"
+               href="http://www.gaslightmedia.com/manuals/html/'.$hp2.'.html"
+               target="_blank">Online Help Guide</a>&nbsp;';
+             */
+            $help_guide .= '<a id="manual-pdf"
+                href="http://www.gaslightmedia.com/manuals/pdf/'.$hp2.'.pdf"
+                target="_blank">Printable Help Guide</a></div>';
+        }
+               //      Default resources for admin pages.
+               $GLOBALS['topScripts'][]  = MEDIA_APP_BASE_URL . 'libjs/jquery/jquery-1.4.2.min.js';
+               if ($includeMainStyleSheet) {
+                       $GLOBALS['styleSheets'][] = MEDIA_BASE_URL . 'userArea/main.css';
+               }
+
+               $topScripts = Toolkit_Common::getScripts($GLOBALS['topScripts']);
+               $styles     = Toolkit_Common::getStyleSheets($GLOBALS['styleSheets']);
+
+               if (!empty($message)) {
+                       $header = "<h1>$message</h1>";
+               }
+
+        $out = '
+            <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
+            "http://www.w3.org/TR/html4/strict.dtd">
+            <html>
+            <head>
+            <title>Untitled</title>
+            <meta http-equiv="content-type" content="text/html;charset=utf-8">
+                       ' . $styles . $topScripts .'
+            </head>
+            <body>' .
+                               $header . ' ' .
+                               $help_guide;
+        echo $out;
+    }
+    // {{{ top2($message, $hp,$hp2 = null)
+
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @param unknown $message Parameter description (if any) ...
+     * @param unknown $hp      Parameter description (if any) ...
+     * @param unknown $hp2     Parameter description (if any) ...
+     *
+     * @return void
+     * @access public
+     */
+    function top2($message, $hp, $hp2 = null)
+    {
+        // make this an alias to top()
+        // by calling top instead of adding extra code
+        GLM_TOOLBOX::top($message, $hp, $hp2);
+    }// }}}
+    // {{{ valid_email($email)
+
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @param unknown $email Parameter description (if any) ...
+     *
+     * @return boolean Return description (if any) ...
+     * @access public
+     */
+    function valid_email($email)
+    {
+        // First, we check that there's one @ symbol, and lengths are right
+        if (!preg_match("/^[^@]{1,64}@[^@]{1,255}$/", $email)) {
+            // Email invalid because wrong number of characters in one section,
+            // or wrong number of @ symbols.
+            return false;
+        }
+        // Split it into sections to make life easier
+        $email_array = explode("@", $email);
+        $local_array = explode(".", $email_array[0]);
+        for ($i = 0; $i < sizeof($local_array); $i++) {
+            $p = "/^(([A-Za-z0-9!#$%&'*+\/=?^_`{"
+                ."|}~-][A-Za-z0-9!#$%&'*+\/=?^_`{|}~\.-]{0,63})"
+                . "|(\"[^(\\|\")]{0,62}\"))$/";
+            if (!preg_match($p, $local_array[$i])) {
+                return false;
+            }
+        }
+        if (!preg_match("/^\[?[0-9\.]+\]?$/", $email_array[1])) {
+            // Check if domain is IP. If not, it should be valid domain name
+            $domain_array = explode(".", $email_array[1]);
+            if (sizeof($domain_array) < 2) {
+                return false; // Not enough parts to domain
+            }
+            for ($i = 0; $i < sizeof($domain_array); $i++) {
+                $p = "/^(([A-Za-z0-9][A-Za-z0-9-]{0,61}[A-Za-z0-9])"
+                    ."|([A-Za-z0-9]+))$/";
+                if (!preg_match($p, $domain_array[$i])) {
+                    return false;
+                }
+            }
+        }
+        return true;
+    }// }}}
+    // {{{ word_limiter($str, $limit = 100, $end_char = '&#8230;')
+
+
+    /**
+     * Short description for function
+     *
+     * Long description (if any) ...
+     *
+     * @param unknown $str      Parameter description (if any) ...
+     * @param mixed   $limit    Parameter description (if any) ...
+     * @param string  $end_char Parameter description (if any) ...
+     *
+     * @return mixed   Return description (if any) ...
+     * @access public
+     */
+    function word_limiter($str, $limit = 100, $end_char = '&#8230;')
+    {
+        if (trim($str) == '') {
+            return $str;
+        }
+
+        preg_match('/\s*(?:\S*\s*) {'. (int) $limit .'}/', $str, $matches);
+
+        if (strlen($matches[0]) == strlen($str)) {
+            $end_char = '';
+        }
+        return rtrim($matches[0]) . $end_char;
+    }// }}}
+}
+?>
diff --git a/config/application.ini b/config/application.ini
new file mode 100644 (file)
index 0000000..09d4e37
--- /dev/null
@@ -0,0 +1,145 @@
+; Production server configuration data
+[production]
+; Turn the banner application On or Off
+bannerdb.application = Off
+; Turn the Shop application On or Off
+retailShop.application = Off
+
+; Turn the contact application On or Off
+contactdb.application = On
+; Set the first year available in the year select lists
+; these are in the report builder form for admin
+; site > admin > contacts > report builder
+contactdb.first_year = 2015
+; used in the email out for the contact db
+contactdb.production_mode = "ON"
+; turn on for html email
+contactdb.html_email = "ON"
+
+; STREAM SEND API
+; Turn on the streamsend module to use with our contact application
+contactdb.streamsend.application = Off
+; Login id for the streamsend account
+contactdb.streamsend.login = Off
+; Transaction key for the streamsend account
+contactdb.streamsend.key = Off
+; Streamsend account name
+contactdb.streamsend.account_name = Off
+; Streamsend audience number
+contactdb.streamsend.audience = Off
+
+; CONSTANT CONTACT API
+; Constant Contact forms
+constantcontact.application = Off
+; login for Constant Contact
+constantcontact.login = Off
+; password for Constant Contact
+constantcontact.password = Off
+; API Key
+constantcontact.apikey = Off
+; API Path
+constantcontact.apipath = Off
+; Constant Contact List - Array of list to add contacts to
+; example BeaverIslandBoatCo/lists/1
+constantcontact.list.0 = Off
+
+; Turn the coupon application On or Off
+coupons.application = Off
+
+; Turn the event application On or Off
+eventdb.application = On
+; Turn the Events Common Application On or Off
+eventdb.commonEvents = On
+; The page id in the toolbox that holds the event calendar
+eventdb.event_page = Off
+; Turn the home page events module On or Off for the event application
+eventdb.home_events = Off
+
+; Turn the events management application On or Off
+event_management.application = Off
+; The page id in the toolbox that is the entry page for Event management - Generally set for each server
+event_management.home_page = Off
+event_management.members_only_page = Off
+
+; Turn the google search application On of Off
+google.search.application = Off
+
+; Turn on Gaslight Media Search application On or Off
+glmsearch.application = On
+glmsearch.index = "Index_1"
+glmsearch.site = "demo.gaslightmedia.com"
+glmsearch.login = "WebsiteServices"
+glmsearch.key = "829800701e8440b67a78e3afbefa1049"
+
+; Turn the home page headlines module On or Off for the toolbox
+headlines.application = Off
+
+; Turn the member application On or Off
+memberdb.application = On
+; The page id in the toolbox that holds the login box to the members only area
+memberdb.login_page = 8
+; The page id in the toolbox that is the members only area home page
+memberdb.members.home_page = 9
+; The page id in the toolbox that is the members only area profile form page
+memberdb.members.profile_form_page = 10
+; The page id in the toolbox that is the members only area coupons page
+memberdb.members.coupons.page = Off
+memberdb.members.coupons.notification_email = Off
+; The page id in the toolbox that is the members only area events page
+memberdb.members.events_page = Off
+memberdb.members.events.notification_email = Off
+; The page id in the toolbox that is the members only reporting page
+memberdb.members.reports_page = 11
+; The page id in the toolbox that is the members only leads page
+memberdb.members.leads_page = Off
+; Turn the trip planner module On or Off for the member application
+memberdb.session.list = Off
+; The page id in the toolbox that holds the output for the trip planner
+memberdb.session.page = Off
+; The page id in the toolbox that holds the create account form for the trip planner
+memberdb.session.form = Off
+; Turn on the streamsend module to use with our member application
+memberdb.streamsend.application = Off
+
+; Turn the news On or Off
+news.application = Off
+; Turn the home page news On or Off
+news.home = Off
+; The page id for news output
+news.page = Off
+
+; Turn the photo gallery application On or Off
+photo_gallery.application = On
+
+; Turn the rotating image application On or Off
+rotating_images.application = Off
+
+; Turn the weather application On or Off
+weather.application = Off
+
+; Turn the gift certificate application On or Off
+gift_certificates.application = Off
+
+ticketing.available = Off
+ticketing.catid = Off
+ticketing.cat_seo = Off
+
+employment.application = Off
+videos.application = On
+seasons.application = Off
+blocks.application = On
+; development server configuration data inherits from production and
+; overrides values as necessary
+[development : production]
+
+; chuck's server configuration data inherits from development
+; and overrides values as necessary
+[chuck : development]
+
+; steve's server configuration data inherits from development
+; and overrides values as necessary
+[steve : development]
+
+; Vagrant server configuration data inherits from development
+; and overrides values as necessary
+[vagrant : development]
diff --git a/config/eventCalendar.ini b/config/eventCalendar.ini
new file mode 100644 (file)
index 0000000..f9b75e7
--- /dev/null
@@ -0,0 +1,42 @@
+; production server configuration for application
+[production]
+; Application specific options
+options.categoryBlocks.show   = On
+options.categoryBlocks.events = 3
+options.defaultSearchDays     = 7
+options.shortCuts.today       = On
+options.shortCuts.tommorrow   = On
+options.hasMajorEvents        = Off
+options.eventsPerPageOnPdf    = 8
+options.hasNameSearch         = On
+; short cuts for next 7 days (date ranges)
+options.shortCuts.dayRange[]  = 7
+
+; for pdf logo path from BASE
+options.pdfLogoPath = Off
+
+; default Flexy Options for this application
+flexyOptions.multiSource      = On
+flexyOptions.templateDir[]    = BASE "Toolkit/Events/views"
+flexyOptions.templateDir[]    = COMMON_APP_PATH "views"
+flexyOptions.compileDir       = BASE "Toolkit/Events/views/compiled"
+flexyOptions.templateDirOrder = "reverse"
+
+css.ignore.regex[] = "Common/EventCalendar_V0/css/events.css"
+css.ignore.regex[] = "response/V0/css/1140.css"
+css.replace[] = MEDIA_BASE_URL "Toolkit/Events/css/event.css"
+; development server configuration inherits from production and
+; overrides values as needed
+[development : production]
+
+; Chuck's server configuration inherits from development and
+; overrides values as needed
+[chuck : development]
+
+; Steve's server configuration inherits from development and
+; overrides values as needed
+[steve : development]
+
+; Vagrant server configuration inherits from development and
+; overrides values as needed
+[vagrant : development]
diff --git a/config/server.ini b/config/server.ini
new file mode 100644 (file)
index 0000000..f02b39e
--- /dev/null
@@ -0,0 +1,310 @@
+; Production site configuration data
+[production]
+; Name used in the title tag, admin area and emails
+site_url = "http://www.gaylordgolfmecca.com/"
+coupon_site_url = "www.gaylordgolfmecca.com"
+
+; Link URL to access mobile site to home page
+mobile.link =
+mobile.hostname = On
+
+; google search api key
+google.search.key = "ABQIAAAANX0yQZ2OteLu_zqbwdfUuRT9PY8-4c5xPr71Q7CZ_E9OOktIvhS7e6r_9XaLW5jy3O84t-dz7SK21Q"
+; google search site restriction
+google.search.site_restriction = "http://demo.gaslightmedia.com/"
+; google maps api key
+google.maps.key   = "ABQIAAAANX0yQZ2OteLu_zqbwdfUuRT9PY8-4c5xPr71Q7CZ_E9OOktIvhS7e6r_9XaLW5jy3O84t-dz7SK21Q"
+
+; unsecure url to servers app.gaslightmedia.com directory
+app.base_url        = "http://app.gaslightmedia.com/"
+; secure url to servers app.gaslightmedia.com directory
+app.base_secure_url = "https://app.gaslightmedia.com/"
+
+; secure url to file server
+file_server.secure   = "https://is0.gaslightmedia.com/"
+; unsecure url to file server
+file_server.unsecure = "http://is0.gaslightmedia.com/"
+; owner id for the file server
+file_server.owner_id = "golfmecca"
+; owner password for the file server
+file_server.owner_password = "83ANraMW"
+
+; Who to send emails to when we handle an error
+error.email = "errors@gaslightmedia.com"
+; level of error reporting
+error.reporting = 0
+; display errors on web page?
+error.display = 0
+; type of PEAR_Log subclass to use for error logging
+error.type = "error_log"
+; constant name of PEAR_Log store to use
+error.name = "PEAR_LOG_TYPE_FILE"
+; identity reported to the log system
+error.ident = "Production"
+; Log error messages up to and including this level
+; HIGHEST PRIORITY
+; +-------------------------------------------+
+; |PEAR_LOG_EMERG   |System is unusable       |
+; +-----------------+-------------------------+
+; |PEAR_LOG_ALERT   |Immediate action required|
+; +-----------------+-------------------------+
+; |PEAR_LOG_CRIT    |Critical conditions      |
+; +-----------------+-------------------------+
+; |PEAR_LOG_ERR     |Error conditions         |
+; +-----------------+-------------------------+
+; |PEAR_LOG_WARNING |Warning conditions       |
+; +-----------------+-------------------------+
+; |PEAR_LOG_NOTICE  |Normal but significant   |
+; +-----------------+-------------------------+
+; |PEAR_LOG_INFO    |Informational            |
+; +-----------------+-------------------------+
+; |PEAR_LOG_DEBUG   |Debug-level messages     |
+; +-------------------------------------------+
+; LOWEST PRIORITY
+error.level            = "PEAR_LOG_WARNING"
+; constant used to figure out what site we are in
+error.conf.server      = "SERVER_DIRECTORY"
+; path to log file
+error.conf.path        = "/var/www/log/"
+; log file name
+error.conf.file        = "php.error"
+; format of printed error message in log file
+error.conf.line_format = "%{timestamp: PRIORITY: %{priority} MESSAGE: %{message}
+"
+
+; type of database we're using
+database.type            = "postgres"
+; adapter for database
+database.adapter         = "pgsql"
+; error message displayed - used for GLM_DB class
+database.error.message   = "An error has occured with the database!"
+; host to connect to
+database.params.host     = "ds3"
+; username to connect as
+database.params.username = "nobody"
+; database name to connect to
+database.params.dbname = "golfmecca"
+
+; if we're in development mode or not
+development = Off
+
+; site owner email address
+email.owner                                 = "contact@gaslightmedia.com"
+; contact form email
+email.contact                               = "contact@gaslightmedia.com"
+; Add Your Event Form Email
+email.addEventForm                          = "contact@gaslightmedia.com"
+; enews form email
+email.enews                                 = "contact@gaslightmedia.com"
+; visitor guide form email
+email.visitor_guide                         = "contact@gaslightmedia.com"
+; employment application form
+email.applicationForm                       = "contact@gaslightmedia.com"
+; reply header for admin/MContact
+email.reply_member_news                     = "contact@gaslightmedia.com"
+; which email address should pending member update notifications go to
+; if you set this to false - no emails will be sent for updates
+email.member_record_updates_advisor         = "contact@gaslightmedia.com"
+; reply-to email used in the members forgot password email
+email.member_forgot_password_email_reply_to = "contact@gaslightmedia.com"
+; This is where the first email goes to notify the site owner that a gift
+; certificate has been ordered
+email.giftcert                              = "contact@gaslightmedia.com"
+; used at the from address for gift certificate emails
+email.giftcert_from                         = "contact@gaslightmedia.com"
+; email contains senstive information and must be an upnorth.net address
+email.giftcert_secure                       = "info@upnorth.net"
+; used in admin/Contact mailout.phtml file in the reply-to header
+email.reply_to                              = "contact@gaslightmedia.com"
+; email address to send the banner reports to for the site admin
+email.banner_reports_admin                  = "contact@gaslightmedia.com"
+; email address to send banner expiration notices to
+email.expiring_banner_notification          = "contact@gaslightmedia.com"
+; from header for admin/Contact mailout
+;
+; the networking department assigns an alias for the
+; "From:newsletter@xxx.com address that forwards to a
+; "xxx_bounce@harbor.gaslightmedia.com" address
+;
+; xxx being the domain of the website
+email.from_news                             = "newsletter@gaslightmedia.com"
+email.server_from                           = "server@gaslightmedia.com"
+; from header for admin/MContact mailout
+;
+; the networking department assigns an alias for the
+; "From:newsletter@xxx.com address that forwards to a
+; "xxx_bounce@harbor.gaslightmedia.com" address
+;
+; xxx being the domain of the website
+email.from_member_news                      = "newsletter@gaslightmedia.com"
+; dead email address for forms w/ one-way communication
+; ie: pending member updates
+email.do_not_reply                          = "donotreply@gaslightmedia.com"
+
+auth.test           = "LOCAL_TEST"
+auth.send_conf      = Off
+auth.merchant_email = Off
+auth.debug          = Off
+
+; turn on/off xhtml output
+; if Off, html 4 strict is used
+xhtml = Off
+
+; version number for site resources
+; ie: stylesheets or scripts
+; this will be used for cache-busting
+resources.version     = 1
+; environment request for resources
+; choices are "dev" or "prod"
+; dev will request resources that are not compressed
+; prod will request resources that have been compressed and minified
+resources.environment.javascript = "prod"
+resources.environment.css = "prod"
+
+; development site configuration data inherits from production and
+; overrides values as necessary
+[development : production]
+site_url = "http://localhost:8080/www.gaylordgolfmecca.com/"
+
+mobile.link =
+mobile.hostname = Off
+
+google.search.key = "ABQIAAAANX0yQZ2OteLu_zqbwdfUuRTeX7yNUukyMrmY8FsCXcCA9axlYBTyhehgzuXOUfNI0E5UYHCLponA0A"
+google.maps.key   = "ABQIAAAANX0yQZ2OteLu_zqbwdfUuRQsHGkczIjpqPY1-dTKNoaiGtfPJBTloI-YH7fzUV-bsMLwcy2Yjyti7A"
+
+app.base_url        = "http://localhost:8080/app.gaslightmedia.com/"
+app.base_secure_url = "https:/localhost:8080/app.gaslightmedia.com/"
+
+error.reporting[] = E_ALL
+error.reporting[] = E_NOTICE
+error.display     = 1
+error.ident       = ""
+error.conf.path        = "/var/www/server/"
+
+database.params.host = "192.168.99.205"
+
+development = On
+
+email.owner                                 = "dev@gaslightmedia.com"
+email.contact                               = "dev@gaslightmedia.com"
+email.addEventForm                          = "dev@gaslightmedia.com"
+email.enews                                 = Off
+email.visitor_guide                         = "dev@gaslightmedia.com"
+email.applicationForm                       = Off
+email.reply_member_news                     = "dev@gaslightmedia.com"
+email.member_record_updates_advisor         = "dev@gaslightmedia.com"
+email.member_forgot_password_email_reply_to = "dev@gaslightmedia.com"
+email.giftcert                              = Off
+email.giftcert_from                         = Off
+email.giftcert_secure                       = Off
+email.reply_to                              = "dev@gaslightmedia.com"
+email.banner_reports_admin                  = "dev@gaslightmedia.com"
+email.expiring_banner_notification          = "dev@gaslightmedia.com"
+email.from_news                             = "dev@gaslightmedia.com"
+email.server_from                           = "server@gaslightmedia.com"
+email.from_member_news                      = "server@gaslightmedia.com"
+
+resources.version     = "rand"
+resources.environment.javascript = "dev"
+resources.environment.css = "dev"
+
+[chuck : development]
+site_url = "http://192.168.44.7/www.gaylordgolfmecca.com/"
+
+mobile.link =
+mobile.hostname = On
+
+google.search.key = "ABQIAAAAFEik3hTZkksVQYtPm0OFmRT2yXp_ZAY8_ufC3CFXhHIE1NvwkxQhDw8ITgl-K4LjnHuSww6VQQFDnA"
+google.maps.key   = "ABQIAAAAFEik3hTZkksVQYtPm0OFmRT2yXp_ZAY8_ufC3CFXhHIE1NvwkxQhDw8ITgl-K4LjnHuSww6VQQFDnA"
+
+app.base_url = "http://192.168.44.7/app.gaslightmedia.com/"
+
+database.params.host = "localhost"
+
+email.owner                                 = "cscott@gaslightmedia.com"
+email.contact                               = "cscott@gaslightmedia.com"
+email.addEventForm                          = "cscott@gaslightmedia.com"
+email.enews                                 = "cscott@gaslightmedia.com"
+email.visitor_guide                         = "cscott@gaslightmedia.com"
+email.applicationForm                       = "cscott@gaslightmedia.com"
+email.reply_member_news                     = "cscott@gaslightmedia.com"
+email.member_record_updates_advisor         = "cscott@gaslightmedia.com"
+email.member_forgot_password_email_reply_to = "cscott@gaslightmedia.com"
+email.giftcert                              = "cscott@gaslightmedia.com"
+email.giftcert_from                         = "cscott@gaslightmedia.com"
+email.giftcert_secure                       = "cscott@gaslightmedia.com"
+email.reply_to                              = "cscott@gaslightmedia.com"
+email.banner_reports_admin                  = "cscott@gaslightmedia.com"
+email.expiring_banner_notification          = "cscott@gaslightmedia.com"
+email.from_news                             = "cscott@gaslightmedia.com"
+email.server_from                           = "cscott@gaslightmedia.com"
+email.from_member_news                      = "cscott@gaslightmedia.com"
+
+[steve : development]
+site_url = "http://vserver/www.gaylordgolfmecca.com/"
+
+mobile.link =
+mobile.hostname = Off
+
+google.search.key = "ABQIAAAAFEik3hTZkksVQYtPm0OFmRT2yXp_ZAY8_ufC3CFXhHIE1NvwkxQhDw8ITgl-K4LjnHuSww6VQQFDnA"
+google.maps.key   = "ABQIAAAAFEik3hTZkksVQYtPm0OFmRT2yXp_ZAY8_ufC3CFXhHIE1NvwkxQhDw8ITgl-K4LjnHuSww6VQQFDnA"
+
+app.base_url = "http://vserver/app.gaslightmedia.com/"
+app.base_secure_url = "https://vserver/app.gaslightmedia.com/"
+
+
+database.params.host = "localhost"
+
+email.owner                                 = "steve+owner@localhost"
+email.contact                               = "steve+contact@localhost"
+email.addEventForm                          = "steve+eventForm@localhost"
+email.enews                                 = "steve+enews@localhost"
+email.visitor_guide                         = "steve+visitor_guide@localhost"
+email.applicationForm                       = "steve+applicationForm@localhost"
+email.reply_member_news                     = "steve+reply_member_news@localhost"
+email.member_record_updates_advisor         = "steve+member_updates@localhost"
+email.member_forgot_password_email_reply_to = "steve+member_forgot@localhost"
+email.giftcert                              = "steve+giftcert@localhost"
+email.giftcert_from                         = "steve+giftcert_from@localhost"
+email.giftcert_secure                       = "steve+giftcert_secure@localhost"
+email.reply_to                              = "steve+reply_to@localhost"
+email.banner_reports_admin                  = "steve+banner_reports@localhost"
+email.expiring_banner_notification          = "steve+exp_banner@localhost"
+email.from_news                             = "steve+from_news@localhost"
+email.server_from                           = "server@gaslightmedia.com"
+email.from_member_news                      = "steve+from_member_news@localhost"
+
+; Vagrant server configuration inherits from development and
+; overrides values as needed
+[vagrant : development]
+site_url = "http://localhost:8080/www.gaylordgolfmecca.com/"
+
+mobile.link =
+mobile.hostname = Off
+
+google.search.key = "ABQIAAAAFEik3hTZkksVQYtPm0OFmRT2yXp_ZAY8_ufC3CFXhHIE1NvwkxQhDw8ITgl-K4LjnHuSww6VQQFDnA"
+google.maps.key   = "ABQIAAAAFEik3hTZkksVQYtPm0OFmRT2yXp_ZAY8_ufC3CFXhHIE1NvwkxQhDw8ITgl-K4LjnHuSww6VQQFDnA"
+
+app.base_url = "http://localhost:8080/app.gaslightmedia.com/"
+app.base_secure_url = "https://localhost:8080/app.gaslightmedia.com/"
+
+database.params.host = "devdb"
+
+email.owner                                 = "vagrant+owner@localhost"
+email.contact                               = "vagrant+contact@localhost"
+email.addEventForm                          = "vagrant+eventForm@localhost"
+email.enews                                 = "vagrant+enews@localhost"
+email.visitor_guide                         = "vagrant+visitor_guide@localhost"
+email.applicationForm                       = "vagrant+applicationForm@localhost"
+email.reply_member_news                     = "vagrant+reply_member_news@localhost"
+email.member_record_updates_advisor         = "vagrant+member_updates@localhost"
+email.member_forgot_password_email_reply_to = "vagrant+member_forgot@localhost"
+email.giftcert                              = "vagrant+giftcert@localhost"
+email.giftcert_from                         = "vagrant+giftcert_from@localhost"
+email.giftcert_secure                       = "vagrant+giftcert_secure@localhost"
+email.reply_to                              = "vagrant+reply_to@localhost"
+email.banner_reports_admin                  = "vagrant+banner_reports@localhost"
+email.expiring_banner_notification          = "vagrant+exp_banner@localhost"
+email.from_news                             = "vagrant+from_news@localhost"
+email.server_from                           = "server@gaslightmedia.com"
+email.from_member_news                      = "vagrant+from_member_news@localhost"
diff --git a/config/site.ini b/config/site.ini
new file mode 100644 (file)
index 0000000..6c6725a
--- /dev/null
@@ -0,0 +1,44 @@
+; Production server configuration data
+[production]
+
+; Web Site Owner Information - for use by certain applications
+owner.name          = "Gaslight Media"
+owner.short_name    = "GLM"
+owner.address1      = "120 E. Lake St."
+owner.address2      = False
+owner.city          = "Petoskey"
+owner.state         = "MI"
+owner.zip           = "49770"
+owner.country       = "US"
+owner.phone         = "231-487-0692"
+owner.toll_free     = False
+
+; Home page id of site in the toolbox
+home_id = 1
+; Name to use in title tag and admin
+sitename = "Gaylord Golf Mecca"
+; used in contactdb to limit number of entries per list
+entries_per_page = 10
+; Turn short urls On or Off
+; requires .htaccess enabled
+short_urls = Off
+; Site is mobile enabled
+mobile_site = Off
+mobile_max_width = 600
+mobile_site_url = "http://m.***DOMAIN_GOES_HERE***/index.php?isMobile=yes"
+
+; development server configuration data inherits from production and
+; overrides values as necessary
+[development : production]
+
+; chuck's server configuration data inherits from development
+; and overrides values as necessary
+[chuck : development]
+
+; steve's server configuration data inherits from development
+; and overrides values as necessary
+[steve : development]
+
+; Vagrant server configuration data inherits from development
+; and overrides values as necessary
+[vagrant : development]
diff --git a/css/contactform.css b/css/contactform.css
new file mode 100644 (file)
index 0000000..9dedb38
--- /dev/null
@@ -0,0 +1,108 @@
+#contact,
+.webform {margin: 10px;}
+
+.req {color: #f00;}
+
+/* Main table */
+#contact table,
+.webform table {
+       background-color: #fff;
+       border: 1px solid #eee;
+       border-collapse: collapse;
+       }
+/* Any Table inside the form */
+#contact table table,
+.webform table table {
+       width: 100%;
+       margin-left: 0;
+       border: 0px solid #ddd;
+       }
+#contact table table td        {       border: 0px solid #ddd;}
+
+/* TD's */
+#contact td,
+.webform td {
+       padding: 3px;
+       font-family: arial, helvetica, sans-serif;
+       color: #000;
+       border: 1px solid #eee;
+       border-collapse: collapse;
+       }
+/* Left Cells */
+.labelcell {
+       background-color: transparent;
+       text-align: right;
+       padding-right: 10px;
+       padding-top: 3px;
+       white-space:nowrap;
+       width: 140px;
+       }
+/*Right Cells */
+.fieldcell {
+       padding-left: 4px;
+       width: 250px;
+       }
+
+/* Misc */
+textarea {width: 95%; height: 100px; display:block;}
+
+/* WARNINGS */
+#form-warning-top {
+       margin-top: 1em;
+       margin-bottom: 0.5em;
+       color: #f00;
+       font-size: 14px;
+       font-weight: bold;
+       }
+/* SHow hide instruction div */
+#contact table tr td .form-warning-inside,
+.webform table tr td .form-warning-inside {
+       display: none;
+       }
+#contact table tr.req td .form-warning-inside,
+.webform table tr.req td .form-warning-inside {
+       display: block;
+       background-image: url('../../../../images/error.gif');
+       background-repeat:no-repeat;
+       background-position: top left;
+       padding: 2px 2px 2px 22px;
+}
+
+/* Showing/hiding rows */
+/*invisible*/
+#contact table tr td.instructioncell,
+.webform table tr td.instructioncell {
+       width: 200px;
+       white-space: nowrap;
+       color: #fff;
+       }
+
+/*visible*/
+table tr.req {
+       border: 2px solid #f00;
+       background-color:#FCD6D4;
+       border-collapse: separate;
+       border-collapse: collapse;
+}
+/*color*/
+table tr.req td                        { background-color: #fcd6d4;}
+table tr.req td.labelcell      { background-color: #FCD6D4; }
+table tr.req td.fieldcell      { background-color: #FCD6D4; }
+table tr.req td.instructioncell        { background-color: #FCD6D4; }
+table tr.req td.instructioncell {
+       padding: 4px;
+       padding-left: 20px;
+       background-image: url('../../../../images/error.gif');
+       background-repeat:no-repeat;
+       background-position: center left;
+       border-width: 0;
+       color: #000;
+       }
+.glmCheckBox {
+       width: 200px;
+       float: left;
+}
+.single-checkbox label.glmCheckBox {
+    width: auto;
+    float: none;
+}
diff --git a/css/email.css b/css/email.css
new file mode 100644 (file)
index 0000000..70d0ad3
--- /dev/null
@@ -0,0 +1,42 @@
+body {
+       background-color: #ff;
+       color: black;
+       font-family: verdana, arial, helvetica, sans-serif;
+}
+h1, h2 {
+       font-family: arial, helvetica, sans-serif;
+}
+h1 {
+       font-size: 18px;
+}
+h2 {
+       font-size: 16px;
+       margin-bottom: 5px;
+}
+p {
+       font-size: 12px;
+}
+.label {
+       font-weight: bold;
+       background-color: transparent;
+       text-align: right;
+       width: 200px;
+       padding: 5px;
+}
+.field {
+       background-color: #fff;
+       padding: 3px;
+}
+table.data {
+       background-color: #fff;
+       color: #000;
+       width: 500px;
+       border: 1px solid #ccc;
+       border-collapse: collapse;
+       margin-left: 20px;
+}
+table.data td {
+       border: 1px solid #ccc;
+       padding-left: 4px;
+       font-size: 12px;
+}
diff --git a/css/gsearch.css b/css/gsearch.css
new file mode 100755 (executable)
index 0000000..f3bb5f4
--- /dev/null
@@ -0,0 +1,851 @@
+/** Copyright 2005 Google Inc. All rights reserved. */
+
+/* the GSearchControl CSS Classes
+ * .gsc-control : the primary class of the control
+ */
+#searchcontrol {background-color:white; padding: 20px; clear: both;}
+.gsc-control {
+  width: auto;
+}
+
+.gsc-control div {
+  position: static;
+}
+
+/* control inputs
+ * .gsc-search-box : the container that hosts the text input area
+ * .gsc-input : the text input area
+ * .gsc-keeper : the save link below savable results
+ */
+form.gsc-search-box {
+  font-size: 13px;
+  margin-top : 0px;
+  margin-right : 0px;
+  margin-bottom : 4px;
+  margin-left : 0px;
+  width: 100%;
+}
+
+/*
+ * This table contains the input element as well as the search button
+ * Note that the search button column is fixed width, designed to hold the
+ * button div's background image
+ */
+table.gsc-search-box {
+  border-style : none;
+  border-width : 0px;
+  border-spacing : 0px 0px;
+  width : 100%;
+  margin-bottom : 2px;
+}
+
+table.gsc-search-box td {
+  vertical-align : middle;
+}
+
+td.gsc-search-button {
+  width : 1%;
+}
+
+td.gsc-clear-button {
+  width : 14px;
+       display: none;
+}
+
+table.gsc-branding {
+  border-style : none;
+  border-width : 0px;
+  border-spacing : 0px 0px;
+  width : 100%;
+       display: none;
+       }
+
+td.gsc-branding-text {
+  vertical-align : top;
+}
+
+td.gsc-branding-text div.gsc-branding-text {
+  padding-bottom : 2px;
+  text-align : right;
+  color : #676767;
+  font-size : 11px;
+  margin-right : 2px;
+}
+
+td.gsc-branding-img-noclear {
+  width : 51px;
+  vertical-align : bottom;
+}
+
+td.gsc-branding-img {
+  width : 65px;
+  vertical-align : bottom;
+}
+
+table.gsc-branding-vertical td.gsc-branding-text div.gsc-branding-text {
+  margin-right : 0px;
+  text-align : center;
+}
+
+table.gsc-branding-vertical td.gsc-branding-img-noclear {
+  text-align : center;
+}
+
+div.gsc-branding-img,
+div.gsc-branding-img-noclear,
+img.gsc-branding-img,
+img.gsc-branding-img-noclear {
+  padding-top : 1px;
+}
+
+img.gsc-branding-img,
+img.gsc-branding-img-noclear {
+  margin : 0 0 0 0;
+  padding-right : 0;
+  padding-left : 0;
+  padding-bottom : 0;
+  border : none;
+}
+
+input.gsc-search-button {
+  margin-left : 4px;
+}
+
+div.gsc-clear-button {
+  display : inline;
+  text-align : right;
+  margin-left : 4px;
+  margin-right : 4px;
+  padding-left : 10px;
+  background-repeat: no-repeat;
+  background-position: center center;
+  background-image: url('http://www.google.com/uds/css/clear.gif');
+  cursor : pointer;
+}
+
+/*
+ * Given that this is sitting in a variable width tabel cell, the idea is
+ * for it to consume the entire cell. The adjacent cell contains the search
+ * button and that is a fixed width cell.
+ */
+input.gsc-input {
+  padding-left : 2px;
+  border-style : solid;
+  border-width : 1px;
+  border-color : #BCCDF0;
+  width : 99%;
+}
+
+.gsc-keeper {
+  color: #3366cc;
+  text-decoration: underline;
+  font-size: 13px;
+  cursor: pointer;
+  font-weight: normal;
+
+  padding-left: 16px;
+  background-repeat: no-repeat;
+  background-position: 1px 3px;
+  background-image: url('http://www.google.com/uds/css/blue_check.gif');
+}
+.gsc-imageResult .gsc-keeper {
+  text-decoration: none;
+}
+
+/* each section of results has a results header table
+ * .gsc-resultsHeader : the header itseld
+ * td.twiddleRegionCell : the section that controls twiddleing of the section to expand/collapse
+ * td.configLabelCell : the twiddler that controls active configuration of a searcher (used in GlocalSearch)
+ * .gsc-twiddle : the twiddle image, note, this is a div that wraps gsc-title so that standard image replacement is feasible
+ * .gsc-twiddle-closed : class added to gsc-twiddle when the twiddler is in the closed state
+ * .gsc-twiddle-opened : class added to gsc-twiddle when the twiddler is in the opened state
+ * .gsc-title : the section's title (e.g., Web Results, etc.)
+ * .gsc-stats : contains the result counts
+ * .gsc-stats
+ */
+.gsc-resultsHeader {
+  clear: both;
+  width: 100%;
+  border-bottom: 1px solid #e9e9e9;
+  margin-bottom : 4px;
+}
+
+.gsc-resultsHeader td.gsc-twiddleRegionCell{
+  width: 75%;
+}
+
+.gsc-resultsHeader td.gsc-configLabelCell{
+  text-align: right;
+  width: 75%;
+}
+
+/*
+ * note that the next three classes are all joined together
+ * to implement the twiddle image. apps can substitute in their
+ * own images but will need to account for the image size here
+ * as well as in the left padding of the title element
+ *
+ * Note: uds provides the following images that work with the geometry/padding defined below
+ *  to use these images simply over-ride the.gsc-twiddle-opened/-closed class and specify an alternate image
+ *  or use an image of your own design
+ */
+.gsc-resultsHeader .gsc-twiddle{
+  margin-top: 4px;
+  display: inline;
+  cursor: pointer;
+  background-repeat: no-repeat;
+  background-position: 0px 2px;
+}
+.gsc-resultsHeader td.gsc-twiddle-closed div.gsc-twiddle{
+  background-image: url('http://www.google.com/uds/css/arrow_close.gif');
+}
+.gsc-resultsHeader td.gsc-twiddle-opened div.gsc-twiddle{
+  background-image: url('http://www.google.com/uds/css/arrow_open.gif');
+}
+
+.gsc-resultsHeader .gsc-title{
+  color: #676767;
+  margin-right: 10px;
+  padding-left: 14px;
+  display: inline;
+}
+
+.gsc-resultsHeader .gsc-stats {
+  color: #676767;
+  font-size: 11px;
+  font-weight: normal;
+  display : inline;
+}
+
+.gsc-resultsHeader td.gsc-twiddle-opened .gsc-stats {
+  display : none;
+}
+
+/*
+ * .gsc-results-selector : box surrounding individual selectors for 1, more, or all results
+ * .gsc-result-selector : an individual selector
+ * .gsc-one-result : single result selector
+ * .gsc-more-results : more (4) results selector
+ * .gsc-all-results : all results (8) selector
+ */
+.gsc-results-selector {
+  display : inline;
+}
+
+.gsc-resultsHeader td.gsc-twiddle-closed .gsc-results-selector {
+  display : none;
+}
+
+.gsc-result-selector {
+  cursor : pointer;
+  display : inline;
+  font-size : 13px;
+  padding-left : 13px;
+  background-repeat: no-repeat;
+  background-position: center left;
+}
+
+/* default mode is dark */
+.gsc-one-result {
+  background-image: url('http://www.google.com/uds/css/one-complex-dark.gif');
+}
+
+.gsc-more-results {
+  background-image: url('http://www.google.com/uds/css/more-complex-dark.gif');
+}
+
+.gsc-all-results {
+  background-image: url('http://www.google.com/uds/css/all-complex-dark.gif');
+  padding-right : 1px;
+}
+
+/* active mode is light */
+.gsc-one-result-active .gsc-one-result {
+  background-image: url('http://www.google.com/uds/css/one-complex-light-blue.gif');
+}
+
+.gsc-more-results-active .gsc-more-results {
+  background-image: url('http://www.google.com/uds/css/more-complex-light-blue.gif');
+}
+
+.gsc-all-results-active .gsc-all-results {
+  background-image: url('http://www.google.com/uds/css/all-complex-light-blue.gif');
+}
+
+.gsc-resultsHeader .gsc-configLabel{
+  color: #676767;
+  display: inline;
+  font-size: 11px;
+  cursor: pointer;
+}
+
+.gsc-resultsHeader td.gsc-configLabelCell span.gsc-twiddle-closed {
+  padding-right: 12px;
+  background-repeat: no-repeat;
+  background-position: center center;
+  background-image: url('http://www.google.com/uds/css/settings.gif');
+}
+
+
+/* tabbed mode of search control
+ * .gsc-tabsArea : the box containing all of the tabs
+ * .gsc-tabsAreaInvisible : same as above, but this is the state when search has been cleared
+ * .gsc-tabHeader : an individual tab
+ * .gsc-tabHeader.gsc-tabhActive : the active tab
+ * .gsc-tabHeader.gsc-tabhInactive : an inactive tab
+ * .gsc-tabData : the data area/box containg results and header data for each tab
+ * .gsc-tabData.gsc-tabdActive : the data area for the active tab
+ * .gsc-tabData.gsc-tabdInactive : the data area for inactive tabs
+ */
+.gsc-tabsArea {
+  clear: both;
+  margin-top: 6px;
+       display: none;
+  }
+
+.gsc-tabsAreaInvisible {
+  display : none;
+  }
+
+.gsc-tabHeader {
+  display: inline;
+  cursor: pointer;
+  padding-left: 6px;
+  padding-right: 6px;
+  margin-right: 2px;
+  }
+
+.gsc-tabHeader.gsc-tabhActive {
+  border-left: 1px solid #e9e9e9;
+  border-right: 1px solid #e9e9e9;
+  border-top: 2px solid #ff9900;
+  color: black;
+  }
+
+.gsc-tabHeader.gsc-tabhInactive {
+  border-left: 1px solid #e9e9e9;
+  border-right: 1px solid #e9e9e9;
+  border-top: 2px solid #e9e9e9;
+  background: #e9e9e9;
+  color: #676767;
+  }
+
+.gsc-tabData.gsc-tabdActive {
+  display: block;
+  }
+
+.gsc-tabData.gsc-tabdInactive {
+  display: none;
+  }
+
+/* tab specific results header supression
+ * - no twiddle, tabbed mode runs in full expand mode
+ * - no title
+ * - no stats
+ */
+.gsc-tabData .gsc-resultsHeader .gsc-title {
+  display: none;
+  }
+
+.gsc-tabData .gsc-resultsHeader .gsc-stats {
+  display: none;
+  }
+
+.gsc-tabData .gsc-resultsHeader .gsc-results-selector {
+  display : none;
+}
+
+
+
+/* the results for each section
+ * .gsc-resultsbox-{visible,invisible} : a complete-collection of results including headers
+ * .gsc-results : the collection of results for a given searcher
+ * .gsc-result : a generic result within the control. each result has this class, as well as .gsc-xxxResult where xxx is web, image, local, blog, etc.
+ */
+.gsc-resultsbox-visible {
+  display : block;
+}
+
+.gsc-resultsbox-invisible {
+  display : none;
+}
+
+.gsc-results {
+  clear: both;
+  padding-bottom: 2px;
+}
+
+.gsc-result {
+  margin-bottom: 10px;
+}
+
+.gsc-result .gs-title {
+  height: 1.4em;
+  overflow: hidden;
+  }
+
+/* specialized, result type specific, fine grained controls */
+.gsc-result div.gs-watermark {
+  display: none;
+}
+
+/* Ads
+ * inline the title div so that we can have an adjecent ad marker
+ * in the control, the ad-marker is supressed since ads are already positioned
+ * underneath a results divider
+ */
+.gsc-webResult a div.gs-title {
+  display: inline;
+}
+.gsc-results .gsc-result img.gs-ad-marker {
+  display: none;
+}
+
+/* Standard configuration div/form */
+div.gsc-config {
+  border: 1px solid #e9e9e9;
+  margin-top: 0px;
+  margin-bottom: 10px;
+  padding-top : 2px;
+  padding-left : 6px;
+  padding-right : 6px;
+  padding-bottom : 6px;
+}
+
+form.gsc-config {
+  margin-bottom : 0px;
+}
+
+.gsc-configSetting {
+  margin-top : 6px;
+  }
+
+.gsc-configSetting_Label {
+  color: #676767;
+  }
+
+.gsc-configSettingInput {
+  color: #676767;
+  border: 1px solid #e9e9e9;
+  width: 75%;
+  }
+
+.gsc-configSettingCheckbox {
+  color: #676767;
+  margin-right: 6px;
+  }
+
+.gsc-configSettingCheckboxLabel {
+  display : inline;
+  color: #676767;
+  }
+
+div.gsc-configSettingSubmit {
+  margin-top : 8px;
+  text-align : right;
+}
+
+input.gsc-configSettingSubmit {
+  display: inline;
+  font-size: 11px;
+  cursor: pointer;
+}
+
+
+/* Image Search
+ * - support for horizontal and vertical orientation
+ * - title, url, and size supression
+ */
+.gsc-results.gsc-imageResult .gsc-imageResult.horizontal{
+  float: left;
+  width: 65px;
+  margin-bottom: 4px;
+  margin-right: 8px;
+  text-align: center;
+}
+
+.gsc-results.gsc-imageResult .gsc-imageResult.horizontal .gs-imageResult {
+  height: 41px;
+}
+
+.gsc-results.gsc-imageResult .gsc-imageResult.vertical{
+  float: none;
+  margin-bottom: 4px;
+  margin-right: 8px;
+  text-align: left;
+}
+
+.gsc-results.gsc-imageResult .gsc-imageResult.horizontal .gsc-keeper {
+  background-position: center;
+}
+
+.gsc-imageResult .gs-title {
+  display: none;
+}
+.gsc-imageResult .gs-visibleUrl {
+  display: none;
+}
+.gsc-imageResult .gs-size {
+  display: none;
+}
+
+/* Video Search
+ * - metadata, publisher small font
+ * - single line title
+ */
+
+.gsc-videoResult .gs-videoResult .gs-metadata {
+  font-size: 11px;
+}
+
+.gsc-videoResult .gs-videoResult .gs-title {
+  line-height: 1.3em;
+  height: 1.3em;
+  overflow: hidden;
+  }
+
+.gsc-videoResult .gs-videoResult .gs-snippet {
+  line-height: 1.3em;
+  max-height: 2.6em;
+  overflow: hidden;
+  }
+
+/*** End of Control, Begin Results ***/
+
+/* generic, cross cutting result style
+ * - in the form of .gs-result .gs-xxx where xxx is the generic style
+ * .gs-title : typically the first line of a result, typically a link, image results over ride this, since for image results, the image is the link
+ * .gs-divider : typically seperates results from ads
+ * .gs-visibleUrl : typically the last line of a result, displayed in green. sometimes a link (like in blog search)
+ * .gs-clusterUrl : for news, and other similar services, this is a cluster of additional results
+ * img.gs-image : an actial image in a result
+ * .gs-phone : a phone number
+ * .gs-address : an address (includes street, city, region, country)
+ * .gs-streetAddress : a street (including #)
+ * .gs-city : a city
+ * .gs-region : a region (zip code, area, etc.)
+ * .gs-country : a country
+ * .gs-snippet : snippetized content
+ * .gs-watermark : indicator that user selected this result
+ * .gs-metadata : generic metadata, e.g.,
+ * .gs-image-box : generic container for a result's image (within a table)
+ * .gs-text-box : generic container for a result's text content (within a table). Note that this class, and image-box are only used in video
+ */
+.gs-result .gs-title,
+.gs-result .gs-title * {
+  color: #0000cc;
+  text-decoration: underline;
+}
+
+.gs-divider {
+  padding-bottom: 8px;
+  text-align: center;
+  color: #676767;
+}
+
+.gs-result a.gs-visibleUrl,
+.gs-result .gs-visibleUrl {
+  color: #008000;
+  text-decoration: none;
+}
+
+/* relative and absolute dates, not, news inlines these */
+.gs-relativePublishedDate,
+.gs-publishedDate {
+  color: #6f6f6f;
+  text-decoration: none;
+}
+.gs-result a.gs-clusterUrl,
+.gs-result .gs-clusterUrl {
+  color: #008000;
+  text-decoration: none;
+  cursor: pointer;
+}
+
+.gs-newsResult .gs-publisher {
+  color: #6f6f6f;
+  display : inline;
+  text-decoration: none;
+}
+
+.gs-bookResult .gs-author {
+  color: #6f6f6f;
+}
+
+/*
+ * For news results there are two dates...
+ * The relative date is visible while in the
+ * search control and the published date
+ * is visible when clipped. Why? It doesn't
+ * make sense to say 4 hours ago for a clipped
+ * result...
+ */
+
+/* establish the base style */
+.gs-newsResult .gs-relativePublishedDate,
+.gs-newsResult .gs-publishedDate {
+  display : inline;
+  margin-left : 4px;
+}
+
+/* base styling for relative date is none */
+.gs-blogResult .gs-relativePublishedDate,
+.gs-newsResult .gs-relativePublishedDate {
+  display : none;
+}
+
+/* suppress publishedDate while in the control */
+.gsc-blogResult .gs-blogResult .gs-publishedDate,
+.gsc-newsResult .gs-newsResult .gs-publishedDate {
+  display : none;
+}
+
+/* enable relativePublishedDate while in the control */
+.gsc-blogResult .gs-blogResult .gs-relativePublishedDate,
+.gsc-newsResult .gs-newsResult .gs-relativePublishedDate {
+  display : inline;
+}
+
+.gs-newsResult .gs-location {
+  color: #6f6f6f;
+  display : inline;
+  text-decoration: none;
+}
+
+.gs-result img.gs-image {
+  vertical-align : middle;
+  border : 1px solid #0000cc;
+}
+
+.gs-result div.gs-phone {}
+
+.gs-result .gs-directions,
+.gs-result .gs-directions * {
+  color: #3366cc;
+  font-weight: normal;
+  text-decoration : underline;
+}
+
+.gs-videoResult a.gs-publisher,
+.gs-videoResult .gs-publisher {
+  color: #008000;
+  text-decoration: none;
+}
+
+.gs-result a {
+  cursor: pointer;
+}
+
+.gs-result .gs-address {
+}
+
+.gs-result .gs-snippet {
+}
+
+.gs-result .gs-watermark{
+  font-size: 10px;
+  color: #7777cc;
+}
+
+div.gs-results-attribution {
+  text-align : center;
+  margin-bottom : 4px;
+}
+
+div.gs-results-attribution,
+div.gs-results-attribution * {
+  font-size : 10px;
+  color : #676767;
+  text-decoration : none;
+}
+
+div.gs-results-attribution a {
+  color: #0000cc;
+  cursor : pointer;
+}
+
+div.gs-results-attribution a:hover {
+  text-decoration : underline;
+}
+
+.gs-result .gs-metadata{
+  color: #676767;
+}
+
+/* searcher specific styling for
+ * - web ad
+ * - web
+ * - local
+ * - image (none)
+ * - blog (none)
+ * - video (none)
+ */
+
+/* webAd search specific over rides
+ * .gs-ad-marker : disabled in control, but on in green to indicate clipped result is an ad
+ */
+.gs-localAd img.gs-ad-marker,
+.gs-webAd img.gs-ad-marker {
+  padding-left: 4px;
+}
+
+.gs-localAd a.gs-visibleUrl div.gs-visibleUrl,
+.gs-webAd a.gs-visibleUrl div.gs-visibleUrl {
+  display : none;
+}
+
+/* default is to not show long mode visible urls
+ * apps should selectively enable this while disabling
+ * div.gs-visibleUrl-short
+ */
+.gs-webResult div.gs-visibleUrl-long {
+  width : 100%;
+  overflow : hidden;
+  display : block;
+}
+
+.gs-webResult div.gs-visibleUrl-short {
+       display: none;
+}
+
+/* local search specific over rides
+ * - city, region displayed inline
+ * - country supressed
+ * - small font size for info window's
+ */
+
+.gs-localAd div.gs-address * {
+  color : #676767;
+}
+
+.gs-localAd div.gs-street {
+  display: inline;
+}
+
+div.gs-city {
+  display: inline;
+}
+
+div.gs-region {
+  display: inline;
+}
+
+div.gs-country {
+  display: none;
+}
+
+div.gs-infoWindow * {
+  font-size: 11px;
+  }
+
+/* video search specific over rides
+ * - align the table data
+ * - default image width garuntee
+ * - appropriate cell seperation
+ */
+
+/* todo(markl): workaround until gre in gmail fixes his styles */
+.gs-videoResult * {
+  font-size: 13px;
+  }
+
+/*
+.gs-videoResult td .gs-image {
+  vertical-align : middle;
+}
+
+.gs-videoResult td.gs-image-box {
+  background-color : #000000;
+}*/
+
+.gs-videoResult td div.gs-image-box {
+  width : 110px;
+  height : 78px;
+}
+
+.gs-videoResult td div.gs-text-box {
+  vertical-align: top;
+  margin-left: 4px;
+  }
+
+
+/* book search specific over rides
+ * - default image width garuntee
+ * - appropriate cell seperation
+ */
+.gs-bookResult td div.gs-image-box {
+  width : 75px;
+  height : 90px;
+}
+
+.gs-bookResult td div.gs-text-box {
+  vertical-align: top;
+  margin-left: 4px;
+  }
+
+.gs-bookResult img.gs-image,
+.gs-bookResult img {
+  border-spacing : 0px 0px;
+  border : none;
+}
+
+.gs-bookResult table.gs-image-box {
+  border-style : none;
+  border-width : 0px;
+  border-spacing : 0px 0px;
+}
+
+.gs-bookResult td.gs-pages img {
+  height : 7px;
+  width : 45px;
+}
+
+.gs-bookResult td.gs-page-edge img {
+  height : 7px;
+  width : 11px;
+}
+
+.gs-bookResult td.gs-shadow {
+  vertical-align : bottom;
+}
+
+.gs-bookResult td.gs-image img {
+  height : 80px;
+}
+.gsc-ad-box { display:none;}
+
+
+/* trailing cursor section
+ */
+.gsc-imageResult .gsc-cursor-box {
+  clear : both;
+}
+
+.gsc-results .gsc-cursor-box .gsc-trailing-more-results {
+  margin-bottom : 0px;
+  display : inline;
+}
+
+.gsc-results .gsc-cursor {
+  display : inline;
+}
+
+.gsc-results .gsc-cursor-box {
+  margin-bottom : 10px;
+}
+
+.gsc-results .gsc-cursor-box .gsc-cursor-page {
+  cursor : pointer;
+  color : #000000;
+  text-decoration: underline;
+  margin-right : 8px;
+  display : inline;
+}
+
+.gsc-results .gsc-cursor-box .gsc-cursor-current-page {
+  color : #A90A08;
+  font-weight : bold;
+  text-decoration: none;
+}
+
+.gsc-resultsHeader *{display:none;}
diff --git a/css/ie7.css b/css/ie7.css
new file mode 100644 (file)
index 0000000..225a21b
--- /dev/null
@@ -0,0 +1,9 @@
+body .imagecaption {
+       position: relative;
+       width: auto;
+       bottom: auto;
+       background: url("../../assets/imagecaptionBg.gif") no-repeat bottom center !important;
+       padding: 0 0 10px 0;
+       margin-top: 4px;
+       }
+
diff --git a/css/siteMap.css b/css/siteMap.css
new file mode 100644 (file)
index 0000000..2300043
--- /dev/null
@@ -0,0 +1,49 @@
+
+#sitemap h2 {
+       font-size: 14px;
+       font-weight: bold;
+       margin: 20px 0 5px 0;
+       padding: 5px;
+       border-bottom: 1px solid #000;
+       }
+#sitemap h2 a {
+       color: #000;
+       }
+#sitemap .row {
+       clear: both;
+       overflow: hidden;
+       }
+.row ol, .row li {
+       list-style-image: none;
+       list-style-type: none;
+       text-transform: capitalize;
+       font-size: 12px;
+       font-weight: normal;
+       margin: 0;
+       padding: 0;
+}
+#sitemap a {
+       border-bottom: none;
+       text-decoration: none;
+       color: #666;
+       }
+#sitemap a:hover {
+       text-decoration: underline;
+       color: #00F;
+       }
+#sitemap a:active {
+       text-decoration: underline;
+       color: #00F;
+       }
+.row > ol {
+       float: left;
+       margin-right: 3%;
+       /* width: 30% */
+       }
+.row > ol > li {
+       margin: 5px;
+       font-weight: bold;
+       }
+.row > ol > li > ol > li {
+       margin: 5px 0 5px 20px;
+       }
\ No newline at end of file
diff --git a/images/add.png b/images/add.png
new file mode 100755 (executable)
index 0000000..6332fef
Binary files /dev/null and b/images/add.png differ
diff --git a/images/amenities/1182867688waterfront.gif b/images/amenities/1182867688waterfront.gif
new file mode 100644 (file)
index 0000000..a675917
Binary files /dev/null and b/images/amenities/1182867688waterfront.gif differ
diff --git a/images/amenities/1182867790sauna.gif b/images/amenities/1182867790sauna.gif
new file mode 100644 (file)
index 0000000..85895fc
Binary files /dev/null and b/images/amenities/1182867790sauna.gif differ
diff --git a/images/amenities/barrierfree.gif b/images/amenities/barrierfree.gif
new file mode 100644 (file)
index 0000000..fcee0ea
Binary files /dev/null and b/images/amenities/barrierfree.gif differ
diff --git a/images/amenities/beachaccess.gif b/images/amenities/beachaccess.gif
new file mode 100644 (file)
index 0000000..b12f22b
Binary files /dev/null and b/images/amenities/beachaccess.gif differ
diff --git a/images/amenities/breakfast.gif b/images/amenities/breakfast.gif
new file mode 100644 (file)
index 0000000..e1ce145
Binary files /dev/null and b/images/amenities/breakfast.gif differ
diff --git a/images/amenities/cable.gif b/images/amenities/cable.gif
new file mode 100644 (file)
index 0000000..35cafca
Binary files /dev/null and b/images/amenities/cable.gif differ
diff --git a/images/amenities/conference.gif b/images/amenities/conference.gif
new file mode 100644 (file)
index 0000000..87ce656
Binary files /dev/null and b/images/amenities/conference.gif differ
diff --git a/images/amenities/cont_break.gif b/images/amenities/cont_break.gif
new file mode 100644 (file)
index 0000000..a82c366
Binary files /dev/null and b/images/amenities/cont_break.gif differ
diff --git a/images/amenities/eff.gif b/images/amenities/eff.gif
new file mode 100644 (file)
index 0000000..1079406
Binary files /dev/null and b/images/amenities/eff.gif differ
diff --git a/images/amenities/excersize.gif b/images/amenities/excersize.gif
new file mode 100644 (file)
index 0000000..657e032
Binary files /dev/null and b/images/amenities/excersize.gif differ
diff --git a/images/amenities/free_internet.gif b/images/amenities/free_internet.gif
new file mode 100644 (file)
index 0000000..0d41ffb
Binary files /dev/null and b/images/amenities/free_internet.gif differ
diff --git a/images/amenities/guest_laundry.gif b/images/amenities/guest_laundry.gif
new file mode 100644 (file)
index 0000000..de795b7
Binary files /dev/null and b/images/amenities/guest_laundry.gif differ
diff --git a/images/amenities/internet.gif b/images/amenities/internet.gif
new file mode 100644 (file)
index 0000000..5d18771
Binary files /dev/null and b/images/amenities/internet.gif differ
diff --git a/images/amenities/kidfriendly.gif b/images/amenities/kidfriendly.gif
new file mode 100644 (file)
index 0000000..69035e5
Binary files /dev/null and b/images/amenities/kidfriendly.gif differ
diff --git a/images/amenities/meeting.gif b/images/amenities/meeting.gif
new file mode 100644 (file)
index 0000000..d307486
Binary files /dev/null and b/images/amenities/meeting.gif differ
diff --git a/images/amenities/pets.gif b/images/amenities/pets.gif
new file mode 100644 (file)
index 0000000..f24ebc7
Binary files /dev/null and b/images/amenities/pets.gif differ
diff --git a/images/amenities/pool-out.gif b/images/amenities/pool-out.gif
new file mode 100644 (file)
index 0000000..d59e1e2
Binary files /dev/null and b/images/amenities/pool-out.gif differ
diff --git a/images/amenities/pool.gif b/images/amenities/pool.gif
new file mode 100644 (file)
index 0000000..cd695c4
Binary files /dev/null and b/images/amenities/pool.gif differ
diff --git a/images/amenities/restaurant.gif b/images/amenities/restaurant.gif
new file mode 100644 (file)
index 0000000..757105a
Binary files /dev/null and b/images/amenities/restaurant.gif differ
diff --git a/images/amenities/restnear.gif b/images/amenities/restnear.gif
new file mode 100644 (file)
index 0000000..af879be
Binary files /dev/null and b/images/amenities/restnear.gif differ
diff --git a/images/amenities/sauna.gif b/images/amenities/sauna.gif
new file mode 100644 (file)
index 0000000..85895fc
Binary files /dev/null and b/images/amenities/sauna.gif differ
diff --git a/images/amenities/smokefree.gif b/images/amenities/smokefree.gif
new file mode 100644 (file)
index 0000000..006d931
Binary files /dev/null and b/images/amenities/smokefree.gif differ
diff --git a/images/amenities/snowmobile.gif b/images/amenities/snowmobile.gif
new file mode 100644 (file)
index 0000000..eeb8f58
Binary files /dev/null and b/images/amenities/snowmobile.gif differ
diff --git a/images/amenities/waterfront.gif b/images/amenities/waterfront.gif
new file mode 100644 (file)
index 0000000..a675917
Binary files /dev/null and b/images/amenities/waterfront.gif differ
diff --git a/images/amenities/waterview.gif b/images/amenities/waterview.gif
new file mode 100644 (file)
index 0000000..0174faa
Binary files /dev/null and b/images/amenities/waterview.gif differ
diff --git a/images/amenities/wireless.gif b/images/amenities/wireless.gif
new file mode 100644 (file)
index 0000000..53c4280
Binary files /dev/null and b/images/amenities/wireless.gif differ
diff --git a/images/application_edit.png b/images/application_edit.png
new file mode 100644 (file)
index 0000000..fb2efb8
Binary files /dev/null and b/images/application_edit.png differ
diff --git a/images/cross.png b/images/cross.png
new file mode 100644 (file)
index 0000000..1514d51
Binary files /dev/null and b/images/cross.png differ
diff --git a/images/delete.png b/images/delete.png
new file mode 100644 (file)
index 0000000..08f2493
Binary files /dev/null and b/images/delete.png differ
diff --git a/images/error.gif b/images/error.gif
new file mode 100644 (file)
index 0000000..48543cd
Binary files /dev/null and b/images/error.gif differ
diff --git a/images/file-ext/cad.gif b/images/file-ext/cad.gif
new file mode 100644 (file)
index 0000000..3947208
Binary files /dev/null and b/images/file-ext/cad.gif differ
diff --git a/images/file-ext/doc.gif b/images/file-ext/doc.gif
new file mode 100644 (file)
index 0000000..7e053f5
Binary files /dev/null and b/images/file-ext/doc.gif differ
diff --git a/images/file-ext/download.gif b/images/file-ext/download.gif
new file mode 100644 (file)
index 0000000..d27f89e
Binary files /dev/null and b/images/file-ext/download.gif differ
diff --git a/images/file-ext/gif.gif b/images/file-ext/gif.gif
new file mode 100644 (file)
index 0000000..2f1cd83
Binary files /dev/null and b/images/file-ext/gif.gif differ
diff --git a/images/file-ext/html.gif b/images/file-ext/html.gif
new file mode 100644 (file)
index 0000000..e92ab0c
Binary files /dev/null and b/images/file-ext/html.gif differ
diff --git a/images/file-ext/jpg.gif b/images/file-ext/jpg.gif
new file mode 100644 (file)
index 0000000..2f1cd83
Binary files /dev/null and b/images/file-ext/jpg.gif differ
diff --git a/images/file-ext/movie.gif b/images/file-ext/movie.gif
new file mode 100755 (executable)
index 0000000..9754248
Binary files /dev/null and b/images/file-ext/movie.gif differ
diff --git a/images/file-ext/mp3.gif b/images/file-ext/mp3.gif
new file mode 100644 (file)
index 0000000..6a41246
Binary files /dev/null and b/images/file-ext/mp3.gif differ
diff --git a/images/file-ext/pdf.png b/images/file-ext/pdf.png
new file mode 100644 (file)
index 0000000..e64037e
Binary files /dev/null and b/images/file-ext/pdf.png differ
diff --git a/images/file-ext/ppt.gif b/images/file-ext/ppt.gif
new file mode 100644 (file)
index 0000000..4f0c034
Binary files /dev/null and b/images/file-ext/ppt.gif differ
diff --git a/images/file-ext/rar.gif b/images/file-ext/rar.gif
new file mode 100644 (file)
index 0000000..4ba0582
Binary files /dev/null and b/images/file-ext/rar.gif differ
diff --git a/images/file-ext/rm.gif b/images/file-ext/rm.gif
new file mode 100644 (file)
index 0000000..9754248
Binary files /dev/null and b/images/file-ext/rm.gif differ
diff --git a/images/file-ext/txt.png b/images/file-ext/txt.png
new file mode 100644 (file)
index 0000000..22e37ee
Binary files /dev/null and b/images/file-ext/txt.png differ
diff --git a/images/file-ext/xls.gif b/images/file-ext/xls.gif
new file mode 100644 (file)
index 0000000..9f5d57b
Binary files /dev/null and b/images/file-ext/xls.gif differ
diff --git a/images/file-ext/zip.png b/images/file-ext/zip.png
new file mode 100644 (file)
index 0000000..ab69b4a
Binary files /dev/null and b/images/file-ext/zip.png differ
diff --git a/images/find.png b/images/find.png
new file mode 100644 (file)
index 0000000..1547479
Binary files /dev/null and b/images/find.png differ
diff --git a/images/flag_green.png b/images/flag_green.png
new file mode 100644 (file)
index 0000000..e4bc611
Binary files /dev/null and b/images/flag_green.png differ
diff --git a/images/folder.png b/images/folder.png
new file mode 100755 (executable)
index 0000000..784e8fa
Binary files /dev/null and b/images/folder.png differ
diff --git a/images/folder_add.png b/images/folder_add.png
new file mode 100755 (executable)
index 0000000..529fe8f
Binary files /dev/null and b/images/folder_add.png differ
diff --git a/images/folder_delete.png b/images/folder_delete.png
new file mode 100755 (executable)
index 0000000..112b016
Binary files /dev/null and b/images/folder_delete.png differ
diff --git a/images/grnball.gif b/images/grnball.gif
new file mode 100755 (executable)
index 0000000..5f7740b
Binary files /dev/null and b/images/grnball.gif differ
diff --git a/images/help.gif b/images/help.gif
new file mode 100755 (executable)
index 0000000..4915678
Binary files /dev/null and b/images/help.gif differ
diff --git a/images/html.gif b/images/html.gif
new file mode 100755 (executable)
index 0000000..21e8e85
Binary files /dev/null and b/images/html.gif differ
diff --git a/images/img.gif b/images/img.gif
new file mode 100755 (executable)
index 0000000..cd2c4a5
Binary files /dev/null and b/images/img.gif differ
diff --git a/images/left.gif b/images/left.gif
new file mode 100644 (file)
index 0000000..29d0ba1
Binary files /dev/null and b/images/left.gif differ
diff --git a/images/loadingAnimation.gif b/images/loadingAnimation.gif
new file mode 100644 (file)
index 0000000..92c5c30
Binary files /dev/null and b/images/loadingAnimation.gif differ
diff --git a/images/logo.gif b/images/logo.gif
new file mode 100755 (executable)
index 0000000..cb56683
Binary files /dev/null and b/images/logo.gif differ
diff --git a/images/magnifier.png b/images/magnifier.png
new file mode 100755 (executable)
index 0000000..cf3d97f
Binary files /dev/null and b/images/magnifier.png differ
diff --git a/images/members/add.png b/images/members/add.png
new file mode 100755 (executable)
index 0000000..6332fef
Binary files /dev/null and b/images/members/add.png differ
diff --git a/images/members/application_edit.png b/images/members/application_edit.png
new file mode 100755 (executable)
index 0000000..fb2efb8
Binary files /dev/null and b/images/members/application_edit.png differ
diff --git a/images/members/cancel.png b/images/members/cancel.png
new file mode 100755 (executable)
index 0000000..c149c2b
Binary files /dev/null and b/images/members/cancel.png differ
diff --git a/images/members/cardamex.gif b/images/members/cardamex.gif
new file mode 100644 (file)
index 0000000..45b4f21
Binary files /dev/null and b/images/members/cardamex.gif differ
diff --git a/images/members/carddinner.gif b/images/members/carddinner.gif
new file mode 100644 (file)
index 0000000..8399020
Binary files /dev/null and b/images/members/carddinner.gif differ
diff --git a/images/members/carddiscover.gif b/images/members/carddiscover.gif
new file mode 100644 (file)
index 0000000..7050611
Binary files /dev/null and b/images/members/carddiscover.gif differ
diff --git a/images/members/cardmaster.gif b/images/members/cardmaster.gif
new file mode 100644 (file)
index 0000000..d3ee08b
Binary files /dev/null and b/images/members/cardmaster.gif differ
diff --git a/images/members/cardvisa.gif b/images/members/cardvisa.gif
new file mode 100644 (file)
index 0000000..0f70efa
Binary files /dev/null and b/images/members/cardvisa.gif differ
diff --git a/images/members/cross.png b/images/members/cross.png
new file mode 100755 (executable)
index 0000000..1514d51
Binary files /dev/null and b/images/members/cross.png differ
diff --git a/images/members/delete.png b/images/members/delete.png
new file mode 100755 (executable)
index 0000000..08f2493
Binary files /dev/null and b/images/members/delete.png differ
diff --git a/images/members/find.png b/images/members/find.png
new file mode 100755 (executable)
index 0000000..1547479
Binary files /dev/null and b/images/members/find.png differ
diff --git a/images/members/img.gif b/images/members/img.gif
new file mode 100644 (file)
index 0000000..cd2c4a5
Binary files /dev/null and b/images/members/img.gif differ
diff --git a/images/members/lock.png b/images/members/lock.png
new file mode 100755 (executable)
index 0000000..2ebc4f6
Binary files /dev/null and b/images/members/lock.png differ
diff --git a/images/members/note_edit.png b/images/members/note_edit.png
new file mode 100755 (executable)
index 0000000..291bfc7
Binary files /dev/null and b/images/members/note_edit.png differ
diff --git a/images/members/page_edit.png b/images/members/page_edit.png
new file mode 100755 (executable)
index 0000000..046811e
Binary files /dev/null and b/images/members/page_edit.png differ
diff --git a/images/members/tick.png b/images/members/tick.png
new file mode 100755 (executable)
index 0000000..a9925a0
Binary files /dev/null and b/images/members/tick.png differ
diff --git a/images/members/user_add.png b/images/members/user_add.png
new file mode 100755 (executable)
index 0000000..deae99b
Binary files /dev/null and b/images/members/user_add.png differ
diff --git a/images/members/user_edit.png b/images/members/user_edit.png
new file mode 100755 (executable)
index 0000000..c1974cd
Binary files /dev/null and b/images/members/user_edit.png differ
diff --git a/images/note_edit.png b/images/note_edit.png
new file mode 100644 (file)
index 0000000..291bfc7
Binary files /dev/null and b/images/note_edit.png differ
diff --git a/images/page_edit.png b/images/page_edit.png
new file mode 100644 (file)
index 0000000..046811e
Binary files /dev/null and b/images/page_edit.png differ
diff --git a/images/redball.gif b/images/redball.gif
new file mode 100755 (executable)
index 0000000..7005133
Binary files /dev/null and b/images/redball.gif differ
diff --git a/images/right.gif b/images/right.gif
new file mode 100644 (file)
index 0000000..d66a1ca
Binary files /dev/null and b/images/right.gif differ
diff --git a/images/shadowb.gif b/images/shadowb.gif
new file mode 100644 (file)
index 0000000..72c4706
Binary files /dev/null and b/images/shadowb.gif differ
diff --git a/images/shadowr.gif b/images/shadowr.gif
new file mode 100644 (file)
index 0000000..f7e19d1
Binary files /dev/null and b/images/shadowr.gif differ
diff --git a/images/size.sh b/images/size.sh
new file mode 100755 (executable)
index 0000000..2471ec3
--- /dev/null
@@ -0,0 +1,13 @@
+#! /bin/bash
+convert='/usr/bin/convert'
+composite='/usr/bin/composite'
+
+for file in original/*
+do 
+       temp=`basename $file` 
+       $convert -scale '287>' $file resized/$temp
+       $convert -scale '210>' $file midsized/$temp
+       $convert -scale '120>' $file thumb/$temp
+
+       echo $temp 
+done
diff --git a/images/tick.png b/images/tick.png
new file mode 100644 (file)
index 0000000..a9925a0
Binary files /dev/null and b/images/tick.png differ
diff --git a/images/top2.jpg b/images/top2.jpg
new file mode 100755 (executable)
index 0000000..350fbe1
Binary files /dev/null and b/images/top2.jpg differ
diff --git a/images/user_add.png b/images/user_add.png
new file mode 100644 (file)
index 0000000..deae99b
Binary files /dev/null and b/images/user_add.png differ
diff --git a/images/user_edit.png b/images/user_edit.png
new file mode 100644 (file)
index 0000000..c1974cd
Binary files /dev/null and b/images/user_edit.png differ
diff --git a/index.php b/index.php
new file mode 100644 (file)
index 0000000..0ed28ac
--- /dev/null
+++ b/index.php
@@ -0,0 +1,129 @@
+<?php
+$includeFunctions = false;
+require_once 'setup.phtml';
+if ($_REQUEST['siteMapXml'] == '1') {
+    // Generate the sitemap
+    $siteMapXml = new Toolkit_SiteMapXml(
+        Toolkit_Database::getInstance()
+    );
+    $xml = $siteMapXml->getXmlSitemap();
+
+    // Save the sitemap into the root of the site
+    $fp = fopen(BASE_PATH.'sitemap.xml', 'w');
+    fwrite($fp, $xml);
+    fclose($fp);
+
+    // Output the sitemap as XML and quit
+    header("content-type: text/xml");
+    header('Content-Length: '.strlen($xml));
+    header('Content-Disposition: attachment;filename="sitemap.xml"');
+    echo $xml;
+    exit;
+}
+$dbh = Toolkit_Database::getInstance();
+if (   defined("SHORT_URLS")
+    && SHORT_URLS
+    && isset($_GET['glmPage'])
+    && $_GET['glmPage']
+) {
+    $shortURL = new Toolkit_ShortURL($dbh);
+    if (!$catid = $shortURL->getShortUrlId($_GET['glmPage'])) {
+        $catid = HOME_ID;
+    }
+    $_REQUEST['catid'] = $_GET['catid'] = $catid;
+} elseif ($catid = filter_input(INPUT_GET, 'catid', FILTER_VALIDATE_INT)) {
+    //    do nothing, catid set and filtered
+} else {
+    $catid = HOME_ID;
+}
+$qfKeys
+    = array_filter(
+        array_keys($_REQUEST),
+        create_function('$key', 'return preg_match("/_qf/", $key);')
+    );
+if ($catid != HOME_ID) {
+    require_once 'Text/CAPTCHA.php';
+    require_once 'Text/CAPTCHA/Driver/Image.php';
+} elseif (empty($qfKeys)) {
+    $_REQUEST['catid'] = $_GET['catid'] = $catid;
+}
+HTTP_Session2::useCookies(false);
+HTTP_Session2::start();
+
+if (isset($qfKeys) && !empty($qfKeys)) {
+    if (ctype_digit($_SESSION['_regWizard_container']['values']['page1']['catid'])) {
+        $catid = $_SESSION['_regWizard_container']['values']['page1']['catid'];
+    } elseif (ctype_digit($_REQUEST['catid'])) {
+        $catid = $_REQUEST['catid'];
+    }
+    $_REQUEST['catid'] = $_POST['catid'] = $_GET['catid'] = $catid;
+}
+require_once 'setup_functions.phtml';
+
+
+// If Ticketing is enabled, run ticket login
+if (defined('TICKETING') && TICKETING) {
+    require_once 'ticketing/ticket_login.inc';
+}
+
+//    Create a new registry so we don't pollute the global namespace
+$registry = new Toolkit_Registry;
+
+$registry->cacheOptions = $GLOBALS['cacheOptions'];
+$registry->flexyOptions = $GLOBALS['flexyOptions'];
+$registry->catid        = $catid;
+$registry->dbh          = $dbh;
+$registry->logger       = Toolkit_Logger::getLogger();
+
+if (defined('SHOP_PAGE_ID') && $catid == SHOP_PAGE_ID) {
+    //    Initiate HTML_Template_Flexy.
+    $template = new HTML_Template_Flexy($registry->flexyOptions);
+    $keywordReplacement = new Toolkit_Template_KeywordReplacement(
+        new Toolkit_Toolbox_PageGatewayPublish(
+            $registry->dbh
+        )
+    );
+    $breadCrumbsFactory = new Toolkit_BreadCrumbsFactory(
+        new Toolkit_Toolbox_PageGatewayPublishFactory(
+            $registry->dbh
+        )
+    );
+    //    Page object used for merging with the flexy template object.
+    //    now using the page class from toolkit
+    $glmPage = new Toolkit_Page(
+        new Toolkit_Template_Page(),
+        $breadCrumbsFactory,
+        new Toolkit_Toolbox_PageGatewayPublishFactory(
+            $registry->dbh
+        ),
+        new Toolkit_Toolbox_ParagraphGatewayPublishFactory(
+            $registry->dbh
+        ),
+        new Toolkit_Template_Navigation_Factory(),
+        $keywordReplacement,
+        $registry->catid
+    );
+    $glmPage->fetchPage();
+    ob_start();
+    include 'shop.php';
+    $glmPage->toolboxContent = ob_get_contents();
+    ob_end_clean();
+
+    $glmPage->topScripts
+        = Toolkit_Common::getScripts($GLOBALS['topScripts']);
+    $glmPage->bottomScripts
+        = Toolkit_Common::getScripts($GLOBALS['bottomScripts']);
+    $glmPage->styles        = Toolkit_Common::getStyleSheets();
+
+    //    Compile the template.html from the templates directory.
+    $template->compile('template.html');
+    //    Merge compiled template with the $glmPage object.
+    $template->outputObject($glmPage);
+} else {
+    //    Create a router so we can get where we need to be.
+    $registry->router = new Toolkit_Router($registry);
+    $registry->router->setPath(BASE . 'Toolkit/Template');
+    $registry->router->setApplication('Template');
+
+    $registry->router->loader();
+}
diff --git a/libjs/couponLimitText.js b/libjs/couponLimitText.js
new file mode 100644 (file)
index 0000000..2444f12
--- /dev/null
@@ -0,0 +1,3 @@
+$(document).ready(function(){
+       $("#description").textlimit("#charleft", 600);
+});
diff --git a/libjs/get_adobe_reader.png b/libjs/get_adobe_reader.png
new file mode 100755 (executable)
index 0000000..ecd6029
Binary files /dev/null and b/libjs/get_adobe_reader.png differ
diff --git a/setup.phtml b/setup.phtml
new file mode 100644 (file)
index 0000000..c72701a
--- /dev/null
@@ -0,0 +1,1195 @@
+<?php
+/**
+ * Media Toolbox(R)
+ *
+ * Setup.phtml file includes the functions that were in the functions.inc
+ * and siteinfo.inc file into one file.
+ * All set up stuff is on the top of the page.
+ *
+ * PHP version 5
+ *
+ * @category Gaslight_Media_Website
+ * @package  Gaslight_Media
+ * @header   Gaslight Media Toolbox
+ * @author   Jamie Kahgee <jamie.kahgee@gmail.com>
+ * @author   Steve Sutton <steve@gaslightmedia.com>
+ * @license     http://www.gaslightmedia.com Gaslightmedia
+ * @version  $Id: setup.phtml,v 1.102 2010/08/10 17:43:11 jamie Exp $
+ * @release     SVN: $Id: $
+ * @link     http://www.gaslightmedia.com
+ */
+
+/**
+ *  DO NOT EDIT THIS SECTION
+ */
+// Find where this file is located
+$BASE_PATH   = dirname(__FILE__);
+$php_version = phpversion();
+// If php5 or above
+if (version_compare($php_version, '5.0.0', '>')) {
+    $CALLED_FROM_DIR = substr(dirname($_SERVER['SCRIPT_FILENAME']), strlen($BASE_PATH));
+    define('GLM_HOST_ID', $_ENV['GLM_HOST_ID']);
+    define('PHP5', true);
+} else {
+    $CALLED_FROM_DIR = substr(dirname($HTTP_SERVER_VARS['PATH_TRANSLATED']), strlen($BASE_PATH));
+    define('GLM_HOST_ID', $_SERVER['GLM_HOST_ID']);
+    define('PHP5', false);
+}
+
+if (($x = strlen($CALLED_FROM_DIR)) > 0) {
+    $base_url = $_SERVER['HTTP_HOST'] . substr(dirname($_SERVER['SCRIPT_NAME']), 0, -strlen($CALLED_FROM_DIR));
+} else {
+    $script_name_dir = dirname($_SERVER['SCRIPT_NAME']);
+    if ($script_name_dir == '/') {
+        $script_name_dir = '';
+    }
+    $base_url = $_SERVER['HTTP_HOST'] . $script_name_dir;
+}
+// Added to strip any trailing /'s to make sure we don't end up with many
+$base_url = preg_replace('|/*$|', '', $base_url);
+$BASE_URL = "http://$base_url";
+
+if (!isset($DEBUG)) {
+    $DEBUG = (isset($mysecretcode) && $mysecretcode == 1234);
+}
+$securePagesArray = array();
+define('SECURE_PAGES', serialize($securePagesArray));
+$nonMobilePagesArray = array();
+define('NON_MOBILE_PAGES', serialize($nonMobilePagesArray));
+/**
+ * URI used for the root of the site
+ */
+define('BASE_URL', "$BASE_URL/");
+/**
+ * Another way of calling the BASE_URL constant
+ */
+define('URL_BASE', "$BASE_URL/");
+/**
+ * root directory path of site in filesystem
+ */
+define('BASE_PATH', "$BASE_PATH/");
+/**
+ * Another way of calling the BASE_PATH constant
+ */
+define('BASE', "$BASE_PATH/");
+
+//  GLM App repository config {{{
+switch ($_ENV['GLM_HOST_ID']) {
+case 'PRODUCTION' : // {{{
+    $glmAppBase = '/var/www/server/app.gaslightmedia.com/';
+    break; //  }}}
+case 'DEVELOPMENT' : //    {{{
+    $glmAppBase = '/var/www/server/app.gaslightmedia.com/';
+    break; //  }}}
+case 'STEVE' : // {{{
+    $glmAppBase = '/var/www/server/app.gaslightmedia.com/';
+    break; //  }}}
+case 'VAGRANT' : // {{{
+    $glmAppBase = '/var/www/server/app.gaslightmedia.com/';
+    break; //  }}}
+case 'CHUCK' : // {{{
+    $glmAppBase = '/var/www/server/app.gaslightmedia.com/';
+    break; // }}}
+case 'JOHN' : // {{{
+    $glmAppBase = '/var/www/server/app.gaslightmedia.com/';
+    break; // }}}
+default : // {{{
+    die('<p>Your request could not be completed at this time, please try again later!</p>');
+    break; // }}}
+} // }}}
+/**
+ * Path to the common GLM application repository
+ */
+define('GLM_APP_BASE', $glmAppBase);
+//    {{{    Path Configuration
+
+//  explode the current include_path by forward slashes (/),
+//  colons (:), or periods (.)
+$path = preg_split('(\/|:|\.)', get_include_path());
+if (!in_array(GLM_APP_BASE . 'glmPEAR', $path)) {
+    set_include_path(
+        GLM_APP_BASE . 'glmPEAR' .  PATH_SEPARATOR . get_include_path()
+    );
+}
+
+if (!in_array(GLM_APP_BASE . 'glmZend', $path)) {
+    set_include_path(
+        GLM_APP_BASE . 'glmZend/1.10.2/library' . PATH_SEPARATOR . get_include_path()
+    );
+}
+
+//  Include path to geocode API
+if (!in_array(GLM_APP_BASE . 'geocode', $path)) {
+    set_include_path(
+        GLM_APP_BASE . 'geocode' . PATH_SEPARATOR . get_include_path()
+    );
+}
+
+//    }}}
+
+//    {{{ autoloader
+//  nullify any existing autoloads
+spl_autoload_register(null, false);
+//  specify extensions that may be loaded
+spl_autoload_extensions();
+
+//  {{{ classLoader()
+
+/**
+ * spl autoloader callback function
+ *
+ * @param string $className Name of the class to autoload
+ *
+ * @return false on error
+ */
+function classLoader($className)
+{
+    switch ($className) {
+    case 'GLM_DB' :
+        require_once BASE . 'classes/class_db.inc';
+        break;
+
+    case 'GLM_TOOLBOX' :
+        require_once BASE . 'classes/class_toolbox.inc';
+        break;
+
+    case 'GLM_TEMPLATE' :
+        require_once BASE . 'classes/class_template.inc';
+        break;
+
+    case 'GLM_EVENTS' :
+        require_once BASE . 'classes/class_events.inc';
+        break;
+
+    case 'guide' :
+        require_once BASE . 'classes/class_visitor_guide_form.inc';
+        break;
+
+    case 'event_form' :
+        require_once BASE . 'classes/class_event_form.inc';
+        break;
+
+    case 'html_quickform_rule_phone' :
+    case 'html_quickform_rule_email' :
+    case 'html_quickform_rule_zip' :
+    case 'html_quickform_rule_state' :
+    case 'html_quickform_rule_banwords' :
+    case 'html_quickform_rule_date' :
+    case 'html_quickform_rule_image' :
+    case 'html_quickform_rule_image2' :
+    case 'html_quickform_rule_memberimage' :
+    case 'html_quickform_rule_memberpackage' :
+    case 'html_quickform_rule_memberlogo' :
+    case 'html_quickform_rule_memberfile' :
+    case 'html_quickform_rule_amenity' :
+    case 'html_quickform_rule_banner' :
+        $path = explode('_', $className);
+        $className = ucfirst(end($path));
+        require_once GLM_APP_BASE . "glmPEAR/HTML/QuickForm/Rule/$className.php";
+        break;
+
+    default :
+        //  Since our old naming conventions conflict w/ the pear
+        //  naming conventions.
+        //  Check to see what we're trying to call by checking if the
+        //  file/class exits in the PEAR sturcture.
+        $class = implode('/', explode('_', $className));
+        if (file_exists(GLM_APP_BASE . "glmPEAR/$class.php")) {
+            require_once GLM_APP_BASE . "glmPEAR/$class.php";
+        } elseif (file_exists(GLM_APP_BASE . "glmZend/1.10.2/library/$class.php")) {
+            require_once GLM_APP_BASE . "glmZend/1.10.2/library/$class.php";
+        } elseif (file_exists(GLM_APP_BASE . "geocode/$class.php")) {
+            require_once GLM_APP_BASE . "geocode/$class.php";
+        } elseif (file_exists(GLM_APP_BASE . "$class.php")) {
+            require_once GLM_APP_BASE . "$class.php";
+        } elseif (file_exists(BASE . "$class.php")) {
+            require_once BASE . "$class.php";
+        } elseif (file_exists(BASE . "classes/$className.php")) {
+            require_once BASE . "classes/$className.php";
+        } else {
+            //  Add check to search our entire search path for
+            //  the desired class.
+            //  fixes phpunit dying for missing classes.
+            $includePath = get_include_path();
+            if (strpos($includePath, PATH_SEPARATOR) !== false) {
+                if (   ($i = explode(PATH_SEPARATOR, $includePath))
+                    && count($i) > 0
+                ) {
+                    foreach ($i as $j) {
+                        if (file_exists("{$j}/{$class}.php")) {
+                            require_once "{$j}/{$class}.php";
+                        }
+                    }
+                }
+            }
+
+            return false;
+        }
+        break;
+    }
+}
+
+//  }}}
+
+//  register the loader functions
+spl_autoload_register('classLoader');
+
+//    }}}
+
+set_error_handler(array('Toolkit_Logger', 'errorHandler'));
+if (!date_default_timezone_set('America/Detroit')) {
+    $logger =& Toolkit_Logger::getLogger();
+    $logger->warning('Default time zone not getting set.');
+}
+
+$serverConfig = new Zend_Config_Ini(
+    BASE . 'config/server.ini',
+    strtolower($_ENV['GLM_HOST_ID'])
+);
+//$serverConfig = Toolkit_ServerConfig::getInstance();
+
+$siteConfig = new Zend_Config_Ini(
+    BASE . 'config/site.ini',
+    strtolower($_ENV['GLM_HOST_ID'])
+);
+//$siteConfig = Toolkit_SiteConfig::getInstance();
+
+$applicationConfig = new Zend_Config_Ini(
+    BASE . 'config/application.ini',
+    strtolower($_ENV['GLM_HOST_ID'])
+);
+//$applicationConfig = Toolkit_ApplicationConfig::getInstance();
+
+/**
+ * Home page id of site
+ */
+define('HOME_ID', $siteConfig->home_id);
+/**
+ * Name used in title tag, admin area and emails
+ */
+define('SITENAME', $siteConfig->sitename);
+/**
+ * DB library type
+ */
+define('DB_TYPE', $serverConfig->database->type);
+/**
+ * Default error message
+ */
+define('DB_ERROR_MSG', $serverConfig->database->error->message);
+/**
+ * Default per page number
+ */
+define('ENTRIES_PER_PAGE', $siteConfig->entries_per_page);
+/**
+ * Turn on/off short url
+ */
+define('SHORT_URLS', $siteConfig->short_urls);
+/**
+ * Site has banner ad application?
+ */
+define('BANNERS', $applicationConfig->bannerdb->application);
+/**
+ * Site has Shop application?
+ */
+define('RETAIL_SHOP', $applicationConfig->retailShop->application);
+/**
+ * Shop Application is set for this page
+ */
+define('SHOP_PAGE_ID', null);
+/**
+ * Site has contact db application?
+ */
+define('CONTACT_DB', $applicationConfig->contactdb->application);
+/**
+ * Sets the first year available in the year select lists in
+ */
+define('CONTACTS_FIRST_YEAR', $applicationConfig->contactdb->first_year);
+/**
+ * Used in the email out for contact DB
+ */
+define('PRODUCTION_MODE', $applicationConfig->contactdb->production_mode);
+/**
+ * Turn ON for html emails
+ */
+define('HTML_EMAIL', $applicationConfig->contactdb->html_email);
+/**
+ * Login Id for StreamSend account
+ */
+define('STREAMSEND_LOGIN_ID', $applicationConfig->contactdb->streamsend->login);
+/**
+ * key for StreamSend account
+ */
+define('STREAMSEND_KEY', $applicationConfig->contactdb->streamsend->key);
+/**
+ * StreamSend Account Name
+ */
+define('STREAMSEND_ACCOUNT_NAME', $applicationConfig->contactdb->streamsend->account_name);
+/**
+ * StreamSend Audience Number
+ */
+define('STREAMSEND_AUDIENCE', $applicationConfig->contactdb->streamsend->audience);
+/**
+ * Site uses streamsend module?
+ */
+define(
+    'STREAMSEND_FORMS_API',
+    $applicationConfig->contactdb->streamsend->application
+);
+/**
+ * Enable the form to send data to constant contact
+ */
+define('CONSTANT_CONTACT', $applicationConfig->constantcontact->application);
+/**
+ * Login for API (required)
+ */
+define('CONSTANT_CONTACT_LOGIN', $applicationConfig->constantcontact->login);
+/**
+ * Password for API (required)
+ */
+define('CONSTANT_CONTACT_PASSWORD', $applicationConfig->constantcontact->password);
+/**
+ * API Key (required)
+ */
+define('CONSTANT_CONTACT_APIKEY', $applicationConfig->constantcontact->apikey);
+/**
+ * API Path (Required)
+ */
+define('CONSTANT_CONTACT_APIPATH', $applicationConfig->constantcontact->apipath);
+define(
+    'CONSTANT_CONTACT_LIST',
+    serialize($applicationConfig->constantcontact->list->toArray())
+);
+/**
+ * Coupons Database Installed?
+ */
+define('COUPONS', $applicationConfig->coupons->application);
+/**
+ * Site has event db application?
+ */
+define('EVENT_DB', $applicationConfig->eventdb->application);
+/**
+ * Site has CommonApps EventCalendar application?
+ */
+define('COMMON_EVENTS', $applicationConfig->eventdb->commonEvents);
+/**
+ * Page for the event calendar
+ */
+define('EVENT_PAGE', $applicationConfig->eventdb->event_page);
+/**
+ * Site has home page Events?
+ */
+define('HOME_EVENTS', $applicationConfig->eventdb->home_events);
+/**
+ * Site has google search api key?
+ */
+define('GOOGLE_SEARCH', $applicationConfig->google->search->application);
+/**
+ * Site has CommonApps GLMSearch application?
+ */
+define('GLM_SEARCH', $applicationConfig->glmsearch->application);
+define('GLM_SEARCH_INDEX', $applicationConfig->glmsearch->index);
+define('GLM_SEARCH_SITE', $applicationConfig->glmsearch->site);
+define('GLM_SEARCH_LOGIN', $applicationConfig->glmsearch->login);
+define('GLM_SEARCH_KEY', $applicationConfig->glmsearch->key);
+/**
+ * Site has home page Headlines?
+ */
+define('HOME_HEADLINES', $applicationConfig->headlines->application);
+/**
+ * Site has Event Management
+ */
+define('EVENT_MANAGEMENT', $applicationConfig->event_management->application);
+define('EVENT_MANAGEMENT_HOME_PAGE', $applicationConfig->event_management->home_page);
+define('EVENT_MANAGEMENT_MEMBERS_PAGE', $applicationConfig->event_management->members_only_page);
+/**
+ * Member Database Installed?
+ */
+define('MEMBERS_DB', $applicationConfig->memberdb->application);
+/**
+ * Members login page catid
+ */
+define('MEMBERS_CATEGORY', $applicationConfig->memberdb->login_page);
+/**
+ * Toolbox page id for splash page when members log into members only area
+ */
+define('MEMBERS_ONLY_HOME_PAGE', $applicationConfig->memberdb->members->home_page);
+/**
+ * Toolbox page id for edit profile form in members only area
+ */
+define('MEMBERS_PROFILE_FORM_PAGE', $applicationConfig->memberdb->members->profile_form_page);
+/**
+ * Toolbox page id for coupon module in members only area
+ */
+define('MEMBERS_COUPONS_PAGE', $applicationConfig->memberdb->members->coupons->page);
+/**
+ * Toolbox page id for coupon module in members only area
+ */
+define('MEMBERS_COUPON_NOTIFICATION_EMAIL', $applicationConfig->memberdb->members->coupons->notification_email);
+/**
+ * Toolbox page id for events module in members only area
+ */
+define('MEMBERS_EVENTS_PAGE', $applicationConfig->memberdb->members->events_page);
+define('MEMBERS_EVENTS_NOTIFICATION_EMAIL', $applicationConfig->memberdb->members->events->notification_email);
+/**
+ * Toolbox page id for reports module in members only area
+ */
+define('MEMBERS_REPORTS_PAGE', $applicationConfig->memberdb->members->reports_page);
+/**
+ * Toolbox page id for leads module in members only area
+ */
+define('MEMBERS_LEADS_PAGE', $applicationConfig->memberdb->members->leads_page);
+/**
+ * Toolbox page id for member trip planner page
+ */
+define('MEMBER_SESSION_LIST', $applicationConfig->memberdb->session->list);
+/**
+ * The id of the page for the planner output
+ */
+define('MEMBER_SESSION_PAGE', $applicationConfig->memberdb->session->page);
+/**
+ * The id of the page for the create account form
+ */
+define('MEMBER_SESSION_FORM', $applicationConfig->memberdb->session->form);
+/**
+ * Members uses streamsend module?
+ */
+define('MEMBER_STREAMSEND_API', $applicationConfig->memberdb->streamsend->application);
+/**
+ * Site has Press/Newsletter?
+ */
+define('PRESS_DB', $applicationConfig->news->application);
+/**
+ * Toolbox Page id for the news output
+ */
+define('PRESS_PAGE_ID', $applicationConfig->news->page);
+/**
+ * Site has home page news Press/Newsletter?
+ */
+define('HOME_NEWS', $applicationConfig->news->home);
+/**
+ * Site has photo gallery?
+ */
+define('PHOTO_GALLERY', $applicationConfig->photo_gallery->application);
+/*
+ * Site has rotating images application?
+ */
+define('ROTATING_IMAGES', $applicationConfig->rotating_images->application);
+/**
+ * Grab weather feed for site?
+ */
+define('WEATHER', $applicationConfig->weather->application);
+define('EMPLOYMENT', $applicationConfig->employment->application);
+define('VIDEOS', $applicationConfig->videos->application);
+define('SEASONS', $applicationConfig->seasons->application);
+define('GLM_BLOCKS', $applicationConfig->blocks->application);
+/**
+ * Grab weather feed for site?
+ */
+define('GIFT_CERTIFICATES', $applicationConfig->gift_certificates->application);
+/**
+ * Site is Mobile enabled
+ */
+define('MOBILE_SITE', $siteConfig->mobile_site);
+
+$pathParts = pathinfo($BASE_PATH);
+/**
+ * Server directory ie (www.gaslightmedia.com, demo.gaslightmedia.com)
+ */
+define('SERVER_DIRECTORY', $pathParts['basename']);
+
+$isSecurePage = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on');
+/**
+ * absolute url path for site
+ */
+define('SITE_URL', $serverConfig->site_url);
+/**
+ * coupon url used in printed PDF coupons
+ */
+define('COUPON_SITE_URL', $serverConfig->coupon_site_url);
+/**
+ * Url for the CDN-Hosted Javascript and css for Jquery
+ */
+$jqueryUrl = ($isSecurePage) ? 'https://' : 'http://';
+define('JQUERY_CDN_JS', '//code.jquery.com/jquery-1.11.0.min.js');
+define('JQUERY_CDN_MIGRATE_JS', '//code.jquery.com/jquery-migrate-1.2.1.js');
+define(
+    'JQUERY_UI_CDN_CSS',
+    $jqueryUrl . 'code.jquery.com/ui/1.10.4/themes/smoothness/jquery-ui.css'
+);
+define(
+    'JQUERY_UI_CDN_JS',
+    $jqueryUrl . 'code.jquery.com/ui/1.10.4/jquery-ui.min.js'
+);
+/**
+ * Link URL to access mobile site home page
+ */
+define('MOBILE_LINK', $serverConfig->mobile->link);
+define('HAVE_MOBILE_HOSTNAME', $serverConfig->mobile->hostname);
+/**
+ * API key for google search AJAX
+ */
+define('GSEARCH_API', $serverConfig->google->search->key);
+/**
+ * site restriction to use for google searches
+*/
+define('GSEARCH_SITE_RESTRICTION', $serverConfig->google->search->site_restriction);
+/**
+ * API key for google maps
+ */
+define('GMAPS_API', $serverConfig->google->maps->key);
+/**
+ * URI to the common GLM application repository
+ */
+define('GLM_APP_BASE_URL', $serverConfig->app->base_url);
+define('GLM_APP_BASE_SECURE_URL', $serverConfig->app->base_secure_url);
+/**
+ * Who to send email address to on production servers when fatal error is encountered
+ */
+define('ERROR_EMAIL_ADDRESS', $serverConfig->error->email);
+if ($serverConfig->error->reporting instanceof Traversable) {
+
+    $values = array();
+    foreach ($serverConfig->error->reporting as $value) {
+        $values[] = $value;
+    }
+    $lambdaFunction = create_function('$a, $b', 'return $a ^ $b;');
+    $level = array_reduce($values, $lambdaFunction, 0);
+} else {
+    $level = $serverConfig->error->reporting;
+}
+/**
+ * Turn off all error reporting
+ */
+error_reporting($level);
+/**
+ * Don't print errors to the screen as part of output
+ */
+ini_set('display_errors', $serverConfig->error->display);
+$fileServerUrl = $isSecurePage
+    ? $serverConfig->file_server->secure
+    : $serverConfig->file_server->unsecure;
+/**
+ * URL of which file server to use
+ */
+define('FILE_SERVER_URL', $fileServerUrl);
+/**
+ * Type of Log subclass to use
+ */
+define('ERROR_LOG_TYPE', $serverConfig->error->type);
+/**
+ * Constant of log store to use
+ */
+define('ERROR_LOG_NAME', constant($serverConfig->error->name));
+/**
+ * Identity reported to the log system
+ */
+define('ERROR_LOG_IDENT', $serverConfig->error->ident);
+/**
+ * Log messages up to and including this level
+ */
+define('ERROR_LOG_LEVEL', constant($serverConfig->error->level));
+/**
+ * additional configuration for ERROR_LOG_TYPE subclass
+ */
+$ERROR_LOG_CONF = array(
+    'destination' => $serverConfig->error->conf->path
+                . constant($serverConfig->error->conf->server)
+                . "/{$serverConfig->error->conf->file}",
+    'lineFormat'  => $serverConfig->error->conf->line_format,
+);
+
+$BASE_SECURE_URL = "https://$base_url";
+/**
+ * Url used for the secure site
+ *
+ * For some sites it is necessary to change BASE_URL when in secure mode
+ */
+define('BASE_SECURE_URL', "$BASE_SECURE_URL/");
+/**
+ * Media Url Base is https if $_SERVER['HTTPS'] is on
+ * http otherwise
+ */
+$mediaBaseUrl
+    = ($isSecurePage)
+    ? BASE_SECURE_URL
+    : BASE_URL;
+define('MEDIA_BASE_URL', $mediaBaseUrl);
+/**
+ * Media Url Base is https if $_SERVER['HTTPS'] is on
+ * http otherwise
+ */
+$mediaAppBaseUrl
+    = ($isSecurePage)
+    ? GLM_APP_BASE_SECURE_URL
+    : GLM_APP_BASE_URL;
+define('MEDIA_APP_BASE_URL', $mediaAppBaseUrl);
+define('CKEDITOR_JS', MEDIA_APP_BASE_URL . 'ckeditor/4.4.4/ckeditor.js');
+// This needs to be set to the real url ie. http://demo.gaslightmedia.com
+if ($isSecurePage) {
+    $BASE_URL = "http://$base_url";
+}
+
+// Set CURRENT_BASE_URL according to whether site is secure or not.
+if ($isSecurePage) {
+    define('CURRENT_BASE_URL', $BASE_SECURE_URL.'/');
+} else {
+    define('CURRENT_BASE_URL', $BASE_URL.'/');
+}
+
+/**
+ * Database Connection String
+ */
+define('CONN_STR', "host={$serverConfig->database->params->host} user={$serverConfig->database->params->username} dbname={$serverConfig->database->params->dbname}");
+/**
+ * DSN Connection String
+ */
+define('DSN', "{$serverConfig->database->adapter}://{$serverConfig->database->params->username}@{$serverConfig->database->params->host}/{$serverConfig->database->params->dbname}");
+/**
+ * Used for error reporting
+ *
+ * When in development mode, more info is displayed if errors occur.
+ */
+define('DEVELOPMENT', $serverConfig->development);
+/**
+ * Site owners email address
+ */
+define('OWNER_EMAIL', $serverConfig->email->owner);
+/**
+ * email for contact form
+ */
+define('CONTACT_EMAIL', $serverConfig->email->contact);
+/**
+ * Add Your Event Form Email
+ */
+define('ADD_EVENT_EMAIL', $serverConfig->email->addEventForm);
+/**
+ * email for enews signup
+ */
+define('ENEWS_EMAIL', $serverConfig->email->enews);
+/**
+ * email for visitor guide form
+ */
+define('VISITOR_GUIDE_EMAIL', $serverConfig->email->visitor_guide);
+/**
+ * email for Employment Application Form
+ */
+define('EMP_APP_EMAIL', $serverConfig->email->applicationForm);
+/**
+ * From header for admin/Contact mailout.
+ */
+define('FROM_NEWS_EMAIL', $serverConfig->email->from_news);
+define('SERVER_FROM_EMAIL', $serverConfig->email->server_from);
+/**
+ * From headers for admin/MContact
+ */
+define('FROM_MEMBER_NEWS_EMAIL', $serverConfig->email->from_member_news);
+/**
+ * Reply header for admin/MContact
+ */
+define('REPLY_MEMBER_NEWS_EMAIL', $serverConfig->email->reply_member_news);
+/**
+ * which email address should the notifications go to when a member
+ */
+define('MEMBER_RECORD_UPDATES_ADVISOR', $serverConfig->email->member_record_updates_advisor);
+/**
+ * Reply-To email used in the members forgot password email
+ */
+define('MEMBER_FORGOT_PASSWORD_EMAIL_REPLY_TO', $serverConfig->email->member_forgot_password_email_reply_to);
+/**
+ * This is where the first email goes.
+ * To notify the site owner that gift cert is ordered
+ */
+define('GIFTCERT_EMAIL', $serverConfig->email->giftcert);
+/**
+ * Used as the From address
+ */
+define('GIFTCERT_FROM_EMAIL', $serverConfig->email->giftcert_from);
+/**
+ * This email containes sensitive info and must be upnorth.net address
+ */
+define('GIFTCERT_SECURE_EMAIL', $serverConfig->email->giftcert_secure);
+/**
+ * in admin/Contact mailout.phtml file
+ * Reply-To: header
+ */
+define('REPLY_TO', $serverConfig->email->reply_to);
+/**
+ * Dead email address for forms w/ one-way communication
+ */
+define('DO_NOT_REPLY_EMAIL', $serverConfig->email->do_not_reply);
+/**
+ * The email address to send the banner reports to
+ * for the site admin
+ */
+define('BANNER_REPORTS_ADMIN_EMAIL', $serverConfig->email->banner_reports_admin);
+/**
+ * The email address to send banner expiration notices to
+ */
+define('EXPIRING_BANNER_NOTIFICATION_EMAIL', $serverConfig->email->expiring->banner_notification);
+/**
+ * Authorize.net processing parameters
+ * ALSO SEE "Authorize.Net Configuration" SECTION BELOW CONDITIONAL SETTINGS
+ */
+define('AUTH_TEST', $serverConfig->auth->test);
+define('AUTH_SEND_CONF', $serverConfig->auth->send_conf);
+define('AUTH_MERCHANT_EMAIL', $serverConfig->auth->merchant_email);
+define('AUTH_DEBUG', $serverConfig->auth->debug);
+/**
+ * Enable Ticketing System
+ */
+define('TICKETING', $applicationConfig->ticketing->available);
+define("TICKETS_CAT_ID", $applicationConfig->ticketing->catid);
+define("TICKETS_CAT_SEO", $applicationConfig->ticketing->cat_seo);
+
+define('LIBJS_ENV', $serverConfig->resources->environment->javascript);
+define('CSS_ENV', $serverConfig->resources->environment->css);
+
+if ($version = filter_var($serverConfig->resources->version, FILTER_VALIDATE_INT)) {
+    //    do nothing - its already set
+} else {
+    $version = mt_rand();
+}
+/**
+ * Site Versioning for cachebusting
+ */
+define('VERSION', $version);
+
+$secureUrl = $isSecurePage ? BASE_SECURE_URL : BASE_URL;
+
+$forceCompile = false;//(DEVELOPMENT || $isSecurePage);
+
+$flexyOptions    = array(
+    'templateDir'  => BASE . 'templates',
+    'compileDir'   => BASE . 'templates/compiled',
+    'forceCompile' => $forceCompile,
+    'url_rewrite'  => "baseurl/::".BASE_URL.",basesecureurl/::$secureUrl,glmappbaseurl/::" . GLM_APP_BASE_URL,
+    'allowPHP'     => true,
+);
+
+$cacheOptions = array(
+    'cacheDir'           => BASE . 'cache/',
+    'writeControl'       => true,
+    'lifeTime'           => (DEVELOPMENT) ? 0 : null,
+    'readControl'        => true,
+    'fileNameProtection' => false,
+    'readControlType'    => 'md5',
+);
+
+//  Global arrays to store resources for apps.
+$topScripts    = array();
+$bottomScripts = array();
+$styleSheets   = array();
+
+// help guide base (depreciated)
+define('HELP_BASE', 'help/');
+// uploads directory path
+define('UP_BASE', BASE . 'uploads/');
+// the images url path
+define('IMG_BASE', MEDIA_BASE_URL . 'images/');
+
+// Toolbox image paths
+define('ORIGINAL_PATH', BASE . 'images/original/');
+define('RESIZED_PATH', BASE . 'images/resized/');
+define('MIDSIZED_PATH', BASE . 'images/midsized/');
+define('THUMB_PATH', BASE . 'images/thumb/');
+
+//  Amenity icon path
+define('AMENITY_PATH', BASE . 'images/amenities/');
+
+$imageURLBase = $isSecurePage ? BASE_SECURE_URL : BASE_URL;
+
+// Toolbox image URLS
+// Now using new Image Server
+/**
+ * Now using new Image Server
+ * http://is0.gaslightmedia.com/admin
+ * setup new owner with owner_id and owner_pw
+ * MUST BE DONE BEFORE uploading any images
+ */
+/**
+* IS_OWNER_ID owner_id from image server config
+ */
+define('IS_OWNER_ID', $serverConfig->file_server->owner_id);
+/**
+ * IS_OWNER_PW owner_pw from image server config
+ */
+define('IS_OWNER_PW', $serverConfig->file_server->owner_password);
+/**
+ * Toolbox image URLS
+ * NOTE: these don't change
+ * There are 4 global "image styles"
+ * original = used for original images no processing
+ * tbs1     = used to be resized
+ * tbs2     = used to be midsized
+ * tbs3     = used to be thumb
+ * check on http://is0.gaslightmedia.com/admin under owner _SYSTEM_
+ * for their sizes
+ * if you need a different size you'll need to create image style for
+ * your owner with new sizes just use one of set style names
+ * tbs1,tbs2,tbs3 (do this before uploading the image will help)
+ */
+define('ORIGINAL', FILE_SERVER_URL.IS_OWNER_ID."/original/");
+define('RESIZED', FILE_SERVER_URL.IS_OWNER_ID."/tbs1/");
+define('MIDSIZED', FILE_SERVER_URL.IS_OWNER_ID."/tbs2/");
+define('THUMB', FILE_SERVER_URL.IS_OWNER_ID."/tbs3/");
+
+//  Toolbox image rules
+define('TOOLBOX_ORIGINAL', ORIGINAL);
+define('TOOLBOX_RESIZED', RESIZED);
+define('TOOLBOX_MIDSIZED', MIDSIZED);
+define('TOOLBOX_THUMB', THUMB);
+
+/**
+ * These are defines for the photo gallery images
+ * NOTICE these are now on image server
+ * There are 2 global "image styles"
+ * pgs1 = used to be photo-large
+ * pgs2 = used to be photo-small
+ * check on http://is0.gaslightmedia.com/admin under owner _SYSTEM_
+ * for their sizes
+ */
+define('UPLOADED_FILES', FILE_SERVER_URL.IS_OWNER_ID.'/');
+
+define('PHOTO_LARGE_URL', FILE_SERVER_URL.IS_OWNER_ID."/pgs1/");
+define('PHOTO_SMALL_URL', FILE_SERVER_URL.IS_OWNER_ID."/pgs2/");
+
+/**
+ * Home page headlines thumbnail
+ */
+define('HOMEPAGE_HEADLINE_THUMB', FILE_SERVER_URL.IS_OWNER_ID."/homepageHeadlineThumb/");
+define('HEADLINE_THUMB', FILE_SERVER_URL.IS_OWNER_ID.'/headline/');
+
+//  Coupon Image rules
+define('COUPON_THUMB', FILE_SERVER_URL.IS_OWNER_ID.'/couponThumb/');
+
+//  Rotating Image rules
+define('ROTATING_IMAGE_THUMB', FILE_SERVER_URL.IS_OWNER_ID.'/rotatingImagesThumb/');
+define('ROTATING_IMAGE_RESIZED', FILE_SERVER_URL.IS_OWNER_ID.'/rotatingImagesResized/');
+
+//  Member image rules
+define('MEMBER_ORIGINAL', FILE_SERVER_URL.IS_OWNER_ID."/original/");
+define('MEMBER_RESIZED', FILE_SERVER_URL.IS_OWNER_ID."/memberResized/");
+define('MEMBER_MIDSIZED', FILE_SERVER_URL.IS_OWNER_ID."/memberMidsized/");
+define('MEMBER_THUMB', FILE_SERVER_URL.IS_OWNER_ID."/memberThumb/");
+define('MEMBER_PHOTOS', FILE_SERVER_URL.IS_OWNER_ID."/memberPhotos/");
+define('MEMBER_PACKAGES', FILE_SERVER_URL.IS_OWNER_ID."/memberResized/");
+define('MEMBER_GOOGLE_MAP', FILE_SERVER_URL.IS_OWNER_ID."/memberGoogleMap/");
+define('MEMBER_UPLOADED_FILES', BASE . 'uploads/member_files/');
+define('TRIP_PLANNER_MAP_IMG', FILE_SERVER_URL.IS_OWNER_ID."/tripPlannerMapImg/");
+
+//  Banner image rules
+define('BANNER_ORIGINAL', FILE_SERVER_URL.IS_OWNER_ID.'/original/');
+define('HORIZONTAL_BANNER', FILE_SERVER_URL.IS_OWNER_ID.'/horizontalBanner/');
+define('HORIZONTAL_BANNER_THUMB', FILE_SERVER_URL.IS_OWNER_ID.'/horizontalBannerThumb/');
+define('VERTICAL_BANNER', FILE_SERVER_URL.IS_OWNER_ID.'/verticalBanner/');
+define('VERTICAL_BANNER_THUMB', FILE_SERVER_URL.IS_OWNER_ID.'/verticalBannerThumb/');
+
+//  Press image rules
+define('PRESS_ORIGINAL', TOOLBOX_ORIGINAL);
+define('PRESS_RESIZED', TOOLBOX_RESIZED);
+define('PRESS_MIDSIZED', TOOLBOX_MIDSIZED);
+define('PRESS_THUMB', TOOLBOX_THUMB);
+
+//  Event image rules
+define('EVENT_ORIGINAL', TOOLBOX_ORIGINAL);
+define('EVENT_RESIZED', TOOLBOX_RESIZED);
+define('EVENT_MIDSIZED', TOOLBOX_MIDSIZED);
+define('EVENT_THUMB', TOOLBOX_THUMB);
+
+//  Form image rules
+define('FORM_ORIGINAL', TOOLBOX_ORIGINAL);
+define('FORM_RESIZED', TOOLBOX_RESIZED);
+define('FORM_MIDSIZED', TOOLBOX_MIDSIZED);
+define('FORM_THUMB', TOOLBOX_THUMB);
+
+//  CKImage image rules
+/**
+ * This is the rule used to create the Maximum Allowed Size image
+ * we allow for CKEditor Images, which is (700x500)
+ *
+ * If the user resizes (edits) the image, it can be smaller
+ */
+define('CKIMAGE', FILE_SERVER_URL.IS_OWNER_ID."/CKImage/");
+define('CKIMAGE_ORIGINAL', TOOLBOX_ORIGINAL);
+define('CKIMAGE_RESIZED', TOOLBOX_RESIZED);
+define('CKIMAGE_MIDSIZED', TOOLBOX_MIDSIZED);
+define('CKIMAGE_THUMB', TOOLBOX_THUMB);
+/**
+ * This is the thumbnail image used in the GLM Image Browser
+ * attached to the new CKEditor
+ */
+define('IMAGE_MANAGER', FILE_SERVER_URL.IS_OWNER_ID."/imgMgr/");
+
+//  Amenity image URLS
+define('AMENITY_THUMB', "{$imageURLBase}images/amenities/");
+//  Icon image URLS
+define('ICONS_URL', "{$imageURLBase}images/");
+
+//
+//  Authorize.Net Configuration
+//
+
+/**
+ * Curl executable
+ */
+define('AUTH_CURL', '/usr/bin/curl');
+/**
+ * Authorization URL
+ */
+define('AUTH_URL', 'https://secure.authorize.net/gateway/transact.dll');
+/**
+ * "TRUE" (use Authorize.net test mode), "FALSE", "LOCAL_TEST", "LOCAL_TEST_ERROR"
+ * Must be "FALSE" for production! - When using "LOCAL_TEST" only approves card '0011001100110011'
+ */
+//define('SI_AUTH_TEST', 'TRUE');
+/**
+ * Authorize.Net Customer Login             -- UNIQE FOR EACH CUSTOMER --
+ */
+define('AUTH_LOGIN', 'xxxxxxx');
+/**
+ * Authorize.Net Customer Transaction Key   -- UNIQE FOR EACH CUSTOMER --
+ */
+define('AUTH_TRAN_KEY', 'xxxxxxxxxxxxx');
+/**
+ * TRUE to have customer's receive E-Mail confirmation
+ */
+//define('AUTH_SEND_CONF', 'FALSE');
+/**
+ * TRUE to have merchant receive E-Mail convirmation of customer charges
+ */
+//define('AUTH_MERCHANT_EMAIL', 'TRUE');
+/**
+ * MD5 Hash secret used by Authorize.Net to generate MD5 response verification data
+ * If empty - don't check MD5 response
+ */
+define('AUTH_SECRET', '');
+
+// Transaction status types
+
+/**
+ * Unknown state
+ */
+define('AUTH_STATUS_UNKNOWN', 0);
+/**
+ * Declined by Authorize.net
+ */
+define('AUTH_STATUS_DECLINED', 1);
+/**
+ * Authorize.Net error
+ */
+define('AUTH_STATUS_ERROR', 2);
+/**
+ * Approved by Authorize.Net
+ */
+define('AUTH_STATUS_APPROVED', 10);
+/**
+ * Card manually processed
+ */
+define('AUTH_STATUS_MANUAL', 11);
+/**
+ * Paid by Check
+ */
+define('AUTH_STATUS_CHECK', 12);
+/**
+ * On Account
+ */
+define('AUTH_STATUS_ACCOUNT', 13);
+/**
+ * Paid by Other means
+ */
+define('AUTH_STATUS_OTHER_PAID', 14);
+/**
+ * Comp, no charge
+ */
+define('AUTH_STATUS_COMP', 15);
+
+$auth_status_list
+    = AUTH_STATUS_UNKNOWN.'^Unknown Card Status~'
+    . AUTH_STATUS_DECLINED.'^Declined by AuthorizeNet~'
+    . AUTH_STATUS_ERROR.'^AuthorizeNet Approval Error~'
+    . AUTH_STATUS_APPROVED.'^Approved by AuthorizeNet~'
+    . AUTH_STATUS_MANUAL.'^Card Approved Manually~'
+    . AUTH_STATUS_CHECK.'^Paid by Check~'
+    . AUTH_STATUS_ACCOUNT.'^Applied to Account~'
+    . AUTH_STATUS_OTHER_PAID.'^Paid by other means~'
+    . AUTH_STATUS_COMP.'^Comp, no charge';
+
+/*
+ *  Load other applications
+ */
+
+if (defined('TICKETING') && TICKETING) {
+    include_once 'ticketing/config.inc';
+}
+
+// [status_US] array of states and their abbr.
+$states_US['']   = '-- Select --';// {{{
+$states_US['AL'] = 'Alabama';
+$states_US['AK'] = 'Alaska';
+$states_US['AZ'] = 'Arizona';
+$states_US['AR'] = 'Arkansas';
+$states_US['CA'] = 'California';
+$states_US['CO'] = 'Colorado';
+$states_US['CT'] = 'Connecticut';
+$states_US['DE'] = 'Delaware';
+$states_US['DC'] = 'District of Columbia';
+$states_US['FL'] = 'Florida';
+$states_US['GA'] = 'Georgia';
+$states_US['HI'] = 'Hawaii';
+$states_US['ID'] = 'Idaho';
+$states_US['IL'] = 'Illinois';
+$states_US['IN'] = 'Indiana';
+$states_US['IA'] = 'Iowa';
+$states_US['KS'] = 'Kansas';
+$states_US['KY'] = 'Kentucky';
+$states_US['LA'] = 'Louisiana';
+$states_US['ME'] = 'Maine';
+$states_US['MD'] = 'Maryland';
+$states_US['MA'] = 'Massachusetts';
+$states_US['MI'] = 'Michigan';
+$states_US['MN'] = 'Minnesota';
+$states_US['MS'] = 'Mississppi';
+$states_US['MO'] = 'Missouri';
+$states_US['MT'] = 'Montana';
+$states_US['NE'] = 'Nebraska';
+$states_US['NV'] = 'Nevada';
+$states_US['NH'] = 'New Hampshire';
+$states_US['NJ'] = 'New Jersey';
+$states_US['NM'] = 'New Mexico';
+$states_US['NY'] = 'New York';
+$states_US['NC'] = 'North Carolina';
+$states_US['ND'] = 'North Dakota';
+$states_US['OH'] = 'Ohio';
+$states_US['OK'] = 'Oklahoma';
+$states_US['OR'] = 'Oregon';
+$states_US['PA'] = 'Pennsylvania';
+$states_US['RI'] = 'Rhode Island';
+$states_US['SC'] = 'South Carolina';
+$states_US['SD'] = 'South Dakota';
+$states_US['TN'] = 'Tennessee';
+$states_US['TX'] = 'Texas';
+$states_US['UT'] = 'Utah';
+$states_US['VT'] = 'Vermont';
+$states_US['VA'] = 'Virginia';
+$states_US['WA'] = 'Washington';
+$states_US['WV'] = 'West Virginia';
+$states_US['WI'] = 'Wisconsin';
+$states_US['WY'] = 'Wyoming';// }}}
+
+// [states] extended states array
+$states['AB'] = 'Alberta';// {{{
+$states['AS'] = 'American Samoa';
+$states['BC'] = 'British Columbia';
+$states['DC'] = 'District of Columbia';
+$states['FM'] = 'Federated States of Micronesia';
+$states['GU'] = 'Guam';
+$states['MB'] = 'Manitoba';
+$states['MH'] = 'Marshall Islands';
+$states['NB'] = 'New Brunswick';
+$states['NF'] = 'Newfoundland';
+$states['MP'] = 'Northern Mariana Islands';
+$states['NT'] = 'Northwest Territories';
+$states['NS'] = 'Nova Scotia';
+$states['ON'] = 'Ontario';
+$states['PW'] = 'Palau';
+$states['PE'] = 'Prince Edward Island';
+$states['PR'] = 'Puerto Rico';
+$states['QC'] = 'Quebec';
+$states['SK'] = 'Saskatchewan';
+$states['VI'] = 'Virgin Islands';
+$states['YT'] = 'Yukon';// }}}
+
+//  Merge the 50 US states together with some of the extended
+//  provinces / territories and then sort them on their keys
+//  Then add the remaining countries and areas at the end
+//  of the array.
+$states = $states_US + $states;
+ksort($states);
+$states = $states + array(// {{{
+    'Asia'          => 'Asia',
+    'Australia'     => 'Australia',
+    'Bahamas'       => 'Bahamas',
+    'Caribbean'     => 'Caribbean',
+    'Costa Rica'    => 'Costa Rica',
+    'South America' => 'South America',
+    'South Africa'  => 'South Africa',
+    'Europe'        => 'Europe',
+    'Mexico'        => 'Mexico',
+);// }}}
+
+// Libraries
+// Replaced with the actual functions instead of includes (2001-12-14)
+
+$cp1252_map = array(
+    "\xc2\x80" => "\xe2\x82\xac",   //  EURO SIGN
+    "\xc2\x82" => "\xe2\x80\x9a",   //  SINGLE LOW-9 QUOTATION MARK
+    "\xc2\x83" => "\xc6\x92",       //  LATIN SMALL LETTER F WITH HOOK
+    "\xc2\x84" => "\xe2\x80\x9e",   //  DOUBLE LOW-9 QUOTATION MARK
+    "\xc2\x85" => "\xe2\x80\xa6",   //  HORIZONTAL ELLIPSIS
+    "\xc2\x86" => "\xe2\x80\xa0",   //  DAGGER
+    "\xc2\x87" => "\xe2\x80\xa1",   //  DOUBLE DAGGER
+    "\xc2\x88" => "\xcb\x86",       //  MODIFIER LETTER CIRCUMFLEX ACCENT
+    "\xc2\x89" => "\xe2\x80\xb0",   //  PER MILLE SIGN
+    "\xc2\x8a" => "\xc5\xa0",       //  LATIN CAPITAL LETTER S WITH CARON
+    "\xc2\x8b" => "\xe2\x80\xb9",   //  SINGLE LEFT-POINTING ANGLE QUOTATION
+    "\xc2\x8c" => "\xc5\x92",       //  LATIN CAPITAL LIGATURE OE
+    "\xc2\x8e" => "\xc5\xbd",       //  LATIN CAPITAL LETTER Z WITH CARON
+    "\xc2\x91" => "\xe2\x80\x98",   //  LEFT SINGLE QUOTATION MARK
+    "\xc2\x92" => "\xe2\x80\x99",   //  RIGHT SINGLE QUOTATION MARK
+    "\xc2\x93" => "\xe2\x80\x9c",   //  LEFT DOUBLE QUOTATION MARK
+    "\xc2\x94" => "\xe2\x80\x9d",   //  RIGHT DOUBLE QUOTATION MARK
+    "\xc2\x95" => "\xe2\x80\xa2",   //  BULLET
+    "\xc2\x96" => "\xe2\x80\x93",   //  EN DASH
+    "\xc2\x97" => "\xe2\x80\x94",   //  EM DASH
+
+    "\xc2\x98" => "\xcb\x9c",       //  SMALL TILDE
+    "\xc2\x99" => "\xe2\x84\xa2",   //  TRADE MARK SIGN
+    "\xc2\x9a" => "\xc5\xa1",       //  LATIN SMALL LETTER S WITH CARON
+    "\xc2\x9b" => "\xe2\x80\xba",   //  SINGLE RIGHT-POINTING ANGLE QUOTATION
+    "\xc2\x9c" => "\xc5\x93",       //  LATIN SMALL LIGATURE OE
+    "\xc2\x9e" => "\xc5\xbe",       //  LATIN SMALL LETTER Z WITH CARON
+    "\xc2\x9f" => "\xc5\xb8",       //  LATIN CAPITAL LETTER Y WITH DIAERESIS
+);
+
+if ($includeFunctions) {
+    /**
+     * all setup function in setup_functions.phtml
+     */
+    require_once BASE_PATH . "setup_functions.phtml";
+}
+
+/**
+ * Chuck and John H. have setup site monitoring script that will hit a
+ * file every so often and make a db connection to test if that database
+ * connection is good. The aim here it to monitor the sites to see if
+ * they go down and page Chuck/John and Dave We'll check for the setup
+ * script in the root of the site if it is not there and the database
+ * has been setup in this file it will create the file.
+ */
+$SiteCheckFile = BASE . 'GLM_site_check.phtml';
+if (!file_exists($SiteCheckFile) && $_ENV['GLM_HOST_ID'] == 'PRODUCTION') {
+    // get the dbname for the connection
+    $siteCheckArray = explode(" ", CONN_STR);
+    foreach ($siteCheckArray as $scaVar) {
+        parse_str($scaVar, $parsedSettings);
+        if ($parsedSettings['dbname']) {
+            $dbname = $parsedSettings['dbname'];
+        }
+    }
+    if ($dbname) {
+        // we're going to assume that the connection
+        // will be for ds3.gaslightmedia.com
+        $fileContent = "<?php \n"
+            . "/**\n"
+            . " * GLM Standard Site Monitoring Target\n"
+            . " */\n\n"
+            . "define('HOST',   'ds4.gaslightmedia.com');\n"
+            . "define('USER',   'nobody');\n"
+            . "define('DBNAME', '$dbname');\n"
+            . "\n"
+            . "// End of parameters to set for each site\n"
+            . "include '/var/www/templates/Global_site_check.phtml';\n"
+            . '?' . '>';
+        file_put_contents($SiteCheckFile, $fileContent);
+    }
+}
diff --git a/setup_functions.phtml b/setup_functions.phtml
new file mode 100644 (file)
index 0000000..6eaec61
--- /dev/null
@@ -0,0 +1,1223 @@
+<?php
+/** @header Gaslight Media Toolbox
+  functions that were in the functions.inc
+ */
+    //  {{{ setSearchPath()
+
+    /**
+     * This is a copy from the DB singletons
+     * Toolkit_Database.php <-- Primary
+     * classes/class_db.inc <-- Secondary - soon to be removed
+     *
+     * the following packages depend on this function. after all are updated
+     * this function can/should be removed
+     *
+     * Admin/Postcards
+     */
+    function setSearchPath(&$dbh)
+    {
+        //  Add schemas to search path.
+        $format = "
+            SELECT set_config(
+                'search_path',
+                current_setting('search_path') || ',%s',
+                false
+            )";
+
+        $sql = sprintf($format, 'toolbox');
+        db_exec($dbh, $sql);
+
+        $sql = sprintf($format, 'ckimages');
+        db_exec($dbh, $sql);
+
+        if (defined('CONTACT_DB') && CONTACT_DB) {
+            //  define banner search path
+            $sql = sprintf($format, 'contacts');
+            db_exec($dbh, $sql);
+        }
+
+        if (defined('BANNERS') && BANNERS) {
+            //  define banner search path
+            $sql = sprintf($format, 'banners');
+            db_exec($dbh, $sql);
+        }
+
+        if (defined('ROTATING_IMAGES') && ROTATING_IMAGES) {
+            //  define rotating images search path
+            $sql = sprintf($format, 'rotatingImages');
+            db_exec($dbh, $sql);
+        }
+
+        if (defined('PHOTO_GALLERY') && PHOTO_GALLERY) {
+            //  define phot gallery search path
+            $sql = sprintf($format, 'photos');
+            db_exec($dbh, $sql);
+        }
+
+        if (defined('EVENT_DB') && EVENT_DB) {
+            //  define members search path
+            $sql = sprintf($format, 'events');
+            db_exec($dbh, $sql);
+        }
+
+        if (defined('POSTCARD_DB') && POSTCARD_DB) {
+            //  define members search path
+            $sql = sprintf($format, 'postcards');
+            db_exec($dbh, $sql);
+        }
+
+        if (defined('MEMBERS_DB') && MEMBERS_DB) {
+            //  define members search path
+            $sql = sprintf($format, 'members');
+            db_exec($dbh, $sql);
+        }
+
+        if (defined('COUPONS') && COUPONS) {
+            //  define coupon search path
+            $sql = sprintf($format, 'coupons');
+            db_exec($dbh, $sql);
+        }
+    }
+
+    //  }}}
+
+    /**
+     * is_utf8
+     *
+     * @param mixed $string
+     * @access public
+     * @return void
+     */
+    function is_utf8($string)
+    {
+        return preg_match('/^([\x00-\x7f]|[\xc2-\xdf][\x80-\xbf]|\xe0[\xa0-\xbf][\x80-\xbf]|[\xe1-\xec][\x80-\xbf]{2}|\xed[\x80-\x9f][\x80-\xbf]|[\xee-\xef][\x80-\xbf]{2}|f0[\x90-\xbf][\x80-\xbf]{2}|[\xf1-\xf3][\x80-\xbf]{3}|\xf4[\x80-\x8f][\x80-\xbf]{2})*$/', $string) === 1;
+    }
+
+    /**
+     * cp1252_to_utf8
+     *
+     * @param mixed $str
+     * @access public
+     * @return void
+     */
+    function cp1252_to_utf8($str)
+    {
+        global $cp1252_map;
+        return  strtr(utf8_encode($str), $cp1252_map);
+    }
+
+    /**
+     * myEncode
+     *
+     * @param mixed $string
+     * @access public
+     * @return void
+     */
+    function myEncode($string)
+    {
+        if (is_utf8($string)) {
+            return $string;
+        } else {
+            return cp1252_to_utf8($string);
+        }
+    }
+
+    /**
+     * CreditVal : CreditVal Checks for a valid credit card number doing Luhn check, if no
+     card type is given, attempts to guess. Then, if a list of
+     accepted types is given, determines whether or not we'll
+     accept it
+     * @param $Num: Credit Card Number
+     * @param $Name = '': Type of Card
+     * @param $Accepted='' : Accepted array
+     *
+     * @return bool
+     * @access
+     **/
+    function CreditVal($Num, $Name = '', $Accepted = '')
+    {
+        $Name = strtolower($Name);
+        $Accepted = strtolower($Accepted);
+        $GoodCard = 1;
+        $Num = preg_replace('/[^[:digit:]]/', '', $Num);
+        switch ($Name) {
+        case 'mastercard' :
+            $GoodCard = preg_match('/^5[1-5].{14}$/', $Num);
+            break;
+
+        case 'visa' :
+            $GoodCard = preg_match('/^4.{15}$|^4.{12}$/', $Num);
+            break;
+
+        case 'americanexpress' :
+            $GoodCard = preg_match('/^3[47].{13}$/', $Num);
+            break;
+
+        case 'discover' :
+            $GoodCard = preg_match('/^6011.{12}$/', $Num);
+            break;
+
+        case 'dinerscard' :
+            $GoodCard = preg_match('/^30[0-5].{11}$|^3[68].{12}$/', $Num);
+            break;
+
+        default:
+            if (preg_match('/^5[1-5].{14}$/', $Num)) {
+                $Name = 'mastercard';
+            }
+            if (preg_match('/^4.{15}$|^4.{12}$/', $Num)){
+                $Name = 'visa';
+            }
+            if (preg_match('/^3[47].{13}$/', $Num)) {
+                $Name = 'americanexpress';
+            }
+            if (preg_match('/^6011.{12}$/', $Num)) {
+                $Name = 'discover';
+            }
+            if (preg_match('/^30[0-5].{11}$|^3[68].{12}$/', $Num)) {
+                $Name = 'dinerscard';
+            }
+            break;
+        }
+
+        // If there's a limit on card types we accept, check for it here.
+        if ($Accepted) {
+            $type_verified = false;
+            $brands = explode_trim(',', $Accepted);
+            foreach ($brands as $brand) {
+                if ($Name == $brand)
+                    $type_verified = true;
+            }
+
+            if (!$type_verified) {
+                return false;
+            }
+        }
+
+        $Num = strrev($Num);
+
+        $Total = 0;
+
+        for ($x = 0; $x < strlen($Num); ++$x) {
+            $digit = substr($Num, $x, 1);
+            if ($x / 2 != floor($x / 2)) {
+                $digit *= 2;
+                if (strlen($digit) == 2)
+                    $digit = substr($digit, 0, 1) + substr($digit, 1, 1);
+            }
+            $Total += $digit;
+        }
+        return ($GoodCard && (($Total % 10) == 0)) ? true : false;
+    }
+    // DataBase Library
+
+    /**
+     * db_connect :Creates a connection to database specified $conn_str
+     * @param $conn="" : connection string
+     *
+     * @return index or bool
+     * @access
+     **/
+    function db_connect($conn = '')
+    {
+        switch (DB_TYPE) {
+        case 'postgres' :
+            if ($conn == '')
+            $conn = CONN_STR;
+            $ret = pg_connect($conn);
+            break;
+
+        default:
+            return 0;
+            break;
+        }
+        return $ret;
+    }
+
+    /**
+     * db_close :Closes the connection to database specified by the handle dbd
+     * @param $$dbd : database handle
+     *
+     * @return bool
+     * @access
+     **/
+    function db_close($dbd)
+    {
+        switch (DB_TYPE) {
+        case 'postgres' :
+            $ret = pg_close($dbd);
+            break;
+
+        default:
+            return 0;
+            break;
+        }
+        return $ret;
+    }
+
+    /**
+      NOTICE DON'T USE THIS
+     * db_pconnect :Creates a persistant connection to database specified in $conn_str
+     * @param $$conn="" : connection string
+     *
+     * @return
+     * @access
+     **/
+    function db_pconnect($conn = '')
+    {
+        return false;
+        switch (DB_TYPE) {
+        case 'postgres' :
+            if($conn == '')
+                $conn == CONN_STR;
+            $ret = pg_pconnect($conn);
+            break;
+
+        default:
+            return 0;
+            break;
+        }
+        return $ret;
+    }
+
+    /**
+     * db_exec : Execute an SQL query
+     * @param $dbd: database handle
+     * @param $$qs : Query
+     *
+     * @return int Returns a valid result index on success 0 on failure
+     * @access
+     **/
+    function db_exec($dbd, $qs)
+    {
+        switch (DB_TYPE) {
+        case 'postgres' :
+            $ret = pg_exec($dbd, $qs);
+            break;
+
+        default:
+            return 0;
+            break;
+        }
+        return $ret;
+    }
+
+    /**
+     * db_fetch_array :Stores the data in associative indices, using the field names as
+     * keys.
+     * @param $res: valid database result index
+     * @param $i: row number
+     * @param $$type : database type
+     *
+     * @return array Returns an associative array of key-value pairs
+     * @access
+     **/
+    function db_fetch_array($res, $i, $type)
+    {
+        switch (DB_TYPE) {
+        case 'postgres' :
+            $row = pg_fetch_array($res, $i, $type);
+            break;
+
+        default:
+            return 0;
+            break;
+        }
+        return $row;
+    }
+
+    /**
+     * db_freeresult :Free result memory.
+     * @param $$res : valid database result index
+     *
+     * @return bool - Returns 1 for success 0 for failure
+     * @access
+     **/
+    function db_freeresult($res)
+    {
+        switch (DB_TYPE) {
+        case 'postgres' :
+            $ret = pg_freeresult($res);
+            break;
+
+        default:
+            return 0;
+            break;
+        }
+        return $ret;
+    }
+
+    /**
+     * db_numrows :Determine number of rows in a result index
+     * @param $$res : valid database result index
+     *
+     * @return int - Returns number of rows
+     * @access
+     **/
+    function db_numrows($res)
+    {
+        switch (DB_TYPE) {
+        case 'postgres' :
+            $ret = pg_numrows($res);
+            break;
+
+        default:
+            return -1;
+            break;
+        }
+        return $ret;
+    }
+
+    /************************************************************************
+     *                                                                      *
+     * BEGIN Auto functions                                                 *
+     *                                                                      *
+     ***********************************************************************/
+
+    /**
+     * db_auto_array :The auto function for retrieving an array based soley on a query
+     * string. This function makes the connection, does the exec, fetches
+     * the array, closes the connection, frees memory used by the result,
+     * and then returns the array
+     * @param $qs: SQL query string
+     * @param $i: row number
+     * @param $$type : PGSQL_ASSOC or PGSQL_BOTH or PSQL_NUM
+     *
+     * @return array - Returns an associative array of key-value pairs
+     * @access
+     **/
+    function db_auto_array($qs, $i, $type)
+    {
+        $dbd = db_connect();
+        if (!$dbd) {
+            return 0;
+        }
+        $res = db_exec($dbd, $qs);
+        if (!$res) {
+            return 0;
+        }
+        $row = db_fetch_array($res, $i, $type);
+        if (!db_freeresult($res)) {
+            return 0;
+        }
+        db_close($dbd);
+        return $row;
+    }
+
+    /**
+     * db_auto_exec :The auto function for executing a query.
+     * This function makes the connection, does the exec, fetches
+     * the array, closes the connection, frees memory used by the result,
+     * and then returns success (not a valid result index)
+     * @param $qs: SQL query string
+     * @param $$conn="" : Connect String
+     *
+     * @return int - Returns 1 (or oid, if available) for success 0 for failure
+     * @access
+     **/
+    function db_auto_exec($qs, $conn = '')
+    {
+        if ($conn == ''){
+            $conn = CONN_STR;
+        }
+        $dbd = db_connect($conn);
+        if (!$dbd) {
+            return 0;
+        }
+        if (!db_exec($dbd, $qs)) {
+            db_close($dbd);
+            return 0;
+        } else {
+            db_close($dbd);
+            return 1;
+        }
+    }
+
+    /**
+     * db_auto_get_data :The auto function for retrieving an array based soley on a query
+     string. This function makes the connection, does the exec, fetches
+     the array, closes the connection, frees memory used by the result,
+     and then returns the array
+     * @param $qs:  SQL query string
+     * @param $CONN_STR: Connect String
+     * @param $$fail_mode=0 : Failure Mode
+     *
+     * @return array Returns an associative array of key-value pairs
+     * @access
+     **/
+    function db_auto_get_data($qs, $conn = CONN_STR, $fail_mode = 0) {
+
+        if (!($dbd = db_connect($conn))) {
+            return false;
+        }
+
+        if (!($res = db_exec($dbd, $qs))) {
+            return false;
+        }
+
+        $totalrows = pg_NumRows($res);
+
+        for ($i = 0 ; $i < $totalrows; ++$i) {
+            $data[$i] = db_fetch_array($res, $i, PGSQL_ASSOC);
+        }
+
+        db_close($dbd);
+        return (isset($data) && $data != '') ? $data : 0;
+    }
+
+    //  HTML Libraries
+
+    /**
+     * html_footer :Generates a footer table on the bottom of the page it's called on.
+     and closes out the body and html tags.
+     *
+     * @return void
+     * @access
+     **/
+    function html_footer()
+    {
+        $footer_table_width = '400';
+        $footer_table_align = 'center';
+        ?>
+            <hr>
+            <table width="<?php echo $footer_table_width?>"
+                align="<?php echo $footer_table_align?>"
+                summary="Footer Information" class="footertable" cellspacing="0">
+                <tr>
+                    <td align="left" class="footertd">
+                        <a href="mailto:<?php echo MASTER_EMAIL;?>"><?php echo MASTER;?></a>
+                    </td>
+                    <td align="right" class="footertd">
+                        <a href="<?php echo FOOTER_URL;?>" target="new">
+                        <img src="<?php echo FOOTER_IMG;?>" border=0 alt="FOOTER_IMG"></a>
+                    </td>
+                </tr>
+            </table>
+            </body>
+            </html>
+            <?php
+            //  We've got to terminate any more output
+            exit;
+    }
+
+    /**
+     * html_error :Generates a footer table on the bottom of the page it's called on.
+     and closes out the body and html tags.
+     * @param $msg: string error message to be displayed
+     * @param $$bail : bool whether or not to exit() after $msg
+     *
+     * @return void
+     * @access
+     **/
+    function html_error($msg, $bail)
+    {
+        ?>
+            <table summary="Error Information" class="errortable" cellspacing="0">
+                <tr class="errortr">
+                    <td class="errortd">
+                        <div class="errormsg"><?echo "<pre>$msg</pre>"?></div>
+                    </td>
+                </tr>
+            </table>
+
+            <?php
+            if ($bail) {
+                html_footer();
+            }
+    }
+
+    /**
+     * html_nav_table :Generates a navigation table on the page it's called on.
+     * @param $nav: associative array with entries like:$nav[text][url]
+     * @param $$w : max width of table
+     *
+     * @return void
+     * @access
+     **/
+    function html_nav_table($nav, $w)
+    {
+        if (is_array($nav)) {
+            $out = '<ul class="admin_nav">';
+            foreach ($nav as $link => $url) {
+                if (is_array($url)) {
+                    $out .= '<li><a href="'.$url[0].'" '.$url[1].'>'.$link.'</a></li>';
+                } else {
+                    $out .= '<li><a href="'.$url.'">'.$link.'</a></li>';
+                }
+            }
+            $out .= '</ul>';
+        }
+        echo $out;
+    }
+
+    /**
+     * html_header :Opens up the html tags, and includes the style sheet link
+     generates a header table on the top of the page it's called on.
+     * @param $title: Page Title
+     * @param $msg: message to display
+     * @param $$img : image to display
+     *
+     * @return void
+     * @access
+     **/
+    function html_header($title, $msg, $img)
+    {
+        $header_table_width = '400';
+        $header_table_align = 'center';
+        ?>
+            <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+            "http://www.w3.org/TR/html4/loose.dtd">
+            <html>
+            <head>
+                <title><?php echo $title?></title>
+                <link type="text/css" rel=stylesheet href="<?php echo STYLE;?>">
+            </head>
+            <body>
+                <table width="<?php echo $header_table_width?>"
+                    align="<?echo $header_table_align?>"
+                    summary="Header Information" class="headertable"
+                    cellspacing="0" cellpadding="3">
+                    <tr class="headertr">
+                        <td class="headertd">
+                            <?php if($img) : ?>
+                            <img src="<?php echo IMG_BASE.$img;?>"
+                                alt="<?php echo HEAD;?>" border="0">
+                            <?php endif; ?>
+                        </td>
+                    </tr>
+                    <tr>
+                        <td class="headertd2" align="center">
+                            <div class="headerh2" align="center">
+                                <?php echo $msg;?>
+                            </div>
+                        </td>
+                    </tr>
+                </table>
+            <?php
+    }
+
+    /**
+     * form_header :Opens up the form tag, and includes the hidden assoc array as hidden
+     fields.
+     * @param $action: string form action string
+     * @param $method: string Method of form
+     * @param $$hidden = "" : assoc array with $hidden($name => $value)
+     *
+     * @return void
+     * @access
+     **/
+    function form_header($action, $method, $hidden = '')
+    {
+        echo '<form action="'.$action.'" method="'.$method.'"
+            enctype="multipart/form-data">';
+        if ($hidden != '' && is_array($hidden)) {
+            foreach ($hidden as $k => $v) {
+                echo '<input type="hidden" name="'.$k.'" value="'.$v.'">';
+            }
+        }
+    }
+
+    /**
+     * text_box :Creates a input box for text with 35 as default size
+     * @param $name: string name of text box
+     * @param $value: string value of text box
+     * @param $$size = 35 : string size of text box
+     *
+     * @return void
+     * @access
+     **/
+    function text_box($name, $value, $size = 35) {
+        echo '<td class="navtd2"><input type="text" name="'.$name.'"
+            value="'.htmlspecialchars($value).'" size="'.$size.'"></td>';
+    }
+
+    /**
+     * form_footer :Closes up the form tag, and includes the submit button
+     * @param $name: string form action string
+     * @param $$suppress = 0: string Method of form
+     * @param $$cs : int colspan for td
+     *
+     * @return void
+     * @access
+     **/
+    function form_footer($name, $suppress = 0, $cs)
+    {
+        echo '<tr><td colspan="'.$cs.'" align=center>
+            <input type="SUBMIT" name="Command" value="'.$name.'">';
+        if ($suppress == 1) {
+            echo '<input type="SUBMIT" name="Command" value="Delete">';
+        }
+        echo '</td>';
+    }
+
+    /**
+     * file_upload
+     * this will replace the older version and that of img_upload which calls this with extra
+     * restricted of true
+     *
+     * @param mixed $form_field
+     * @param mixed $file_name
+     * @param mixed $destination_path
+     * @access public
+     * @return string
+     */
+    function file_upload($form_field, $file_name, $destination_path, $restricted = false)
+    {
+        $file_name_in_use = false;
+        $file_name = preg_replace('/[!@#$%^&()+={};:\'\"\/ ]/', '-', $file_name);
+        if ($restricted) {
+            $size = getImageSize($form_field);
+            if (!in_array($size[2], array(1, 2, 3))) {
+                echo '<p style="background-color:red;color:white;">' .
+                'The file you uploaded was of an incorect type,
+                 please only upload .gif,.png or .jpg files' .
+                '<BR CLEAR=ALL>' .
+                '</p>' .
+                "Hit your browser's back button to continue" .
+                '<p>';
+                $error[0] = 'ERROR';
+                return $error;
+            }
+        }
+        if (file_exists($destination_path . $file_name)) {
+            $file_name_in_use = true;
+        }
+        if ($file_name_in_use == true) {
+            $new_file_name = mktime() . $file_name;
+            $new_file_location = $destination_path . $new_file_name;
+            copy($form_field, $new_file_location);
+            $file_upload = $new_file_name;
+            $file_upload_array = array( $new_file_name, $new_file_location);
+        } else {
+            $new_file_name = $file_name;
+            $new_file_location = $destination_path.$new_file_name;
+            copy($form_field, $new_file_location);
+            $file_upload = $new_file_name;
+            $file_upload_array = array($new_file_name, $new_file_location);
+        }
+        if (is_file($new_file_location)) {
+            chmod($new_file_location, 0666);
+        }
+        return ($restricted) ? $file_upload_array : $file_upload;
+    }
+
+    //  Misc. Functions
+
+    /**
+     * http_strip :Strips the http:// part from start of string
+     * @param $&$string : $string
+     *
+     * @return string $stirng minus http:// in front
+     * @access
+     **/
+    function http_strip(&$string)
+    {
+        $test_string = strtolower($string);
+        if(substr($test_string, 0, 7) == 'http://') {
+            $string = substr($string, 7);
+        }
+    }
+
+    /**
+     * footer : used for admin page footer to close out the top function
+     *
+     * @return void
+     * @access
+     **/
+    function footer()
+    {
+        echo "\n\t</body>\n</html>";
+    }
+
+    /**
+     * top :Output the starting html and admin table tags
+     * @param $message: The title
+     * @param $hp: The help file to use
+     * @param $$hp2 = NULL : The help file to use (links to gaslightmedia.com)
+     *
+     * @return void
+     * @access
+     **/
+    function top($message, $hp, $hp2 = NULL)
+    {
+        if ($hp2 != '') {
+            $help_guide = '<div id="glm-manual">';
+            /*
+               $help_guide = '<div id="glm-manual"><a id="manual-html"
+               href="http://www.gaslightmedia.com/manuals/html/'.$hp2.'.html"
+               target="_blank">Online Help Guide</a>&nbsp;';
+             */
+            $help_guide .= '<a id="manual-pdf"
+                href="http://www.gaslightmedia.com/manuals/pdf/'.$hp2.'.pdf"
+                target="_blank">Printable Help Guide</a></div>';
+        }
+        $out = '
+            <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
+            "http://www.w3.org/TR/html4/strict.dtd">
+            <html>
+            <head>
+                <title>Untitled</title>
+                <meta http-equiv="content-type" content="text/html;charset=utf-8">
+                <link rel="stylesheet" type="text/css" href="../main.css">
+            <script type="text/javascript" src="'.GLM_APP_BASE_URL.'libjs/jquery/jquery-1.4.2.min.js"></script>
+            </head>
+            <body>
+                <h1>'.$message.'</h1>
+                ' . $help_guide;
+        echo $out;
+    }
+
+    /**
+     * top2 : alias to top()
+     * @param $message: message title
+     * @param $hp: help file
+     * @param $$hp2 = NULL : gaslight help file
+     *
+     * @return
+     * @access
+     **/
+    function top2($message, $hp,$hp2 = NULL) {
+        // make this an alias to top()
+        // by calling top instead of adding extra code
+        top($message, $hp, $hp2);
+
+    }
+
+    /********************************************************************************
+     *
+     *  DO NOT EDIT THIS SECTION
+     *
+     ********************************************************************************/
+
+    if ($DEBUG) {
+        echo '<CENTER>
+            <TABLE BORDER=0 CELLPADDING=3 CELLSPACING=1 WIDTH=600 BGCOLOR="#000000" ALIGN="CENTER">
+            <TR VALIGN="middle" BGCOLOR="#9999CC">
+            <TD COLSPAN="2" ALIGN="center"><H1>Portable Site Data - setup.phtml </H1></TD>
+            </TR>
+            <TR VALIGN="baseline" BGCOLOR="#CCCCCC">
+            <TD BGCOLOR="#CCCCFF" ><B>CVS Version Id:</B></TD>
+            <TD ALIGN="left">$Id: setup_functions.phtml,v 1.7 2010/06/17 12:50:52 jamie Exp $</TD>
+            </TR>
+            <TR VALIGN="baseline" BGCOLOR="#CCCCCC">
+            <TD BGCOLOR="#CCCCFF" ><B>SITENAME</B></TD>
+            <TD ALIGN="left">'.SITENAME.'</TD>
+            </TR>
+            <TR VALIGN="baseline" BGCOLOR="#CCCCCC">
+            <TD BGCOLOR="#CCCCFF" ><B>BASE</B></TD>
+            <TD ALIGN="left">'.BASE.'</TD>
+            </TR>
+            <TR VALIGN="baseline" BGCOLOR="#CCCCCC">
+            <TD BGCOLOR="#CCCCFF" ><B>UP_BASE</B></TD>
+            <TD ALIGN="left">'.UP_BASE.'</TD>
+            </TR>
+            <TR VALIGN="baseline" BGCOLOR="#CCCCCC">
+            <TD BGCOLOR="#CCCCFF" ><B>HELP_BASE</B></TD>
+            <TD ALIGN="left">'.HELP_BASE.'</TD>
+            </TR>
+            <TR VALIGN="baseline" BGCOLOR="#CCCCCC">
+            <TD BGCOLOR="#CCCCFF" ><B>IMG_BASE</B></TD>
+            <TD ALIGN="left">'.IMG_BASE.'</TD>
+            </TR>
+            <TR VALIGN="baseline" BGCOLOR="#CCCCCC">
+            <TD BGCOLOR="#CCCCFF" ><B>URL_BASE</B></TD>
+            <TD ALIGN="left">'.URL_BASE.'</TD>
+            </TR>
+            <TR VALIGN="baseline" BGCOLOR="#CCCCCC">
+            <TD BGCOLOR="#CCCCFF" ><B>CONN_STR</B></TD>
+            <TD ALIGN="left">'.CONN_STR.'</TD>
+            </TR>
+            <TR VALIGN="baseline" BGCOLOR="#CCCCCC">
+            <TD BGCOLOR="#CCCCFF" ><B>STYLE</B></TD>
+            <TD ALIGN="left">'.STYLE.'</TD>
+            </TR>
+            <TR VALIGN="baseline" BGCOLOR="#CCCCCC">
+            <TD BGCOLOR="#CCCCFF" ><B>ORIGINAL_PATH</B></TD>
+            <TD ALIGN="left">'.ORIGINAL_PATH.'</TD>
+            </TR>
+            <TR VALIGN="baseline" BGCOLOR="#CCCCCC">
+            <TD BGCOLOR="#CCCCFF" ><B>RESIZED_PATH</B></TD>
+            <TD ALIGN="left">'.RESIZED_PATH.'</TD>
+            </TR>
+            <TR VALIGN="baseline" BGCOLOR="#CCCCCC">
+            <TD BGCOLOR="#CCCCFF" ><B>MIDSIZED_PATH</B></TD>
+            <TD ALIGN="left">'.MIDSIZED_PATH.'</TD>
+            </TR>
+            <TR VALIGN="baseline" BGCOLOR="#CCCCCC">
+            <TD BGCOLOR="#CCCCFF" ><B>THUMB_PATH</B></TD>
+            <TD ALIGN="left">'.THUMB_PATH.'</TD>
+            </TR>
+            <TR VALIGN="baseline" BGCOLOR="#CCCCCC">
+            <TD BGCOLOR="#CCCCFF" ><B>ORIGINAL</B></TD>
+            <TD ALIGN="left">'.ORIGINAL.'</TD>
+            </TR>
+            <TR VALIGN="baseline" BGCOLOR="#CCCCCC">
+            <TD BGCOLOR="#CCCCFF" ><B>RESIZED</B></TD>
+            <TD ALIGN="left">'.RESIZED.'</TD>
+            </TR>
+            <TR VALIGN="baseline" BGCOLOR="#CCCCCC">
+            <TD BGCOLOR="#CCCCFF" ><B>MIDSIZED</B></TD>
+            <TD ALIGN="left">'.MIDSIZED.'</TD>
+            </TR>
+            <TR VALIGN="baseline" BGCOLOR="#CCCCCC">
+            <TD BGCOLOR="#CCCCFF" ><B>THUMB</B></TD>
+            <TD ALIGN="left">'.THUMB.'</TD>
+            </TR>
+            <TR VALIGN="baseline" BGCOLOR="#CCCCCC">
+            <TD BGCOLOR="#CCCCFF" ><B>$CALLED_FROM_DIR</B></TD>
+            <TD ALIGN="left">'.$CALLED_FROM_DIR.'&nbsp;</TD>
+            </TR>
+            <TR VALIGN="baseline" BGCOLOR="#CCCCCC">
+            <TD BGCOLOR="#CCCCFF" ><B>$BASE_PATH</B></TD>
+            <TD ALIGN="left">'.$BASE_PATH.'</TD>
+            </TR>
+            <TR VALIGN="baseline" BGCOLOR="#CCCCCC">
+            <TD BGCOLOR="#CCCCFF" ><B>$BASE_URL</B></TD>
+            <TD ALIGN="left"><A HREF="'.$BASE_URL.'">'.$BASE_URL.'</A></TD>
+            </TR>
+            <TR VALIGN="baseline" BGCOLOR="#CCCCCC">
+            <TD BGCOLOR="#CCCCFF" ><B>$BASE_SECURE_URL</B></TD>
+            <TD ALIGN="left">'.$BASE_SECURE_URL.'</TD>
+            </TR>
+            </TABLE>
+            &nbsp;
+        <P>
+            <HR WIDTH="600">
+            <P>
+            </CENTER>
+            ';
+    }
+
+    /**
+     * date_entry : Generate the select boxes for date entry
+     * month-day-year as drop down select
+     * @param $month:
+     * @param $day:
+     * @param $year:
+     * @param $month_name: name of select month
+     * @param $day_name: name of select day
+     * @param $$year_name : name of select year
+     *
+     * @return
+     * @access
+     **/
+    function date_entry($month, $day, $year, $month_name, $day_name, $year_name, $onChange = NULL)
+    {
+        $cur_date = getdate();
+
+        if ($month == '') {
+            $month = $cur_date['mon'];
+        }
+        if($day == '') {
+            $day = $cur_date['mday'];
+        }
+        if($year == '') {
+            $year = $cur_date['year'];
+        }
+        $date = '<select id="'.$month_name.'" name="'.$month_name.'" '.$onChange.'>';
+        for ($i = 1; $i < 13; ++$i) {
+            $date .= '<option value="';
+            if ($i < 10) {
+                $date .= '0';
+            }
+            $date .= $i.'"';
+            if ($i == $month) {
+                $date .= ' selected';
+            }
+            $date .= ">$i";
+        }
+        $date .= '</select>';
+        $date .= '<select id="'.$day_name.'" name="'.$day_name.'" '.$onChange.'>';
+        for ($i = 1; $i < 32; ++$i) {
+            $date .= '<option value="';
+            if ($i < 10) {
+                $date .= "0";
+            }
+            $date .= $i.'"';
+            if($i == $day) {
+                $date .= ' selected';
+            }
+            $date .= ">$i";
+        }
+        $date .= '</select>';
+        $date .= '<select id="'.$year_name.'" name="'.$year_name.'" '.$onChange.'>';
+        for ($i = 2000; $i < 2023; ++$i) {
+            $date .= '<option value="'.$i.'"';
+            if ($i == $year) {
+                $date .= ' selected';
+            }
+            $date .= ">$i";
+        }
+        $date .= '</select>';
+        return $date;
+    }
+
+    /**
+     * contact_date_entry : build select boxes for date entry going backwords in years
+     * @param $month:
+     * @param $day:
+     * @param $year:
+     * @param $month_name: name of select month
+     * @param $day_name: name of select day
+     * @param $$year_name : name of select year
+     *
+     * @return void
+     * @access
+     **/
+    function contact_date_entry($month, $day, $year, $month_name, $day_name, $year_name)
+    {
+        $cur_date = getdate();
+
+        if ($month == '') {
+            $month = $cur_date['mon'];
+        }
+        if ($day == '') {
+            $day = $cur_date['mday'];
+        }
+        if ($year == '') {
+            $year = $cur_date['year'];
+        }
+        $date = '<select name="'.$month_name.'">';
+        for ($i = 1; $i < 13; ++$i) {
+            $date .= '<option value="';
+            if ($i < 10) {
+                $date .= '0';
+            }
+            $date .= $i.'"';
+            if ($i == $month) {
+                $date .= ' selected';
+            }
+            $date .= ">$i";
+        }
+        $date .= '</select>';
+        $date .= '<select name="'.$day_name.'">';
+        for ($i = 1; $i < 32; ++$i) {
+            $date .= '<option value="';
+            if($i < 10) {
+                $date .= '0';
+            }
+            $date .= $i.'"';
+            if ($i == $day) {
+                $date .= ' selected';
+            }
+            $date .= ">$i";
+        }
+        $date .= '</select>';
+        $date .= '<select name="'.$year_name.'">';
+        $ystart = $cur_date['year'] - 10;
+        for ($i = $ystart; $i <= $year; ++$i) {
+            $date .= '<option value="'.$i.'"';
+            if ($i == $year) {
+                $date .= ' selected';
+            }
+            $date .= ">$i";
+        }
+        $date .= '</select>';
+        return $date;
+    }
+
+    /**
+     * time_entry : build select boxes for time entry
+     * @param $H:
+     * @param $m:
+     * @param $F:
+     * @param $H_name: name of select hour
+     * @param $m_name: name of select min
+     * @param $$F_name : name of select sec
+     *
+     * @return
+     * @access
+     **/
+    function time_entry($H, $m, $F, $H_name, $m_name, $F_name)
+    {
+        $cur_date = getdate();
+        if ($H == '') {
+            $H = $cur_date['hours'];
+        }
+        if ($m == '') {
+            $m = $cur_date['minutes'];
+        }
+        if ($H > 12) {
+            $F = 'PM';
+            $H = $H - 12;
+        }
+        $time = "Hr:<select name=\"$H_name\" size=\"1\">";
+        for ($i = 1; $i <= 12; ++$i) {
+            $time .= "<option value=\"";
+            if ($i < 10) {
+                $time .= "0";
+            }
+            $time .= "$i\"";
+            if ($i == $H) {
+                $time .= " selected";
+            }
+            $time .= ">$i\n";
+        }
+        $time .= "</select>\n";
+        $time .= "Min:<select name=\"$m_name\" size=\"1\">";
+        for ($i = 0; $i < 60; $i = $i + 15) {
+            $time .= "<Option value=\"";
+            if ($i < 10) {
+                $time .= "0";
+            }
+            $time .= "$i\"";
+            if ($i == $m) {
+                $time .= " selected";
+            }
+            $time .= ">";
+            if ($i < 10) {
+                $time .= "0";
+            }
+            $time .= "$i\n";
+        }
+        $time .= "</select>";
+        $time .= "<select name=\"$F_name\" size=\"1\">";
+        $time .= "<option value=\"AM\"";
+        if ($F == "AM") {
+            $time .= " selected";
+        }
+        $time .= ">AM\n";
+        $time .= "<option value=\"PM\"";
+        if ($F == "PM") {
+            $time .= " selected";
+        }
+        $time .= ">PM\n";
+        $time .= "</select>\n";
+        return $time;
+    }
+
+    /**
+     * get_parentid: get the (highest level) parent category for this id
+     * @param $id: id from bus_category table
+     *
+     * @return int parent
+     * @access
+     **/
+    function get_parentid($id)
+    {
+        static $parentshow;
+        if ($id == 0) {
+            return 0;
+        }
+        if (!is_array($parentshow)) {
+            $sql = "
+                SELECT parent
+                  FROM bus_category
+                 WHERE id = $id";
+            $parentrow = db_auto_get_data($qs);
+        }
+        if ($parentrow[0]['parent'] == 0) {
+            return $id;
+        } else {
+            return get_parentid($parentrow[0]['parent']);
+        }
+    }
+
+    /**
+     * build_picklist:Builds a pick list from an array
+     * @param $fieldname: fieldname field name for select
+     * @param $data: data array of data
+     * @param $selected: selected witch element is selected
+     * @param $$type = "standard": type Standard,multi
+     * @param $$auto = 0: auto
+     * @param $$width = NULL : width width controlled by css
+     *
+     * @return void
+     * @access
+     **/
+    function build_picklist( $fieldname, $data, $selected, $type = "standard",$auto = 0,$width = NULL ) {
+        if(!is_array($selected)) {
+            $sel[0] = $selected;
+        } else {
+            $sel = $selected;
+        }
+        if($auto == 1)
+            $autosubmit = "onChange=\"form.submit()\"";
+        if($width)
+            $autosubmit .= "style=\"width:".$width."px;\"";
+        switch( $type ) {
+            case "multiple":
+                $str = "<select name=\"".$fieldname."\" multiple size=\"10\" ".$autosubmit.">\n";
+            while( list($key, $val) = each($data) ) {
+                if( in_array($key,$sel) ) {
+                    $select = " selected ";
+                }
+                else
+                    $select = "";
+                $str .= "   <option value=\"$key\"".$select.">$val\n";
+            }
+            break;
+            case "simple":
+                $str = "<select name=\"$fieldname\" ".$autosubmit.">\n";
+            for( $i=0 ; $i<count($data) ; $i++ ) {
+                $select = (in_array($data[$i],$sel)) ? " selected ":"";
+                $str .= "   <option value=\"".$data[$i]."\"".$select.">".$data[$i]."\n";
+            }
+            break;
+
+            case "standard":
+            default:
+                $str = "<select name=\"$fieldname\" ".$autosubmit.">\n";
+                while( list($key, $val) = each($data) ) {
+                    $select = (in_array($key,$sel)) ? " selected ":"";
+                    $str .= "   <option value=\"$key\"".$select.">$val\n";
+                }
+                break;
+        }
+        $str .= "</select>\n";
+
+        return $str;
+
+    }
+
+    /**
+     * create_page_links:Create prev and next links
+     * to page through the results.
+     * @param $totalnum: The total result of the query
+     * @param $num: The total result for the page
+     * @param $$start=0: The starting num defaults to 0
+     * @param $params: variables to add to the url
+     * @param $ENTRIES_PER_PAGE: number of items on page defaults to the ENTRIES_PER_PAGE
+     *
+     * @return string of links
+     * @access
+     **/
+    function create_page_links($totalnum,$num,$start=0,$params,$page_length=ENTRIES_PER_PAGE) {
+        // find out which page we're on.
+        if($totalnum!=0) {
+            $total_pages = floor($totalnum / $page_length);     // total pages = the total result divided by page length rounded down
+            $total_pages++;                                     // then add one
+            if($start == 0) {                                   // if start is 0 then page is one {
+                $page = 1;
+            } else  {
+                $page = ($start / $page_length) + 1;
+            }
+        }
+
+        if($totalnum > $page_length && ( $page != $totalpages ) ) {
+            $end = $page_length + $start;
+        } else {
+            $end = $totalnum;
+        }
+        $last = $start - $page_length;
+        if(($start - $page_length) < 0)
+            $prev = "";
+        else
+            $prev = "<span class=\"accenttext\">[</span><a class=\"small\"
+                href=\"$GLOBALS[PHP_SELF]?start=".$last."&$params\">PREVIOUS PAGE</a><span
+                class=\"accenttext\"> ]</span>";
+        if($end < $totalnum)
+            $next = "<span class=\"accenttext\">[</span><a class=\"small\"
+                href=\"$GLOBALS[PHP_SELF]?start=".$end."&$params\">NEXT PAGE</a><span
+                class=\"accenttext\"> ]</span>";
+        else
+            $next = "";
+        $starting = $start + 1;
+        $last_c = $start + $num;
+        $links = '<center><span class="pagetitle">Listings Displayed: </span><span
+            class="accenttext">'.$starting.' to '.$last_c.'</span>
+            <span class="pagetitle"> of '.$totalnum.'<br></span> '.$prev. ' &nbsp; <span
+            class="pagetitle"></span> '.$next.'<BR></span></center>';
+        return $links;
+    }
+?>
diff --git a/static/.keepme b/static/.keepme
new file mode 100755 (executable)
index 0000000..e69de29
diff --git a/static/8.phtml b/static/8.phtml
new file mode 100644 (file)
index 0000000..2851e3e
--- /dev/null
@@ -0,0 +1,30 @@
+<?php
+//  application configuration
+$conf = new Config;
+$root =& $conf->parseConfig(BASE . 'Toolkit/Members/config.ini', 'IniFile');
+
+$class = 'Toolkit_Members_Auth';
+$form  = isset($_GET['forgot']) ? 'passwordForm' : 'loginForm';
+
+$authContainer = new Toolkit_Members_AuthContainer(
+       Toolkit_Database::getInstance(),
+       array(
+               'table'                 => 'member',
+               'usernamecol'   => 'member_login',
+               'passwordcol'   => 'member_passwd',
+               'db_fields'             => array('member_id', 'member_name'),
+               'cryptType'             => 'none',
+               'db_where'              => 'new_member <> true AND active = true',
+       )
+);
+
+$memberAuth = new Toolkit_Members_Auth(
+       $root,
+    $authContainer,
+       array($class, $form),
+       true
+);
+$memberAuth->start();
+if ($memberAuth->getAuth()) {
+    header('Location: ' . BASE_URL . 'members-only-area/');
+}
diff --git a/templates/siteMap.html b/templates/siteMap.html
new file mode 100644 (file)
index 0000000..0afda42
--- /dev/null
@@ -0,0 +1,34 @@
+<div id="sitemap">
+  <ol>
+    <li flexy:foreach="nav,main">
+      <h2><a href="{main[url]:h}">{main[label]:h}</a></h2>
+      {if:main[subs]}
+      <?php
+      $t->chunks = array_chunk($main['subs'], 3, true);
+      ?>
+        <div class="row" flexy:foreach="chunks,chunky">
+          <ol>
+            <li flexy:foreach="chunky,chunk">
+              <a href="{chunk[url]:h}">{chunk[label]:h}</a>
+              {if:chunk[subs]}
+              <ol>
+                <li flexy:foreach="chunk[subs],subs,sChunk">
+                  <a href="{sChunk[url]:h}">{sChunk[label]:h}</a>
+
+                      {if:sChunk[subs]}
+                      <ol>
+                        <li flexy:foreach="sChunk[subs],subs2,sChunk2">
+                          <a href="{sChunk2[url]:h}">{sChunk2[label]:h}</a>
+                        </li>
+                      </ol>
+                      {end:}
+                </li>
+              </ol>
+              {end:}
+            </li>
+          </ol>
+        </div>
+      {end:}
+    </li>
+  </ol>
+</div>
\ No newline at end of file
diff --git a/templates/template.html b/templates/template.html
new file mode 100644 (file)
index 0000000..fe3073c
--- /dev/null
@@ -0,0 +1,96 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
+"http://www.w3.org/TR/html4/strict.dtd">
+<html>
+<head>
+<title>{pageTitle:h}</title>
+<meta http-equiv="content-type" content="text/html;charset=utf-8">
+<meta name="description" content="{metaTags:h}">
+<meta http-equiv="imagetoolbar" content="no">
+<meta http-equiv="imagetoolbar" content="false">
+{styles:h}
+{topScripts:h}
+</head>
+<body id="inside"><!-- body id home on homepage only , #inside for inside pages-->
+<div id="wrapper">
+    <div id="wrapperInner">
+        <div id="top">
+            <a href="baseurl/index.php">
+                <img id="logo" src="baseurl/assets/logo.png" width="520" height="50">
+            </a>
+        </div><!-- /#top -->
+        <ul id="nav">
+            <li><a href="#">Home</a></li>
+            <li><a href="#">Services</a></li>
+            <li><a href="#">Patient Information</a></li>
+            <li><a href="#">About Us</a></li>
+            <li><a href="#">Location</a></li>
+            <li id="lastli"><a href="#">Contact Us</a></li>
+        </ul>
+        <div id="column">
+            <div id="subnav"><!-- only if there are subpages -->
+                <h2>Services</h2>
+                <ul>
+                    <li><a href="#">Navigation Name</a></li>
+                    <li><a href="#">Halloisann hu</a></li>
+                    <li><a href="#">Lorem Ipsum</a></li>
+                    <li><a href="#">Navigator Tools</a></li>
+                    <li><a href="#">Nav</a></li>
+                    <li><a href="#">Navigation Name</a></li>
+                    <li><a href="#">Halloisann hu</a></li>
+                    <li><a href="#">Lorem Ipsum</a></li>
+                    <li><a href="#">Navigator Tools</a></li>
+                    <li></li>
+                </ul>
+            </div><!-- /#subnav -->
+        </div><!-- /#column -->
+        <div id="main">
+            <div id="toolbox">
+                <div id="category">
+                    <h1>Page Header</h1>
+                    <div class="imageright">
+                        <img src="baseurl/assets/toolboxTMP.gif" width="325" height="218" alt="toolboxTMP (26K)">
+                        <div class="imagecaption">This is the imagecaption</div>
+                    </div><!-- /..imageright -->
+                    <p>Located in the exact center of Northern Michigan on the 45th parallel, Gaylord is unique because of its stunning surroundings, up north ambience and easy commuting access. </p>
+                    <p>Located in the exact center of Northern Michigan on the 45th parallel, Gaylord is unique because of its stunning surroundings, up north ambience and easy commuting access. </p>
+                    <p>Located in the exact center of Northern Michigan on the 45th parallel, Gaylord is unique because of its stunning surroundings, up north ambience and easy commuting access. </p>
+                </div><!-- /#category -->
+                <div class="listing">
+                    <h2>Header</h2>
+                    <div class="imageleft">
+                        <img src="baseurl/assets/toolboxTMP.gif" width="325" height="218" alt="toolboxTMP (26K)">
+                        <div class="imagecaption">This is the imagecaption</div>
+                    </div><!-- /..imageleft -->
+                    <p>Located in the exact center of Northern Michigan on the 45th parallel, Gaylord is unique because of its stunning surroundings, up north ambience and easy commuting access. </p>
+                    <p>Located in the exact center of Northern Michigan on the 45th parallel, Gaylord is unique because of its stunning surroundings, up north ambience and easy commuting access. </p>
+                    <p>Located in the exact center of Northern Michigan on the 45th parallel, Gaylord is unique because of its stunning surroundings, up north ambience and easy commuting access. </p>
+                </div><!-- /.listin\g -->   
+                <div class="listing">
+                    <h2>Header</h2>
+                    <div class="imageright">
+                        <img src="baseurl/assets/toolboxTMP.gif" width="325" height="218" alt="toolboxTMP (26K)">
+                        <div class="imagecaption">This is the imagecaption</div>
+                    </div><!-- /..imageright -->
+                    <p>Located in the exact center of Northern Michigan on the 45th parallel, Gaylord is unique because of its stunning surroundings, up north ambience and easy commuting access. </p>
+                    <p>Located in the exact center of Northern Michigan on the 45th parallel, Gaylord is unique because of its stunning surroundings, up north ambience and easy commuting access. </p>
+                    <p>Located in the exact center of Northern Michigan on the 45th parallel, Gaylord is unique because of its stunning surroundings, up north ambience and easy commuting access. </p>
+                </div><!-- /.listin\g -->   
+            </div><!-- /#toolbox -->
+        </div><!-- /#main -->
+        <div id="footer">
+            <p>Dermatology Associates of Northern Michigan, P.C. 2240 Mitchell Park Petoskey, MI 49770 &middot; 231-487-2230</p>
+        </div>
+        <ul id="accreditations">
+            <li id="aAAD">AAD</li>
+            <li id="aABD">ABD</li>
+            <li id="aACMS">ACMS</li>
+            <li id="aASDS">ASDS</li>    
+        </ul>
+        <div id="copyright">
+            Copyright&copy;<?php echo date('Y');?> {siteName:h} - Produced by <a href="http://www.gaslightmedia.com">Gaslight Media</a>, All Rights Reserved.
+        </div><!-- /#copyright -->
+    </div><!-- /#wrapperInner" -->
+</div><!-- /#wrapper -->
+{bottomScripts:h}
+</body>
+</html>