Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

make catalyst branch the new trunk

  • Loading branch information...
commit 634795b6632debd9d23923deb8ddd598847e60b6 0 parents
autarch authored
Showing with 28,668 additions and 0 deletions.
  1. +8 −0 Build.PL
  2. +1 −0  README
  3. +17 −0 bin/regen-data-feed-rss
  4. +25 −0 bin/send-watch-list-emails
  5. BIN  data/alzabo/schemas/RegVeg/RegVeg.create.alz
  6. +1 −0  data/alzabo/schemas/RegVeg/RegVeg.rdbms
  7. BIN  data/alzabo/schemas/RegVeg/RegVeg.runtime.alz
  8. +1 −0  data/alzabo/schemas/RegVeg/RegVeg.version
  9. +88 −0 image-files/close-button.svg
  10. +528 −0 inc/VegGuide/Build.pm
  11. +145 −0 lib/VegGuide.pm
  12. +34 −0 lib/VegGuide/Action/REST.pm
  13. +67 −0 lib/VegGuide/AlternateLinks.pm
  14. +182 −0 lib/VegGuide/AlzaboWrapper.pm
  15. +30 −0 lib/VegGuide/Attribute.pm
  16. +105 −0 lib/VegGuide/Breadcrumbs.pm
  17. +408 −0 lib/VegGuide/CachedHierarchy.pm
  18. +69 −0 lib/VegGuide/Category.pm
  19. +91 −0 lib/VegGuide/Chart.pm
  20. +185 −0 lib/VegGuide/Client.pm
  21. +23 −0 lib/VegGuide/Comment.pm
  22. +377 −0 lib/VegGuide/Config.pm
  23. +83 −0 lib/VegGuide/Controller/AlzaboGUI.pm
  24. +152 −0 lib/VegGuide/Controller/Base.pm
  25. +54 −0 lib/VegGuide/Controller/DirectToView.pm
  26. +858 −0 lib/VegGuide/Controller/Entry.pm
  27. +10 −0 lib/VegGuide/Controller/Error.pm
  28. +666 −0 lib/VegGuide/Controller/Region.pm
  29. +177 −0 lib/VegGuide/Controller/Root.pm
  30. +413 −0 lib/VegGuide/Controller/Site.pm
  31. +69 −0 lib/VegGuide/Controller/Site/Admin.pm
  32. +65 −0 lib/VegGuide/Controller/Skin.pm
  33. +30 −0 lib/VegGuide/Controller/Stats.pm
  34. +59 −0 lib/VegGuide/Controller/Suggestion.pm
  35. +649 −0 lib/VegGuide/Controller/User.pm
  36. +110 −0 lib/VegGuide/Cuisine.pm
  37. +126 −0 lib/VegGuide/Email.pm
  38. +216 −0 lib/VegGuide/Engine.pm
  39. +94 −0 lib/VegGuide/Exceptions.pm
  40. +53 −0 lib/VegGuide/FillInFormBridge.pm
  41. +225 −0 lib/VegGuide/Geocoder.pm
  42. +84 −0 lib/VegGuide/GreatCircle.pm
  43. +18 −0 lib/VegGuide/JSON.pm
  44. +78 −0 lib/VegGuide/Javascript.pm
  45. +36 −0 lib/VegGuide/Keywords.pm
  46. +286 −0 lib/VegGuide/Locale.pm
  47. +1,847 −0 lib/VegGuide/Location.pm
  48. +74 −0 lib/VegGuide/NewsItem.pm
  49. +11 −0 lib/VegGuide/Pageset.pm
  50. +71 −0 lib/VegGuide/Plugin/Authentication.pm
  51. +27 −0 lib/VegGuide/Plugin/Authentication/Credential/DBMS.pm
  52. +67 −0 lib/VegGuide/Plugin/Authentication/Store/DBMS.pm
  53. +38 −0 lib/VegGuide/Plugin/Authentication/User.pm
  54. +32 −0 lib/VegGuide/Plugin/ErrorHandling.pm
  55. +60 −0 lib/VegGuide/Plugin/InOutEncoding.pm
  56. +18 −0 lib/VegGuide/Plugin/Redirect.pm
  57. +25 −0 lib/VegGuide/Plugin/ResponseAttributes.pm
  58. +129 −0 lib/VegGuide/Plugin/Session.pm
  59. +74 −0 lib/VegGuide/Plugin/Session/State/URI.pm
  60. +19 −0 lib/VegGuide/Plugin/Session/Store/VegGuide.pm
  61. +49 −0 lib/VegGuide/PriceRange.pm
  62. +32 −0 lib/VegGuide/Queue.pm
  63. +393 −0 lib/VegGuide/RSSWriter.pm
  64. +196 −0 lib/VegGuide/Request.pm
  65. +10 −0 lib/VegGuide/Response.pm
  66. +54 −0 lib/VegGuide/Role/Controller/Comment.pm
  67. +79 −0 lib/VegGuide/Role/Controller/Feed.pm
  68. +230 −0 lib/VegGuide/Role/Controller/Search.pm
  69. +53 −0 lib/VegGuide/Role/FeedEntry.pm
  70. +212 −0 lib/VegGuide/Schema.pm
  71. +75 −0 lib/VegGuide/Search.pm
  72. +157 −0 lib/VegGuide/Search/User.pm
  73. +554 −0 lib/VegGuide/Search/Vendor.pm
  74. +227 −0 lib/VegGuide/Search/Vendor/ByLatLong.pm
  75. +188 −0 lib/VegGuide/Search/Vendor/ByLocation.pm
  76. +169 −0 lib/VegGuide/Search/Vendor/ByName.pm
  77. +181 −0 lib/VegGuide/SiteURI.pm
  78. +225 −0 lib/VegGuide/Skin.pm
  79. +99 −0 lib/VegGuide/Team.pm
  80. +34 −0 lib/VegGuide/UniqueArray.pm
  81. +1,448 −0 lib/VegGuide/User.pm
  82. +222 −0 lib/VegGuide/Util.pm
  83. +97 −0 lib/VegGuide/Validate.pm
  84. +3,647 −0 lib/VegGuide/Vendor.pm
  85. +272 −0 lib/VegGuide/VendorImage.pm
  86. +41 −0 lib/VegGuide/View/AlzaboGUI.pm
  87. +80 −0 lib/VegGuide/View/Mason.pm
  88. +167 −0 lib/XML/Generator/RSS10/regveg.pm
  89. +77 −0 notes/LAUNCHPLAN3.0
  90. +13 −0 notes/memory-usage-3.0
  91. +21 −0 script/migrations/1/00-price-ranges
  92. +41 −0 script/migrations/1/01-locales
  93. +16 −0 script/migrations/1/02-hours
  94. +19 −0 script/migrations/1/03-ratings
  95. +36 −0 script/migrations/1/04-skins
  96. +43 −0 script/migrations/1/05-import-old-news
  97. +93 −0 script/migrations/1/06-import-images
  98. +26 −0 script/migrations/1/07-geocoding
  99. +17 −0 script/migrations/1/old-news-items/20030312.mas
  100. +10 −0 script/migrations/1/old-news-items/20030317.mas
  101. +13 −0 script/migrations/1/old-news-items/20030320.mas
  102. +31 −0 script/migrations/1/old-news-items/20030322.mas
  103. +25 −0 script/migrations/1/old-news-items/20030323.mas
  104. +14 −0 script/migrations/1/old-news-items/20030407.mas
  105. +30 −0 script/migrations/1/old-news-items/20030426.mas
  106. +15 −0 script/migrations/1/old-news-items/20030503.mas
  107. +14 −0 script/migrations/1/old-news-items/20030508.mas
  108. +14 −0 script/migrations/1/old-news-items/20030509.mas
  109. +13 −0 script/migrations/1/old-news-items/20030522.mas
  110. +15 −0 script/migrations/1/old-news-items/20030622.mas
  111. +14 −0 script/migrations/1/old-news-items/20030730.mas
  112. +18 −0 script/migrations/1/old-news-items/20030803-2.mas
  113. +17 −0 script/migrations/1/old-news-items/20030803.mas
  114. +14 −0 script/migrations/1/old-news-items/20030805.mas
  115. +15 −0 script/migrations/1/old-news-items/20030808.mas
  116. +14 −0 script/migrations/1/old-news-items/20030904.mas
  117. +13 −0 script/migrations/1/old-news-items/20030905.mas
  118. +14 −0 script/migrations/1/old-news-items/20030924.mas
  119. +19 −0 script/migrations/1/old-news-items/20030926-2.mas
  120. +19 −0 script/migrations/1/old-news-items/20030926.mas
  121. +16 −0 script/migrations/1/old-news-items/20031002.mas
  122. +17 −0 script/migrations/1/old-news-items/20031003.mas
  123. +19 −0 script/migrations/1/old-news-items/20031021.mas
  124. +17 −0 script/migrations/1/old-news-items/20031026.mas
  125. +20 −0 script/migrations/1/old-news-items/20031114.mas
  126. +17 −0 script/migrations/1/old-news-items/20031122.mas
  127. +18 −0 script/migrations/1/old-news-items/20031222.mas
  128. +16 −0 script/migrations/1/old-news-items/20031225-2.mas
  129. +20 −0 script/migrations/1/old-news-items/20031225.mas
  130. +18 −0 script/migrations/1/old-news-items/20040115-2.mas
  131. +19 −0 script/migrations/1/old-news-items/20040115.mas
  132. +18 −0 script/migrations/1/old-news-items/20040116.mas
  133. +19 −0 script/migrations/1/old-news-items/20040120.mas
  134. +13 −0 script/migrations/1/old-news-items/20040201-2.mas
  135. +23 −0 script/migrations/1/old-news-items/20040201.mas
  136. +15 −0 script/migrations/1/old-news-items/20040311-2.mas
  137. +15 −0 script/migrations/1/old-news-items/20040311.mas
  138. +18 −0 script/migrations/1/old-news-items/20040317.mas
  139. +15 −0 script/migrations/1/old-news-items/20040411.mas
  140. +14 −0 script/migrations/1/old-news-items/20040414.mas
  141. +16 −0 script/migrations/1/old-news-items/20040508.mas
  142. +16 −0 script/migrations/1/old-news-items/20040619.mas
  143. +17 −0 script/migrations/1/old-news-items/20040625.mas
  144. +15 −0 script/migrations/1/old-news-items/20040707.mas
  145. +18 −0 script/migrations/1/old-news-items/20040709.mas
  146. +14 −0 script/migrations/1/old-news-items/20040712.mas
  147. +17 −0 script/migrations/1/old-news-items/20040910.mas
  148. +14 −0 script/migrations/1/old-news-items/20040911.mas
  149. +17 −0 script/migrations/1/old-news-items/20040912.mas
  150. +20 −0 script/migrations/1/old-news-items/20041127.mas
  151. +15 −0 script/migrations/1/old-news-items/20050208.mas
  152. +49 −0 script/migrations/1/old-news-items/20050416.mas
  153. +19 −0 script/migrations/1/old-news-items/20050424.mas
  154. +15 −0 script/migrations/1/old-news-items/20050514.mas
  155. +20 −0 script/migrations/1/old-news-items/20050529.mas
  156. +16 −0 script/migrations/1/old-news-items/20060123.mas
  157. +17 −0 script/migrations/1/old-news-items/20061218.mas
  158. +17 −0 script/migrations/1/old-news-items/20070724.mas
  159. +22 −0 script/migrations/1/old-news-items/20071016.mas
  160. +37 −0 script/vegguide_cgi.pl
  161. +29 −0 script/vegguide_clean_db.pl
  162. +75 −0 script/vegguide_create.pl
  163. +80 −0 script/vegguide_fastcgi.pl
  164. +21 −0 script/vegguide_recreate_db.pl
  165. +65 −0 script/vegguide_run_migrations.pl
  166. +111 −0 script/vegguide_server.pl
  167. +165 −0 script/vegguide_sync_db.pl
  168. +54 −0 script/vegguide_test.pl
  169. +92 −0 share/css/ie.css
  170. +24 −0 share/css/printable.css
  171. +1,782 −0 share/css/vegguide.css
  172. +16 −0 share/email-templates/forgot-password.html
  173. +12 −0 share/email-templates/forgot-password.txt
  174. +34 −0 share/email-templates/suggestion-processed.html
  175. +24 −0 share/email-templates/suggestion-processed.txt
  176. +30 −0 share/email-templates/watch-list.html
  177. +23 −0 share/email-templates/watch-list.txt
  178. +5 −0 share/feed-templates/vendor-comment-content.mas
  179. +35 −0 share/feed-templates/vendor-entry-content.mas
  180. BIN  share/images/american_express.png
  181. BIN  share/images/atom-button.gif
  182. BIN  share/images/close-button.png
  183. BIN  share/images/diner_s_club.png
  184. BIN  share/images/discover.png
  185. BIN  share/images/eye.png
  186. BIN  share/images/feed-icon.png
  187. BIN  share/images/icons/map.png
  188. BIN  share/images/icons/open.png
  189. BIN  share/images/icons/photo.png
  190. BIN  share/images/icons/reviews.png
  191. BIN  share/images/icons/smoke-free.png
  192. BIN  share/images/link-100-75.jpg
  193. BIN  share/images/link-60-45.jpg
  194. BIN  share/images/map-icons/bar.png
  195. BIN  share/images/map-icons/catering.png
  196. BIN  share/images/map-icons/coffee.png
  197. BIN  share/images/map-icons/food_court.png
  198. BIN  share/images/map-icons/general_store.png
  199. BIN  share/images/map-icons/grocery.png
  200. BIN  share/images/map-icons/lodging.png
  201. BIN  share/images/map-icons/organization.png
  202. BIN  share/images/map-icons/other.png
  203. BIN  share/images/map-icons/restaurant.png
  204. BIN  share/images/map-icons/restaurant1.png
  205. BIN  share/images/map-icons/restaurant2.png
  206. BIN  share/images/map-icons/restaurant3.png
  207. BIN  share/images/map-icons/restaurant5.png
  208. BIN  share/images/map-icons/shadow.png
  209. BIN  share/images/map-icons/transparent.png
  210. BIN  share/images/mastercard.png
  211. BIN  share/images/ratings/blue-0-00.png
  212. BIN  share/images/ratings/blue-1-00.png
  213. BIN  share/images/ratings/blue-2-00.png
  214. BIN  share/images/ratings/blue-3-00.png
  215. BIN  share/images/ratings/blue-4-00.png
  216. BIN  share/images/ratings/blue-5-00.png
  217. BIN  share/images/ratings/green-0-00.png
  218. BIN  share/images/ratings/green-0-25.png
  219. BIN  share/images/ratings/green-0-50.png
  220. BIN  share/images/ratings/green-0-75.png
  221. BIN  share/images/ratings/green-1-00.png
  222. BIN  share/images/ratings/green-1-25.png
  223. BIN  share/images/ratings/green-1-50.png
  224. BIN  share/images/ratings/green-1-75.png
  225. BIN  share/images/ratings/green-2-00.png
  226. BIN  share/images/ratings/green-2-25.png
  227. BIN  share/images/ratings/green-2-50.png
  228. BIN  share/images/ratings/green-2-75.png
  229. BIN  share/images/ratings/green-3-00.png
  230. BIN  share/images/ratings/green-3-25.png
  231. BIN  share/images/ratings/green-3-50.png
  232. BIN  share/images/ratings/green-3-75.png
  233. BIN  share/images/ratings/green-4-00.png
  234. BIN  share/images/ratings/green-4-25.png
  235. BIN  share/images/ratings/green-4-50.png
  236. BIN  share/images/ratings/green-4-75.png
  237. BIN  share/images/ratings/green-5-00.png
  238. BIN  share/images/red-bullet-small.png
  239. BIN  share/images/red-bullet.png
  240. BIN  share/images/rss-1-button.gif
  241. BIN  share/images/rss-button.gif
  242. BIN  share/images/sort-down.gif
  243. BIN  share/images/sort-none.gif
  244. BIN  share/images/sort-up.gif
  245. BIN  share/images/transparent.gif
  246. BIN  share/images/upload-an-image.png
  247. BIN  share/images/vegguide-logo.png
  248. BIN  share/images/visa.png
  249. +216 −0 share/js-source/DOM/Element.js
  250. +262 −0 share/js-source/DOM/Events.js
  251. +276 −0 share/js-source/DOM/Find.js
  252. +310 −0 share/js-source/DOM/Ready.js
  253. +67 −0 share/js-source/DOM/Utils.js
  254. +147 −0 share/js-source/Form/Serializer.js
  255. +262 −0 share/js-source/HTTP/Request.js
  256. +410 −0 share/js-source/List/Utils.js
  257. +2 −0  share/js-source/VegGuide.js
  258. +38 −0 share/js-source/VegGuide/Browser.js
  259. +20 −0 share/js-source/VegGuide/Element.js
  260. +45 −0 share/js-source/VegGuide/Enhancements.js
  261. +313 −0 share/js-source/VegGuide/EntryFilters.js
  262. +94 −0 share/js-source/VegGuide/EntryForm.js
  263. +195 −0 share/js-source/VegGuide/EntryImageSlideshow.js
  264. +77 −0 share/js-source/VegGuide/Form.js
  265. +171 −0 share/js-source/VegGuide/GoogleMap.js
  266. +178 −0 share/js-source/VegGuide/HoursForm.js
  267. +42 −0 share/js-source/VegGuide/IEPngFilter.js
  268. +105 −0 share/js-source/VegGuide/InlineSearchForm.js
  269. +43 −0 share/js-source/VegGuide/LocaleList.js
  270. +66 −0 share/js-source/VegGuide/LocationSearch.js
  271. +48 −0 share/js-source/VegGuide/Pagination.js
  272. +238 −0 share/js-source/VegGuide/RatingStars.js
  273. +15 −0 share/js-source/VegGuide/RegionForm.js
  274. +77 −0 share/js-source/VegGuide/SitewideSearch.js
  275. +37 −0 share/js-source/VegGuide/Suggestions.js
  276. +59 −0 share/js-source/VegGuide/UserSearch.js
  277. +162 −0 share/js-source/VegGuide/Widget/PairedMultiSelect.js
  278. +143 −0 share/js-source/Widget/Lightbox2.js
  279. +357 −0 share/js-source/Widget/PairedMultiSelect.js
  280. +125 −0 share/mason/autohandler
  281. +120 −0 share/mason/entry/autohandler
  282. +24 −0 share/mason/entry/edit-form
  283. +117 −0 share/mason/entry/edit-hours-form
  284. +30 −0 share/mason/entry/hours-box.mas
  285. +93 −0 share/mason/entry/images-form
  286. +1 −0  share/mason/entry/index
  287. +84 −0 share/mason/entry/info-box.mas
  288. +84 −0 share/mason/entry/large-map
  289. +64 −0 share/mason/entry/map-and-images.mas
  290. +7 −0 share/mason/entry/profile-and-hours.mas
  291. +38 −0 share/mason/entry/review-form
  292. +30 −0 share/mason/entry/slideshow.mas
  293. +157 −0 share/mason/entry/view
  294. +29 −0 share/mason/error/404
  295. +30 −0 share/mason/error/500
  296. +51 −0 share/mason/featured-entry.mas
  297. +70 −0 share/mason/footer.mas
  298. +53 −0 share/mason/header.mas
  299. +75 −0 share/mason/index
  300. +64 −0 share/mason/lib/comments.mas
Sorry, we could not display the entire diff because too many files (450) changed.
8 Build.PL
@@ -0,0 +1,8 @@
+use strict;
+use warnings;
+
+use lib 'inc';
+use VegGuide::Build;
+
+
+VegGuide::Build->new()->create_build_script();
1  README
@@ -0,0 +1 @@
+Run script/vegguide_server.pl to test the application.
17 bin/regen-data-feed-rss
@@ -0,0 +1,17 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use VegGuide::Location;
+
+
+VegGuide::Location->DataFeedRSSFile();
+
+for my $location ( VegGuide::Location->All() )
+{
+ next unless $location->descendants_vendor_count() > 500;
+
+ $location->data_feed_rss_file();
+}
+
25 bin/send-watch-list-emails
@@ -0,0 +1,25 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Getopt::Long;
+use VegGuide::User;
+
+
+my %opts;
+GetOptions( 'user-id:i' => \$opts{user_id} );
+
+
+if ( $opts{user_id} )
+{
+ my $user = VegGuide::User->new( user_id => $opts{user_id} );
+ die "No such user_id $opts{user_id}\n"
+ unless $user;
+
+ $user->send_subscription_email();
+}
+else
+{
+ VegGuide::User->SendSubscriptionEmails();
+}
BIN  data/alzabo/schemas/RegVeg/RegVeg.create.alz
Binary file not shown
1  data/alzabo/schemas/RegVeg/RegVeg.rdbms
@@ -0,0 +1 @@
+MySQL
BIN  data/alzabo/schemas/RegVeg/RegVeg.runtime.alz
Binary file not shown
1  data/alzabo/schemas/RegVeg/RegVeg.version
@@ -0,0 +1 @@
+0.92
88 image-files/close-button.svg
@@ -0,0 +1,88 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://web.resource.org/cc/"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="744.09448819"
+ height="1052.3622047"
+ id="svg2"
+ sodipodi:version="0.32"
+ inkscape:version="0.45"
+ sodipodi:docbase="/home/autarch/projects/RegVeg/VegGuide/image-files"
+ sodipodi:docname="close-button.svg"
+ inkscape:output_extension="org.inkscape.output.svg.inkscape"
+ inkscape:export-filename="/home/autarch/projects/RegVeg/VegGuide/close-button.png"
+ inkscape:export-xdpi="5.6100001"
+ inkscape:export-ydpi="5.6100001"
+ sodipodi:modified="TRUE">
+ <defs
+ id="defs4" />
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ gridtolerance="10000"
+ guidetolerance="10"
+ objecttolerance="10"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="1.979899"
+ inkscape:cx="258.13632"
+ inkscape:cy="767.90827"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ inkscape:window-width="1280"
+ inkscape:window-height="929"
+ inkscape:window-x="0"
+ inkscape:window-y="0" />
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1">
+ <rect
+ style="fill:#049b00;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ id="rect3141"
+ width="237.14285"
+ height="237.14285"
+ x="151.04544"
+ y="177.02661" />
+ <rect
+ style="fill:#027b00;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;fill-opacity:1"
+ id="rect2160"
+ width="237.14285"
+ height="237.14285"
+ x="148.57143"
+ y="175.21933" />
+ <text
+ xml:space="preserve"
+ style="font-size:328.79611206px;font-style:normal;font-weight:normal;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ x="135.68983"
+ y="437.51117"
+ id="text2162"
+ transform="scale(1.0777821,0.9278313)"
+ inkscape:export-filename="/home/autarch/projects/RegVeg/VegGuide/text2162.png"
+ inkscape:export-xdpi="5.6100001"
+ inkscape:export-ydpi="5.6100001"><tspan
+ sodipodi:role="line"
+ id="tspan2164"
+ x="135.68983"
+ y="437.51117">X</tspan></text>
+ </g>
+</svg>
528 inc/VegGuide/Build.pm
@@ -0,0 +1,528 @@
+package VegGuide::Build;
+
+use strict;
+use warnings;
+
+use base 'Module::Build';
+
+use File::Basename qw( basename dirname );
+use File::Find::Rule;
+use File::Path qw( mkpath );
+use File::Spec;
+
+my %Requires =
+ ( 'Alzabo' => 0.92,
+ 'Captcha::reCAPTCHA' => 0,
+ 'Catalyst' => 5.7007,
+ 'Catalyst::Action::REST' => 0.5,
+ 'Catalyst::Engine::Apache2::MP20' => 0,
+ 'Catalyst::Plugin::Authentication' => 0.1,
+ 'Catalyst::Plugin::Cache::Store::FastMmap' => 0,
+ 'Catalyst::Plugin::Log::Dispatch' => 0,
+ 'Catalyst::Plugin::Session' => 0.17,
+ 'Catalyst::Plugin::Session::State' => 0,
+ 'Catalyst::Plugin::Session::Store' => 0,
+ 'Catalyst::Plugin::Session::Store::DBI' => 0,
+ 'Catalyst::Plugin::StackTrace' => 0,
+ 'Catalyst::Plugin::Static::Simple' => 0,
+ 'Catalyst::Plugin::SubRequest' => 0,
+ 'Catalyst::View::Mason' => 0.13,
+ 'Chart::OFC' => 0,
+ 'Class::AlzaboWrapper' => 0,
+ 'Class::Trait' => 0.22,
+ 'Data::Dump::Streamer' => 0,
+ 'Data::Pageset' => 0,
+ 'DateTime' => 0,
+ 'DateTime::Format::MySQL' => 0,
+ 'DateTime::Format::Strptime' => 0,
+ 'DateTime::Format::W3CDTF' => 0,
+ 'DateTime::Locale' => 0.3,
+ 'DateTime::TimeZone' => 0.66,
+ 'Digest::SHA1' => 0,
+ 'Email::Address' => 1.886,
+ 'Email::Date' => 1.101,
+ 'Email::MessageID' => 1.35,
+ 'Email::Send' => 2.185,
+ 'Email::MIME::CreateHTML' => 0,
+ 'Email::Valid' => 0,
+ 'Encode' => 2.23,
+ 'Exception::Class' => 0,
+ 'File::chdir' => 0,
+ 'File::Find::Rule' => 0,
+ 'File::MMagic' => 0,
+ 'File::Slurp' => 0,
+ 'Geo::Coder::Google' => 0,
+ 'Geography::States' => 0,
+ 'HTML::Entities' => 0,
+ 'HTML::FillInForm' => 1.07,
+ 'Image::Magick' => 0,
+ 'Image::Size' => 0,
+ 'JavaScript::Squish' => 0,
+ 'JSAN::ServerSide' => 0.04,
+ 'JSON::XS' => 0,
+ 'Lingua::EN::Inflect' => 0,
+ 'List::MoreUtils' => 0,
+ 'List::Util' => 0,
+ 'LockFile::Simple' => 0,
+ 'Math::Round' => 0,
+ 'Params::Validate' => 0,
+ 'Sys::Hostname' => 0,
+ 'Text::WikiFormat' => 0,
+ 'Text::Wrap' => 0,
+ 'Tie::IxHash' => 0,
+ 'Time::HiRes' => 0,
+ 'URI::Escape' => 0,
+ 'URI::FromHash' => 0,
+ 'WebService::StreetMapLink' => 0.21,
+ 'XML::Atom' => 0,
+ 'XML::Feed' => 0,
+ 'XML::Generator::RSS10' => 0,
+ 'XML::RSS' => 0,
+ 'XML::SAX::Writer' => 0,
+ );
+
+sub new
+{
+ my $class = shift;
+
+ return $class->SUPER::new
+ ( license => 'perl',
+ module_name => 'VegGuide',
+ requires => \%Requires,
+ script_files => [ glob('bin/*') ],
+ test_files => [ glob('t/*.t'), glob('t/*/*.t') ]
+ );
+}
+
+sub ACTION_modules
+{
+ delete $Requires{'Image::Magick'};
+
+ print join ' ', sort keys %Requires;
+ print "\n";
+}
+
+sub ACTION_install
+{
+ my $self = shift;
+
+ $self->SUPER::ACTION_install(@_);
+
+ $self->_install_extra();
+}
+
+sub _install_extra
+{
+ my $self = shift;
+
+ $self->dispatch('copy_share_files');
+ $self->dispatch('copy_system_files');
+ $self->dispatch('make_entry_images_dir');
+ $self->dispatch('make_skin_images_dir');
+ $self->dispatch('make_etc_dir');
+ $self->dispatch('make_cache_dir');
+ $self->dispatch('generate_combined_js');
+ $self->dispatch('copy_alzabo_schema');
+ $self->dispatch('sync_db');
+ $self->dispatch('generate_secrets');
+ $self->dispatch('run_migrations');
+ $self->dispatch('manual_reminder');
+}
+
+our $FAKE = 0;
+sub ACTION_fakeinstall
+{
+ my $self = shift;
+
+ $self->SUPER::ACTION_fakeinstall(@_);
+
+ local $FAKE = 1;
+ $self->_install_extra();
+}
+
+sub ACTION_copy_share_files
+{
+ my $self = shift;
+
+ my @files = $self->_find_things('share');
+
+ require VegGuide::Config;
+ my $target_dir = VegGuide::Config->ShareDir();
+
+ foreach my $file ( sort @files )
+ {
+ my $dir = dirname($file);
+ $dir =~ s{^.*?share/?}{};
+
+ my $to =
+ File::Spec->catfile( $target_dir,
+ $dir,
+ basename($file) );
+
+ if ($FAKE)
+ {
+ $self->log_info("Copying $file -> $to\n");
+ next;
+ }
+
+ $self->copy_if_modified( from => $file,
+ to => $to,
+ flatten => 1,
+ );
+ }
+}
+
+sub ACTION_copy_system_files
+{
+ my $self = shift;
+
+ my @files = $self->_find_things( 'system', 'include dirs' );
+
+ require VegGuide::Config;
+
+ foreach my $file ( sort @files )
+ {
+ next if $file eq 'system';
+
+ if ( -d $file )
+ {
+ ( my $dir = $file ) =~ s{^.*?system}{};
+
+ next if -d $dir;
+
+ if ($FAKE)
+ {
+ $self->log_info( "mkpath $dir" );
+ }
+ else
+ {
+ mkpath( $dir, 1, 0755 )
+ }
+
+ next;
+ }
+
+ my $dir = dirname($file);
+ $dir =~ s{^.*?system}{};
+
+ my $to =
+ File::Spec->catfile( $dir,
+ basename($file) );
+
+ if ($FAKE)
+ {
+ $self->log_info("Copying $file -> $to\n");
+ next;
+ }
+
+ $self->copy_if_modified( from => $file,
+ to => $to,
+ flatten => 1,
+ );
+ }
+
+ for my $file ( File::Find::Rule->new()->file()->in( '/etc/apache2/mods-available' ) )
+ {
+ my $to = File::Spec->catfile( '/etc/apache2-backend/mods-available',
+ basename($file) );
+
+ if ($FAKE)
+ {
+ $self->log_info("Copying $file -> $to\n");
+ next;
+ }
+
+ $self->copy_if_modified( from => $file,
+ to => $to,
+ flatten => 1,
+ );
+ }
+
+ my $dir = '/var/log/apache2-backend';
+ chmod 0750, $dir
+ or die "Cannot chmod 0640 $dir: $!";
+
+ my $adm_gid = getgrnam('adm');
+ chown 0, $adm_gid, $dir
+ or die "Cannot chown 0:$adm_gid $dir: $!";
+}
+
+sub _find_things
+{
+ my $self = shift;
+ my $dir = shift;
+ my $include_dirs = shift;
+
+ my $rule = File::Find::Rule->new();
+
+ $rule = $rule->or( $rule->new()
+ ->directory()
+ ->name('.svn')
+ ->prune()
+ ->discard(),
+
+ $rule->new()
+ ->name('*~')
+ ->prune()
+ ->discard(),
+
+ $rule->new()
+ ->name('#*#')
+ ->prune()
+ ->discard(),
+
+ $rule->new(),
+ );
+
+ $rule->file()
+ unless $include_dirs;
+
+ return $rule->in($dir);
+}
+
+sub ACTION_make_entry_images_dir
+{
+ my $self = shift;
+
+ return if $FAKE;
+
+ require VegGuide::Config;
+ my $target_dir = File::Spec->catdir( VegGuide::Config->VarLibDir(), 'entry-images', );
+
+ mkpath( $target_dir, 1, 0755 );
+
+ my ( $uid, $gid ) = $self->_server_uid_gid();
+
+ chown $uid, $gid, $target_dir
+ or die "Cannot chown $uid:$gid $target_dir: $!";
+}
+
+sub ACTION_make_skin_images_dir
+{
+ my $self = shift;
+
+ return if $FAKE;
+
+ require VegGuide::Config;
+ my $target_dir = File::Spec->catdir( VegGuide::Config->VarLibDir(), 'skin-images', );
+
+ mkpath( $target_dir, 1, 0755 );
+
+ my ( $uid, $gid ) = $self->_server_uid_gid();
+
+ chown $uid, $gid, $target_dir
+ or die "Cannot chown $uid:$gid $target_dir: $!";
+}
+
+sub _server_uid_gid
+{
+ my $uid = getpwnam('www-data');
+ my $gid = getgrnam('www-data');
+
+ return ( $uid, $gid );
+}
+
+sub ACTION_make_etc_dir
+{
+ my $self = shift;
+
+ return if $FAKE;
+
+ require VegGuide::Config;
+
+ mkpath( VegGuide::Config->EtcDir(), 1, 0755 );
+}
+
+sub ACTION_make_cache_dir
+{
+ my $self = shift;
+
+ return if $FAKE;
+
+ require VegGuide::Config;
+
+ my $rss_cache_dir = File::Spec->catdir( VegGuide::Config->CacheDir(), 'rss' );
+
+ mkpath( $rss_cache_dir, 1, 0755 );
+
+ my ( $uid, $gid ) = $self->_server_uid_gid();
+
+ chown $uid, $gid, $rss_cache_dir
+ or die "Cannot chown $uid:$gid $rss_cache_dir: $!";
+}
+
+sub ACTION_generate_combined_js
+{
+ my $self = shift;
+
+ require VegGuide::Config;
+ my $target_dir = VegGuide::Config->VarLibDir();
+
+ mkpath( $target_dir, 1, 0755 )
+ unless $FAKE;
+
+ $self->log_info("Generating combined JS source file\n");
+
+ return if $FAKE;
+
+ require VegGuide::Javascript;
+ VegGuide::Javascript->CreateSingleFile();
+
+ my $file = VegGuide::Javascript->CombinedFile();
+ chmod 0644, $file
+ or die "Cannot chmod 0644 $file: $!";
+}
+
+sub ACTION_copy_alzabo_schema
+{
+ my $self = shift;
+
+ require Alzabo::Config;
+
+ my $schema_svn_dir =
+ File::Spec->catdir( $self->base_dir(), qw( data alzabo schemas RegVeg ) );
+
+ my $to_dir = File::Spec->catdir( Alzabo::Config::schema_dir(), 'RegVeg' );
+
+ foreach my $file ( glob File::Spec->catfile( $schema_svn_dir, 'RegVeg.*' ) )
+ {
+ if ($FAKE)
+ {
+ $self->log_info("Copying $file -> $to_dir\n");
+ next;
+ }
+
+ $self->copy_if_modified( from => $file,
+ to_dir => $to_dir,
+ flatten => 1,
+ );
+ }
+}
+
+sub ACTION_sync_db
+{
+ my $self = shift;
+
+ return if $FAKE;
+
+ $self->do_system( './script/vegguide_sync_db.pl', '--data' );
+}
+
+sub ACTION_generate_secrets
+{
+ my $self = shift;
+
+ require VegGuide::Config;
+
+ my $target_dir = VegGuide::Config->EtcDir();
+
+ mkpath( $target_dir, 1, 0755 )
+ unless $FAKE;
+
+ foreach my $f ( map { File::Spec->catfile( $target_dir, $_ ) }
+ qw( mac-secret forgot-pw-secret ) )
+ {
+ next if -e $f;
+
+ $self->log_info("Writing secret to $f\n");
+
+ next if $FAKE;
+
+ open my $fh, '>', $f
+ or die "Cannot write to $f: $!";
+
+ print $fh $self->_generate_mac_secret()
+ or die "Cannot write to $f: $!";
+
+ close $fh;
+
+ chmod 0640, $f
+ or die "Cannot chmod 0640 $f: $!";
+ }
+}
+
+sub _generate_mac_secret
+{
+ my @chars = ( 'a'..'z', 'A'..'Z', 0..9 );
+
+ my $secret = '';
+ $secret .= $chars[ rand @chars ] for 1..12;
+
+ return $secret;
+}
+
+sub ACTION_run_migrations
+{
+ my $self = shift;
+
+ return if $FAKE;
+
+ mkpath( '/etc/vegguide', 1, 0755 )
+ unless $FAKE;
+
+ $self->do_system( qw( script/vegguide_run_migrations.pl ) );
+}
+
+sub ACTION_manual_reminder
+{
+ my $self = shift;
+
+ require VegGuide::Config;
+ my $etc_dir = VegGuide::Config->EtcDir();
+
+ my $file = File::Spec->catfile( $etc_dir, 'recaptcha-key' );
+ print "\n*** Don't forget to create $file\n\n"
+ unless -f $file;
+}
+
+sub ACTION_find_unused_files
+{
+ my $self = shift;
+
+ my %distro_share = $self->_file_map('share');
+
+ require VegGuide::Config;
+
+ my %system_share = $self->_file_map( VegGuide::Config->ShareDir() );
+
+ $self->_print_non_matching( \%distro_share, \%system_share );
+
+ my %distro_pm = $self->_file_map('lib/VegGuide');
+
+ my $system_lib = File::Spec->catdir( $self->install_destination('lib'), 'VegGuide' );
+ my %system_pm = $self->_file_map($system_lib);
+
+ $self->_print_non_matching( \%distro_pm, \%system_pm );
+}
+
+sub _file_map
+{
+ my $self = shift;
+ my $dir = shift;
+
+ my %map;
+ for my $file ( $self->_find_things($dir) )
+ {
+ ( my $trimmed = $file ) =~ s{^\Q$dir\E/}{};;
+
+ $map{$trimmed} = $file;
+ }
+
+ return %map;
+}
+
+sub _print_non_matching
+{
+ my $self = shift;
+ my $distro = shift;
+ my $system = shift;
+
+ for my $file ( sort keys %{ $system } )
+ {
+ print $system->{$file}, "\n"
+ unless $distro->{$file};
+ }
+}
+
+
+1;
145 lib/VegGuide.pm
@@ -0,0 +1,145 @@
+package VegGuide;
+
+use strict;
+use warnings;
+
+our $VERSION = '0.01';
+
+use Catalyst;
+use DateTime;
+use VegGuide::Client;
+use VegGuide::Config;
+use VegGuide::Request;
+use VegGuide::Response;
+
+use VegGuide::Engine;
+
+use VegGuide::Attribute;
+use VegGuide::Comment;
+use VegGuide::Cuisine;
+use VegGuide::Location;
+use VegGuide::Skin;
+use VegGuide::User;
+use VegGuide::Vendor;
+
+
+BEGIN
+{
+ Catalyst->import( VegGuide::Config->CatalystImports() );
+}
+
+__PACKAGE__->config( name => 'VegGuide',
+ VegGuide::Config->CatalystConfig(),
+ );
+
+__PACKAGE__->request_class( 'VegGuide::Request' );
+__PACKAGE__->response_class( 'VegGuide::Response' );
+
+__PACKAGE__->setup();
+
+
+sub vg_user
+{
+ my $self = shift;
+
+ return $self->{vg_user} ||= $self->user()->get_object();
+}
+
+sub skin
+{
+ my $self = shift;
+
+ return $self->{skin}
+ if $self->{skin};
+
+ if ( $self->request()->param('skin_id') )
+ {
+ return $self->{skin} =
+ VegGuide::Skin->new( skin_id => $self->request()->param('skin_id' ) );
+ }
+
+ return $self->{skin} =
+ VegGuide::Skin->SkinForHostname( ( $self->request()->hostname() || '' ) );
+}
+
+sub client
+{
+ my $self = shift;
+
+ my $stash = $self->stash();
+
+ return $stash->{client} if $stash->{client};
+
+ my $location = $stash->{location};
+ $location ||= $stash->{vendor}->location()
+ if $stash->{vendor};
+
+ my $locale;
+ $locale = $location->locale() if $location;
+
+ return
+ $stash->{client} =
+ VegGuide::Client->new( $self->request(), $locale );
+}
+
+{
+ package Devel::InnerPackage;
+
+ no warnings 'redefine';
+
+ sub list_packages {
+ my $pack = shift; $pack .= "::" unless $pack =~ m!::$!;
+
+ no strict 'refs';
+ my @packs;
+ my @stuff = grep !/^(main|)::$/, keys %{$pack};
+
+ # This is a monkey-patch for some weirdness where D::IP ends
+ # up thinking VegGuide::View::Mason has an inner package of
+ # VegGuide::View::Mason::SUPER. This only happens when the
+ # full Catalyst stack is also loaded.
+ for my $cand ( grep { ! /SUPER::$/ } grep /::$/, @stuff)
+ {
+ $cand =~ s!::$!!;
+ my @children = list_packages($pack.$cand);
+
+ push @packs, "$pack$cand" unless $cand =~ /^::/ ||
+ !__PACKAGE__->_loaded($pack.$cand); # or @children;
+ push @packs, @children;
+ }
+ return grep {$_ !~ /::::ISA::CACHE/} @packs;
+ }
+
+}
+
+
+1;
+
+__END__
+
+=head1 NAME
+
+VegGuide - Catalyst based application
+
+=head1 SYNOPSIS
+
+ script/vegguide_server.pl
+
+=head1 DESCRIPTION
+
+Catalyst based application.
+
+=head1 METHODS
+
+=head2 default
+
+=head1 AUTHOR
+
+Dave Rolsky,,,
+
+=head1 LICENSE
+
+This library is free software, you can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
34 lib/VegGuide/Action/REST.pm
@@ -0,0 +1,34 @@
+package VegGuide::Action::REST;
+
+use strict;
+use warnings;
+
+use base 'Catalyst::Action::REST';
+
+
+sub dispatch
+{
+ my $self = shift;
+ my $c = shift;
+
+ if ( $c->request()->looks_like_browser()
+ && uc $c->request()->method() eq 'GET' )
+ {
+ my $controller = $self->class();
+ my $method = $self->name() . '_GET_html';
+
+ if ( $controller->can($method) )
+ {
+ $c->execute( $self->class, $self, @{ $c->req->args } );
+
+ return
+ $controller->$method
+ ( $c, @{ $c->request()->args() } );
+ }
+ }
+
+ return $self->NEXT::dispatch($c);
+}
+
+
+1;
67 lib/VegGuide/AlternateLinks.pm
@@ -0,0 +1,67 @@
+package VegGuide::AlternateLinks;
+
+use strict;
+use warnings;
+
+use Scalar::Util qw( weaken );
+use VegGuide::UniqueArray;
+use VegGuide::Validate qw( validate_pos );
+
+
+sub new
+{
+ my $class = shift;
+
+ return
+ bless { array => VegGuide::UniqueArray->new() }, $class;
+}
+
+{
+ sub add
+ {
+ my $self = shift;
+
+ $self->{array}->push( VegGuide::AlternateLink->new(@_) );
+ }
+}
+
+sub all
+{
+ return $_[0]->{array}->values();
+}
+
+
+package VegGuide::AlternateLink;
+
+use overload ( '""' => 'as_string',
+ 'eq' => sub { $_[0]->as_string() eq $_[1]->as_string() },
+ fallback => 1
+ );
+
+use VegGuide::Validate qw( validate SCALAR_TYPE );
+
+
+{
+ my $spec = { mime_type => SCALAR_TYPE,
+ title => SCALAR_TYPE,
+ uri => SCALAR_TYPE,
+ };
+ sub new
+ {
+ my $class = shift;
+ my %p = validate( @_, $spec );
+
+ return bless \%p, $class;
+ }
+}
+
+sub mime_type { $_[0]->{mime_type} }
+
+sub uri { $_[0]->{uri} }
+
+sub title { $_[0]->{title} }
+
+sub as_string { $_[0]->uri() }
+
+
+1;
182 lib/VegGuide/AlzaboWrapper.pm
@@ -0,0 +1,182 @@
+package VegGuide::AlzaboWrapper;
+
+use strict;
+use warnings;
+
+use base 'Class::AlzaboWrapper';
+
+use Class::AlzaboWrapper 0.12;
+
+use DateTime::Format::MySQL;
+use Encode ();
+
+use VegGuide::Exceptions ( 'error' );
+
+
+sub import
+{
+ my $class = shift;
+
+ return unless @_;
+
+ my %p = @_;
+
+ my $caller = (caller(0))[0];
+
+ my %skip_decode;
+ if ( $p{skip_decode} )
+ {
+ %skip_decode =
+ map { $_ => 1 } ref $p{skip_decode} ? @{ $p{skip_decode} } : $p{skip_decode};
+ }
+
+ my @char_cols =
+ ( grep { ! $skip_decode{$_} }
+ map { $_->name }
+ grep { $_->is_character || $_->is_blob } $p{table}->columns
+ );
+
+ $p{skip} = $p{skip} ? [ @{ $p{skip} }, @char_cols ] : \@char_cols;
+
+ delete $p{skip_decode};
+
+ $class->SUPER::import( %p,
+ base => $class,
+ caller => $caller,
+ );
+
+ $class->_make_char_col_methods( $caller, @char_cols );
+
+ $class->_make_datetime_col_methods( $caller, grep { $_->is_date } $p{table}->columns );
+}
+
+# This ensures that UTF-8 data coming from MySQL is treated as UTF-8
+# by Perl.
+sub _make_char_col_methods
+{
+ my $class = shift;
+ my $caller = shift;
+
+ foreach my $c (@_)
+ {
+ my $key = '__decoded_' . $c . '__';
+
+ no strict 'refs';
+
+ *{"$caller\::$c"} =
+ sub { unless ( exists $_[0]->{$key} )
+ {
+ my $val = $_[0]->row_object->select($c);
+ $val = Encode::decode( 'utf-8', $val )
+ unless Encode::is_utf8($val);
+ $_[0]->{$key} = $val;
+ }
+ $_[0]->{$key} };
+
+ $class->_RecordAttributeCreation( $caller => $c );
+ }
+}
+
+sub _make_datetime_col_methods
+{
+ my $self = shift;
+ my $caller = shift;
+ my @cols = @_;
+
+ foreach my $c (@cols)
+ {
+ if ( $c->is_datetime )
+ {
+ my $name = $c->name;
+ my $key = '__datetime_' . $name . '__';
+
+ no strict 'refs';
+ *{"$caller\::${name}_object"} =
+ sub { my $val = $_[0]->$name();
+ return unless defined $_[0]->$name();
+ return if $val eq '0000-00-00 00:00:00';
+ $_[0]->{$key} ||= DateTime::Format::MySQL->parse_datetime( $_[0]->$name );
+ return $_[0]->{$key} };
+ }
+ else
+ {
+ my $name = $c->name;
+ my $key = '__date_' . $name . '__';
+
+ no strict 'refs';
+ *{"$caller\::${name}_object"} =
+ sub { my $val = $_[0]->$name();
+ return unless defined $val;
+ return if $val eq '0000-00-00';
+ $_[0]->{$key} ||= DateTime::Format::MySQL->parse_date( $_[0]->$name );
+ return $_[0]->{$key} };
+ }
+ }
+}
+
+sub create
+{
+ my $class = shift;
+ my %p = @_;
+
+ $class->_validate_data(\%p)
+ if $class->can('_validate_data');
+
+ return $class->SUPER::create(%p);
+}
+
+sub update
+{
+ my $self = shift;
+ my %p = @_;
+
+ $self->_validate_data(\%p)
+ if $self->can('_validate_data');
+
+ return $self->SUPER::update(%p);
+}
+
+sub IsValidId
+{
+ my $class = shift;
+
+ my @pk = $class->Table->primary_key;
+
+ error "Cannot call IsValidId on $class (multi-column pk)"
+ if @pk > 1;
+
+ return
+ $class->Table->row_count( where => [ $pk[0], '=', shift ] );
+}
+
+sub Count { $_[0]->Table->row_count }
+
+sub DDS_freeze
+{
+ my $self = shift;
+
+ my $out = ref $self;
+ $out .= ': ';
+
+ my @pk_vals;
+ for my $pk ( $self->Table()->primary_key() )
+ {
+ push @pk_vals,
+ $pk->name() . ' = ' . $self->select( $pk->name() );
+ }
+
+ $out .= join '|', @pk_vals;
+
+ return $out;
+}
+
+sub ClearCache
+{
+ VegGuide::Vendor->ClearCache()
+ if VegGuide::Vendor->can('ClearCache');
+}
+
+
+1;
+
+__END__
30 lib/VegGuide/Attribute.pm
@@ -0,0 +1,30 @@
+package VegGuide::Attribute;
+
+use strict;
+use warnings;
+
+use VegGuide::Schema;
+use VegGuide::AlzaboWrapper ( table => VegGuide::Schema->Schema()->Attribute_t() );
+
+
+my %Attributes;
+my %AttributesByName;
+
+BEGIN
+{
+ %Attributes =
+ ( map { $_->select('attribute_id') => VegGuide::Attribute->SUPER::new( object => $_ ) }
+ VegGuide::Schema->Connect()->Attribute_t()->all_rows()->all_rows()
+ );
+
+ %AttributesByName =
+ map { $_->name() => $_ } values %Attributes;
+}
+
+sub All
+{
+ return map { $AttributesByName{$_} } sort keys %AttributesByName;
+}
+
+
+1;
105 lib/VegGuide/Breadcrumbs.pm
@@ -0,0 +1,105 @@
+package VegGuide::Breadcrumbs;
+
+use strict;
+use warnings;
+
+use Scalar::Util qw( weaken );
+use VegGuide::SiteURI qw( region_uri );
+use VegGuide::UniqueArray;
+use VegGuide::Validate qw( validate_pos );
+
+
+{
+ my @spec = ( { can => 'uri_for' } );
+ sub new
+ {
+ my $class = shift;
+ my ($request) = validate_pos( @_, @spec );
+
+ my $self =
+ bless { array => VegGuide::UniqueArray->new() }, $class;
+
+ $self->{catalyst} = $request;
+ weaken $self->{catalyst};
+
+ return $self;
+ }
+}
+
+sub add
+{
+ my $self = shift;
+
+ $self->{array}->push( VegGuide::Breadcrumb->new(@_) );
+}
+
+sub add_region_breadcrumbs
+{
+ my $self = shift;
+ my $location = shift;
+
+ for my $l ( $location->ancestors(), $location )
+ {
+ $self->add
+ ( uri => region_uri( location => $l ),
+ label => $l->name(),
+ );
+ }
+}
+
+sub add_standard_breadcrumb
+{
+ my $self = shift;
+
+ $self->add( uri => $self->{catalyst}->request()->uri()->as_string(),
+ label => shift,
+ );
+}
+
+sub all
+{
+ return $_[0]->{array}->values();
+}
+
+
+package VegGuide::Breadcrumb;
+
+use overload ( '""' => 'as_string',
+ 'eq' => sub { $_[0]->as_string() eq $_[1]->as_string() },
+ fallback => 1
+ );
+
+use Scalar::Util qw( blessed );
+use VegGuide::Validate qw( validate SCALAR_TYPE );
+
+
+{
+ my $spec = { uri =>
+ { callbacks =>
+ { 'string or URI object' =>
+ sub { return 1 if defined $_[0] && ! ref $_[0] && length $_[0];
+ return 1 if blessed $_[0] && $_[0]->can('as_string') },
+ },
+ },
+ label => SCALAR_TYPE,
+ };
+ sub new
+ {
+ my $class = shift;
+ my %p = validate( @_, $spec );
+
+ return bless \%p, $class;
+ }
+}
+
+sub uri { $_[0]->{uri} }
+
+sub label { $_[0]->{label} }
+
+sub as_string
+{
+ return join '|', $_[0]->uri(), $_[0]->label();
+}
+
+
+1;
408 lib/VegGuide/CachedHierarchy.pm
@@ -0,0 +1,408 @@
+package VegGuide::CachedHierarchy;
+
+use strict;
+use warnings;
+
+use File::Spec;
+
+use VegGuide::Validate qw( validate validate_with SCALAR_TYPE ARRAYREF_TYPE OBJECT ARRAYREF );
+
+
+# needs to be a global so we can use local()
+use vars qw( $Checked );
+
+my %Meta;
+my %Cache;
+my %Times;
+
+{
+ my $spec = { parent => SCALAR_TYPE,
+ roots => SCALAR_TYPE,
+ id => SCALAR_TYPE,
+ order_by => { type => OBJECT | ARRAYREF,
+ optional => 1 },
+ flags => ARRAYREF_TYPE( default => [] ),
+ first => SCALAR_TYPE( default => 0 ),
+ };
+ sub _build_cache
+ {
+ my $class = shift;
+ my %p = validate( @_, $spec );
+
+ my $first = delete $p{first};
+
+ my $r = $p{roots};
+
+ my $roots = $class->$r();
+
+ $Cache{$class} = { roots => [] };
+ $Meta{$class}{params} = \%p;
+
+ my $clean = $class;
+ $clean =~ s/::/-/g;
+ $Meta{$class}{file} =
+ File::Spec->catfile( File::Spec->tmpdir, $clean );
+
+ $class->_add_nodes($roots);
+
+ my $f = $Meta{$class}{file};
+
+ _touch_file($f) if $first && $ENV{MOD_PERL};
+
+ $Meta{$class}{last_build} = (stat $f)[9];
+
+ # $class->_dump( '_cached_roots', 0 );
+ }
+}
+
+sub _touch_file
+{
+ my $file = shift;
+
+ open my $fh, ">$file" or die "Cannot write to $file";
+ # The contents don't matter, only the last mod time.
+ print $fh "1\n";
+ close $fh;
+
+ unless ( $> || $< )
+ {
+ my ( $uid, $gid ) = _get_uid_gid();
+
+ chown $uid, $gid, $file
+ or die "Cannot chown $file to Apache server uid/gid: $!";
+ }
+}
+
+# Copied from Mason's ApacheHandler
+sub _get_uid_gid
+{
+ # Apache2 lacks $s->uid.
+ # Workaround by searching the config tree.
+ require Apache2::Directive;
+
+ my $conftree = Apache2::Directive::conftree();
+ my $user = $conftree->lookup('User');
+ my $group = $conftree->lookup('Group');
+
+ $user =~ s/^["'](.*)["']$/$1/;
+ $group =~ s/^["'](.*)["']$/$1/;
+
+ my $uid = $user ? getpwnam($user) : $>;
+ my $gid = $group ? getgrnam($group) : $);
+
+ return ( $uid, $gid );
+}
+
+sub _dump
+{
+ my $thing = shift;
+ my $meth = shift;
+ my $i = shift;
+
+ foreach my $n ( $thing->$meth() )
+ {
+ print ' ' x $i;
+ print '- ';
+ print $n->name;
+ print "\n";
+
+ $n->_dump( 'children', $i + 2 );
+ }
+}
+
+sub _add_nodes
+{
+ my $class = shift;
+ my $cursor = shift;
+ my $parent = shift;
+ my $children = shift;
+
+ my $table = $class->table;
+ my $parent_col = $table->column( $Meta{$class}{params}{parent} );
+
+ my $id = $Meta{$class}{params}{id};
+
+ while ( my $node = $cursor->next )
+ {
+ if ($children)
+ {
+ push @$children, $node;
+ }
+ else
+ {
+ push @{ $Cache{$class}{roots} }, $node;
+ }
+
+ my $id_val = $node->$id();
+
+ $Cache{$class}{by_id}{$id_val} = $node;
+
+ foreach my $flag ( @{ $Meta{$class}{params}{flags} } )
+ {
+ $Cache{$class}{nodes}{$id_val}{flags}{$flag} = $node->$flag();
+ }
+
+ $Cache{$class}{nodes}{$id_val}{parent} = $parent;
+
+ my $cursor =
+ $table->rows_where
+ ( where =>
+ [ $parent_col, '=', $id_val ],
+ ( $Meta{$class}{params}{order_by} ?
+ ( order_by => $Meta{$class}{params}{order_by} ) :
+ ()
+ )
+ );
+
+ my $children =
+ Class::AlzaboWrapper::Cursor->new( cursor => $cursor );
+
+ my @children;
+ $Cache{$class}{nodes}{$id_val}{children} = \@children;
+
+ $class->_add_nodes( $children, $node, \@children );
+ }
+}
+
+sub _cached_roots
+{
+ my $class = shift;
+
+ $class->_check_cache_time;
+
+ return @{ $Cache{$class}{roots} };
+}
+
+sub all
+{
+ my $class = shift;
+
+ $class->_check_cache_time;
+
+ local $Checked = 1;
+
+ return map { $_, $_->descendants } @{ $Cache{$class}{roots} };
+}
+*All = \&all;
+
+sub all_with_flag
+{
+ my $class = shift;
+ my $flag = shift;
+ my $val = shift;
+
+ $class->_check_cache_time;
+
+ my $id = $Meta{$class}{params}{id};
+
+ local $Checked = 1;
+
+ return
+ ( grep { $Cache{$class}{nodes}{ $_->$id() }{flags}{$flag} == $val }
+ map { $_, $_->descendants }
+ grep { $Cache{$class}{nodes}{ $_->$id() }{flags}{$flag} == $val }
+ @{ $Cache{$class}{roots} }
+ );
+}
+
+sub ByID
+{
+ my $class = shift;
+ my $id = shift;
+
+ return $Cache{$class}{by_id}{$id};
+}
+
+sub parent
+{
+ my $self = shift;
+ my $class = ref $self;
+
+ $class->_check_cache_time unless $Checked;
+
+ my $id = $Meta{$class}{params}{id};
+
+ return $Cache{$class}{nodes}{ $self->$id() }{parent};
+}
+
+sub children
+{
+ my $self = shift;
+ my $class = ref $self;
+
+ my $id = $Meta{$class}{params}{id};
+
+ return $class->children_of( $self->$id() );
+}
+
+sub children_of
+{
+ my $class = shift;
+ my $id_val = shift;
+
+ return $class->_cached_roots unless defined $id_val;
+
+ $class->_check_cache_time unless $Checked;
+
+ return ( $Cache{$class}{nodes}{$id_val}{children} ?
+ @{ $Cache{$class}{nodes}{$id_val}{children} } :
+ ()
+ );
+}
+
+sub _cached_children_with_flag
+{
+ my $self = shift;
+ my $flag = shift;
+ my $val = shift;
+ my $class = ref $self;
+
+ $class->_check_cache_time;
+
+ my $id = $Meta{$class}{params}{id};
+
+ return
+ ( grep { $Cache{$class}{nodes}{ $_->$id() }{flags}{$flag} == $val }
+ @{ $Cache{$class}{nodes}{ $self->$id() }{children} }
+ );
+}
+
+sub child_count
+{
+ my $self = shift;
+ my $class = ref $self;
+
+ $class->_check_cache_time;
+
+ my $id = $Meta{$class}{params}{id};
+
+ # potential rows
+ return 0 unless defined $self->$id();
+
+ return 0 unless defined $Cache{$class}{nodes}{ $self->$id() }{children};
+
+ return scalar @{ $Cache{$class}{nodes}{ $self->$id() }{children} };
+}
+
+sub _cached_child_count_with_flag
+{
+ my $self = shift;
+ my $flag = shift;
+ my $val = shift;
+ my $class = ref $self;
+
+ $class->_check_cache_time;
+
+ my $id = $Meta{$class}{params}{id};
+
+ return
+ scalar ( grep { $Cache{$class}{nodes}{ $_->$id() }{flags}{$flag} == $val }
+ @{ $Cache{$class}{nodes}{ $self->$id() }{children} }
+ );
+}
+
+sub _cached_ancestors
+{
+ my $self = shift;
+ my $class = ref $self;
+
+ my @a;
+
+ $class->_check_cache_time;
+
+ local $Checked = 1;
+
+ my $node = $self;
+ while ( $node = $node->parent )
+ {
+ unshift @a, $node;
+ }
+
+ return @a;
+}
+
+sub _cached_descendants
+{
+ my $self = shift;
+ my $class = ref $self;
+
+ $class->_check_cache_time unless $Checked;
+
+ local $Checked = 1;
+
+ my @d = $self->children;
+
+ my @c = @d;
+
+ while ( my $node = shift @c )
+ {
+ my @c1 = $node->children;
+
+ push @d, @c1;
+
+ push @c, @c1;
+ }
+
+ return @d;
+}
+
+sub descendants { $_[0]->_cached_descendants }
+
+sub descendant_ids
+{
+ my $self = shift;
+ my $class = ref $self;
+
+ my $id = $Meta{$class}{params}{id};
+
+ return map { $_->$id() } $self->descendants;
+}
+
+sub ancestors { $_[0]->_cached_ancestors }
+
+sub ancestor_ids
+{
+ my $self = shift;
+ my $class = ref $self;
+
+ my $id = $Meta{$class}{params}{id};
+
+ return map { $_->$id() } $self->ancestors;
+}
+
+sub _check_cache_time
+{
+ my $class = shift;
+
+ return unless $ENV{MOD_PERL};
+
+ my $last_mod = (stat $Meta{$class}{file})[9];
+
+ if ( $last_mod > $Meta{$class}{last_build} )
+ {
+ $class->_rebuild_cache;
+ }
+}
+
+sub _rebuild_cache
+{
+ my $class = shift;
+
+ my %p = %{ $Meta{$class}{params} };
+
+ $class->_build_cache(%p);
+}
+
+sub _cached_data_has_changed
+{
+ my $class = ref $_[0] || $_[0];
+
+ $class->_rebuild_cache;
+
+ _touch_file( $Meta{$class}{file} );
+}
+
+1;
+
+__END__
+
69 lib/VegGuide/Category.pm
@@ -0,0 +1,69 @@
+package VegGuide::Category;
+
+use strict;
+use warnings;
+
+use Lingua::EN::Inflect qw( PL );
+use VegGuide::Schema;
+use VegGuide::AlzaboWrapper ( table => VegGuide::Schema->Schema()->Category_t() );
+
+
+my %Categories;
+my %CategoriesByName;
+
+BEGIN
+{
+ %Categories =
+ ( map { $_->select('category_id') => VegGuide::Category->SUPER::new( object => $_ ) }
+ VegGuide::Schema->Connect->Category_t->all_rows->all_rows
+ );
+
+ %CategoriesByName =
+ map { $_->name => $_ } values %Categories;
+}
+
+sub new
+{
+ my $class = shift;
+ my %p = @_;
+
+ return $Categories{ $p{category_id} };
+}
+
+sub plural_name
+{
+ return PL( $_[0]->name() );
+}
+
+sub All
+{
+ return sort { $a->display_order <=> $b->display_order } values %Categories;
+}
+
+sub Restaurant
+{
+ return $CategoriesByName{Restaurant};
+}
+
+sub GroceryBakeryDeli
+{
+ return $CategoriesByName{'Grocery/Bakery/Deli'};
+}
+
+sub Bar
+{
+ return $CategoriesByName{Bar};
+}
+
+sub CoffeeTeaJuice
+{
+ return $CategoriesByName{'Coffee/Tea/Juice'};
+}
+
+sub Organization
+{
+ return $CategoriesByName{Organization};
+}
+
+
+1;
91 lib/VegGuide/Chart.pm
<
@@ -0,0 +1,91 @@
+package VegGuide::Chart;
+
+use strict;
+use warnings;
+
+use Chart::OFC;
+use List::Util qw( max );
+use Math::Round qw( nhimult );
+use VegGuide::User;
+use VegGuide::Vendor;
+
+
+sub GrowthOverTime
+{
+ my $start = VegGuide::Vendor->EarliestCreationDate();
+ $start->truncate( to => 'month' )->add( months => 1 );
+
+ my $today = DateTime->today();
+
+ my @dates;
+ for ( my $date = $start; $date < $today; $date->add( months => 1 ) )
+ {
+ push @dates, $date->clone;
+ }
+
+ my @user_count;
+ my @vendor_count;
+
+ my @labels;
+ for my $d (@dates)
+ {
+ push @labels, $d->ymd;
+
+ my $u_count =
+ VegGuide::User->CountForDateSpan
+ ( start_date => $d->clone()->subtract( months => 1 ),
+ end_date => $d,
+ );
+
+ push @user_count, ( $user_count[-1] || 0 ) + $u_count;
+
+ my $v_count =
+ VegGuide::Vendor->CountForDateSpan
+ ( start_date => $d->clone()->subtract( months => 1 ),
+ end_date => $d,
+ );
+
+ push @vendor_count, ( $vendor_count[-1] || 0 ) + $v_count;
+ }
+
+ my $xaxis =
+ Chart::OFC::XAxis->new
+ ( axis_label => 'Date',
+ labels => \@labels,
+ label_steps => 4,
+ orientation => 'diagonal',
+ );
+
+ my $biggest = max @user_count, @vendor_count;
+
+ my $yaxis =
+ Chart::OFC::YAxis->new( axis_label => 'Count',
+ label_steps => 500,
+ max => nhimult( 500, $biggest + 1 ),
+ );
+
+ my $user_ds =
+ Chart::OFC::Dataset::Line->new( values => \@user_count,
+ label => 'Users',
+ color => '#08B000',
+ );
+
+ my $vendor_ds =
+ Chart::OFC::Dataset::Line->new( values => \@vendor_count,