WP Recipe Maker - Version 1.7.0

Version Description

  • Feature: Import recipe from text
  • Feature: Add nofollow links in summary and instructions
  • Feature: Import recipes from Meal Planner Pro
  • Feature: Import recipes from BigOven
  • Improvement: Setting to disable comment ratings
  • Improvement: Recognize unicode fractions when importing ingredients
  • Improvement: Import nutrition facts from WP Ultimate Recipe
  • Fix: Only show nutritional metadata if present
  • Fix: Consistent behaviour for automatic time calculations
Download this release

Release Info

Developer BrechtVds
Plugin Icon 128x128 WP Recipe Maker
Version 1.7.0
Comparing to
See all releases

Code changes from version 1.6.0 to 1.7.0

Files changed (41) hide show
  1. assets/css/admin/modal.min.css +1 -1
  2. assets/css/admin/modal.scss +1 -0
  3. assets/css/admin/modal/_container.scss +15 -0
  4. assets/css/admin/modal/_form.scss +2 -1
  5. assets/css/admin/modal/_import-text.scss +36 -0
  6. assets/css/public/_template_reset.scss +10 -0
  7. assets/css/public/public.min.css +1 -1
  8. assets/css/public/public.scss +1 -0
  9. assets/js/admin/import-text.js +249 -0
  10. assets/js/admin/modal.js +2 -0
  11. assets/js/admin/recipe-form.js +66 -37
  12. assets/js/admin/rich-editor.js +579 -4
  13. includes/admin/class-wprm-recipe-parser.php +63 -3
  14. includes/admin/import/class-wprm-import-bigoven.php +291 -0
  15. includes/admin/import/class-wprm-import-mealplannerpro.php +396 -0
  16. includes/admin/import/class-wprm-import-wpultimaterecipe.php +25 -1
  17. includes/admin/modal/class-wprm-modal.php +11 -0
  18. includes/class-wp-recipe-maker.php +9 -3
  19. includes/public/class-wprm-fallback-recipe.php +33 -0
  20. includes/public/class-wprm-metadata.php +1 -1
  21. includes/public/class-wprm-print.php +3 -0
  22. includes/public/class-wprm-settings.php +50 -11
  23. includes/public/class-wprm-shortcode.php +35 -1
  24. includes/public/class-wprm-template-manager.php +62 -16
  25. readme.txt +22 -0
  26. templates/admin/menu/addons.php +4 -0
  27. templates/admin/menu/faq/whats_new.php +13 -0
  28. templates/admin/modal/modal.php +3 -0
  29. templates/admin/modal/tabs/import-text.php +74 -0
  30. templates/admin/modal/tabs/recipe-details.php +16 -5
  31. templates/admin/modal/tabs/recipe-ingredients-instructions.php +4 -0
  32. templates/admin/settings.php +1 -0
  33. templates/admin/settings/features.php +68 -0
  34. templates/recipe/clean-print-with-image/clean-print-with-image.min.css +1 -1
  35. templates/recipe/clean-print-with-image/clean-print-with-image.scss +1 -0
  36. templates/recipe/clean-print/clean-print.min.css +1 -1
  37. templates/recipe/clean-print/clean-print.scss +1 -0
  38. templates/recipe/simple/simple.min.css +1 -1
  39. templates/recipe/simple/simple.scss +1 -0
  40. vendor/texthighlighter/TextHighlighter.min.js +40 -0
  41. wp-recipe-maker.php +1 -1
assets/css/admin/modal.min.css CHANGED
@@ -1 +1 @@
1
- .wprm-frame-title .dashicons,.wprm-frame.hide-router .wprm-frame-router,.wprm-modal-container{display:none}.select2_wprm-container{z-index:100075}.select2_wprm-container.select2_wprm-container--focus .select2_wprm-selection{border-color:#5b9dd9}.select2_wprm-container li{margin:0}.select2_wprm-container .select2_wprm-selection{border-color:#ddd}.medium-editor-toolbar{z-index:100080}.medium-editor-element{border:1px solid #ddd;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,.07);box-shadow:inset 0 1px 2px rgba(0,0,0,.07);min-height:68px;padding:3px 5px}.medium-editor-element:focus{border-color:#5b9dd9;-webkit-box-shadow:0 0 2px rgba(30,140,190,.8);box-shadow:0 0 2px rgba(30,140,190,.8)}.medium-editor-element:after{font-style:normal;color:#999;opacity:.5;font-size:14px;line-height:28px}.medium-editor-element p:first-child{margin-top:0}.medium-editor-element p:last-child{margin-bottom:0}.wprm-modal-hint{max-width:400px;margin-bottom:20px}.wprm-modal-hint .wprm-modal-hint-header{font-weight:700;font-variant:small-caps}.wprm-modal-hint .wprm-modal-hint-text{font-style:italic}.wprm-modal-backdrop{position:fixed;top:0;left:0;right:0;bottom:0;min-height:360px;background:#000;opacity:.7;z-index:100000}.wprm-modal{position:fixed;top:30px;left:30px;right:30px;bottom:30px;z-index:100050}.wprm-modal *{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.wprm-modal-close{position:absolute;top:0;right:0;width:50px;height:50px;padding:0;z-index:1000;-webkit-transition:color .1s ease-in-out,background .1s ease-in-out;transition:color .1s ease-in-out,background .1s ease-in-out}.wprm-modal-close .wprm-modal-icon:before{content:"\f158";font:400 20px/1 dashicons;speak:none;vertical-align:middle;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;color:#666}.wprm-modal-content{position:absolute;top:0;left:0;right:0;bottom:0;overflow:auto;min-height:300px;-webkit-box-shadow:0 5px 15px rgba(0,0,0,.7);box-shadow:0 5px 15px rgba(0,0,0,.7);background:#fcfcfc;-webkit-font-smoothing:subpixel-antialiased}.wprm-modal-content ::-webkit-input-placeholder{color:#999;opacity:.5}.wprm-modal-content :-moz-placeholder{color:#999;opacity:.5}.wprm-modal-content ::-moz-placeholder{color:#999;opacity:.5}.wprm-modal-content :-ms-input-placeholder{color:#999;opacity:.5}.wprm-frame{overflow:hidden;right:0}.wprm-frame,.wprm-frame-menu{position:absolute;left:0;bottom:0;top:0}.wprm-frame-menu{width:200px;z-index:150}.wprm-frame-title{top:0;height:50px}.wprm-frame-router,.wprm-frame-title{z-index:200;left:200px;position:absolute;right:0}.wprm-menu-item{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}.wprm-menu{position:absolute;left:0;margin:0;padding:10px 0;border-right-width:1px;border-right-style:solid;border-right-color:#ccc;user-select:none}.wprm-menu,.wprm-sidebar{top:0;bottom:0;background:#f3f3f3;right:0}.wprm-menu .active,.wprm-menu .active:hover{color:#23282d;font-weight:700}.wprm-menu>a{display:block;position:relative;padding:8px 20px;margin:0;color:#0073aa}.wprm-frame .hidden,.wprm-frame-content-tab:not(.active),.wprm-router:not(.active){display:none}.wprm-menu>a,.wprm-router>a{line-height:18px;font-size:14px;text-decoration:none}.wprm-frame a{border-bottom:none;color:#0073aa}.wprm-menu .separator{height:0;margin:12px 20px;padding:0;border-top:1px solid #ddd}.wprm-frame-title h1{padding:0 16px;font-size:22px;line-height:50px;margin:0}.wprm-frame-router{top:50px;height:36px}.wprm-router{position:relative;padding:0 6px;margin:0;clear:both;user-select:none}.wprm-router>a{position:relative;float:left;padding:8px 10px 9px;margin:0;height:18px}.wprm-router a{-webkit-transition:none;transition:none}.wprm-router>a:last-child{border-right:0}.wprm-router .active,.wprm-router>a.active:last-child{margin:-1px -1px 0;background:#fff;border:1px solid #ddd;border-bottom:none}.wprm-router .active,.wprm-router .active:hover{color:#32373c}.wprm-frame-content{position:absolute;top:84px;left:200px;right:0;bottom:61px;height:auto;width:auto;margin:0;overflow:auto;background:#fff;border-top:1px solid #ddd;border-bottom:1px solid #ddd}.wprm-frame-toolbar,.wprm-toolbar{position:absolute;right:0;z-index:100;height:60px}.wprm-frame-content-tab{margin:20px}.wprm-frame-toolbar{left:200px;bottom:0}.wprm-toolbar{top:0;left:0;padding:0 16px;border:0 solid #ddd;overflow:hidden}.wprm-modal-content .wprm-toolbar-primary.search-form{width:33%}.wprm-toolbar-primary{float:right;height:100%}.wprm-modal-content .wprm-toolbar-primary .wprm-button{float:right}.wprm-toolbar-primary>.wprm-button,.wprm-toolbar-primary>.wprm-button-group{margin-left:10px;float:left;margin-top:15px}@media only screen and (max-width:900px){.wprm-frame:not(.hide-menu) .wprm-frame-title .dashicons{display:inline-block;line-height:50px}.wprm-frame:not(.hide-menu) .wprm-frame-menu{position:static;width:0}.wprm-frame:not(.hide-menu) .wprm-frame-content,.wprm-frame:not(.hide-menu) .wprm-frame-router,.wprm-frame:not(.hide-menu) .wprm-frame-title,.wprm-frame:not(.hide-menu) .wprm-frame-toolbar,.wprm-frame:not(.hide-menu) .wprm-menu.visible{left:0}.wprm-frame:not(.hide-menu) .wprm-menu{width:auto;max-width:80%;overflow:auto;z-index:2000;top:50px;left:-300px;right:auto;bottom:auto;padding:5px 0;border:1px solid #ccc}.wprm-frame:not(.hide-menu) .wprm-menu>a.active{display:none}.wprm-frame:not(.hide-menu) .wprm-menu>a{padding:12px 16px;font-size:16px}.wprm-frame:not(.hide-menu) .wprm-menu .separator{margin:5px 10px}.wprm-frame:not(.hide-menu) .wprm-frame-title h1{color:#0073aa;line-height:3;font-size:18px;float:left;cursor:pointer}}@media only screen and (max-width:640px),screen and (max-height:400px){.wprm-modal{position:fixed;top:0;left:0;right:0;bottom:0}.wprm-modal .wprm-frame-title{height:40px}}@media only screen and (max-width:480px){.wprm-frame:not(.hide-menu) .wprm-frame-title .dashicons{line-height:40px}.wprm-frame-router,.wprm-frame:not(.hide-menu) .wprm-menu{top:40px}.wprm-frame:not(.hide-menu) .wprm-frame-title h1,.wprm-modal .wprm-frame-title h1{font-size:18px;line-height:40px}.wprm-frame-content{top:74px}}.wprm-recipe-form{max-width:600px}.wprm-recipe-form .wprm-recipe-form-container{margin-bottom:15px;vertical-align:top}.wprm-recipe-form .wprm-recipe-form-container-halfs{display:inline-block;width:50%}.wprm-recipe-form .wprm-recipe-form-container-thirds{display:inline-block;width:33.3%}.wprm-recipe-form label{display:block;font-weight:700;margin-bottom:5px}.wprm-recipe-form input{margin:0;width:100%;max-width:120px;height:34px;line-height:34px}.wprm-recipe-form input[type=number]{max-width:50px}.wprm-recipe-form input.select2_wprm-search__field{height:18px;line-height:18px}.wprm-recipe-form select{width:100%}.wprm-recipe-form textarea{width:100%;resize:vertical}.wprm-recipe-details-form input#wprm-recipe-name{max-width:450px}.wprm-recipe-details-form input#wprm-recipe-author-name{max-width:none}.wprm-recipe-details-form input#wprm-recipe-calories{max-width:70px}.wprm-recipe-details-form .wprm-recipe-image-preview{float:right;max-width:100px}.wprm-recipe-details-form .wprm-recipe-image-preview img{max-width:100%;height:auto}.wprm-recipe-details-form .wprm-recipe-summary-container{clear:both}@media only screen and (max-width:480px){.wprm-recipe-form .wprm-recipe-form-container-halfs,.wprm-recipe-form .wprm-recipe-form-container-thirds{display:block;width:100%}.wprm-recipe-details-form .wprm-recipe-image-preview{float:none;margin:10px auto}}.wprm-recipe-ingredients-form,.wprm-recipe-instructions-form{max-width:750px;margin-bottom:15px}.wprm-recipe-ingredients-form input,.wprm-recipe-instructions-form input{margin:0;width:100%;height:34px;line-height:34px}.wprm-recipe-ingredients-form textarea,.wprm-recipe-instructions-form textarea{width:100%;resize:vertical}.wprm-recipe-ingredients-form ::-webkit-input-placeholder,.wprm-recipe-instructions-form ::-webkit-input-placeholder{color:#999;opacity:.5}.wprm-recipe-ingredients-form :-moz-placeholder,.wprm-recipe-instructions-form :-moz-placeholder{color:#999;opacity:.5}.wprm-recipe-ingredients-form ::-moz-placeholder,.wprm-recipe-instructions-form ::-moz-placeholder{color:#999;opacity:.5}.wprm-recipe-ingredients-form :-ms-input-placeholder,.wprm-recipe-instructions-form :-ms-input-placeholder{color:#999;opacity:.5}.wprm-recipe-ingredients-form table,.wprm-recipe-instructions-form table{width:100%}.wprm-recipe-ingredients-form table.wprm-recipe-ingredients-container th,.wprm-recipe-instructions-form table.wprm-recipe-ingredients-container th{text-align:left}.wprm-recipe-ingredients-form table.wprm-recipe-ingredients-container th:nth-child(1),.wprm-recipe-ingredients-form table.wprm-recipe-ingredients-container th:nth-child(6),.wprm-recipe-instructions-form table.wprm-recipe-ingredients-container th:nth-child(1),.wprm-recipe-instructions-form table.wprm-recipe-ingredients-container th:nth-child(6){width:5%;text-align:center}.wprm-recipe-ingredients-form table.wprm-recipe-ingredients-container th:nth-child(2),.wprm-recipe-instructions-form table.wprm-recipe-ingredients-container th:nth-child(2){width:10%}.wprm-recipe-ingredients-form table.wprm-recipe-ingredients-container th:nth-child(3),.wprm-recipe-instructions-form table.wprm-recipe-ingredients-container th:nth-child(3){width:15%}.wprm-recipe-ingredients-form table.wprm-recipe-ingredients-container th:nth-child(4),.wprm-recipe-instructions-form table.wprm-recipe-ingredients-container th:nth-child(4){width:40%}.wprm-recipe-ingredients-form table.wprm-recipe-ingredients-container th:nth-child(5),.wprm-recipe-instructions-form table.wprm-recipe-ingredients-container th:nth-child(5){width:25%}.wprm-recipe-ingredients-form table.wprm-recipe-instructions-container th,.wprm-recipe-instructions-form table.wprm-recipe-instructions-container th{text-align:left}.wprm-recipe-ingredients-form table.wprm-recipe-instructions-container th:nth-child(1),.wprm-recipe-ingredients-form table.wprm-recipe-instructions-container th:nth-child(4),.wprm-recipe-instructions-form table.wprm-recipe-instructions-container th:nth-child(1),.wprm-recipe-instructions-form table.wprm-recipe-instructions-container th:nth-child(4){width:5%;text-align:center}.wprm-recipe-ingredients-form table.wprm-recipe-instructions-container th:nth-child(2),.wprm-recipe-instructions-form table.wprm-recipe-instructions-container th:nth-child(2){width:65%}.wprm-recipe-ingredients-form table.wprm-recipe-instructions-container th:nth-child(3),.wprm-recipe-instructions-form table.wprm-recipe-instructions-container th:nth-child(3){width:25%}.wprm-recipe-ingredients-form table .wprm-recipe-ingredients-placeholder,.wprm-recipe-ingredients-form table .wprm-recipe-instructions-placeholder,.wprm-recipe-instructions-form table .wprm-recipe-ingredients-placeholder,.wprm-recipe-instructions-form table .wprm-recipe-instructions-placeholder{display:none}.wprm-recipe-ingredients-form table td,.wprm-recipe-instructions-form table td{text-align:left;vertical-align:top}.wprm-recipe-ingredients-form table td:first-child,.wprm-recipe-ingredients-form table td:last-child,.wprm-recipe-instructions-form table td:first-child,.wprm-recipe-instructions-form table td:last-child{text-align:center;vertical-align:middle}.wprm-recipe-ingredients-form .ui-sortable-helper,.wprm-recipe-instructions-form .ui-sortable-helper{display:table}.wprm-recipe-ingredients-form .ui-sortable-helper .wprm-recipe-instruction-text,.wprm-recipe-instructions-form .ui-sortable-helper .wprm-recipe-instruction-text{min-width:200px}.wprm-recipe-ingredients-form .wprm-recipe-ingredients-instructions-sort,.wprm-recipe-instructions-form .wprm-recipe-ingredients-instructions-sort{cursor:move}.wprm-recipe-ingredients-form .wprm-recipe-ingredients-instructions-delete,.wprm-recipe-instructions-form .wprm-recipe-ingredients-instructions-delete{cursor:pointer}.wprm-recipe-ingredients-form .wprm-recipe-ingredients-instructions-delete,.wprm-recipe-ingredients-form .wprm-recipe-ingredients-instructions-sort,.wprm-recipe-instructions-form .wprm-recipe-ingredients-instructions-delete,.wprm-recipe-instructions-form .wprm-recipe-ingredients-instructions-sort{color:#999;opacity:.5}.wprm-recipe-ingredients-form .wprm-recipe-ingredients-instructions-delete:hover,.wprm-recipe-ingredients-form .wprm-recipe-ingredients-instructions-sort:hover,.wprm-recipe-instructions-form .wprm-recipe-ingredients-instructions-delete:hover,.wprm-recipe-instructions-form .wprm-recipe-ingredients-instructions-sort:hover{color:#444;opacity:1}.wprm-recipe-ingredients-form .wprm-recipe-ingredients-actions,.wprm-recipe-ingredients-form .wprm-recipe-instructions-actions,.wprm-recipe-instructions-form .wprm-recipe-ingredients-actions,.wprm-recipe-instructions-form .wprm-recipe-instructions-actions{margin:10px}.wprm-recipe-ingredients-form .wprm-recipe-image-preview,.wprm-recipe-instructions-form .wprm-recipe-image-preview{max-width:75px}.wprm-recipe-ingredients-form .wprm-recipe-image-preview img,.wprm-recipe-instructions-form .wprm-recipe-image-preview img{max-width:100%;height:auto}.wprm-shortcode-builder .wprm-shortcode-builder-container{margin-bottom:15px;vertical-align:top}.wprm-shortcode-builder .wprm-shortcode-builder-container-halfs{display:inline-block;width:50%}.wprm-shortcode-builder .wprm-shortcode-builder-container-thirds{display:inline-block;width:33.3%}.wprm-shortcode-builder label{display:block;font-weight:700;margin-bottom:5px}.wprm-shortcode-builder .wprm-shortcode-builder-helper{margin-left:5px;font-size:.8em;font-style:italic}.wprm-shortcode-builder input{margin:0;width:100%;max-width:250px;height:34px;line-height:34px}.wprm-shortcode-builder input.select2_wprm-search__field{height:18px;line-height:18px}.wprm-shortcode-builder select{width:250px}
1
+ .wprm-frame-title .dashicons,.wprm-frame.hide-router .wprm-frame-router,.wprm-modal-container{display:none}.wprm-easyrecipe-warning{display:none;border:1px solid #8b0000;background-color:rgba(255,0,0,.15);padding:10px 10px 0;margin-bottom:10px}.select2_wprm-container{z-index:100075}.select2_wprm-container.select2_wprm-container--focus .select2_wprm-selection{border-color:#5b9dd9}.select2_wprm-container li{margin:0}.select2_wprm-container .select2_wprm-selection{border-color:#ddd}.medium-editor-toolbar{z-index:100080}.medium-editor-element{border:1px solid #ddd;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,.07);box-shadow:inset 0 1px 2px rgba(0,0,0,.07);min-height:68px;padding:3px 5px}.medium-editor-element:focus{border-color:#5b9dd9;-webkit-box-shadow:0 0 2px rgba(30,140,190,.8);box-shadow:0 0 2px rgba(30,140,190,.8)}.medium-editor-element:after{font-style:normal;color:#999;opacity:.5;font-size:14px;line-height:28px}.medium-editor-element p:first-child{margin-top:0}.medium-editor-element p:last-child{margin-bottom:0}.wprm-modal-hint{max-width:400px;margin-bottom:20px}.wprm-modal-hint .wprm-modal-hint-header{font-weight:700;font-variant:small-caps}.wprm-modal-hint .wprm-modal-hint-text{font-style:italic}.wprm-modal-backdrop{position:fixed;top:0;left:0;right:0;bottom:0;min-height:360px;background:#000;opacity:.7;z-index:100000}.wprm-modal{position:fixed;top:30px;left:30px;right:30px;bottom:30px;z-index:100050}.wprm-modal *{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.wprm-modal-close{position:absolute;top:0;right:0;width:50px;height:50px;padding:0;z-index:1000;-webkit-transition:color .1s ease-in-out,background .1s ease-in-out;transition:color .1s ease-in-out,background .1s ease-in-out}.wprm-modal-close .wprm-modal-icon:before{content:"\f158";font:400 20px/1 dashicons;speak:none;vertical-align:middle;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;color:#666}.wprm-modal-content{position:absolute;top:0;left:0;right:0;bottom:0;overflow:auto;min-height:300px;-webkit-box-shadow:0 5px 15px rgba(0,0,0,.7);box-shadow:0 5px 15px rgba(0,0,0,.7);background:#fcfcfc;-webkit-font-smoothing:subpixel-antialiased}.wprm-modal-content ::-webkit-input-placeholder{color:#999;opacity:.5}.wprm-modal-content :-moz-placeholder{color:#999;opacity:.5}.wprm-modal-content ::-moz-placeholder{color:#999;opacity:.5}.wprm-modal-content :-ms-input-placeholder{color:#999;opacity:.5}.wprm-frame{overflow:hidden;right:0}.wprm-frame,.wprm-frame-menu{position:absolute;left:0;bottom:0;top:0}.wprm-frame-menu{width:200px;z-index:150}.wprm-frame-title{top:0;height:50px}.wprm-frame-router,.wprm-frame-title{z-index:200;left:200px;position:absolute;right:0}.wprm-menu-item{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}.wprm-menu{position:absolute;left:0;margin:0;padding:10px 0;border-right-width:1px;border-right-style:solid;border-right-color:#ccc;user-select:none}.wprm-menu-hidden{padding:5px 5px 0 10px;font-style:italic}.wprm-menu,.wprm-sidebar{top:0;bottom:0;background:#f3f3f3;right:0}.wprm-menu .active,.wprm-menu .active:hover{color:#23282d;font-weight:700}.wprm-menu>a{display:block;position:relative;padding:8px 20px;margin:0;color:#0073aa}.wprm-frame .hidden,.wprm-frame-content-tab:not(.active),.wprm-router:not(.active){display:none}.wprm-menu>a,.wprm-router>a{line-height:18px;font-size:14px;text-decoration:none}.wprm-frame a{border-bottom:none;color:#0073aa}.wprm-menu .separator{height:0;margin:12px 20px;padding:0;border-top:1px solid #ddd}.wprm-frame-title h1{padding:0 16px;font-size:22px;line-height:50px;margin:0}.wprm-frame-router{top:50px;height:36px}.wprm-router{position:relative;padding:0 6px;margin:0;clear:both;user-select:none}.wprm-router>a{position:relative;float:left;padding:8px 10px 9px;margin:0;height:18px}.wprm-router a{-webkit-transition:none;transition:none}.wprm-router>a:last-child{border-right:0}.wprm-router .active,.wprm-router>a.active:last-child{margin:-1px -1px 0;background:#fff;border:1px solid #ddd;border-bottom:none}.wprm-router .active,.wprm-router .active:hover{color:#32373c}.wprm-frame-content{position:absolute;top:84px;left:200px;right:0;bottom:61px;height:auto;width:auto;margin:0;overflow:auto;background:#fff;border-top:1px solid #ddd;border-bottom:1px solid #ddd}.wprm-frame-toolbar,.wprm-toolbar{position:absolute;right:0;z-index:100;height:60px}.wprm-frame-content-tab{margin:20px}.wprm-frame-toolbar{left:200px;bottom:0}.wprm-toolbar{top:0;left:0;padding:0 16px;border:0 solid #ddd;overflow:hidden}.wprm-modal-content .wprm-toolbar-primary.search-form{width:33%}.wprm-toolbar-primary{float:right;height:100%}.wprm-modal-content .wprm-toolbar-primary .wprm-button{float:right}.wprm-toolbar-primary>.wprm-button,.wprm-toolbar-primary>.wprm-button-group{margin-left:10px;float:left;margin-top:15px}@media only screen and (max-width:900px){.wprm-frame:not(.hide-menu) .wprm-frame-title .dashicons{display:inline-block;line-height:50px}.wprm-frame:not(.hide-menu) .wprm-frame-menu{position:static;width:0}.wprm-frame:not(.hide-menu) .wprm-frame-content,.wprm-frame:not(.hide-menu) .wprm-frame-router,.wprm-frame:not(.hide-menu) .wprm-frame-title,.wprm-frame:not(.hide-menu) .wprm-frame-toolbar,.wprm-frame:not(.hide-menu) .wprm-menu.visible{left:0}.wprm-frame:not(.hide-menu) .wprm-menu{width:auto;max-width:80%;overflow:auto;z-index:2000;top:50px;left:-300px;right:auto;bottom:auto;padding:5px 0;border:1px solid #ccc}.wprm-frame:not(.hide-menu) .wprm-menu>a.active{display:none}.wprm-frame:not(.hide-menu) .wprm-menu>a{padding:12px 16px;font-size:16px}.wprm-frame:not(.hide-menu) .wprm-menu .separator{margin:5px 10px}.wprm-frame:not(.hide-menu) .wprm-frame-title h1{color:#0073aa;line-height:3;font-size:18px;float:left;cursor:pointer}}@media only screen and (max-width:640px),screen and (max-height:400px){.wprm-modal{position:fixed;top:0;left:0;right:0;bottom:0}.wprm-modal .wprm-frame-title{height:40px}}@media only screen and (max-width:480px){.wprm-frame:not(.hide-menu) .wprm-frame-title .dashicons{line-height:40px}.wprm-frame-router,.wprm-frame:not(.hide-menu) .wprm-menu{top:40px}.wprm-frame:not(.hide-menu) .wprm-frame-title h1,.wprm-modal .wprm-frame-title h1{font-size:18px;line-height:40px}.wprm-frame-content{top:74px}}.wprm-recipe-form{max-width:600px}.wprm-recipe-form .wprm-recipe-form-container{margin-bottom:15px;vertical-align:top}.wprm-recipe-form .wprm-recipe-form-container-halfs{display:inline-block;width:50%}.wprm-recipe-form .wprm-recipe-form-container-thirds{display:inline-block;width:33.3%}.wprm-recipe-form label{display:block;font-weight:700;margin-bottom:5px}.wprm-recipe-form input[type=text],.wprm-recipe-form input[type=number]{margin:0;width:100%;max-width:120px;height:34px;line-height:34px}.wprm-recipe-form input[type=text][type=number],.wprm-recipe-form input[type=number][type=number]{max-width:50px}.wprm-recipe-form input[type=text].select2_wprm-search__field,.wprm-recipe-form input[type=number].select2_wprm-search__field{height:18px;line-height:18px}.wprm-recipe-form select{width:100%}.wprm-recipe-form textarea{width:100%;resize:vertical}@media only screen and (max-width:480px){.wprm-recipe-form .wprm-recipe-form-container-halfs,.wprm-recipe-form .wprm-recipe-form-container-thirds{display:block;width:100%}}.wprm-recipe-import-text-form .import-text-buttons{margin-bottom:10px}.wprm-recipe-import-text-form .import-text-step{display:none}.wprm-recipe-import-text-form .import-text-step#import-text-step-input{display:block}.wprm-recipe-import-text-form #import-text-highlight-sandbox,.wprm-recipe-import-text-form .import-text-input{margin-top:10px}.wprm-recipe-import-text-form #import-text-ingredient-groups label,.wprm-recipe-import-text-form #import-text-instruction-groups label{display:inline;font-weight:400}.wprm-recipe-import-text-form #import-text-highlight-sandbox{display:none;max-height:400px;overflow:scroll;padding:10px;border:1px dashed #999}.wprm-recipe-details-form input#wprm-recipe-name{max-width:450px}.wprm-recipe-details-form input#wprm-recipe-author-name{max-width:none}.wprm-recipe-details-form input#wprm-recipe-calories{max-width:70px}.wprm-recipe-details-form .wprm-recipe-image-preview{float:right;max-width:100px}.wprm-recipe-details-form .wprm-recipe-image-preview img{max-width:100%;height:auto}.wprm-recipe-details-form .wprm-recipe-summary-container{clear:both}@media only screen and (max-width:480px){.wprm-recipe-details-form .wprm-recipe-image-preview{float:none;margin:10px auto}}.wprm-recipe-ingredients-form,.wprm-recipe-instructions-form{max-width:750px;margin-bottom:15px}.wprm-recipe-ingredients-form input,.wprm-recipe-instructions-form input{margin:0;width:100%;height:34px;line-height:34px}.wprm-recipe-ingredients-form textarea,.wprm-recipe-instructions-form textarea{width:100%;resize:vertical}.wprm-recipe-ingredients-form ::-webkit-input-placeholder,.wprm-recipe-instructions-form ::-webkit-input-placeholder{color:#999;opacity:.5}.wprm-recipe-ingredients-form :-moz-placeholder,.wprm-recipe-instructions-form :-moz-placeholder{color:#999;opacity:.5}.wprm-recipe-ingredients-form ::-moz-placeholder,.wprm-recipe-instructions-form ::-moz-placeholder{color:#999;opacity:.5}.wprm-recipe-ingredients-form :-ms-input-placeholder,.wprm-recipe-instructions-form :-ms-input-placeholder{color:#999;opacity:.5}.wprm-recipe-ingredients-form table,.wprm-recipe-instructions-form table{width:100%}.wprm-recipe-ingredients-form table.wprm-recipe-ingredients-container th,.wprm-recipe-instructions-form table.wprm-recipe-ingredients-container th{text-align:left}.wprm-recipe-ingredients-form table.wprm-recipe-ingredients-container th:nth-child(1),.wprm-recipe-ingredients-form table.wprm-recipe-ingredients-container th:nth-child(6),.wprm-recipe-instructions-form table.wprm-recipe-ingredients-container th:nth-child(1),.wprm-recipe-instructions-form table.wprm-recipe-ingredients-container th:nth-child(6){width:5%;text-align:center}.wprm-recipe-ingredients-form table.wprm-recipe-ingredients-container th:nth-child(2),.wprm-recipe-instructions-form table.wprm-recipe-ingredients-container th:nth-child(2){width:10%}.wprm-recipe-ingredients-form table.wprm-recipe-ingredients-container th:nth-child(3),.wprm-recipe-instructions-form table.wprm-recipe-ingredients-container th:nth-child(3){width:15%}.wprm-recipe-ingredients-form table.wprm-recipe-ingredients-container th:nth-child(4),.wprm-recipe-instructions-form table.wprm-recipe-ingredients-container th:nth-child(4){width:40%}.wprm-recipe-ingredients-form table.wprm-recipe-ingredients-container th:nth-child(5),.wprm-recipe-instructions-form table.wprm-recipe-ingredients-container th:nth-child(5){width:25%}.wprm-recipe-ingredients-form table.wprm-recipe-instructions-container th,.wprm-recipe-instructions-form table.wprm-recipe-instructions-container th{text-align:left}.wprm-recipe-ingredients-form table.wprm-recipe-instructions-container th:nth-child(1),.wprm-recipe-ingredients-form table.wprm-recipe-instructions-container th:nth-child(4),.wprm-recipe-instructions-form table.wprm-recipe-instructions-container th:nth-child(1),.wprm-recipe-instructions-form table.wprm-recipe-instructions-container th:nth-child(4){width:5%;text-align:center}.wprm-recipe-ingredients-form table.wprm-recipe-instructions-container th:nth-child(2),.wprm-recipe-instructions-form table.wprm-recipe-instructions-container th:nth-child(2){width:65%}.wprm-recipe-ingredients-form table.wprm-recipe-instructions-container th:nth-child(3),.wprm-recipe-instructions-form table.wprm-recipe-instructions-container th:nth-child(3){width:25%}.wprm-recipe-ingredients-form table .wprm-recipe-ingredients-placeholder,.wprm-recipe-ingredients-form table .wprm-recipe-instructions-placeholder,.wprm-recipe-instructions-form table .wprm-recipe-ingredients-placeholder,.wprm-recipe-instructions-form table .wprm-recipe-instructions-placeholder{display:none}.wprm-recipe-ingredients-form table td,.wprm-recipe-instructions-form table td{text-align:left;vertical-align:top}.wprm-recipe-ingredients-form table td:first-child,.wprm-recipe-ingredients-form table td:last-child,.wprm-recipe-instructions-form table td:first-child,.wprm-recipe-instructions-form table td:last-child{text-align:center;vertical-align:middle}.wprm-recipe-ingredients-form .ui-sortable-helper,.wprm-recipe-instructions-form .ui-sortable-helper{display:table}.wprm-recipe-ingredients-form .ui-sortable-helper .wprm-recipe-instruction-text,.wprm-recipe-instructions-form .ui-sortable-helper .wprm-recipe-instruction-text{min-width:200px}.wprm-recipe-ingredients-form .wprm-recipe-ingredients-instructions-sort,.wprm-recipe-instructions-form .wprm-recipe-ingredients-instructions-sort{cursor:move}.wprm-recipe-ingredients-form .wprm-recipe-ingredients-instructions-delete,.wprm-recipe-instructions-form .wprm-recipe-ingredients-instructions-delete{cursor:pointer}.wprm-recipe-ingredients-form .wprm-recipe-ingredients-instructions-delete,.wprm-recipe-ingredients-form .wprm-recipe-ingredients-instructions-sort,.wprm-recipe-instructions-form .wprm-recipe-ingredients-instructions-delete,.wprm-recipe-instructions-form .wprm-recipe-ingredients-instructions-sort{color:#999;opacity:.5}.wprm-recipe-ingredients-form .wprm-recipe-ingredients-instructions-delete:hover,.wprm-recipe-ingredients-form .wprm-recipe-ingredients-instructions-sort:hover,.wprm-recipe-instructions-form .wprm-recipe-ingredients-instructions-delete:hover,.wprm-recipe-instructions-form .wprm-recipe-ingredients-instructions-sort:hover{color:#444;opacity:1}.wprm-recipe-ingredients-form .wprm-recipe-ingredients-actions,.wprm-recipe-ingredients-form .wprm-recipe-instructions-actions,.wprm-recipe-instructions-form .wprm-recipe-ingredients-actions,.wprm-recipe-instructions-form .wprm-recipe-instructions-actions{margin:10px}.wprm-recipe-ingredients-form .wprm-recipe-image-preview,.wprm-recipe-instructions-form .wprm-recipe-image-preview{max-width:75px}.wprm-recipe-ingredients-form .wprm-recipe-image-preview img,.wprm-recipe-instructions-form .wprm-recipe-image-preview img{max-width:100%;height:auto}.wprm-shortcode-builder .wprm-shortcode-builder-container{margin-bottom:15px;vertical-align:top}.wprm-shortcode-builder .wprm-shortcode-builder-container-halfs{display:inline-block;width:50%}.wprm-shortcode-builder .wprm-shortcode-builder-container-thirds{display:inline-block;width:33.3%}.wprm-shortcode-builder label{display:block;font-weight:700;margin-bottom:5px}.wprm-shortcode-builder .wprm-shortcode-builder-helper{margin-left:5px;font-size:.8em;font-style:italic}.wprm-shortcode-builder input{margin:0;width:100%;max-width:250px;height:34px;line-height:34px}.wprm-shortcode-builder input.select2_wprm-search__field{height:18px;line-height:18px}.wprm-shortcode-builder select{width:250px}
assets/css/admin/modal.scss CHANGED
@@ -1,5 +1,6 @@
1
  @import "modal/container";
2
  @import "modal/form";
 
3
  @import "modal/recipe-details";
4
  @import "modal/recipe-ingredients";
5
  @import "modal/shortcode-builder";
1
  @import "modal/container";
2
  @import "modal/form";
3
+ @import "modal/import-text";
4
  @import "modal/recipe-details";
5
  @import "modal/recipe-ingredients";
6
  @import "modal/shortcode-builder";
assets/css/admin/modal/_container.scss CHANGED
@@ -1,5 +1,14 @@
1
  // main: ../modal.scss
2
 
 
 
 
 
 
 
 
 
 
3
  .select2_wprm-container {
4
  z-index: 100075;
5
 
@@ -63,6 +72,7 @@
63
  font-style: italic;
64
  }
65
  }
 
66
  /* Media Modal Replication */
67
  .wprm-modal-container {
68
  display: none;
@@ -204,6 +214,11 @@
204
  user-select: none;
205
  }
206
 
 
 
 
 
 
207
  .wprm-menu,
208
  .wprm-sidebar {
209
  top: 0;
1
  // main: ../modal.scss
2
 
3
+ .wprm-easyrecipe-warning {
4
+ display: none;
5
+ border: 1px solid darkred;
6
+ background-color: rgba(255,0,0,0.15);
7
+ padding: 10px;
8
+ padding-bottom: 0;
9
+ margin-bottom: 10px;
10
+ }
11
+
12
  .select2_wprm-container {
13
  z-index: 100075;
14
 
72
  font-style: italic;
73
  }
74
  }
75
+
76
  /* Media Modal Replication */
77
  .wprm-modal-container {
78
  display: none;
214
  user-select: none;
215
  }
216
 
217
+ .wprm-menu-hidden {
218
+ padding: 5px 5px 0 10px;
219
+ font-style: italic;
220
+ }
221
+
222
  .wprm-menu,
223
  .wprm-sidebar {
224
  top: 0;
assets/css/admin/modal/_form.scss CHANGED
@@ -23,7 +23,8 @@
23
  margin-bottom: 5px;
24
  }
25
 
26
- input {
 
27
  margin: 0;
28
  width: 100%;
29
  max-width: 120px;
23
  margin-bottom: 5px;
24
  }
25
 
26
+ input[type="text"],
27
+ input[type="number"] {
28
  margin: 0;
29
  width: 100%;
30
  max-width: 120px;
assets/css/admin/modal/_import-text.scss ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // main: ../modal.scss
2
+
3
+ .wprm-recipe-import-text-form {
4
+ .import-text-buttons {
5
+ margin-bottom: 10px;
6
+ }
7
+
8
+ .import-text-step {
9
+ display: none;
10
+
11
+ &#import-text-step-input {
12
+ display: block;
13
+ }
14
+ }
15
+
16
+ .import-text-input,
17
+ #import-text-highlight-sandbox {
18
+ margin-top: 10px;
19
+ }
20
+
21
+ #import-text-ingredient-groups,
22
+ #import-text-instruction-groups {
23
+ label {
24
+ display: inline;
25
+ font-weight: normal;
26
+ }
27
+ }
28
+
29
+ #import-text-highlight-sandbox {
30
+ display: none;
31
+ max-height: 400px;
32
+ overflow: scroll;
33
+ padding: 10px;
34
+ border: 1px dashed #999;
35
+ }
36
+ }
assets/css/public/_template_reset.scss ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ // main: public.scss
2
+ .wprm-recipe {
3
+ p, li {
4
+ font-size: 1em;
5
+ }
6
+
7
+ li:before {
8
+ display: none;
9
+ }
10
+ }
assets/css/public/public.min.css CHANGED
@@ -1 +1 @@
1
- .comment-form-wprm-rating{display:none}.comment-form-wprm-rating .wprm-rating-star{cursor:pointer}.wprm-rating-star svg{vertical-align:middle;width:16px;height:16px;margin:0}.wprm-rating-star.rated svg polygon{fill:#000}
1
+ .comment-form-wprm-rating,.wprm-recipe li:before{display:none}.comment-form-wprm-rating .wprm-rating-star{cursor:pointer}.wprm-rating-star svg{vertical-align:middle;width:16px;height:16px;margin:0}.wprm-rating-star.rated svg polygon{fill:#000}.wprm-recipe li,.wprm-recipe p{font-size:1em}
assets/css/public/public.scss CHANGED
@@ -1 +1,2 @@
1
  @import "comments";
 
1
  @import "comments";
2
+ @import "template_reset";
assets/js/admin/import-text.js ADDED
@@ -0,0 +1,249 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ var wprm_admin = wprm_admin || {};
2
+
3
+ wprm_admin.text_import_step = '';
4
+ wprm_admin.text_import_highlighter;
5
+ wprm_admin.text_import = {};
6
+
7
+ wprm_admin.start_text_import = function() {
8
+ jQuery('.wprm-button-import-text-reset').removeAttr('disabled');
9
+ jQuery('.wprm-button-import-text-clear').removeAttr('disabled');
10
+ jQuery('.wprm-button-import-text-next').removeAttr('disabled');
11
+
12
+ wprm_admin.text_import_step = 'input';
13
+ jQuery('#import-text-highlight-sandbox').textHighlighter();
14
+ wprm_admin.text_import_highlighter = jQuery('#import-text-highlight-sandbox').getHighlighter()
15
+ };
16
+
17
+ wprm_admin.btn_text_import_reset = function() {
18
+ wprm_admin.btn_text_import_clear(true);
19
+ jQuery('#import-text-highlight-sandbox').text('');
20
+ wprm_admin.text_import = {};
21
+
22
+ jQuery('.wprm-button-import-text-reset').attr('disabled', 'disabled');
23
+ jQuery('.wprm-button-import-text-clear').attr('disabled', 'disabled');
24
+ jQuery('.wprm-button-import-text-next').attr('disabled', 'disabled');
25
+
26
+ jQuery('.import-text-step').hide();
27
+ jQuery('#import-text-step-input').show();
28
+ jQuery('#import-text-highlight-sandbox').hide();
29
+ };
30
+
31
+ wprm_admin.btn_text_import_clear = function(all) {
32
+ if(all || wprm_admin.text_import_step == 'input') {
33
+ jQuery('#import-text-input-recipe').val('');
34
+ }
35
+
36
+ if(all || wprm_admin.text_import_step == 'ingredient-groups') {
37
+ jQuery('#import-text-ingredient-groups').find('input').attr('checked', false);
38
+ }
39
+
40
+ if(all || wprm_admin.text_import_step == 'instruction-groups') {
41
+ jQuery('#import-text-instruction-groups').find('input').attr('checked', false);
42
+ }
43
+
44
+ wprm_admin.text_import_highlighter.removeHighlights();
45
+ };
46
+
47
+ wprm_admin.btn_text_import_next = function() {
48
+ if(wprm_admin.text_import_step == 'input') {
49
+ wprm_admin.text_import.raw = jQuery('#import-text-input-recipe').val();
50
+ jQuery('#import-text-highlight-sandbox').html(wprm_admin.text_import.raw.replace(/\r?\n/g,'<br/>')).show();
51
+
52
+ wprm_admin.text_import_step = 'name';
53
+ } else if(wprm_admin.text_import_step == 'name') {
54
+ wprm_admin.text_import.name = wprm_admin.get_highlighted_text();
55
+
56
+ wprm_admin.text_import_step = 'summary';
57
+ } else if(wprm_admin.text_import_step == 'summary') {
58
+ wprm_admin.text_import.summary = wprm_admin.get_highlighted_text();
59
+
60
+ jQuery('#import-text-highlight-sandbox').show();
61
+
62
+ wprm_admin.text_import_step = 'ingredients';
63
+ } else if(wprm_admin.text_import_step == 'ingredients') {
64
+ var ingredients = wprm_admin.text_import_highlighter.getHighlights();
65
+ wprm_admin.text_import.ingredients_raw = ingredients;
66
+
67
+ jQuery('#import-text-ingredient-groups').html('');
68
+ for(var i = 0, l = ingredients.length; i<l; i++) {
69
+ var text = jQuery(ingredients[i]).text().trim();
70
+ text = text.replace(/\d\.\s+|[a-z]\)\s+|•\s+|[A-Z]\.\s+|[IVX]+\.\s+/g, "");
71
+ var ingredient = '<div class="import-text-ingredient"><input type="checkbox" id="ingredient-' + i + '"> ' + '<label for="ingredient-' + i + '">' + text + '</label></div>';
72
+ jQuery('#import-text-ingredient-groups').append(ingredient);
73
+ }
74
+
75
+ if(ingredients.length == 0) {
76
+ jQuery('#import-text-highlight-sandbox').show();
77
+ wprm_admin.text_import.ingredients = [];
78
+ wprm_admin.text_import_step = 'instructions';
79
+ } else {
80
+ jQuery('#import-text-highlight-sandbox').hide();
81
+ wprm_admin.text_import_step = 'ingredient-groups';
82
+ }
83
+ } else if(wprm_admin.text_import_step == 'ingredient-groups') {
84
+ var ingredients = [],
85
+ ingredient_group = {
86
+ name: '',
87
+ ingredients: []
88
+ };
89
+
90
+ jQuery('#import-text-ingredient-groups').find('.import-text-ingredient').each(function() {
91
+ var is_ingredient_group = jQuery(this).find('input').is(':checked'),
92
+ ingredient = jQuery(this).find('label').text();
93
+
94
+ if(is_ingredient_group) {
95
+ ingredients.push(ingredient_group);
96
+
97
+ ingredient_group = {
98
+ name: ingredient,
99
+ ingredients: []
100
+ }
101
+ } else {
102
+ ingredient_group.ingredients.push({raw: ingredient});
103
+ }
104
+ });
105
+ ingredients.push(ingredient_group);
106
+
107
+ wprm_admin.text_import.ingredients = [];
108
+
109
+ // Parse ingredients
110
+ var data = {
111
+ action: 'wprm_parse_ingredients',
112
+ security: wprm_modal.nonce,
113
+ ingredients: ingredients
114
+ };
115
+
116
+ jQuery.post(wprm_modal.ajax_url, data, function(out) {
117
+ if (out.success) {
118
+ wprm_admin.text_import.ingredients = out.data.ingredients;
119
+ }
120
+ }, 'json');
121
+
122
+ jQuery('#import-text-highlight-sandbox').show();
123
+
124
+ wprm_admin.text_import_step = 'instructions';
125
+ } else if(wprm_admin.text_import_step == 'instructions') {
126
+ var instructions = wprm_admin.text_import_highlighter.getHighlights();
127
+ wprm_admin.text_import.instructions_raw = instructions;
128
+
129
+ jQuery('#import-text-instruction-groups').html('');
130
+ for(var i = 0, l = instructions.length; i<l; i++) {
131
+ var text = jQuery(instructions[i]).text().trim();
132
+ text = text.replace(/\d\.\s+|[a-z]\)\s+|•\s+|[A-Z]\.\s+|[IVX]+\.\s+/g, "");
133
+ var instruction = '<div class="import-text-instruction"><input type="checkbox" id="instruction-' + i + '"> ' + '<label for="instruction-' + i + '">' + text + '</label></div>';
134
+ jQuery('#import-text-instruction-groups').append(instruction);
135
+ }
136
+
137
+ if(instructions.length == 0) {
138
+ jQuery('#import-text-highlight-sandbox').show();
139
+ wprm_admin.text_import.instructions = [];
140
+ wprm_admin.text_import_step = 'notes';
141
+ } else {
142
+ jQuery('#import-text-highlight-sandbox').hide();
143
+ wprm_admin.text_import_step = 'instruction-groups';
144
+ }
145
+ } else if(wprm_admin.text_import_step == 'instruction-groups') {
146
+ var instructions = [],
147
+ instruction_group = {
148
+ name: '',
149
+ instructions: []
150
+ };
151
+
152
+ jQuery('#import-text-instruction-groups').find('.import-text-instruction').each(function() {
153
+ var is_instruction_group = jQuery(this).find('input').is(':checked'),
154
+ instruction = jQuery(this).find('label').text();
155
+
156
+ if(is_instruction_group) {
157
+ instructions.push(instruction_group);
158
+
159
+ instruction_group = {
160
+ name: instruction,
161
+ instructions: []
162
+ }
163
+ } else {
164
+ instruction_group.instructions.push({text: instruction});
165
+ }
166
+ });
167
+ instructions.push(instruction_group);
168
+
169
+ wprm_admin.text_import.instructions = instructions;
170
+
171
+ jQuery('#import-text-highlight-sandbox').show();
172
+
173
+ wprm_admin.text_import_step = 'notes';
174
+ } else if(wprm_admin.text_import_step == 'notes') {
175
+ wprm_admin.text_import.notes = wprm_admin.get_highlighted_text();
176
+
177
+ jQuery('#import-text-highlight-sandbox').hide();
178
+ jQuery('.wprm-button-import-text-clear').attr('disabled', 'disabled');
179
+ jQuery('.wprm-button-import-text-next').attr('disabled', 'disabled');
180
+
181
+ wprm_admin.import_recipe();
182
+ wprm_admin.text_import_step = 'finished';
183
+ }
184
+
185
+ jQuery('.import-text-step').hide();
186
+ jQuery('#import-text-step-' + wprm_admin.text_import_step).show();
187
+ wprm_admin.text_import_highlighter.removeHighlights();
188
+ };
189
+
190
+ wprm_admin.get_highlighted_text = function() {
191
+ var highlight_parts = wprm_admin.text_import_highlighter.getHighlights();
192
+ var highlight = '';
193
+
194
+ for(var i = 0, l = highlight_parts.length; i<l; i++) {
195
+ if(i > 0) {
196
+ highlight += ' ';
197
+ }
198
+ highlight += jQuery(highlight_parts[i]).text().trim();
199
+ }
200
+
201
+ return highlight;
202
+ };
203
+
204
+ wprm_admin.import_recipe = function() {
205
+ if(wprm_admin.text_import.name) {
206
+ jQuery('#wprm-recipe-name').val(wprm_admin.text_import.name);
207
+ }
208
+
209
+ if(wprm_admin.text_import.summary) {
210
+ wprm_admin.rich_editor.setContent(wprm_admin.text_import.summary);
211
+ }
212
+
213
+ if(wprm_admin.text_import.notes) {
214
+ if (typeof tinyMCE !== 'undefined' && tinyMCE.get('wprm_recipe_notes') && !tinyMCE.get('wprm_recipe_notes').isHidden()) {
215
+ tinyMCE.get('wprm_recipe_notes').focus(true);
216
+ tinyMCE.activeEditor.setContent(wprm_admin.text_import.notes);
217
+ } else {
218
+ jQuery('#wprm_recipe_notes').val(wprm_admin.text_import.notes);
219
+ }
220
+ }
221
+
222
+ if(wprm_admin.text_import.instructions.length > 0) {
223
+ wprm_admin.set_recipe_instruction_fields(wprm_admin.text_import.instructions);
224
+ }
225
+
226
+ if(wprm_admin.text_import.ingredients.length > 0) {
227
+ wprm_admin.set_recipe_ingredient_fields(wprm_admin.text_import.ingredients);
228
+ }
229
+ };
230
+
231
+ jQuery(document).ready(function($) {
232
+ jQuery('#import-text-input-recipe').on('keydown change', function() {
233
+ wprm_admin.start_text_import();
234
+ });
235
+
236
+ jQuery('.wprm-button-import-text-reset').on('click', function() {
237
+ if(confirm(wprm_modal.text.import_text_reset)) {
238
+ wprm_admin.btn_text_import_reset();
239
+ }
240
+ });
241
+
242
+ jQuery('.wprm-button-import-text-clear').on('click', function() {
243
+ wprm_admin.btn_text_import_clear(false);
244
+ });
245
+
246
+ jQuery('.wprm-button-import-text-next').on('click', function() {
247
+ wprm_admin.btn_text_import_next();
248
+ });
249
+ });
assets/js/admin/modal.js CHANGED
@@ -4,6 +4,7 @@ wprm_admin.active_editor_id = false;
4
 
5
  wprm_admin.disable_menu = function() {
6
  jQuery('.wprm-frame-menu').find('.wprm-menu-item').hide();
 
7
  };
8
 
9
  wprm_admin.open_modal = function(editor_id, args) {
@@ -14,6 +15,7 @@ wprm_admin.open_modal = function(editor_id, args) {
14
 
15
  // Enable menu items
16
  jQuery('.wprm-menu-item').show();
 
17
 
18
  wprm_admin.active_editor_id = editor_id;
19
  jQuery('.wprm-modal-container').show();
4
 
5
  wprm_admin.disable_menu = function() {
6
  jQuery('.wprm-frame-menu').find('.wprm-menu-item').hide();
7
+ jQuery('.wprm-menu-hidden').show();
8
  };
9
 
10
  wprm_admin.open_modal = function(editor_id, args) {
15
 
16
  // Enable menu items
17
  jQuery('.wprm-menu-item').show();
18
+ jQuery('.wprm-menu-hidden').hide();
19
 
20
  wprm_admin.active_editor_id = editor_id;
21
  jQuery('.wprm-modal-container').show();
assets/js/admin/recipe-form.js CHANGED
@@ -54,6 +54,10 @@ wprm_admin.clear_recipe_fields = function() {
54
  jQuery('#wprm-recipe-cook-time').val('');
55
  jQuery('#wprm-recipe-total-time').val('');
56
 
 
 
 
 
57
  jQuery('.wprm-recipe-tags').val(null).trigger('change');
58
 
59
  // Ingredients & Instructions
@@ -66,7 +70,14 @@ wprm_admin.clear_recipe_fields = function() {
66
  // Recipe Notes
67
  if (typeof tinyMCE !== 'undefined' && tinyMCE.get('wprm_recipe_notes') && !tinyMCE.get('wprm_recipe_notes').isHidden()) {
68
  tinyMCE.get('wprm_recipe_notes').focus(true);
69
- tinyMCE.activeEditor.setContent('');
 
 
 
 
 
 
 
70
  } else {
71
  jQuery('#wprm_recipe_notes').val('');
72
  }
@@ -97,49 +108,66 @@ wprm_admin.set_recipe_fields = function(recipe) {
97
  jQuery('#wprm-recipe-cook-time').val(cook_time);
98
  jQuery('#wprm-recipe-total-time').val(total_time);
99
 
 
 
 
 
100
  wprm_admin.set_recipe_tags(recipe, 'course');
101
  wprm_admin.set_recipe_tags(recipe, 'cuisine');
102
 
103
  // Ingredients & Instructions
104
- jQuery('.wprm-recipe-ingredients .wprm-recipe-ingredients-instructions-delete, .wprm-recipe-instructions .wprm-recipe-ingredients-instructions-delete').each(function() {
105
- jQuery(this).click();
106
- });
107
 
108
- var i, l, group, j, m;
 
 
 
 
 
 
 
109
 
110
- for (i = 0, l = recipe.ingredients.length; i < l; i++) {
111
- group = recipe.ingredients[i];
 
 
112
 
113
- if (i > 0 || group.name !== '') {
114
- wprm_admin.add_ingredient_group(group.name);
115
- }
116
 
117
- for (j = 0, m = group.ingredients.length; j < m; j++) {
118
- var ingredient = group.ingredients[j];
119
- wprm_admin.add_ingredient(ingredient.amount, ingredient.unit, ingredient.name, ingredient.notes);
120
- }
 
 
 
 
 
 
121
  }
 
 
122
 
123
- for (i = 0, l = recipe.instructions.length; i < l; i++) {
124
- group = recipe.instructions[i];
 
 
125
 
126
- if (i > 0 || group.name !== '') {
127
- wprm_admin.add_instruction_group(group.name);
128
- }
129
 
130
- for (j = 0, m = group.instructions.length; j < m; j++) {
131
- var instruction = group.instructions[j];
132
- wprm_admin.add_instruction(instruction.text, instruction.image);
133
- }
 
134
  }
135
 
136
- // Recipe Notes
137
- if (typeof tinyMCE !== 'undefined' && tinyMCE.get('wprm_recipe_notes') && !tinyMCE.get('wprm_recipe_notes').isHidden()) {
138
- tinyMCE.get('wprm_recipe_notes').focus(true);
139
- tinyMCE.activeEditor.setContent(recipe.notes);
140
- } else {
141
- jQuery('#wprm_recipe_notes').val(recipe.notes);
142
  }
 
143
  };
144
 
145
  wprm_admin.set_recipe_tags = function(recipe, tag) {
@@ -179,18 +207,19 @@ wprm_admin.select_media_image = function(container) {
179
  frame.open();
180
  };
181
  wprm_admin.set_media_image = function(container, image_id, image_url) {
182
- container.find('.wprm-recipe-image-preview').append('<img src="' + image_url + '" />');
183
- container.find('input').val(image_id);
 
184
 
185
- container.find('.wprm-recipe-image-add').addClass('hidden');
186
- container.find('.wprm-recipe-image-remove').removeClass('hidden');
187
  };
188
  wprm_admin.remove_media_image = function(container) {
189
- container.find('.wprm-recipe-image-preview').html('');
190
- container.find('input').val('');
191
 
192
- container.find('.wprm-recipe-image-add').removeClass('hidden');
193
- container.find('.wprm-recipe-image-remove').addClass('hidden');
194
  };
195
 
196
  wprm_admin.start_loader = function(button) {
54
  jQuery('#wprm-recipe-cook-time').val('');
55
  jQuery('#wprm-recipe-total-time').val('');
56
 
57
+ wprm_admin.prep_time_set = false;
58
+ wprm_admin.cook_time_set = false;
59
+ wprm_admin.total_time_set = false;
60
+
61
  jQuery('.wprm-recipe-tags').val(null).trigger('change');
62
 
63
  // Ingredients & Instructions
70
  // Recipe Notes
71
  if (typeof tinyMCE !== 'undefined' && tinyMCE.get('wprm_recipe_notes') && !tinyMCE.get('wprm_recipe_notes').isHidden()) {
72
  tinyMCE.get('wprm_recipe_notes').focus(true);
73
+
74
+ // Check for error caused by EasyRecipe.
75
+ jQuery('.wprm-easyrecipe-warning').hide();
76
+ try {
77
+ tinyMCE.activeEditor.setContent('');
78
+ } catch(err) {
79
+ jQuery('.wprm-easyrecipe-warning').show();
80
+ }
81
  } else {
82
  jQuery('#wprm_recipe_notes').val('');
83
  }
108
  jQuery('#wprm-recipe-cook-time').val(cook_time);
109
  jQuery('#wprm-recipe-total-time').val(total_time);
110
 
111
+ if (prep_time) wprm_admin.prep_time_set = true;
112
+ if (cook_time) wprm_admin.cook_time_set = true;
113
+ if (total_time) wprm_admin.total_time_set = true;
114
+
115
  wprm_admin.set_recipe_tags(recipe, 'course');
116
  wprm_admin.set_recipe_tags(recipe, 'cuisine');
117
 
118
  // Ingredients & Instructions
119
+ wprm_admin.set_recipe_ingredient_fields(recipe.ingredients);
120
+ wprm_admin.set_recipe_instruction_fields(recipe.instructions);
 
121
 
122
+ // Recipe Notes
123
+ if (typeof tinyMCE !== 'undefined' && tinyMCE.get('wprm_recipe_notes') && !tinyMCE.get('wprm_recipe_notes').isHidden()) {
124
+ tinyMCE.get('wprm_recipe_notes').focus(true);
125
+ tinyMCE.activeEditor.setContent(recipe.notes);
126
+ } else {
127
+ jQuery('#wprm_recipe_notes').val(recipe.notes);
128
+ }
129
+ };
130
 
131
+ wprm_admin.set_recipe_ingredient_fields = function(ingredients) {
132
+ jQuery('.wprm-recipe-ingredients .wprm-recipe-ingredients-instructions-delete').each(function() {
133
+ jQuery(this).click();
134
+ });
135
 
136
+ var i, l, group, j, m;
 
 
137
 
138
+ for (i = 0, l = ingredients.length; i < l; i++) {
139
+ group = ingredients[i];
140
+
141
+ if (i > 0 || group.name !== '') {
142
+ wprm_admin.add_ingredient_group(group.name);
143
+ }
144
+
145
+ for (j = 0, m = group.ingredients.length; j < m; j++) {
146
+ var ingredient = group.ingredients[j];
147
+ wprm_admin.add_ingredient(ingredient.amount, ingredient.unit, ingredient.name, ingredient.notes);
148
  }
149
+ }
150
+ };
151
 
152
+ wprm_admin.set_recipe_instruction_fields = function(instructions) {
153
+ jQuery('.wprm-recipe-instructions .wprm-recipe-ingredients-instructions-delete').each(function() {
154
+ jQuery(this).click();
155
+ });
156
 
157
+ var i, l, group, j, m;
 
 
158
 
159
+ for (i = 0, l = instructions.length; i < l; i++) {
160
+ group = instructions[i];
161
+
162
+ if (i > 0 || group.name !== '') {
163
+ wprm_admin.add_instruction_group(group.name);
164
  }
165
 
166
+ for (j = 0, m = group.instructions.length; j < m; j++) {
167
+ var instruction = group.instructions[j];
168
+ wprm_admin.add_instruction(instruction.text, instruction.image);
 
 
 
169
  }
170
+ }
171
  };
172
 
173
  wprm_admin.set_recipe_tags = function(recipe, tag) {
207
  frame.open();
208
  };
209
  wprm_admin.set_media_image = function(container, image_id, image_url) {
210
+ container.find('.wprm-recipe-image-preview').html('');
211
+ container.find('.wprm-recipe-image-preview').append('<img src="' + image_url + '" />');
212
+ container.find('input').val(image_id);
213
 
214
+ container.find('.wprm-recipe-image-add').addClass('hidden');
215
+ container.find('.wprm-recipe-image-remove').removeClass('hidden');
216
  };
217
  wprm_admin.remove_media_image = function(container) {
218
+ container.find('.wprm-recipe-image-preview').html('');
219
+ container.find('input').val('');
220
 
221
+ container.find('.wprm-recipe-image-add').removeClass('hidden');
222
+ container.find('.wprm-recipe-image-remove').addClass('hidden');
223
  };
224
 
225
  wprm_admin.start_loader = function(button) {
assets/js/admin/rich-editor.js CHANGED
@@ -9,17 +9,18 @@ wprm_admin.init_rich_editor = function() {
9
  autoLink: true,
10
  imageDragging: false,
11
  toolbar: {
12
- buttons: ['bold', 'italic', 'underline', {
13
- name: 'anchor',
14
- contentDefault: '<span class="dashicons dashicons-admin-links"></span>'
15
- }]
16
  },
17
  extensions: {}
18
  };
19
 
 
 
 
20
  if(wprm_modal.addons.premium) {
21
  args.toolbar.buttons.push('adjustable_servings');
22
  args.toolbar.buttons.push('timer');
 
23
  args.extensions.adjustable_servings = new wprm_admin.rich_editor_adjustable_servings();
24
  args.extensions.timer = new wprm_admin.rich_editor_timer();
25
  }
@@ -90,4 +91,578 @@ wprm_admin.rich_editor_timer = MediumEditor.Extension.extend({
90
  range.insertNode(text);
91
  rangy.getSelection().setSingleRange(original_range);
92
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
93
  });
9
  autoLink: true,
10
  imageDragging: false,
11
  toolbar: {
12
+ buttons: ['bold', 'italic', 'underline']
 
 
 
13
  },
14
  extensions: {}
15
  };
16
 
17
+ args.toolbar.buttons.push('links');
18
+ args.extensions.links = new wprm_admin.rich_editor_links();
19
+
20
  if(wprm_modal.addons.premium) {
21
  args.toolbar.buttons.push('adjustable_servings');
22
  args.toolbar.buttons.push('timer');
23
+
24
  args.extensions.adjustable_servings = new wprm_admin.rich_editor_adjustable_servings();
25
  args.extensions.timer = new wprm_admin.rich_editor_timer();
26
  }
91
  range.insertNode(text);
92
  rangy.getSelection().setSingleRange(original_range);
93
  }
94
+ });
95
+
96
+ // Source: medium-editor.js
97
+ MediumEditor.prototype.createLink = function (opts) {
98
+ console.log(opts);
99
+ var currentEditor = MediumEditor.selection.getSelectionElement(this.options.contentWindow),
100
+ customEvent = {},
101
+ targetUrl;
102
+
103
+ // Make sure the selection is within an element this editor is tracking
104
+ if (this.elements.indexOf(currentEditor) === -1) {
105
+ return;
106
+ }
107
+
108
+ try {
109
+ this.events.disableCustomEvent('editableInput');
110
+ // TODO: Deprecate support for opts.url in 6.0.0
111
+ if (opts.url) {
112
+ MediumEditor.util.deprecated('.url option for createLink', '.value', '6.0.0');
113
+ }
114
+ targetUrl = opts.url || opts.value;
115
+ if (targetUrl && targetUrl.trim().length > 0) {
116
+ var currentSelection = this.options.contentWindow.getSelection();
117
+ if (currentSelection) {
118
+ var currRange = currentSelection.getRangeAt(0),
119
+ commonAncestorContainer = currRange.commonAncestorContainer,
120
+ exportedSelection,
121
+ startContainerParentElement,
122
+ endContainerParentElement,
123
+ textNodes;
124
+
125
+ // If the selection is contained within a single text node
126
+ // and the selection starts at the beginning of the text node,
127
+ // MSIE still says the startContainer is the parent of the text node.
128
+ // If the selection is contained within a single text node, we
129
+ // want to just use the default browser 'createLink', so we need
130
+ // to account for this case and adjust the commonAncestorContainer accordingly
131
+ if (currRange.endContainer.nodeType === 3 &&
132
+ currRange.startContainer.nodeType !== 3 &&
133
+ currRange.startOffset === 0 &&
134
+ currRange.startContainer.firstChild === currRange.endContainer) {
135
+ commonAncestorContainer = currRange.endContainer;
136
+ }
137
+
138
+ startContainerParentElement = MediumEditor.util.getClosestBlockContainer(currRange.startContainer);
139
+ endContainerParentElement = MediumEditor.util.getClosestBlockContainer(currRange.endContainer);
140
+
141
+ // If the selection is not contained within a single text node
142
+ // but the selection is contained within the same block element
143
+ // we want to make sure we create a single link, and not multiple links
144
+ // which can happen with the built in browser functionality
145
+ if (commonAncestorContainer.nodeType !== 3 && commonAncestorContainer.textContent.length !== 0 && startContainerParentElement === endContainerParentElement) {
146
+ var parentElement = (startContainerParentElement || currentEditor),
147
+ fragment = this.options.ownerDocument.createDocumentFragment();
148
+
149
+ // since we are going to create a link from an extracted text,
150
+ // be sure that if we are updating a link, we won't let an empty link behind (see #754)
151
+ // (Workaroung for Chrome)
152
+ this.execAction('unlink');
153
+
154
+ exportedSelection = this.exportSelection();
155
+ fragment.appendChild(parentElement.cloneNode(true));
156
+
157
+ if (currentEditor === parentElement) {
158
+ // We have to avoid the editor itself being wiped out when it's the only block element,
159
+ // as our reference inside this.elements gets detached from the page when insertHTML runs.
160
+ // If we just use [parentElement, 0] and [parentElement, parentElement.childNodes.length]
161
+ // as the range boundaries, this happens whenever parentElement === currentEditor.
162
+ // The tradeoff to this workaround is that a orphaned tag can sometimes be left behind at
163
+ // the end of the editor's content.
164
+ // In Gecko:
165
+ // as an empty <strong></strong> if parentElement.lastChild is a <strong> tag.
166
+ // In WebKit:
167
+ // an invented <br /> tag at the end in the same situation
168
+ MediumEditor.selection.select(
169
+ this.options.ownerDocument,
170
+ parentElement.firstChild,
171
+ 0,
172
+ parentElement.lastChild,
173
+ parentElement.lastChild.nodeType === 3 ?
174
+ parentElement.lastChild.nodeValue.length : parentElement.lastChild.childNodes.length
175
+ );
176
+ } else {
177
+ MediumEditor.selection.select(
178
+ this.options.ownerDocument,
179
+ parentElement,
180
+ 0,
181
+ parentElement,
182
+ parentElement.childNodes.length
183
+ );
184
+ }
185
+
186
+ var modifiedExportedSelection = this.exportSelection();
187
+
188
+ textNodes = MediumEditor.util.findOrCreateMatchingTextNodes(
189
+ this.options.ownerDocument,
190
+ fragment,
191
+ {
192
+ start: exportedSelection.start - modifiedExportedSelection.start,
193
+ end: exportedSelection.end - modifiedExportedSelection.start,
194
+ editableElementIndex: exportedSelection.editableElementIndex
195
+ }
196
+ );
197
+ // If textNodes are not present, when changing link on images
198
+ // ex: <a><img src="http://image.test.com"></a>, change fragment to currRange.startContainer
199
+ // and set textNodes array to [imageElement, imageElement]
200
+ if (textNodes.length === 0) {
201
+ fragment = this.options.ownerDocument.createDocumentFragment();
202
+ fragment.appendChild(commonAncestorContainer.cloneNode(true));
203
+ textNodes = [fragment.firstChild.firstChild, fragment.firstChild.lastChild];
204
+ }
205
+
206
+ // Creates the link in the document fragment
207
+ MediumEditor.util.createLink(this.options.ownerDocument, textNodes, targetUrl.trim());
208
+
209
+ // Chrome trims the leading whitespaces when inserting HTML, which messes up restoring the selection.
210
+ var leadingWhitespacesCount = (fragment.firstChild.innerHTML.match(/^\s+/) || [''])[0].length;
211
+
212
+ // Now move the created link back into the original document in a way to preserve undo/redo history
213
+ MediumEditor.util.insertHTMLCommand(this.options.ownerDocument, fragment.firstChild.innerHTML.replace(/^\s+/, ''));
214
+ exportedSelection.start -= leadingWhitespacesCount;
215
+ exportedSelection.end -= leadingWhitespacesCount;
216
+
217
+ this.importSelection(exportedSelection);
218
+ } else {
219
+ this.options.ownerDocument.execCommand('createLink', false, targetUrl);
220
+ }
221
+
222
+ if (this.options.targetBlank || opts.target === '_blank') {
223
+ MediumEditor.util.setTargetBlank(MediumEditor.selection.getSelectionStart(this.options.ownerDocument), targetUrl);
224
+ } else {
225
+ MediumEditor.util.removeTargetBlank(MediumEditor.selection.getSelectionStart(this.options.ownerDocument), targetUrl);
226
+ }
227
+
228
+ if (opts.rel === 'nofollow') {
229
+ MediumEditor.util.setNofollow(MediumEditor.selection.getSelectionStart(this.options.ownerDocument), targetUrl);
230
+ } else {
231
+ MediumEditor.util.removeNofollow(MediumEditor.selection.getSelectionStart(this.options.ownerDocument), targetUrl);
232
+ }
233
+
234
+ if (opts.buttonClass) {
235
+ MediumEditor.util.addClassToAnchors(MediumEditor.selection.getSelectionStart(this.options.ownerDocument), opts.buttonClass);
236
+ }
237
+ }
238
+ }
239
+ // Fire input event for backwards compatibility if anyone was listening directly to the DOM input event
240
+ if (this.options.targetBlank || opts.target === '_blank' || opts.rel === 'nofollow' || opts.buttonClass) {
241
+ customEvent = this.options.ownerDocument.createEvent('HTMLEvents');
242
+ customEvent.initEvent('input', true, true, this.options.contentWindow);
243
+ for (var i = 0, len = this.elements.length; i < len; i += 1) {
244
+ this.elements[i].dispatchEvent(customEvent);
245
+ }
246
+ }
247
+ } finally {
248
+ this.events.enableCustomEvent('editableInput');
249
+ }
250
+ // Fire our custom editableInput event
251
+ this.events.triggerCustomEvent('editableInput', customEvent, currentEditor);
252
+ };
253
+
254
+ MediumEditor.util.setNofollow = function (el, anchorUrl) {
255
+ var i, url = anchorUrl || false;
256
+ if (el.nodeName.toLowerCase() === 'a') {
257
+ el.rel = 'nofollow';
258
+ } else {
259
+ el = el.getElementsByTagName('a');
260
+
261
+ for (i = 0; i < el.length; i += 1) {
262
+ if (false === url || url === el[i].attributes.href.value) {
263
+ el[i].rel = 'nofollow';
264
+ }
265
+ }
266
+ }
267
+ };
268
+
269
+ MediumEditor.util.removeNofollow = function (el, anchorUrl) {
270
+ var i;
271
+ if (el.nodeName.toLowerCase() === 'a') {
272
+ el.removeAttribute('rel');
273
+ } else {
274
+ el = el.getElementsByTagName('a');
275
+
276
+ for (i = 0; i < el.length; i += 1) {
277
+ if (anchorUrl === el[i].attributes.href.value) {
278
+ el[i].removeAttribute('rel');
279
+ }
280
+ }
281
+ }
282
+ };
283
+
284
+ wprm_admin.rich_editor_links = MediumEditor.extensions.form.extend({
285
+ /* Anchor Form Options */
286
+
287
+ /* customClassOption: [string] (previously options.anchorButton + options.anchorButtonClass)
288
+ * Custom class name the user can optionally have added to their created links (ie 'button').
289
+ * If passed as a non-empty string, a checkbox will be displayed allowing the user to choose
290
+ * whether to have the class added to the created link or not.
291
+ */
292
+ customClassOption: null,
293
+
294
+ /* customClassOptionText: [string]
295
+ * text to be shown in the checkbox when the __customClassOption__ is being used.
296
+ */
297
+ customClassOptionText: 'Button',
298
+
299
+ /* linkValidation: [boolean] (previously options.checkLinkFormat)
300
+ * enables/disables check for common URL protocols on anchor links.
301
+ */
302
+ linkValidation: false,
303
+
304
+ /* placeholderText: [string] (previously options.anchorInputPlaceholder)
305
+ * text to be shown as placeholder of the anchor input.
306
+ */
307
+ placeholderText: 'Paste or type a link',
308
+
309
+ /* targetCheckbox: [boolean] (previously options.anchorTarget)
310
+ * enables/disables displaying a "Open in new window" checkbox, which when checked
311
+ * changes the `target` attribute of the created link.
312
+ */
313
+ targetCheckbox: true,
314
+
315
+ /* targetCheckboxText: [string] (previously options.anchorInputCheckboxLabel)
316
+ * text to be shown in the checkbox enabled via the __targetCheckbox__ option.
317
+ */
318
+ targetCheckboxText: 'Open in new tab',
319
+
320
+ nofollowCheckbox: true,
321
+ nofollowCheckboxText: 'Use nofollow',
322
+
323
+ // Options for the Button base class
324
+ name: 'links',
325
+ action: 'createLink',
326
+ aria: 'link',
327
+ tagNames: ['a'],
328
+ contentDefault: '<span class="dashicons dashicons-admin-links"></span>',
329
+ contentFA: '<i class="fa fa-link"></i>',
330
+
331
+ init: function () {
332
+ MediumEditor.extensions.form.prototype.init.apply(this, arguments);
333
+
334
+ this.subscribe('editableKeydown', this.handleKeydown.bind(this));
335
+ },
336
+
337
+ // Called when the button the toolbar is clicked
338
+ // Overrides ButtonExtension.handleClick
339
+ handleClick: function (event) {
340
+ event.preventDefault();
341
+ event.stopPropagation();
342
+
343
+ var range = MediumEditor.selection.getSelectionRange(this.document);
344
+
345
+ if (range.startContainer.nodeName.toLowerCase() === 'a' ||
346
+ range.endContainer.nodeName.toLowerCase() === 'a' ||
347
+ MediumEditor.util.getClosestTag(MediumEditor.selection.getSelectedParentElement(range), 'a')) {
348
+ return this.execAction('unlink');
349
+ }
350
+
351
+ if (!this.isDisplayed()) {
352
+ this.showForm();
353
+ }
354
+
355
+ return false;
356
+ },
357
+
358
+ // Called when user hits the defined shortcut (CTRL / COMMAND + K)
359
+ handleKeydown: function (event) {
360
+ if (MediumEditor.util.isKey(event, MediumEditor.util.keyCode.K) && MediumEditor.util.isMetaCtrlKey(event) && !event.shiftKey) {
361
+ this.handleClick(event);
362
+ }
363
+ },
364
+
365
+ // Called by medium-editor to append form to the toolbar
366
+ getForm: function () {
367
+ if (!this.form) {
368
+ this.form = this.createForm();
369
+ }
370
+ return this.form;
371
+ },
372
+
373
+ getTemplate: function () {
374
+ var template = [
375
+ '<input type="text" class="medium-editor-toolbar-input" placeholder="', this.placeholderText, '" style="padding-left: 25px; width: 265px;">'
376
+ ];
377
+
378
+ template.push(
379
+ '<a href="#" class="medium-editor-toolbar-save">',
380
+ this.getEditorOption('buttonLabels') === 'fontawesome' ? '<i class="fa fa-check"></i>' : this.formSaveLabel,
381
+ '</a>'
382
+ );
383
+
384
+ template.push('<a href="#" class="medium-editor-toolbar-close">',
385
+ this.getEditorOption('buttonLabels') === 'fontawesome' ? '<i class="fa fa-times"></i>' : this.formCloseLabel,
386
+ '</a>');
387
+
388
+ // both of these options are slightly moot with the ability to
389
+ // override the various form buildup/serialize functions.
390
+
391
+ if (this.targetCheckbox) {
392
+ // fixme: ideally, this targetCheckboxText would be a formLabel too,
393
+ // figure out how to deprecate? also consider `fa-` icon default implcations.
394
+ template.push(
395
+ '<div class="medium-editor-toolbar-form-row" style="padding-left: 20px;">',
396
+ '<input type="checkbox" class="medium-editor-toolbar-anchor-target">',
397
+ '<label>',
398
+ this.targetCheckboxText,
399
+ '</label>',
400
+ '</div>'
401
+ );
402
+ }
403
+
404
+ if (this.nofollowCheckbox) {
405
+ template.push(
406
+ '<div class="medium-editor-toolbar-form-row" style="padding-left: 20px;">',
407
+ '<input type="checkbox" class="medium-editor-toolbar-anchor-nofollow">',
408
+ '<label>',
409
+ this.nofollowCheckboxText,
410
+ '</label>',
411
+ '</div>'
412
+ );
413
+ }
414
+
415
+ if (this.customClassOption) {
416
+ // fixme: expose this `Button` text as a formLabel property, too
417
+ // and provide similar access to a `fa-` icon default.
418
+ template.push(
419
+ '<div class="medium-editor-toolbar-form-row">',
420
+ '<input type="checkbox" class="medium-editor-toolbar-anchor-button">',
421
+ '<label>',
422
+ this.customClassOptionText,
423
+ '</label>',
424
+ '</div>'
425
+ );
426
+ }
427
+
428
+ return template.join('');
429
+
430
+ },
431
+
432
+ // Used by medium-editor when the default toolbar is to be displayed
433
+ isDisplayed: function () {
434
+ return MediumEditor.extensions.form.prototype.isDisplayed.apply(this);
435
+ },
436
+
437
+ hideForm: function () {
438
+ MediumEditor.extensions.form.prototype.hideForm.apply(this);
439
+ this.getInput().value = '';
440
+ },
441
+
442
+ showForm: function (opts) {
443
+ var input = this.getInput(),
444
+ targetCheckbox = this.getAnchorTargetCheckbox(),
445
+ nofollowCheckbox = this.getAnchorNofollowCheckbox(),
446
+ buttonCheckbox = this.getAnchorButtonCheckbox();
447
+
448
+ opts = opts || { value: '' };
449
+ // TODO: This is for backwards compatability
450
+ // We don't need to support the 'string' argument in 6.0.0
451
+ if (typeof opts === 'string') {
452
+ opts = {
453
+ value: opts
454
+ };
455
+ }
456
+
457
+ this.base.saveSelection();
458
+ this.hideToolbarDefaultActions();
459
+ MediumEditor.extensions.form.prototype.showForm.apply(this);
460
+ this.setToolbarPosition();
461
+
462
+ input.value = opts.value;
463
+ input.focus();
464
+
465
+ // If we have a target checkbox, we want it to be checked/unchecked
466
+ // based on whether the existing link has target=_blank
467
+ if (targetCheckbox) {
468
+ targetCheckbox.checked = opts.target === '_blank';
469
+ }
470
+
471
+ if (nofollowCheckbox) {
472
+ nofollowCheckbox.checked = opts.rel === 'nofollow';
473
+ }
474
+
475
+ // If we have a custom class checkbox, we want it to be checked/unchecked
476
+ // based on whether an existing link already has the class
477
+ if (buttonCheckbox) {
478
+ var classList = opts.buttonClass ? opts.buttonClass.split(' ') : [];
479
+ buttonCheckbox.checked = (classList.indexOf(this.customClassOption) !== -1);
480
+ }
481
+ },
482
+
483
+ // Called by core when tearing down medium-editor (destroy)
484
+ destroy: function () {
485
+ if (!this.form) {
486
+ return false;
487
+ }
488
+
489
+ if (this.form.parentNode) {
490
+ this.form.parentNode.removeChild(this.form);
491
+ }
492
+
493
+ delete this.form;
494
+ },
495
+
496
+ // core methods
497
+
498
+ getFormOpts: function () {
499
+ // no notion of private functions? wanted `_getFormOpts`
500
+ var targetCheckbox = this.getAnchorTargetCheckbox(),
501
+ nofollowCheckbox = this.getAnchorNofollowCheckbox(),
502
+ buttonCheckbox = this.getAnchorButtonCheckbox(),
503
+ opts = {
504
+ value: this.getInput().value.trim()
505
+ };
506
+
507
+ if (this.linkValidation) {
508
+ opts.value = this.checkLinkFormat(opts.value);
509
+ }
510
+
511
+ opts.target = '_self';
512
+ if (targetCheckbox && targetCheckbox.checked) {
513
+ opts.target = '_blank';
514
+ }
515
+
516
+ opts.rel = '';
517
+ if (nofollowCheckbox && nofollowCheckbox.checked) {
518
+ opts.rel = 'nofollow';
519
+ }
520
+
521
+ if (buttonCheckbox && buttonCheckbox.checked) {
522
+ opts.buttonClass = this.customClassOption;
523
+ }
524
+
525
+ return opts;
526
+ },
527
+
528
+ doFormSave: function () {
529
+ var opts = this.getFormOpts();
530
+ this.completeFormSave(opts);
531
+ },
532
+
533
+ completeFormSave: function (opts) {
534
+ this.base.restoreSelection();
535
+ this.execAction(this.action, opts);
536
+ this.base.checkSelection();
537
+ },
538
+
539
+ ensureEncodedUri: function (str) {
540
+ return str === decodeURI(str) ? encodeURI(str) : str;
541
+ },
542
+
543
+ ensureEncodedUriComponent: function (str) {
544
+ return str === decodeURIComponent(str) ? encodeURIComponent(str) : str;
545
+ },
546
+
547
+ ensureEncodedParam: function (param) {
548
+ var split = param.split('='),
549
+ key = split[0],
550
+ val = split[1];
551
+
552
+ return key + (val === undefined ? '' : '=' + this.ensureEncodedUriComponent(val));
553
+ },
554
+
555
+ ensureEncodedQuery: function (queryString) {
556
+ return queryString.split('&').map(this.ensureEncodedParam.bind(this)).join('&');
557
+ },
558
+
559
+ checkLinkFormat: function (value) {
560
+ // Matches any alphabetical characters followed by ://
561
+ // Matches protocol relative "//"
562
+ // Matches common external protocols "mailto:" "tel:" "maps:"
563
+ // Matches relative hash link, begins with "#"
564
+ var urlSchemeRegex = /^([a-z]+:)?\/\/|^(mailto|tel|maps):|^\#/i,
565
+ // telRegex is a regex for checking if the string is a telephone number
566
+ telRegex = /^\+?\s?\(?(?:\d\s?\-?\)?){3,20}$/,
567
+ split = value.split('?'),
568
+ path = split[0],
569
+ query = split[1];
570
+
571
+ if (telRegex.test(value)) {
572
+ return 'tel:' + value;
573
+ } else {
574
+ // Check for URL scheme and default to http:// if none found
575
+ return (urlSchemeRegex.test(value) ? '' : 'http://') +
576
+ // Ensure path is encoded
577
+ this.ensureEncodedUri(path) +
578
+ // Ensure query is encoded
579
+ (query === undefined ? '' : '?' + this.ensureEncodedQuery(query));
580
+ }
581
+ },
582
+
583
+ doFormCancel: function () {
584
+ this.base.restoreSelection();
585
+ this.base.checkSelection();
586
+ },
587
+
588
+ // form creation and event handling
589
+ attachFormEvents: function (form) {
590
+ var close = form.querySelector('.medium-editor-toolbar-close'),
591
+ save = form.querySelector('.medium-editor-toolbar-save'),
592
+ input = form.querySelector('.medium-editor-toolbar-input');
593
+
594
+ // Handle clicks on the form itself
595
+ this.on(form, 'click', this.handleFormClick.bind(this));
596
+
597
+ // Handle typing in the textbox
598
+ this.on(input, 'keyup', this.handleTextboxKeyup.bind(this));
599
+
600
+ // Handle close button clicks
601
+ this.on(close, 'click', this.handleCloseClick.bind(this));
602
+
603
+ // Handle save button clicks (capture)
604
+ this.on(save, 'click', this.handleSaveClick.bind(this), true);
605
+
606
+ },
607
+
608
+ createForm: function () {
609
+ var doc = this.document,
610
+ form = doc.createElement('div');
611
+
612
+ // Anchor Form (div)
613
+ form.className = 'medium-editor-toolbar-form';
614
+ form.id = 'medium-editor-toolbar-form-anchor-' + this.getEditorId();
615
+ form.innerHTML = this.getTemplate();
616
+ this.attachFormEvents(form);
617
+
618
+ return form;
619
+ },
620
+
621
+ getInput: function () {
622
+ return this.getForm().querySelector('input.medium-editor-toolbar-input');
623
+ },
624
+
625
+ getAnchorTargetCheckbox: function () {
626
+ return this.getForm().querySelector('.medium-editor-toolbar-anchor-target');
627
+ },
628
+
629
+ getAnchorNofollowCheckbox: function () {
630
+ return this.getForm().querySelector('.medium-editor-toolbar-anchor-nofollow');
631
+ },
632
+
633
+ getAnchorButtonCheckbox: function () {
634
+ return this.getForm().querySelector('.medium-editor-toolbar-anchor-button');
635
+ },
636
+
637
+ handleTextboxKeyup: function (event) {
638
+ // For ENTER -> create the anchor
639
+ if (event.keyCode === MediumEditor.util.keyCode.ENTER) {
640
+ event.preventDefault();
641
+ this.doFormSave();
642
+ return;
643
+ }
644
+
645
+ // For ESCAPE -> close the form
646
+ if (event.keyCode === MediumEditor.util.keyCode.ESCAPE) {
647
+ event.preventDefault();
648
+ this.doFormCancel();
649
+ }
650
+ },
651
+
652
+ handleFormClick: function (event) {
653
+ // make sure not to hide form when clicking inside the form
654
+ event.stopPropagation();
655
+ },
656
+
657
+ handleSaveClick: function (event) {
658
+ // Clicking Save -> create the anchor
659
+ event.preventDefault();
660
+ this.doFormSave();
661
+ },
662
+
663
+ handleCloseClick: function (event) {
664
+ // Click Close -> close the form
665
+ event.preventDefault();
666
+ this.doFormCancel();
667
+ }
668
  });
includes/admin/class-wprm-recipe-parser.php CHANGED
@@ -20,16 +20,74 @@
20
  class WPRM_Recipe_Parser {
21
 
22
  /**
23
- * Sanitize recipe array.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
24
  *
25
  * @since 1.0.0
26
- * @param mixed $raw Text to parse into an ingredient.
27
  */
28
  public static function parse_ingredient( $raw ) {
29
  // Amount.
30
  $amount = '';
31
 
32
- preg_match( '/^\s*(\d[\s\/\-\d.,]*)(.*)/', $raw, $match );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33
  if ( isset( $match[0] ) ) {
34
  $amount = trim( $match[1] );
35
  $raw = trim( $match[2] );
@@ -186,3 +244,5 @@ class WPRM_Recipe_Parser {
186
  return array_map( 'sanitize_text_field', $units );
187
  }
188
  }
 
 
20
  class WPRM_Recipe_Parser {
21
 
22
  /**
23
+ * Register actions and filters.
24
+ *
25
+ * @since 1.7.0
26
+ */
27
+ public static function init() {
28
+ add_action( 'wp_ajax_wprm_parse_ingredients', array( __CLASS__, 'ajax_parse_ingredients' ) );
29
+ }
30
+
31
+ /**
32
+ * Parse ingredients submitted through AJAX.
33
+ *
34
+ * @since 1.7.0
35
+ */
36
+ public static function ajax_parse_ingredients() {
37
+ if ( check_ajax_referer( 'wprm', 'security', false ) ) {
38
+ $ingredients = isset( $_POST['ingredients'] ) ? wp_unslash( $_POST['ingredients'] ) : array(); // Input var okay.
39
+ $parsed_ingredients = array();
40
+ $parsed_ingredient_group = array();
41
+
42
+ foreach ( $ingredients as $ingredient_or_group ) {
43
+ if ( isset( $ingredient_or_group['raw'] ) ) {
44
+ $parsed_ingredients[] = self::parse_ingredient( $ingredient_or_group['raw'] );
45
+ } elseif ( isset( $ingredient_or_group['ingredients'] ) ) {
46
+ $parsed_group_ingredients = array();
47
+
48
+ foreach ( $ingredient_or_group['ingredients'] as $ingredient ) {
49
+ $parsed_group_ingredients[] = self::parse_ingredient( $ingredient['raw'] );
50
+ }
51
+ $parsed_ingredients[] = array(
52
+ 'name' => $ingredient_or_group['name'],
53
+ 'ingredients' => $parsed_group_ingredients,
54
+ );
55
+ }
56
+ }
57
+
58
+ wp_send_json_success( array(
59
+ 'ingredients' => $parsed_ingredients,
60
+ ) );
61
+ }
62
+
63
+ wp_die();
64
+ }
65
+
66
+ /**
67
+ * Parse text to ingredient fields.
68
  *
69
  * @since 1.0.0
70
+ * @param mixed $raw Text to parse into an ingredient.
71
  */
72
  public static function parse_ingredient( $raw ) {
73
  // Amount.
74
  $amount = '';
75
 
76
+ $unicode_map = array(
77
+ '00BC' => ' 1/4', '00BD' => ' 1/2', '00BE' => ' 3/4', '2150' => ' 1/7',
78
+ '2151' => ' 1/9', '2152' => ' 1/10', '2153' => ' 1/3', '2154' => ' 2/3',
79
+ '2155' => ' 1/5', '2156' => ' 2/5', '2157' => ' 3/5', '2158' => ' 4/5',
80
+ '2159' => ' 1/6', '215A' => ' 5/6', '215B' => ' 1/8', '215C' => ' 3/8',
81
+ '215D' => ' 5/8', '215E' => ' 7/8'
82
+ );
83
+
84
+ $unicode_regex = '';
85
+ foreach ( $unicode_map as $unicode => $normal ) {
86
+ $unicode_regex .= '\x{' . $unicode . '}';
87
+ }
88
+ $amount_regex = '/^\s*([\d' . $unicode_regex . '][\s\/\-\d.,' . $unicode_regex . ']*)(.*)/u';
89
+
90
+ preg_match( $amount_regex, $raw, $match );
91
  if ( isset( $match[0] ) ) {
92
  $amount = trim( $match[1] );
93
  $raw = trim( $match[2] );
244
  return array_map( 'sanitize_text_field', $units );
245
  }
246
  }
247
+
248
+ WPRM_Recipe_Parser::init();
includes/admin/import/class-wprm-import-bigoven.php ADDED
@@ -0,0 +1,291 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Responsible for importing BigOven recipes.
4
+ *
5
+ * @link http://bootstrapped.ventures
6
+ * @since 1.7.0
7
+ *
8
+ * @package WP_Recipe_Maker
9
+ * @subpackage WP_Recipe_Maker/includes/admin/import
10
+ */
11
+
12
+ /**
13
+ * Responsible for importing BigOven recipes.
14
+ *
15
+ * @since 1.7.0
16
+ * @package WP_Recipe_Maker
17
+ * @subpackage WP_Recipe_Maker/includes/admin/import
18
+ * @author Brecht Vandersmissen <brecht@bootstrapped.ventures>
19
+ */
20
+ class WPRM_Import_Bigoven extends WPRM_Import {
21
+ /**
22
+ * Get the UID of this import source.
23
+ *
24
+ * @since 1.7.0
25
+ */
26
+ public function get_uid() {
27
+ return 'bigoven';
28
+ }
29
+
30
+ /**
31
+ * Get the name of this import source.
32
+ *
33
+ * @since 1.7.0
34
+ */
35
+ public function get_name() {
36
+ return 'BigOven';
37
+ }
38
+
39
+ /**
40
+ * Get HTML for the import settings.
41
+ *
42
+ * @since 1.7.0
43
+ */
44
+ public function get_settings_html() {
45
+ return '';
46
+ }
47
+
48
+ /**
49
+ * Get a list of recipes that are available to import.
50
+ *
51
+ * @since 1.7.0
52
+ */
53
+ public function get_recipes() {
54
+ $recipes = array();
55
+
56
+ // Loop through all posts.
57
+ $limit = 100;
58
+ $offset = 0;
59
+
60
+ while ( true ) {
61
+ $args = array(
62
+ 'post_type' => 'bo-recipe',
63
+ 'post_status' => 'any',
64
+ 'orderby' => 'date',
65
+ 'order' => 'DESC',
66
+ 'posts_per_page' => $limit,
67
+ 'offset' => $offset,
68
+ );
69
+
70
+ $query = new WP_Query( $args );
71
+
72
+ if ( ! $query->have_posts() ) {
73
+ break;
74
+ }
75
+
76
+ $posts = $query->posts;
77
+
78
+ foreach ( $posts as $post ) {
79
+ $recipes[ $post->ID ] = array(
80
+ 'name' => $post->post_title,
81
+ 'url' => get_edit_post_link( $post->ID ),
82
+ );
83
+
84
+ wp_cache_delete( $post->ID, 'posts' );
85
+ wp_cache_delete( $post->ID, 'post_meta' );
86
+ }
87
+
88
+ $offset += $limit;
89
+ wp_cache_flush();
90
+ }
91
+
92
+ return $recipes;
93
+ }
94
+
95
+ /**
96
+ * Get recipe with the specified ID in the import format.
97
+ *
98
+ * @since 1.7.0
99
+ * @param mixed $id ID of the recipe we want to import.
100
+ * @param array $post_data POST data passed along when submitting the form.
101
+ */
102
+ public function get_recipe( $id, $post_data ) {
103
+ $recipe = array(
104
+ 'import_id' => $id,
105
+ 'import_backup' => array(
106
+ 'bo_recipe_id' => $id,
107
+ ),
108
+ );
109
+
110
+ $post = get_post( $id );
111
+ $bo_recipe = get_post_meta( $id, 'clc-recipe-attributes', true );
112
+
113
+ // Featured Image.
114
+ $recipe['image_id'] = get_post_thumbnail_id( $id );
115
+
116
+ // Simple Matching.
117
+ $recipe['name'] = $post->post_title;
118
+ $recipe['summary'] = $post->post_content;
119
+
120
+ // Servings.
121
+ $match = preg_match( '/^\s*\d+/', $bo_recipe['yield'], $servings_array );
122
+ if ( 1 === $match ) {
123
+ $servings = str_replace( ' ','', $servings_array[0] );
124
+ } else {
125
+ $servings = '';
126
+ }
127
+
128
+ $servings_unit = preg_replace( '/^\s*\d+\s*/', '', $bo_recipe['yield'] );
129
+
130
+ $recipe['servings'] = $servings;
131
+ $recipe['servings_unit'] = $servings_unit;
132
+
133
+ // Recipe Times.
134
+ $recipe['prep_time'] = $bo_recipe['time-preparation'] ? $this->time_to_minutes( $bo_recipe['time-preparation'] ) : 0;
135
+ $recipe['cook_time'] = $bo_recipe['time-cook'] ? $this->time_to_minutes( $bo_recipe['time-cook'] ) : 0;
136
+ $recipe['total_time'] = $bo_recipe['time-total'] ? $this->time_to_minutes( $bo_recipe['time-total'] ) : 0;
137
+
138
+ // Ingredients.
139
+ $ingredients = array();
140
+ $group = array(
141
+ 'ingredients' => array(),
142
+ 'name' => '',
143
+ );
144
+
145
+ $bo_ingredients = preg_split( '/$\R?^/m', $bo_recipe['ingredients'] );
146
+
147
+ foreach ( $bo_ingredients as $bo_ingredient ) {
148
+ $bo_ingredient = $this->derichify( $bo_ingredient );
149
+
150
+ if ( '!' === substr( $bo_ingredient, 0, 1 ) ) {
151
+ $ingredients[] = $group;
152
+ $group = array(
153
+ 'ingredients' => array(),
154
+ 'name' => substr( $bo_ingredient, 1 ),
155
+ );
156
+ } else {
157
+ $group['ingredients'][] = array(
158
+ 'raw' => $bo_ingredient,
159
+ );
160
+ }
161
+ }
162
+ $ingredients[] = $group;
163
+ $recipe['ingredients'] = $ingredients;
164
+
165
+ // Instructions.
166
+ $instructions = array();
167
+ $group = array(
168
+ 'instructions' => array(),
169
+ 'name' => '',
170
+ );
171
+
172
+ $bo_instructions = preg_split( '/$\R?^/m', $bo_recipe['instructions'] );
173
+
174
+ foreach ( $bo_instructions as $bo_instruction ) {
175
+ if ( '!' === substr( $bo_instruction, 0, 1 ) ) {
176
+ $instructions[] = $group;
177
+ $group = array(
178
+ 'instructions' => array(),
179
+ 'name' => $this->derichify( substr( $bo_instruction, 1 ) ),
180
+ );
181
+ } else {
182
+ $group['instructions'][] = array(
183
+ 'text' => $this->richify( $bo_instruction ),
184
+ );
185
+ }
186
+ }
187
+ $instructions[] = $group;
188
+ $recipe['instructions'] = $instructions;
189
+
190
+ // Nutrition Facts.
191
+ $recipe['nutrition'] = array();
192
+
193
+ $recipe['nutrition']['serving_size'] = $bo_recipe['serving-size'];
194
+ $recipe['nutrition']['calories'] = $bo_recipe['nutrition-calories'];
195
+ $recipe['nutrition']['fat'] = $bo_recipe['nutrition-fat'];
196
+ $recipe['nutrition']['carbohydrates'] = $bo_recipe['nutrition-carbohydrates'];
197
+ $recipe['nutrition']['protein'] = $bo_recipe['nutrition-protein'];
198
+
199
+ return $recipe;
200
+ }
201
+
202
+ /**
203
+ * Replace the original recipe with the newly imported WPRM one.
204
+ *
205
+ * @since 1.7.0
206
+ * @param mixed $id ID of the recipe we want replace.
207
+ * @param mixed $wprm_id ID of the WPRM recipe to replace with.
208
+ * @param array $post_data POST data passed along when submitting the form.
209
+ */
210
+ public function replace_recipe( $id, $wprm_id, $post_data ) {
211
+ // We don't know which posts use this recipe so we rely on the fallback shortcode.
212
+ }
213
+
214
+ /**
215
+ * Richify text by adding links and styling.
216
+ *
217
+ * @since 1.7.0
218
+ * @param mixed $text Text to richify.
219
+ */
220
+ private function richify( $text ) {
221
+ $output = $text;
222
+
223
+ $link_ptr = '#\[(.*?)\]\((.*?)\)#';
224
+ preg_match_all(
225
+ $link_ptr,
226
+ $text,
227
+ $matches
228
+ );
229
+
230
+ if ( isset( $matches[0] ) ) {
231
+ $orig = $matches[0];
232
+ $substitution = preg_replace(
233
+ $link_ptr,
234
+ '<a href="\\2">\\1</a>',
235
+ str_replace( '"', '', $orig )
236
+ );
237
+ $output = str_replace( $orig, $substitution, $text );
238
+ }
239
+
240
+ $output = preg_replace( '#\*\*(.*?)\*\*#s', '<strong>\1</strong>', $output );
241
+ $output = preg_replace( '#\*(.*?)\*#s', '<em>\1</em>', $output );
242
+ return $output;
243
+ }
244
+
245
+ /**
246
+ * Derichify text by removing links and styling.
247
+ *
248
+ * @since 1.7.0
249
+ * @param mixed $text Text to derichify.
250
+ */
251
+ private function derichify( $text ) {
252
+ $output = $text;
253
+
254
+ $link_ptr = '#\[(.*?)\]\((.*?)\)#';
255
+ preg_match_all(
256
+ $link_ptr,
257
+ $text,
258
+ $matches
259
+ );
260
+
261
+ if ( isset( $matches[0] ) ) {
262
+ $orig = $matches[0];
263
+ $substitution = preg_replace(
264
+ $link_ptr,
265
+ '\\1',
266
+ str_replace( '"', '', $orig )
267
+ );
268
+ $output = str_replace( $orig, $substitution, $text );
269
+ }
270
+
271
+ $output = preg_replace( '#\*\*(.*?)\*\*#s', '\1', $output );
272
+ $output = preg_replace( '#\*(.*?)\*#s', '\1', $output );
273
+ return $output;
274
+ }
275
+
276
+ /**
277
+ * Convert time field to minutes.
278
+ *
279
+ * @since 1.7.0
280
+ * @param mixed $time_string Time to convert.
281
+ */
282
+ private function time_to_minutes( $time_string ) {
283
+ $time = strtotime( $time_string, 0 );
284
+
285
+ if ( $time ) {
286
+ return intval( ceil( $time / 60 ) );
287
+ } else {
288
+ return 0;
289
+ }
290
+ }
291
+ }
includes/admin/import/class-wprm-import-mealplannerpro.php ADDED
@@ -0,0 +1,396 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Responsible for importing Meal Planner Pro recipes.
4
+ *
5
+ * @link http://bootstrapped.ventures
6
+ * @since 1.7.0
7
+ *
8
+ * @package WP_Recipe_Maker
9
+ * @subpackage WP_Recipe_Maker/includes/admin/import
10
+ */
11
+
12
+ /**
13
+ * Responsible for importing Meal Planner Pro recipes.
14
+ *
15
+ * @since 1.7.0
16
+ * @package WP_Recipe_Maker
17
+ * @subpackage WP_Recipe_Maker/includes/admin/import
18
+ * @author Brecht Vandersmissen <brecht@bootstrapped.ventures>
19
+ */
20
+ class WPRM_Import_Mealplannerpro extends WPRM_Import {
21
+ /**
22
+ * Get the UID of this import source.
23
+ *
24
+ * @since 1.7.0
25
+ */
26
+ public function get_uid() {
27
+ return 'mealplannerpro';
28
+ }
29
+
30
+ /**
31
+ * Get the name of this import source.
32
+ *
33
+ * @since 1.7.0
34
+ */
35
+ public function get_name() {
36
+ return 'Meal Planner Pro';
37
+ }
38
+
39
+ /**
40
+ * Get HTML for the import settings.
41
+ *
42
+ * @since 1.7.0
43
+ */
44
+ public function get_settings_html() {
45
+ return '';
46
+ }
47
+
48
+ /**
49
+ * Get a list of recipes that are available to import.
50
+ *
51
+ * @since 1.7.0
52
+ */
53
+ public function get_recipes() {
54
+ $recipes = array();
55
+
56
+ global $wpdb;
57
+ $mpp_recipes = $wpdb->get_results( 'SELECT recipe_id, post_id, server_recipe_id, recipe_title FROM ' . $wpdb->prefix . 'mpprecipe_recipes' );
58
+
59
+ foreach ( $mpp_recipes as $mpp_recipe ) {
60
+ if ( 'wprm-' !== substr( $mpp_recipe->server_recipe_id, 0, 5 ) ) {
61
+ $recipes[ $mpp_recipe->recipe_id ] = array(
62
+ 'name' => $mpp_recipe->recipe_title,
63
+ 'url' => get_edit_post_link( $mpp_recipe->post_id ),
64
+ );
65
+ }
66
+ }
67
+
68
+ return $recipes;
69
+ }
70
+
71
+ /**
72
+ * Get recipe with the specified ID in the import format.
73
+ *
74
+ * @since 1.7.0
75
+ * @param mixed $id ID of the recipe we want to import.
76
+ * @param array $post_data POST data passed along when submitting the form.
77
+ */
78
+ public function get_recipe( $id, $post_data ) {
79
+ $recipe = array(
80
+ 'import_id' => 0, // Set to 0 because we need to create a new recipe post.
81
+ 'import_backup' => array(
82
+ 'mpp_recipe_id' => $id,
83
+ ),
84
+ );
85
+
86
+ global $wpdb;
87
+ $mpp_recipe = $wpdb->get_row( 'SELECT * FROM ' . $wpdb->prefix . 'mpprecipe_recipes WHERE recipe_id=' . $id );
88
+ $post_id = $mpp_recipe->post_id;
89
+
90
+ // Featured Image.
91
+ if ( $mpp_recipe->recipe_image ) {
92
+ $image_id = $this->get_or_upload_attachment( $post_id, $mpp_recipe->recipe_image );
93
+
94
+ if ( $image_id ) {
95
+ $recipe['image_id'] = $image_id;
96
+ }
97
+ }
98
+
99
+ // Simple Matching.
100
+ $recipe['name'] = $mpp_recipe->recipe_title;
101
+ $recipe['summary'] = $this->richify( $mpp_recipe->summary );
102
+ $recipe['notes'] = $this->richify( $mpp_recipe->notes );
103
+
104
+ // Author.
105
+ $recipe['author_name'] = $mpp_recipe->author;
106
+ if ( '' !== trim( $recipe['author_name'] ) ) {
107
+ $recipe['author_display'] = 'custom';
108
+ }
109
+
110
+ // Servings.
111
+ $match = preg_match( '/^\s*\d+/', $mpp_recipe->yield, $servings_array );
112
+ if ( 1 === $match ) {
113
+ $servings = str_replace( ' ','', $servings_array[0] );
114
+ } else {
115
+ $servings = '';
116
+ }
117
+
118
+ $servings_unit = preg_replace( '/^\s*\d+\s*/', '', $mpp_recipe->yield );
119
+
120
+ $recipe['servings'] = $servings;
121
+ $recipe['servings_unit'] = $servings_unit;
122
+
123
+ // Recipe Times.
124
+ $recipe['prep_time'] = $mpp_recipe->prep_time ? $this->time_to_minutes( $mpp_recipe->prep_time ) : 0;
125
+ $recipe['cook_time'] = $mpp_recipe->cook_time ? $this->time_to_minutes( $mpp_recipe->cook_time ) : 0;
126
+ $recipe['total_time'] = $mpp_recipe->total_time ? $this->time_to_minutes( $mpp_recipe->total_time ) : 0;
127
+
128
+ // Recipe Tags.
129
+ $courses = str_replace( ';', ',', $mpp_recipe->type );
130
+ $courses = preg_split( '/[\s*,\s*]*,+[\s*,\s*]*/', $courses );
131
+ $courses = '' === $courses[0] ? array() : $courses;
132
+
133
+ $cuisines = str_replace( ';', ',', $mpp_recipe->cuisine );
134
+ $cuisines = preg_split( '/[\s*,\s*]*,+[\s*,\s*]*/', $cuisines );
135
+ $cuisines = '' === $cuisines[0] ? array() : $cuisines;
136
+
137
+ $recipe['tags'] = array(
138
+ 'course' => $courses,
139
+ 'cuisine' => $cuisines,
140
+ );
141
+
142
+ // Ingredients.
143
+ $ingredients = array();
144
+ $group = array(
145
+ 'ingredients' => array(),
146
+ 'name' => '',
147
+ );
148
+
149
+ $mpp_ingredients = preg_split( '/$\R?^/m', $mpp_recipe->ingredients );
150
+
151
+ foreach ( $mpp_ingredients as $mpp_ingredient ) {
152
+ $mpp_ingredient = $this->derichify( $mpp_ingredient );
153
+
154
+ if ( '!' === substr( $mpp_ingredient, 0, 1 ) ) {
155
+ $ingredients[] = $group;
156
+ $group = array(
157
+ 'ingredients' => array(),
158
+ 'name' => substr( $mpp_ingredient, 1 ),
159
+ );
160
+ } else {
161
+ $group['ingredients'][] = array(
162
+ 'raw' => $mpp_ingredient,
163
+ );
164
+ }
165
+ }
166
+ $ingredients[] = $group;
167
+ $recipe['ingredients'] = $ingredients;
168
+
169
+ // Instructions.
170
+ $instructions = array();
171
+ $group = array(
172
+ 'instructions' => array(),
173
+ 'name' => '',
174
+ );
175
+
176
+ $mpp_instructions = preg_split( '/$\R?^/m', $mpp_recipe->instructions );
177
+
178
+ foreach ( $mpp_instructions as $mpp_instruction ) {
179
+ if ( '!' === substr( $mpp_instruction, 0, 1 ) ) {
180
+ $instructions[] = $group;
181
+ $group = array(
182
+ 'instructions' => array(),
183
+ 'name' => $this->derichify( substr( $mpp_instruction, 1 ) ),
184
+ );
185
+ } else {
186
+ $group['instructions'][] = array(
187
+ 'text' => $this->richify( $mpp_instruction ),
188
+ );
189
+ }
190
+ }
191
+ $instructions[] = $group;
192
+ $recipe['instructions'] = $instructions;
193
+
194
+ // Nutrition Facts.
195
+ $recipe['nutrition'] = array();
196
+
197
+ return $recipe;
198
+ }
199
+
200
+ /**
201
+ * Replace the original recipe with the newly imported WPRM one.
202
+ *
203
+ * @since 1.7.0
204
+ * @param mixed $id ID of the recipe we want replace.
205
+ * @param mixed $wprm_id ID of the WPRM recipe to replace with.
206
+ * @param array $post_data POST data passed along when submitting the form.
207
+ */
208
+ public function replace_recipe( $id, $wprm_id, $post_data ) {
209
+ global $wpdb;
210
+ $mpp_recipe = $wpdb->get_row( 'SELECT post_id, server_recipe_id FROM ' . $wpdb->prefix . 'mpprecipe_recipes WHERE recipe_id=' . $id );
211
+ $post_id = $mpp_recipe->post_id;
212
+ $server_recipe_id = $mpp_recipe->server_recipe_id;
213
+
214
+ // Update server_recipe_id field to show that this recipe has been imported.
215
+ $wpdb->update( $wpdb->prefix . 'mpprecipe_recipes', array( 'server_recipe_id' => 'wprm-' . $server_recipe_id ), array( 'recipe_id' => $id ), array( '%s' ), array( '%d' ) );
216
+
217
+ $post = get_post( $post_id );
218
+ $content = $post->post_content;
219
+
220
+ $content = str_ireplace( '[mpprecipe-recipe:' . $id . ']', '[wprm-recipe id="' . $wprm_id . '"]', $content );
221
+
222
+ $update_content = array(
223
+ 'ID' => $post_id,
224
+ 'post_content' => $content,
225
+ );
226
+ wp_update_post( $update_content );
227
+ }
228
+
229
+ /**
230
+ * Richify text by adding links and styling.
231
+ * Source: Meal Planner Pro.
232
+ *
233
+ * @since 1.7.0
234
+ * @param mixed $text Text to richify.
235
+ */
236
+ private function richify( $text ) {
237
+ $output = $text;
238
+
239
+ $link_ptr = '#\[(.*?)\|(.*?)( (.*?))?\]#';
240
+ preg_match_all(
241
+ $link_ptr,
242
+ $text,
243
+ $matches
244
+ );
245
+
246
+ if ( isset( $matches[0] ) ) {
247
+
248
+ $orig = $matches[0];
249
+ $substitution = preg_replace(
250
+ $link_ptr,
251
+ '<a href="\\2" target="_blank" \\3>\\1</a>',
252
+ str_replace( '"', '', $orig )
253
+ );
254
+ $output = str_replace( $orig, $substitution, $text );
255
+ }
256
+
257
+ $output = preg_replace( '/(^|\s)\*([^\s\*][^\*]*[^\s\*]|[^\s\*])\*(\W|$)/', '\\1<span class="bold">\\2</span>\\3', $output );
258
+ $output = preg_replace( '#\[br\]#', '<br/>', $output );
259
+ $output = preg_replace( '#\[b\](.*?)\[\/b\]#s', '<strong>\1</strong>', $output );
260
+ $output = preg_replace( '#\[i\](.*?)\[\/i\]#s', '<em>\1</em>', $output );
261
+ $output = preg_replace( '#\[u\](.*?)\[\/u\]#s', '<u>\1</u>', $output );
262
+ return preg_replace( '/(^|\s)_([^\s_][^_]*[^\s_]|[^\s_])_(\W|$)/', '\\1<span class="italic">\\2</span>\\3', $output );
263
+ }
264
+
265
+ /**
266
+ * Derichify text by removing links and styling.
267
+ *
268
+ * @since 1.7.0
269
+ * @param mixed $text Text to derichify.
270
+ */
271
+ private function derichify( $text ) {
272
+ $output = $text;
273
+
274
+ $link_ptr = '#\[(.*?)\|(.*?)( (.*?))?\]#';
275
+ preg_match_all(
276
+ $link_ptr,
277
+ $text,
278
+ $matches
279
+ );
280
+
281
+ if ( isset( $matches[0] ) ) {
282
+
283
+ $orig = $matches[0];
284
+ $substitution = preg_replace(
285
+ $link_ptr,
286
+ '\\1',
287
+ str_replace( '"', '', $orig )
288
+ );
289
+ $output = str_replace( $orig, $substitution, $text );
290
+ }
291
+
292
+ $output = preg_replace( '/(^|\s)\*([^\s\*][^\*]*[^\s\*]|[^\s\*])\*(\W|$)/', '\\1\\2\\3', $output );
293
+ $output = preg_replace( '#\[br\]#', '', $output );
294
+ $output = preg_replace( '#\[b\](.*?)\[\/b\]#s', '\1', $output );
295
+ $output = preg_replace( '#\[i\](.*?)\[\/i\]#s', '\1', $output );
296
+ $output = preg_replace( '#\[u\](.*?)\[\/u\]#s', '\1', $output );
297
+ return preg_replace( '/(^|\s)_([^\s_][^_]*[^\s_]|[^\s_])_(\W|$)/', '\\1\\2\\3', $output );
298
+ }
299
+
300
+ /**
301
+ * Convert time metadata to minutes.
302
+ *
303
+ * @since 1.7.0
304
+ * @param mixed $duration Time to convert.
305
+ */
306
+ private function time_to_minutes( $duration = 'PT' ) {
307
+ $date_abbr = array(
308
+ 'd' => 60 * 24,
309
+ 'h' => 60,
310
+ 'i' => 1,
311
+ );
312
+ $result = 0;
313
+
314
+ $arr = explode( 'T', $duration );
315
+ if ( isset( $arr[1] ) ) {
316
+ $arr[1] = str_replace( 'M', 'I', $arr[1] );
317
+ }
318
+ $duration = implode( 'T', $arr );
319
+
320
+ foreach ( $date_abbr as $abbr => $time ) {
321
+ if ( preg_match( '/(\d+)' . $abbr . '/i', $duration, $val ) ) {
322
+ $result += intval( $val[1] ) * $time;
323
+ }
324
+ }
325
+
326
+ return $result;
327
+ }
328
+
329
+ /**
330
+ * Get image attachment ID from a given URL or sideload the image if not on the website.
331
+ *
332
+ * @since 1.7.0
333
+ * @param int $post_id Post to associate the image with.
334
+ * @param mixed $url Image URL.
335
+ */
336
+ private function get_or_upload_attachment( $post_id, $url ) {
337
+ $image_id = $this->get_attachment_id_from_url( $url );
338
+
339
+ if ( $image_id ) {
340
+ return $image_id;
341
+ } else {
342
+ $media = media_sideload_image( $url, $post_id );
343
+
344
+ $attachments = get_posts( array(
345
+ 'numberposts' => '1',
346
+ 'post_parent' => $post_id,
347
+ 'post_type' => 'attachment',
348
+ 'post_mime_type' => 'image',
349
+ 'orderby' => 'post_date',
350
+ 'order' => 'DESC',
351
+ )
352
+ );
353
+
354
+ if ( count( $attachments ) > 0 ) {
355
+ return $attachments[0]->ID;
356
+ }
357
+ }
358
+
359
+ return false;
360
+ }
361
+
362
+ /**
363
+ * Get image attachment ID from a given URL.
364
+ * Source: https://philipnewcomer.net/2012/11/get-the-attachment-id-from-an-image-url-in-wordpress/
365
+ *
366
+ * @since 1.7.0
367
+ * @param mixed $attachment_url Image URL.
368
+ */
369
+ private function get_attachment_id_from_url( $attachment_url = '' ) {
370
+ global $wpdb;
371
+ $attachment_id = false;
372
+
373
+ // If there is no url, return.
374
+ if ( '' === $attachment_url ) {
375
+ return;
376
+ }
377
+
378
+ // Get the upload directory paths.
379
+ $upload_dir_paths = wp_upload_dir();
380
+
381
+ // Make sure the upload path base directory exists in the attachment URL, to verify that we're working with a media library image.
382
+ if ( false !== strpos( $attachment_url, $upload_dir_paths['baseurl'] ) ) {
383
+
384
+ // If this is the URL of an auto-generated thumbnail, get the URL of the original image.
385
+ $attachment_url = preg_replace( '/-\d+x\d+(?=\.(jpg|jpeg|png|gif)$)/i', '', $attachment_url );
386
+
387
+ // Remove the upload path base directory from the attachment URL.
388
+ $attachment_url = str_replace( $upload_dir_paths['baseurl'] . '/', '', $attachment_url );
389
+
390
+ // Finally, run a custom database query to get the attachment ID from the modified attachment URL.
391
+ $attachment_id = $wpdb->get_var( $wpdb->prepare( "SELECT wposts.ID FROM $wpdb->posts wposts, $wpdb->postmeta wpostmeta WHERE wposts.ID = wpostmeta.post_id AND wpostmeta.meta_key = '_wp_attached_file' AND wpostmeta.meta_value = '%s' AND wposts.post_type = 'attachment'", $attachment_url ) ); // @codingStandardsIgnoreLine
392
+ }
393
+
394
+ return $attachment_id;
395
+ }
396
+ }
includes/admin/import/class-wprm-import-wpultimaterecipe.php CHANGED
@@ -254,8 +254,32 @@ class WPRM_Import_Wpultimaterecipe extends WPRM_Import {
254
  // Recipe Nutrition.
255
  $recipe['nutrition'] = array();
256
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
257
  $nutrition = isset( $post_meta['recipe_nutritional'] ) ? maybe_unserialize( $post_meta['recipe_nutritional'][0] ) : array();
258
- $recipe['nutrition']['calories'] = isset( $nutrition['calories'] ) ? $nutrition['calories'] : '';
 
 
 
259
 
260
  return $recipe;
261
  }
254
  // Recipe Nutrition.
255
  $recipe['nutrition'] = array();
256
 
257
+ $nutrition_mapping = array(
258
+ 'serving_size' => 'serving_size',
259
+ 'calories' => 'calories',
260
+ 'carbohydrate' => 'carbohydrates',
261
+ 'protein' => 'protein',
262
+ 'fat' => 'fat',
263
+ 'saturated_fat' => 'saturated_fat',
264
+ 'polyunsaturated_fat' => 'polyunsaturated_fat',
265
+ 'monounsaturated_fat' => 'monounsaturated_fat',
266
+ 'trans_fat' => 'trans_fat',
267
+ 'cholesterol' => 'cholesterol',
268
+ 'sodium' => 'sodium',
269
+ 'potassium' => 'potassium',
270
+ 'fiber' => 'fiber',
271
+ 'sugar' => 'sugar',
272
+ 'vitamin_a' => 'vitamin_a',
273
+ 'vitamin_c' => 'vitamin_c',
274
+ 'calcium' => 'calcium',
275
+ 'iron' => 'iron',
276
+ );
277
+
278
  $nutrition = isset( $post_meta['recipe_nutritional'] ) ? maybe_unserialize( $post_meta['recipe_nutritional'][0] ) : array();
279
+
280
+ foreach ( $nutrition_mapping as $wpurp_field => $wprm_field ) {
281
+ $recipe['nutrition'][ $wprm_field ] = isset( $nutrition[ $wpurp_field ] ) ? $nutrition[ $wpurp_field ] : '';
282
+ }
283
 
284
  return $recipe;
285
  }
includes/admin/modal/class-wprm-modal.php CHANGED
@@ -46,7 +46,10 @@ class WPRM_Modal {
46
  wp_enqueue_script( 'rangy', WPRM_URL . 'vendor/rangy/rangy-core.js', array(), WPRM_VERSION, true );
47
  wp_enqueue_script( 'wprm-rich-editor', WPRM_URL . 'assets/js/admin/rich-editor.js', array( 'jquery', 'medium-editor', 'rangy' ), WPRM_VERSION, true );
48
  wp_enqueue_script( 'wprm-select2', WPRM_URL . 'vendor/select2/js/select2.min.js', array( 'jquery' ), WPRM_VERSION, true );
 
 
49
  wp_enqueue_script( 'wprm-modal', WPRM_URL . 'assets/js/admin/modal.js', array( 'jquery' ), WPRM_VERSION, true );
 
50
  wp_enqueue_script( 'wprm-recipe-form', WPRM_URL . 'assets/js/admin/recipe-form.js', array( 'jquery', 'jquery-ui-sortable', 'wprm-modal', 'medium-editor', 'wprm-select2' ), WPRM_VERSION, true );
51
  wp_enqueue_script( 'wprm-recipe-snippets', WPRM_URL . 'assets/js/admin/recipe-snippets.js', array( 'jquery', 'wprm-modal' ), WPRM_VERSION, true );
52
 
@@ -59,6 +62,7 @@ class WPRM_Modal {
59
  'media_title' => __( 'Select or Upload Image', 'wp-recipe-maker' ),
60
  'media_button' => __( 'Use Image', 'wp-recipe-maker' ),
61
  'shortcode_remove' => __( 'Are you sure you want to remove this recipe?', 'wp-recipe-maker' ),
 
62
  ),
63
  'addons' => array(
64
  'premium' => WPRM_Addons::is_active( 'premium' ),
@@ -112,6 +116,13 @@ class WPRM_Modal {
112
  'default' => true,
113
  'label' => __( 'Recipe', 'wp-recipe-maker' ),
114
  'tabs' => array(
 
 
 
 
 
 
 
115
  'recipe-details' => array(
116
  'order' => 100,
117
  'label' => __( 'Recipe Details', 'wp-recipe-maker' ),
46
  wp_enqueue_script( 'rangy', WPRM_URL . 'vendor/rangy/rangy-core.js', array(), WPRM_VERSION, true );
47
  wp_enqueue_script( 'wprm-rich-editor', WPRM_URL . 'assets/js/admin/rich-editor.js', array( 'jquery', 'medium-editor', 'rangy' ), WPRM_VERSION, true );
48
  wp_enqueue_script( 'wprm-select2', WPRM_URL . 'vendor/select2/js/select2.min.js', array( 'jquery' ), WPRM_VERSION, true );
49
+ wp_enqueue_script( 'texthighlighter', WPRM_URL . 'vendor/texthighlighter/TextHighlighter.min.js', array( 'jquery' ), WPRM_VERSION, true );
50
+
51
  wp_enqueue_script( 'wprm-modal', WPRM_URL . 'assets/js/admin/modal.js', array( 'jquery' ), WPRM_VERSION, true );
52
+ wp_enqueue_script( 'wprm-import-text', WPRM_URL . 'assets/js/admin/import-text.js', array( 'jquery', 'wprm-modal', 'texthighlighter' ), WPRM_VERSION, true );
53
  wp_enqueue_script( 'wprm-recipe-form', WPRM_URL . 'assets/js/admin/recipe-form.js', array( 'jquery', 'jquery-ui-sortable', 'wprm-modal', 'medium-editor', 'wprm-select2' ), WPRM_VERSION, true );
54
  wp_enqueue_script( 'wprm-recipe-snippets', WPRM_URL . 'assets/js/admin/recipe-snippets.js', array( 'jquery', 'wprm-modal' ), WPRM_VERSION, true );
55
 
62
  'media_title' => __( 'Select or Upload Image', 'wp-recipe-maker' ),
63
  'media_button' => __( 'Use Image', 'wp-recipe-maker' ),
64
  'shortcode_remove' => __( 'Are you sure you want to remove this recipe?', 'wp-recipe-maker' ),
65
+ 'import_text_reset' => __( 'Are you sure you want to start over with importing from text?', 'wp-recipe-maker' ),
66
  ),
67
  'addons' => array(
68
  'premium' => WPRM_Addons::is_active( 'premium' ),
116
  'default' => true,
117
  'label' => __( 'Recipe', 'wp-recipe-maker' ),
118
  'tabs' => array(
119
+ 'text-import' => array(
120
+ 'order' => 50,
121
+ 'label' => __( 'Import from Text', 'wp-recipe-maker' ),
122
+ 'template' => WPRM_DIR . 'templates/admin/modal/tabs/import-text.php',
123
+ 'callback' => '',
124
+ 'init' => 'set_recipe',
125
+ ),
126
  'recipe-details' => array(
127
  'order' => 100,
128
  'label' => __( 'Recipe Details', 'wp-recipe-maker' ),
includes/class-wp-recipe-maker.php CHANGED
@@ -31,7 +31,7 @@ class WP_Recipe_Maker {
31
  * @since 1.0.0
32
  */
33
  private function define_constants() {
34
- define( 'WPRM_VERSION', '1.6.0' );
35
  define( 'WPRM_POST_TYPE', 'wprm_recipe' );
36
  define( 'WPRM_DIR', plugin_dir_path( dirname( __FILE__ ) ) );
37
  define( 'WPRM_URL', plugin_dir_url( dirname( __FILE__ ) ) );
@@ -57,17 +57,23 @@ class WP_Recipe_Maker {
57
  // General.
58
  require_once( WPRM_DIR . 'includes/class-wprm-i18n.php' );
59
 
 
 
 
60
  // Public.
61
  require_once( WPRM_DIR . 'includes/public/class-wprm-addons.php' );
62
  require_once( WPRM_DIR . 'includes/public/class-wprm-api.php' );
63
- require_once( WPRM_DIR . 'includes/public/class-wprm-comment-rating.php' );
 
 
 
 
64
  require_once( WPRM_DIR . 'includes/public/class-wprm-fallback-recipe.php' );
65
  require_once( WPRM_DIR . 'includes/public/class-wprm-metadata.php' );
66
  require_once( WPRM_DIR . 'includes/public/class-wprm-post-type.php' );
67
  require_once( WPRM_DIR . 'includes/public/class-wprm-print.php' );
68
  require_once( WPRM_DIR . 'includes/public/class-wprm-recipe-manager.php' );
69
  require_once( WPRM_DIR . 'includes/public/class-wprm-recipe.php' );
70
- require_once( WPRM_DIR . 'includes/public/class-wprm-settings.php' );
71
  require_once( WPRM_DIR . 'includes/public/class-wprm-shortcode.php' );
72
  require_once( WPRM_DIR . 'includes/public/class-wprm-taxonomies.php' );
73
  require_once( WPRM_DIR . 'includes/public/class-wprm-template-helper.php' );
31
  * @since 1.0.0
32
  */
33
  private function define_constants() {
34
+ define( 'WPRM_VERSION', '1.7.0' );
35
  define( 'WPRM_POST_TYPE', 'wprm_recipe' );
36
  define( 'WPRM_DIR', plugin_dir_path( dirname( __FILE__ ) ) );
37
  define( 'WPRM_URL', plugin_dir_url( dirname( __FILE__ ) ) );
57
  // General.
58
  require_once( WPRM_DIR . 'includes/class-wprm-i18n.php' );
59
 
60
+ // Priority.
61
+ require_once( WPRM_DIR . 'includes/public/class-wprm-settings.php' );
62
+
63
  // Public.
64
  require_once( WPRM_DIR . 'includes/public/class-wprm-addons.php' );
65
  require_once( WPRM_DIR . 'includes/public/class-wprm-api.php' );
66
+
67
+ if ( WPRM_Settings::get( 'features_comment_ratings' ) ) {
68
+ require_once( WPRM_DIR . 'includes/public/class-wprm-comment-rating.php' );
69
+ }
70
+
71
  require_once( WPRM_DIR . 'includes/public/class-wprm-fallback-recipe.php' );
72
  require_once( WPRM_DIR . 'includes/public/class-wprm-metadata.php' );
73
  require_once( WPRM_DIR . 'includes/public/class-wprm-post-type.php' );
74
  require_once( WPRM_DIR . 'includes/public/class-wprm-print.php' );
75
  require_once( WPRM_DIR . 'includes/public/class-wprm-recipe-manager.php' );
76
  require_once( WPRM_DIR . 'includes/public/class-wprm-recipe.php' );
 
77
  require_once( WPRM_DIR . 'includes/public/class-wprm-shortcode.php' );
78
  require_once( WPRM_DIR . 'includes/public/class-wprm-taxonomies.php' );
79
  require_once( WPRM_DIR . 'includes/public/class-wprm-template-helper.php' );
includes/public/class-wprm-fallback-recipe.php CHANGED
@@ -25,6 +25,7 @@ class WPRM_Fallback_Recipe {
25
  * @since 1.0.0
26
  */
27
  public static function init() {
 
28
  add_filter( 'the_content', array( __CLASS__, 'replace_fallback_with_shortcode' ), 1 );
29
  add_filter( 'content_edit_pre', array( __CLASS__, 'replace_fallback_with_shortcode' ) );
30
 
@@ -79,6 +80,38 @@ class WPRM_Fallback_Recipe {
79
  return $content;
80
  }
81
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
82
  /**
83
  * Get fallback HTML for a specific recipe.
84
  *
25
  * @since 1.0.0
26
  */
27
  public static function init() {
28
+ add_filter( 'the_content', array( __CLASS__, 'replace_imported_shortcodes' ), 1 );
29
  add_filter( 'the_content', array( __CLASS__, 'replace_fallback_with_shortcode' ), 1 );
30
  add_filter( 'content_edit_pre', array( __CLASS__, 'replace_fallback_with_shortcode' ) );
31
 
80
  return $content;
81
  }
82
 
83
+ /**
84
+ * Replace imported shortcodes to make sure recipes are displayed.
85
+ *
86
+ * @since 1.7.0
87
+ * @param mixed $content Content we want to filter before it gets passed along.
88
+ */
89
+ public static function replace_imported_shortcodes( $content ) {
90
+ // BigOven.
91
+ if ( defined( 'BO_RECIPES_VERSION' ) ) {
92
+ $recipe_shortcodes = array();
93
+ $pattern = get_shortcode_regex( array( 'seo_recipe' ) );
94
+
95
+ if ( preg_match_all( '/' . $pattern . '/s', $content, $matches ) && array_key_exists( 2, $matches ) ) {
96
+ foreach ( $matches[2] as $key => $value ) {
97
+ if ( 'seo_recipe' === $value ) {
98
+ $recipe_shortcodes[ $matches[0][ $key ] ] = shortcode_parse_atts( stripslashes( $matches[3][ $key ] ) );
99
+ }
100
+ }
101
+ }
102
+
103
+ foreach ( $recipe_shortcodes as $shortcode => $shortcode_options ) {
104
+ $recipe_id = isset( $shortcode_options['id'] ) ? intval( $shortcode_options['id'] ) : 0;
105
+
106
+ if ( WPRM_POST_TYPE === get_post_type( $recipe_id ) ) {
107
+ $content = str_replace( $shortcode, '[wprm-recipe id="' . $recipe_id . '"]', $content );
108
+ }
109
+ }
110
+ }
111
+
112
+ return $content;
113
+ }
114
+
115
  /**
116
  * Get fallback HTML for a specific recipe.
117
  *
includes/public/class-wprm-metadata.php CHANGED
@@ -160,7 +160,7 @@ class WPRM_Metadata {
160
  }
161
 
162
  foreach ( $nutrition as $field => $value ) {
163
- if ( array_key_exists( $field, $nutrition_mapping ) ) {
164
  $unit = esc_html__( 'g', 'wp-recipe-maker' );
165
 
166
  if ( 'calories' === $field ) {
160
  }
161
 
162
  foreach ( $nutrition as $field => $value ) {
163
+ if ( $value && array_key_exists( $field, $nutrition_mapping ) ) {
164
  $unit = esc_html__( 'g', 'wp-recipe-maker' );
165
 
166
  if ( 'calories' === $field ) {
includes/public/class-wprm-print.php CHANGED
@@ -59,6 +59,9 @@ class WPRM_Print {
59
  $recipe = WPRM_Recipe_Manager::get_recipe( $recipe_id );
60
 
61
  $styles = WPRM_Template_Manager::get_template_styles( $recipe, 'print' );
 
 
 
62
 
63
  $scripts = '';
64
  if ( WPRM_Addons::is_active( 'premium' ) ) {
59
  $recipe = WPRM_Recipe_Manager::get_recipe( $recipe_id );
60
 
61
  $styles = WPRM_Template_Manager::get_template_styles( $recipe, 'print' );
62
+ if ( WPRM_Addons::is_active( 'premium' ) ) {
63
+ $styles .= '<link rel="stylesheet" type="text/css" href="' . WPRMP_URL . 'assets/css/public/public.min.css"/>';
64
+ }
65
 
66
  $scripts = '';
67
  if ( WPRM_Addons::is_active( 'premium' ) ) {
includes/public/class-wprm-settings.php CHANGED
@@ -35,8 +35,13 @@ class WPRM_Settings {
35
  * @var array $defaults Default values for unset settings.
36
  */
37
  private static $defaults = array(
 
38
  'default_recipe_template' => 'simple',
39
  'default_print_template' => 'clean-print',
 
 
 
 
40
  );
41
 
42
  /**
@@ -47,7 +52,8 @@ class WPRM_Settings {
47
  public static function init() {
48
  add_action( 'admin_menu', array( __CLASS__, 'add_submenu_page' ), 20 );
49
  add_action( 'admin_enqueue_scripts', array( __CLASS__, 'enqueue' ) );
50
- add_action( 'admin_post_wprm_settings_appearance', array( __CLASS__, 'form_save_settings' ) );
 
51
 
52
  add_action( 'wprm_settings_page', array( __CLASS__, 'settings_page' ) );
53
  }
@@ -81,6 +87,8 @@ class WPRM_Settings {
81
  public static function settings_page( $sub ) {
82
  if ( 'appearance' === $sub ) {
83
  require_once( WPRM_DIR . 'templates/admin/settings/appearance.php' );
 
 
84
  }
85
  }
86
 
@@ -105,12 +113,22 @@ class WPRM_Settings {
105
  if ( isset( $settings[ $setting ] ) ) {
106
  return $settings[ $setting ];
107
  } else {
108
- $defaults = self::get_defaults();
109
- if ( isset( $defaults[ $setting ] ) ) {
110
- return $defaults[ $setting ];
111
- } else {
112
- return false;
113
- }
 
 
 
 
 
 
 
 
 
 
114
  }
115
  }
116
 
@@ -164,11 +182,11 @@ class WPRM_Settings {
164
  }
165
 
166
  /**
167
- * Save the settings.
168
  *
169
- * @since 1.2.0
170
  */
171
- public static function form_save_settings() {
172
  if ( isset( $_POST['wprm_settings'] ) && wp_verify_nonce( sanitize_key( $_POST['wprm_settings'] ), 'wprm_settings' ) ) { // Input var okay.
173
  $default_recipe_template = isset( $_POST['default_recipe_template'] ) ? sanitize_text_field( wp_unslash( $_POST['default_recipe_template'] ) ) : ''; // Input var okay.
174
  $default_print_template = isset( $_POST['default_print_template'] ) ? sanitize_text_field( wp_unslash( $_POST['default_print_template'] ) ) : ''; // Input var okay.
@@ -185,7 +203,28 @@ class WPRM_Settings {
185
 
186
  self::update_settings( $settings );
187
  }
188
- wp_safe_redirect( admin_url( 'admin.php?page=wprm_settings' ) );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
189
  exit();
190
  }
191
  }
35
  * @var array $defaults Default values for unset settings.
36
  */
37
  private static $defaults = array(
38
+ // Appearance.
39
  'default_recipe_template' => 'simple',
40
  'default_print_template' => 'clean-print',
41
+ // Features.
42
+ 'features_comment_ratings' => true,
43
+ // Features Premium.
44
+ 'features_adjustable_servings' => true,
45
  );
46
 
47
  /**
52
  public static function init() {
53
  add_action( 'admin_menu', array( __CLASS__, 'add_submenu_page' ), 20 );
54
  add_action( 'admin_enqueue_scripts', array( __CLASS__, 'enqueue' ) );
55
+ add_action( 'admin_post_wprm_settings_appearance', array( __CLASS__, 'form_save_settings_appearance' ) );
56
+ add_action( 'admin_post_wprm_settings_features', array( __CLASS__, 'form_save_settings_features' ) );
57
 
58
  add_action( 'wprm_settings_page', array( __CLASS__, 'settings_page' ) );
59
  }
87
  public static function settings_page( $sub ) {
88
  if ( 'appearance' === $sub ) {
89
  require_once( WPRM_DIR . 'templates/admin/settings/appearance.php' );
90
+ } elseif ( 'features' === $sub ) {
91
+ require_once( WPRM_DIR . 'templates/admin/settings/features.php' );
92
  }
93
  }
94
 
113
  if ( isset( $settings[ $setting ] ) ) {
114
  return $settings[ $setting ];
115
  } else {
116
+ self::get_default( $setting );
117
+ }
118
+ }
119
+
120
+ /**
121
+ * Get the default for a specific setting.
122
+ *
123
+ * @since 1.7.0
124
+ * @param mixed $setting Setting to get the default for.
125
+ */
126
+ public static function get_default( $setting ) {
127
+ $defaults = self::get_defaults();
128
+ if ( isset( $defaults[ $setting ] ) ) {
129
+ return $defaults[ $setting ];
130
+ } else {
131
+ return false;
132
  }
133
  }
134
 
182
  }
183
 
184
  /**
185
+ * Save the appearance settings.
186
  *
187
+ * @since 1.7.0
188
  */
189
+ public static function form_save_settings_appearance() {
190
  if ( isset( $_POST['wprm_settings'] ) && wp_verify_nonce( sanitize_key( $_POST['wprm_settings'] ), 'wprm_settings' ) ) { // Input var okay.
191
  $default_recipe_template = isset( $_POST['default_recipe_template'] ) ? sanitize_text_field( wp_unslash( $_POST['default_recipe_template'] ) ) : ''; // Input var okay.
192
  $default_print_template = isset( $_POST['default_print_template'] ) ? sanitize_text_field( wp_unslash( $_POST['default_print_template'] ) ) : ''; // Input var okay.
203
 
204
  self::update_settings( $settings );
205
  }
206
+ wp_safe_redirect( admin_url( 'admin.php?page=wprm_settings&sub=appearance' ) );
207
+ exit();
208
+ }
209
+
210
+ /**
211
+ * Save the features settings.
212
+ *
213
+ * @since 1.7.0
214
+ */
215
+ public static function form_save_settings_features() {
216
+ if ( isset( $_POST['wprm_settings'] ) && wp_verify_nonce( sanitize_key( $_POST['wprm_settings'] ), 'wprm_settings' ) ) { // Input var okay.
217
+ $features_comment_ratings = isset( $_POST['features_comment_ratings'] ) && sanitize_key( $_POST['features_comment_ratings'] ) ? true : false; // Input var okay.
218
+ $features_adjustable_servings = isset( $_POST['features_adjustable_servings'] ) && sanitize_key( $_POST['features_adjustable_servings'] ) ? true : false; // Input var okay.
219
+
220
+ $settings = array();
221
+
222
+ $settings['features_comment_ratings'] = $features_comment_ratings;
223
+ $settings['features_adjustable_servings'] = $features_adjustable_servings;
224
+
225
+ self::update_settings( $settings );
226
+ }
227
+ wp_safe_redirect( admin_url( 'admin.php?page=wprm_settings&sub=features' ) );
228
  exit();
229
  }
230
  }
includes/public/class-wprm-shortcode.php CHANGED
@@ -33,6 +33,7 @@ class WPRM_Shortcode {
33
  add_shortcode( 'timer', array( __CLASS__, 'timer_shortcode' ) );
34
 
35
  add_filter( 'content_edit_pre', array( __CLASS__, 'replace_wpultimaterecipe_shortcode' ) );
 
36
 
37
  add_action( 'init', array( __CLASS__, 'fallback_shortcodes' ), 11 );
38
  add_action( 'wp_enqueue_scripts', array( __CLASS__, 'enqueue' ) );
@@ -53,6 +54,10 @@ class WPRM_Shortcode {
53
  * @since 1.3.0
54
  */
55
  public static function fallback_shortcodes() {
 
 
 
 
56
  if ( ! shortcode_exists( 'ultimate-recipe' ) ) {
57
  add_shortcode( 'ultimate-recipe', array( __CLASS__, 'recipe_shortcode' ) );
58
  }
@@ -61,6 +66,10 @@ class WPRM_Shortcode {
61
  add_shortcode( 'nutrition-label', array( __CLASS__, 'remove_shortcode' ) );
62
  add_shortcode( 'ultimate-nutrition-label', array( __CLASS__, 'remove_shortcode' ) );
63
  }
 
 
 
 
64
  }
65
 
66
  /**
@@ -80,6 +89,23 @@ class WPRM_Shortcode {
80
  return $content;
81
  }
82
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
83
  /**
84
  * To be used for shortcodes we want to (temporarily) remove from the content.
85
  *
@@ -189,9 +215,11 @@ class WPRM_Shortcode {
189
  public static function nutrition_label_shortcode( $atts ) {
190
  $atts = shortcode_atts( array(
191
  'id' => '0',
 
192
  ), $atts, 'wprm_nutrition_label' );
193
 
194
  $recipe_id = intval( $atts['id'] );
 
195
 
196
  // Get first recipe in post content if no ID is set.
197
  if ( ! $recipe_id ) {
@@ -205,7 +233,13 @@ class WPRM_Shortcode {
205
 
206
  if ( $recipe_id && WPRM_Addons::is_active( 'premium' ) ) {
207
  $recipe = WPRM_Recipe_Manager::get_recipe( $recipe_id );
208
- return WPRMP_Nutrition_Label::nutrition_label( $recipe );
 
 
 
 
 
 
209
  } else {
210
  return '';
211
  }
33
  add_shortcode( 'timer', array( __CLASS__, 'timer_shortcode' ) );
34
 
35
  add_filter( 'content_edit_pre', array( __CLASS__, 'replace_wpultimaterecipe_shortcode' ) );
36
+ add_filter( 'content_edit_pre', array( __CLASS__, 'replace_bigoven_shortcode' ) );
37
 
38
  add_action( 'init', array( __CLASS__, 'fallback_shortcodes' ), 11 );
39
  add_action( 'wp_enqueue_scripts', array( __CLASS__, 'enqueue' ) );
54
  * @since 1.3.0
55
  */
56
  public static function fallback_shortcodes() {
57
+ if ( ! shortcode_exists( 'seo_recipe' ) ) {
58
+ add_shortcode( 'seo_recipe', array( __CLASS__, 'recipe_shortcode' ) );
59
+ }
60
+
61
  if ( ! shortcode_exists( 'ultimate-recipe' ) ) {
62
  add_shortcode( 'ultimate-recipe', array( __CLASS__, 'recipe_shortcode' ) );
63
  }
66
  add_shortcode( 'nutrition-label', array( __CLASS__, 'remove_shortcode' ) );
67
  add_shortcode( 'ultimate-nutrition-label', array( __CLASS__, 'remove_shortcode' ) );
68
  }
69
+
70
+ if ( ! shortcode_exists( 'recipe-timer' ) ) {
71
+ add_shortcode( 'recipe-timer', array( __CLASS__, 'timer_shortcode' ) );
72
+ }
73
  }
74
 
75
  /**
89
  return $content;
90
  }
91
 
92
+ /**
93
+ * Replace BigOven shortcode with ours.
94
+ *
95
+ * @since 1.7.0
96
+ * @param mixed $content Content we want to filter before it gets passed along.
97
+ */
98
+ public static function replace_bigoven_shortcode( $content ) {
99
+ preg_match_all( "/\[seo_recipe\s.*?id='?\"?(\d+).*?]/im", $content, $matches );
100
+ foreach ( $matches[0] as $key => $match ) {
101
+ if ( WPRM_POST_TYPE === get_post_type( $matches[1][ $key ] ) ) {
102
+ $content = str_replace( $match, '[wprm-recipe id="' . $matches[1][ $key ] . '"]', $content );
103
+ }
104
+ }
105
+
106
+ return $content;
107
+ }
108
+
109
  /**
110
  * To be used for shortcodes we want to (temporarily) remove from the content.
111
  *
215
  public static function nutrition_label_shortcode( $atts ) {
216
  $atts = shortcode_atts( array(
217
  'id' => '0',
218
+ 'align' => 'left',
219
  ), $atts, 'wprm_nutrition_label' );
220
 
221
  $recipe_id = intval( $atts['id'] );
222
+ $align = in_array( $atts['align'], array( 'center', 'right' ) ) ? $atts['align'] : 'left';
223
 
224
  // Get first recipe in post content if no ID is set.
225
  if ( ! $recipe_id ) {
233
 
234
  if ( $recipe_id && WPRM_Addons::is_active( 'premium' ) ) {
235
  $recipe = WPRM_Recipe_Manager::get_recipe( $recipe_id );
236
+ $label = WPRMP_Nutrition_Label::nutrition_label( $recipe );
237
+
238
+ if ( 'left' !== $align ) {
239
+ $label = '<div class="wprm-nutrition-label-container" style="text-align: ' . $align . ';">' . $label . '</div>';
240
+ }
241
+
242
+ return $label;
243
  } else {
244
  return '';
245
  }
includes/public/class-wprm-template-manager.php CHANGED
@@ -42,8 +42,7 @@ class WPRM_Template_Manager {
42
  * @since 1.0.0
43
  */
44
  public static function enqueue() {
45
- $template_slug = WPRM_Settings::get( 'default_recipe_template' );
46
- $template = self::get_template_by_slug( $template_slug );
47
 
48
  wp_enqueue_style( 'wprm-template', $template['url'] . '/' . $template['slug'] . '.min.css', array(), WPRM_VERSION, 'all' );
49
  }
@@ -62,13 +61,7 @@ class WPRM_Template_Manager {
62
  }
63
 
64
  if ( ! $slug || ! $template ) {
65
- if ( 'print' === $type ) {
66
- $template_slug = WPRM_Settings::get( 'default_print_template' );
67
- } else {
68
- $template_slug = WPRM_Settings::get( 'default_recipe_template' );
69
- }
70
-
71
- $template = self::get_template_by_slug( $template_slug );
72
  }
73
 
74
  ob_start();
@@ -79,6 +72,36 @@ class WPRM_Template_Manager {
79
  // Prevent infinite shortcode loop.
80
  $template = preg_replace( "/\[wprm-recipe\s+id=\"?'?" . $recipe->id() . "\"?'?\]/im", '', $template );
81
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
82
  return do_shortcode( $template );
83
  }
84
 
@@ -90,13 +113,7 @@ class WPRM_Template_Manager {
90
  * @param mixed $type Type of template we want to get, defaults to single.
91
  */
92
  public static function get_template_styles( $recipe, $type = 'single' ) {
93
- if ( 'print' === $type ) {
94
- $template_slug = WPRM_Settings::get( 'default_print_template' );
95
- $template = self::get_template_by_slug( $template_slug );
96
- } else {
97
- $template_slug = WPRM_Settings::get( 'default_recipe_template' );
98
- $template = self::get_template_by_slug( $template_slug );
99
- }
100
 
101
  ob_start();
102
  require( $template['dir'] . '/' . $template['slug'] . '.min.css' );
@@ -121,6 +138,35 @@ class WPRM_Template_Manager {
121
  return $template;
122
  }
123
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
124
  /**
125
  * Get all available templates.
126
  *
42
  * @since 1.0.0
43
  */
44
  public static function enqueue() {
45
+ $template = self::get_template_by_type( 'single' );
 
46
 
47
  wp_enqueue_style( 'wprm-template', $template['url'] . '/' . $template['slug'] . '.min.css', array(), WPRM_VERSION, 'all' );
48
  }
61
  }
62
 
63
  if ( ! $slug || ! $template ) {
64
+ $template = self::get_template_by_type( $type );
 
 
 
 
 
 
65
  }
66
 
67
  ob_start();
72
  // Prevent infinite shortcode loop.
73
  $template = preg_replace( "/\[wprm-recipe\s+id=\"?'?" . $recipe->id() . "\"?'?\]/im", '', $template );
74
 
75
+ // Replace nutrition label shortcode without ID in the print version.
76
+ if ( 'print' === $type ) {
77
+ $label_shortcodes = array();
78
+ $pattern = get_shortcode_regex( array( 'wprm-nutrition-label' ) );
79
+
80
+ if ( preg_match_all( '/' . $pattern . '/s', $template, $matches ) && array_key_exists( 2, $matches ) ) {
81
+ foreach ( $matches[2] as $key => $value ) {
82
+ if ( 'wprm-nutrition-label' === $value ) {
83
+ $label_shortcodes[ $matches[0][ $key ] ] = shortcode_parse_atts( stripslashes( $matches[3][ $key ] ) );
84
+ }
85
+ }
86
+ }
87
+
88
+ foreach ( $label_shortcodes as $shortcode => $shortcode_options ) {
89
+ $recipe_id = isset( $shortcode_options['id'] ) ? intval( $shortcode_options['id'] ) : 0;
90
+
91
+ if ( ! $recipe_id ) {
92
+ $shortcode_options['id'] = $recipe->id();
93
+
94
+ $new_shortcode = '[wprm-nutrition-label';
95
+ foreach ( $shortcode_options as $attr => $val ) {
96
+ $new_shortcode .= ' ' . $attr . '="' . $val . '"';
97
+ }
98
+ $new_shortcode .= ']';
99
+
100
+ $template = str_replace( $shortcode, $new_shortcode, $template );
101
+ }
102
+ }
103
+ }
104
+
105
  return do_shortcode( $template );
106
  }
107
 
113
  * @param mixed $type Type of template we want to get, defaults to single.
114
  */
115
  public static function get_template_styles( $recipe, $type = 'single' ) {
116
+ $template = self::get_template_by_type( $type );
 
 
 
 
 
 
117
 
118
  ob_start();
119
  require( $template['dir'] . '/' . $template['slug'] . '.min.css' );
138
  return $template;
139
  }
140
 
141
+ /**
142
+ * Get template by type.
143
+ *
144
+ * @since 1.7.0
145
+ * @param mixed $type Type of template we want to get, defaults to single.
146
+ */
147
+ public static function get_template_by_type( $type = 'single' ) {
148
+ if ( 'print' === $type ) {
149
+ $template_slug = WPRM_Settings::get( 'default_print_template' );
150
+ } else {
151
+ $template_slug = WPRM_Settings::get( 'default_recipe_template' );
152
+ }
153
+
154
+ $template = self::get_template_by_slug( $template_slug );
155
+
156
+ // Get default template if the template in the settings doesn't exist anymore.
157
+ if ( ! $template ) {
158
+ if ( 'print' === $type ) {
159
+ $template_slug = WPRM_Settings::get_default( 'default_print_template' );
160
+ } else {
161
+ $template_slug = WPRM_Settings::get_default( 'default_recipe_template' );
162
+ }
163
+
164
+ $template = self::get_template_by_slug( $template_slug );
165
+ }
166
+
167
+ return $template;
168
+ }
169
+
170
  /**
171
  * Get all available templates.
172
  *
readme.txt CHANGED
@@ -33,6 +33,8 @@ Currently using another recipe plugin? No problem! You can easily migrate all yo
33
 
34
  * EasyRecipe
35
  * WP Ultimate Recipe
 
 
36
  * (More coming soon!)
37
 
38
  Looking for some more advanced functionality? We also have the [WP Recipe Maker Premium](http://bootstrapped.ventures/wp-recipe-maker/) add-on available with the following features:
@@ -77,6 +79,20 @@ Yes! We pride ourselves on offering awesome support and almost always answer sup
77
 
78
  == Changelog ==
79
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
80
  = 1.6.0 =
81
  * Feature: Show hours for longer recipe times
82
  * Improvement: Prevent font size inconsistencies in template
@@ -135,6 +151,12 @@ Yes! We pride ourselves on offering awesome support and almost always answer sup
135
 
136
  == Upgrade notice ==
137
 
 
 
 
 
 
 
138
  = 1.6.0 =
139
  Some minor updates and the release of WP Recipe Maker Premium
140
 
33
 
34
  * EasyRecipe
35
  * WP Ultimate Recipe
36
+ * Meal Planner Pro
37
+ * BigOven
38
  * (More coming soon!)
39
 
40
  Looking for some more advanced functionality? We also have the [WP Recipe Maker Premium](http://bootstrapped.ventures/wp-recipe-maker/) add-on available with the following features:
79
 
80
  == Changelog ==
81
 
82
+ = 1.7.0 =
83
+ * Feature: Import recipe from text
84
+ * Feature: Add nofollow links in summary and instructions
85
+ * Feature: Import recipes from Meal Planner Pro
86
+ * Feature: Import recipes from BigOven
87
+ * Improvement: Setting to disable comment ratings
88
+ * Improvement: Recognize unicode fractions when importing ingredients
89
+ * Improvement: Import nutrition facts from WP Ultimate Recipe
90
+ * Fix: Only show nutritional metadata if present
91
+ * Fix: Consistent behaviour for automatic time calculations
92
+
93
+ = 1.6.1 =
94
+ * Improvement: Show warning if EasyRecipe is breaking things
95
+
96
  = 1.6.0 =
97
  * Feature: Show hours for longer recipe times
98
  * Improvement: Prevent font size inconsistencies in template
151
 
152
  == Upgrade notice ==
153
 
154
+ = 1.7.0 =
155
+ Lots of great improvements for WP Recipe Maker
156
+
157
+ = 1.6.1 =
158
+ Warning message for EasyRecipe users
159
+
160
  = 1.6.0 =
161
  Some minor updates and the release of WP Recipe Maker Premium
162
 
templates/admin/menu/addons.php CHANGED
@@ -14,6 +14,9 @@
14
  <div class="wrap wprm-addons">
15
  <h1><?php echo esc_html_e( 'Add-Ons', 'wp-recipe-maker' ); ?></h1>
16
  <h2>WP Recipe Maker Premium</h2>
 
 
 
17
  <ul>
18
  <li>Use <strong>ingredient links</strong> for linking to products or other recipes</li>
19
  <li><strong>Adjustable servings</strong> make it easy for your visitors</li>
@@ -22,4 +25,5 @@
22
  <li>More <strong>Premium templates</strong> for a unique recipe template</li>
23
  </ul>
24
  <a class="button button-primary" href="http://bootstrapped.ventures/wp-recipe-maker/get-the-plugin/" target="_blank">Get the Plugin</a>
 
25
  </div>
14
  <div class="wrap wprm-addons">
15
  <h1><?php echo esc_html_e( 'Add-Ons', 'wp-recipe-maker' ); ?></h1>
16
  <h2>WP Recipe Maker Premium</h2>
17
+ <?php if ( WPRM_Addons::is_active( 'premium' ) ) : ?>
18
+ <p>This add-on is active.</p>
19
+ <?php else : ?>
20
  <ul>
21
  <li>Use <strong>ingredient links</strong> for linking to products or other recipes</li>
22
  <li><strong>Adjustable servings</strong> make it easy for your visitors</li>
25
  <li>More <strong>Premium templates</strong> for a unique recipe template</li>
26
  </ul>
27
  <a class="button button-primary" href="http://bootstrapped.ventures/wp-recipe-maker/get-the-plugin/" target="_blank">Get the Plugin</a>
28
+ <?php endif; // Premium active. ?>
29
  </div>
templates/admin/menu/faq/whats_new.php CHANGED
@@ -11,6 +11,19 @@
11
 
12
  ?>
13
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
  <h3>2016-10-25 | WP Recipe Maker Premium 1.0.0</h3>
15
  <ul>
16
  <li>Feature: Ingredient Links</li>
11
 
12
  ?>
13
 
14
+ <h3>2016-11-10 | WP Recipe Maker 1.7.0</h3>
15
+ <ul>
16
+ <li>Feature: Import recipe from text</li>
17
+ <li>Feature: Add nofollow links in summary and instructions</li>
18
+ <li>Feature: Import recipes from Meal Planner Pro</li>
19
+ <li>Feature: Import recipes from BigOven</li>
20
+ <li>Improvement: Setting to disable comment ratings</li>
21
+ <li>Improvement: Recognize unicode fractions when importing ingredients</li>
22
+ <li>Improvement: Import nutrition facts from WP Ultimate Recipe</li>
23
+ <li>Fix: Only show nutritional metadata if present</li>
24
+ <li>Fix: Consistent behaviour for automatic time calculations</li>
25
+ </ul>
26
+
27
  <h3>2016-10-25 | WP Recipe Maker Premium 1.0.0</h3>
28
  <ul>
29
  <li>Feature: Ingredient Links</li>
templates/admin/modal/modal.php CHANGED
@@ -34,6 +34,9 @@
34
  echo '<a href="#" class="wprm-menu-item' . esc_attr( $active_class ) . '" data-menu="' . esc_attr( $menu_item ) . '" data-tab="' . esc_attr( $menu_item ) . '-' . esc_attr( $default_tab ) . '">' . esc_html( $label ) . '</a>';
35
  }
36
  ?>
 
 
 
37
  </div>
38
  </div>
39
  <div class="wprm-frame-title">
34
  echo '<a href="#" class="wprm-menu-item' . esc_attr( $active_class ) . '" data-menu="' . esc_attr( $menu_item ) . '" data-tab="' . esc_attr( $menu_item ) . '-' . esc_attr( $default_tab ) . '">' . esc_html( $label ) . '</a>';
35
  }
36
  ?>
37
+ <div class="wprm-menu-hidden">
38
+ <?php echo esc_html__( "You're currently editing a recipe.", 'wp-recipe-maker' ) . ' ' . esc_html__( 'Use the "WP Recipe Maker" button to access other features.', 'wp-recipe-maker' ); ?>
39
+ </div>
40
  </div>
41
  </div>
42
  <div class="wprm-frame-title">
templates/admin/modal/tabs/import-text.php ADDED
@@ -0,0 +1,74 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Template for the Import from Text tab in the modal.
4
+ *
5
+ * @link http://bootstrapped.ventures
6
+ * @since 1.0.0
7
+ *
8
+ * @package WP_Recipe_Maker
9
+ * @subpackage WP_Recipe_Maker/templates/admin/modal/tabs
10
+ */
11
+
12
+ ?>
13
+
14
+ <div class="wprm-recipe-form wprm-recipe-import-text-form">
15
+ <div class="import-text-buttons">
16
+ <button type="button" class="button wprm-button wprm-button-import-text-reset" disabled="disabled"><?php esc_html_e( 'Start Over', 'wp-recipe-maker' ); ?></button>
17
+ <button type="button" class="button wprm-button wprm-button-import-text-clear" disabled="disabled"><?php esc_html_e( 'Clear', 'wp-recipe-maker' ); ?></button>
18
+ <button type="button" class="button button-primary wprm-button wprm-button-import-text-next" disabled="disabled"><?php esc_html_e( 'Next', 'wp-recipe-maker' ); ?></button>
19
+ </div>
20
+ <div id="import-text-step-input" class="import-text-step">
21
+ <div class="import-text-description">
22
+ <?php esc_html_e( 'Paste the recipe you want to import in the textarea below to get started.', 'wp-recipe-maker' ); ?>
23
+ </div>
24
+ <div class="import-text-input">
25
+ <textarea id="import-text-input-recipe" rows="20"></textarea>
26
+ </div>
27
+ </div>
28
+ <div id="import-text-step-name" class="import-text-step">
29
+ <div class="import-text-description">
30
+ <?php esc_html_e( 'Highlight this part of the recipe:', 'wp-recipe-maker' ); ?> <strong><?php esc_html_e( 'Name', 'wp-recipe-maker' ); ?></strong>
31
+ </div>
32
+ </div>
33
+ <div id="import-text-step-summary" class="import-text-step">
34
+ <div class="import-text-description">
35
+ <?php esc_html_e( 'Highlight this part of the recipe:', 'wp-recipe-maker' ); ?> <strong><?php esc_html_e( 'Summary', 'wp-recipe-maker' ); ?></strong>
36
+ </div>
37
+ </div>
38
+ <div id="import-text-step-ingredients" class="import-text-step">
39
+ <div class="import-text-description">
40
+ <?php esc_html_e( 'Highlight this part of the recipe:', 'wp-recipe-maker' ); ?> <strong><?php esc_html_e( 'Ingredients', 'wp-recipe-maker' ); ?></strong>
41
+ </div>
42
+ </div>
43
+ <div id="import-text-step-ingredient-groups" class="import-text-step">
44
+ <div class="import-text-description">
45
+ <?php esc_html_e( 'Check any ingredient groups in this list:', 'wp-recipe-maker' ); ?>
46
+ </div>
47
+ <div id="import-text-ingredient-groups" class="import-text-input">
48
+ </div>
49
+ </div>
50
+ <div id="import-text-step-instructions" class="import-text-step">
51
+ <div class="import-text-description">
52
+ <?php esc_html_e( 'Highlight this part of the recipe:', 'wp-recipe-maker' ); ?> <strong><?php esc_html_e( 'Instructions', 'wp-recipe-maker' ); ?></strong>
53
+ </div>
54
+ </div>
55
+ <div id="import-text-step-instruction-groups" class="import-text-step">
56
+ <div class="import-text-description">
57
+ <?php esc_html_e( 'Check any instruction groups in this list:', 'wp-recipe-maker' ); ?>
58
+ </div>
59
+ <div id="import-text-instruction-groups" class="import-text-input">
60
+ </div>
61
+ </div>
62
+ <div id="import-text-step-notes" class="import-text-step">
63
+ <div class="import-text-description">
64
+ <?php esc_html_e( 'Highlight this part of the recipe:', 'wp-recipe-maker' ); ?> <strong><?php esc_html_e( 'Notes', 'wp-recipe-maker' ); ?></strong>
65
+ </div>
66
+ </div>
67
+ <div id="import-text-step-finished" class="import-text-step">
68
+ <div class="import-text-description">
69
+ <?php esc_html_e( 'Finished the text import.', 'wp-recipe-maker' ); ?>
70
+ </div>
71
+ </div>
72
+ <div id="import-text-highlight-sandbox">
73
+ </div>
74
+ </div>
templates/admin/modal/tabs/recipe-details.php CHANGED
@@ -12,6 +12,13 @@
12
  ?>
13
 
14
  <div class="wprm-recipe-form wprm-recipe-details-form">
 
 
 
 
 
 
 
15
  <div class="wprm-recipe-form-container wprm-recipe-image-container">
16
  <label for="wprm-recipe-image-id"><?php esc_html_e( 'Image', 'wp-recipe-maker' ); ?></label>
17
  <button type="button" class="button wprm-recipe-image-add"><?php esc_html_e( 'Add Image', 'wp-recipe-maker' ); ?></button>
@@ -27,6 +34,10 @@
27
  <label for="wprm-recipe-summary"><?php esc_html_e( 'Summary', 'wp-recipe-maker' ); ?></label>
28
  <textarea id="wprm-recipe-summary" class="wprm-rich-editor" rows="4"></textarea>
29
  </div>
 
 
 
 
30
  <div class="wprm-recipe-form-container wprm-recipe-form-container-halfs">
31
  <label for="wprm-recipe-author-display"><?php esc_html_e( 'Author', 'wp-recipe-maker' ); ?></label>
32
  <select id="wprm-recipe-author-display">
@@ -40,23 +51,23 @@
40
  </div>
41
  <div class="wprm-recipe-form-container">
42
  <label for="wprm-recipe-servings"><?php esc_html_e( 'Servings', 'wp-recipe-maker' ); ?></label>
43
- <input type="number" id="wprm-recipe-servings" placeholder="4" /> <input type="text" id="wprm-recipe-servings-unit" placeholder="<?php esc_attr_e( 'people', 'wp-recipe-maker' ); ?>" />
44
  </div>
45
  <?php if ( ! WPRM_Addons::is_active( 'premium' ) ) : ?>
46
  <div class="wprm-recipe-form-container">
47
  <label for="wprm-recipe-calories"><?php esc_html_e( 'Calories', 'wp-recipe-maker' ); ?></label>
48
- <input type="number" id="wprm-recipe-calories" placeholder="280" /> <?php esc_html_e( 'kcal', 'wp-recipe-maker' ); ?>
49
  </div>
50
  <?php endif; // Calories. ?>
51
  <div class="wprm-recipe-form-container wprm-recipe-form-container-thirds">
52
  <label for="wprm-recipe-prep-time"><?php esc_html_e( 'Prep Time', 'wp-recipe-maker' ); ?></label>
53
- <input type="number" id="wprm-recipe-prep-time" class="wprm-recipe-time" placeholder="10" /> <?php esc_html_e( 'minutes', 'wp-recipe-maker' ); ?>
54
  </div><div class="wprm-recipe-form-container wprm-recipe-form-container-thirds">
55
  <label for="wprm-recipe-cook-time"><?php esc_html_e( 'Cook Time', 'wp-recipe-maker' ); ?></label>
56
- <input type="number" id="wprm-recipe-cook-time" class="wprm-recipe-time" placeholder="20" /> <?php esc_html_e( 'minutes', 'wp-recipe-maker' ); ?>
57
  </div><div class="wprm-recipe-form-container wprm-recipe-form-container-thirds">
58
  <label for="wprm-recipe-total-time"><?php esc_html_e( 'Total Time', 'wp-recipe-maker' ); ?></label>
59
- <input type="number" id="wprm-recipe-total-time" class="wprm-recipe-time" placeholder="30" /> <?php esc_html_e( 'minutes', 'wp-recipe-maker' ); ?>
60
  </div>
61
  <div class="wprm-recipe-form-container wprm-recipe-form-container-halfs">
62
  <label for="wprm-recipe-tag-course"><?php esc_html_e( 'Course', 'wp-recipe-maker' ); ?></label>
12
  ?>
13
 
14
  <div class="wprm-recipe-form wprm-recipe-details-form">
15
+ <div class="wprm-easyrecipe-warning" style="display: none;">
16
+ <strong>Warning!</strong>
17
+ <p>It looks this page already has an EasyRecipe recipe in it. Unfortunately their code is breaking things and preventing our plugin (and others) from working correctly.</p>
18
+ <p>WP Recipe Maker should work correctly if you remove the EasyRecipe recipe first and update the page before using our plugin.</p>
19
+ <p>We also have an <a href="http://bootstrapped.ventures/wp-recipe-maker/import-from-easyrecipe/" target="_blank">EasyRecipe import feature</a> if you'd like to migrate those recipes!</p>
20
+ <p>This problem does not occur for new posts or posts without recipes. If you're getting this warning in those cases, please <a href="http://bootstrapped.ventures/wp-recipe-maker/support/" target="_blank">contact us</a>!</p>
21
+ </div>
22
  <div class="wprm-recipe-form-container wprm-recipe-image-container">
23
  <label for="wprm-recipe-image-id"><?php esc_html_e( 'Image', 'wp-recipe-maker' ); ?></label>
24
  <button type="button" class="button wprm-recipe-image-add"><?php esc_html_e( 'Add Image', 'wp-recipe-maker' ); ?></button>
34
  <label for="wprm-recipe-summary"><?php esc_html_e( 'Summary', 'wp-recipe-maker' ); ?></label>
35
  <textarea id="wprm-recipe-summary" class="wprm-rich-editor" rows="4"></textarea>
36
  </div>
37
+ <div class='wprm-modal-hint'>
38
+ <span class="wprm-modal-hint-header"><?php esc_html_e( 'Hint', 'wp-recipe-maker' ); ?></span>
39
+ <span class="wprm-modal-hint-text"><?php esc_html_e( 'Select text to add styling or links.', 'wp-recipe-maker' ); ?></span>
40
+ </div>
41
  <div class="wprm-recipe-form-container wprm-recipe-form-container-halfs">
42
  <label for="wprm-recipe-author-display"><?php esc_html_e( 'Author', 'wp-recipe-maker' ); ?></label>
43
  <select id="wprm-recipe-author-display">
51
  </div>
52
  <div class="wprm-recipe-form-container">
53
  <label for="wprm-recipe-servings"><?php esc_html_e( 'Servings', 'wp-recipe-maker' ); ?></label>
54
+ <input type="number" min="0" id="wprm-recipe-servings" placeholder="4" /> <input type="text" id="wprm-recipe-servings-unit" placeholder="<?php esc_attr_e( 'people', 'wp-recipe-maker' ); ?>" />
55
  </div>
56
  <?php if ( ! WPRM_Addons::is_active( 'premium' ) ) : ?>
57
  <div class="wprm-recipe-form-container">
58
  <label for="wprm-recipe-calories"><?php esc_html_e( 'Calories', 'wp-recipe-maker' ); ?></label>
59
+ <input type="number" min="0" id="wprm-recipe-calories" placeholder="280" /> <?php esc_html_e( 'kcal', 'wp-recipe-maker' ); ?>
60
  </div>
61
  <?php endif; // Calories. ?>
62
  <div class="wprm-recipe-form-container wprm-recipe-form-container-thirds">
63
  <label for="wprm-recipe-prep-time"><?php esc_html_e( 'Prep Time', 'wp-recipe-maker' ); ?></label>
64
+ <input type="number" id="wprm-recipe-prep-time" class="wprm-recipe-time" placeholder="10" min="0" /> <?php esc_html_e( 'minutes', 'wp-recipe-maker' ); ?>
65
  </div><div class="wprm-recipe-form-container wprm-recipe-form-container-thirds">
66
  <label for="wprm-recipe-cook-time"><?php esc_html_e( 'Cook Time', 'wp-recipe-maker' ); ?></label>
67
+ <input type="number" id="wprm-recipe-cook-time" class="wprm-recipe-time" placeholder="20" min="0" /> <?php esc_html_e( 'minutes', 'wp-recipe-maker' ); ?>
68
  </div><div class="wprm-recipe-form-container wprm-recipe-form-container-thirds">
69
  <label for="wprm-recipe-total-time"><?php esc_html_e( 'Total Time', 'wp-recipe-maker' ); ?></label>
70
+ <input type="number" id="wprm-recipe-total-time" class="wprm-recipe-time" placeholder="30" min="0" /> <?php esc_html_e( 'minutes', 'wp-recipe-maker' ); ?>
71
  </div>
72
  <div class="wprm-recipe-form-container wprm-recipe-form-container-halfs">
73
  <label for="wprm-recipe-tag-course"><?php esc_html_e( 'Course', 'wp-recipe-maker' ); ?></label>
templates/admin/modal/tabs/recipe-ingredients-instructions.php CHANGED
@@ -87,3 +87,7 @@
87
  <span class="wprm-modal-hint-header"><?php esc_html_e( 'Hint', 'wp-recipe-maker' ); ?></span>
88
  <span class="wprm-modal-hint-text"><?php esc_html_e( 'Use the TAB key to easily move from field to field and add ingredients/instructions without having to click the button.', 'wp-recipe-maker' ); ?></span>
89
  </div>
 
 
 
 
87
  <span class="wprm-modal-hint-header"><?php esc_html_e( 'Hint', 'wp-recipe-maker' ); ?></span>
88
  <span class="wprm-modal-hint-text"><?php esc_html_e( 'Use the TAB key to easily move from field to field and add ingredients/instructions without having to click the button.', 'wp-recipe-maker' ); ?></span>
89
  </div>
90
+ <div class='wprm-modal-hint'>
91
+ <span class="wprm-modal-hint-header"><?php esc_html_e( 'Hint', 'wp-recipe-maker' ); ?></span>
92
+ <span class="wprm-modal-hint-text"><?php esc_html_e( 'Select text to add styling or links.', 'wp-recipe-maker' ); ?></span>
93
+ </div>
templates/admin/settings.php CHANGED
@@ -14,6 +14,7 @@ $sub = isset( $_GET['sub'] ) ? sanitize_key( wp_unslash( $_GET['sub'] ) ) : '';
14
 
15
  $tabs = apply_filters( 'wprm_settings_tabs', array(
16
  'appearance' => __( 'Appearance', 'wp-recipe-maker' ),
 
17
  ) );
18
 
19
  if ( ! array_key_exists( $sub, $tabs ) ) {
14
 
15
  $tabs = apply_filters( 'wprm_settings_tabs', array(
16
  'appearance' => __( 'Appearance', 'wp-recipe-maker' ),
17
+ 'features' => __( 'Features', 'wp-recipe-maker' ),
18
  ) );
19
 
20
  if ( ! array_key_exists( $sub, $tabs ) ) {
templates/admin/settings/features.php ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Template for the features settings sub page.
4
+ *
5
+ * @link http://bootstrapped.ventures
6
+ * @since 1.0.0
7
+ *
8
+ * @package WP_Recipe_Maker
9
+ * @subpackage WP_Recipe_Maker/templates/admin/settings
10
+ */
11
+
12
+ ?>
13
+
14
+ <form method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>">
15
+ <input type="hidden" name="action" value="wprm_settings_features">
16
+ <?php wp_nonce_field( 'wprm_settings', 'wprm_settings', false ); ?>
17
+ <h2 class="title"><?php esc_html_e( 'Features', 'wp-recipe-maker' ); ?></h2>
18
+ <p>
19
+ <?php esc_html_e( 'Choose the features you want to use on your website.', 'wp-recipe-maker' ); ?>
20
+ </p>
21
+ <table class="form-table">
22
+ <tbody>
23
+ <tr>
24
+ <th scope="row">
25
+ <?php esc_html_e( 'Comment Ratings', 'wp-recipe-maker' ); ?>
26
+ </th>
27
+ <td>
28
+ <label for="features_comment_ratings">
29
+ <?php $checked = WPRM_Settings::get( 'features_comment_ratings' ) ? ' checked="checked"' : ''; ?>
30
+ <input name="features_comment_ratings" type="checkbox" id="features_comment_ratings"<?php echo esc_html( $checked ); ?> />
31
+ <?php esc_html_e( 'Allow visitors to vote on your recipes when commenting', 'wp-recipe-maker' ); ?>
32
+ </label>
33
+ <p class="description">
34
+ <a href="http://bootstrapped.ventures/wp-recipe-maker/comment-ratings/" target="_blank"><?php esc_html_e( 'Learn more', 'wp-recipe-maker' ); ?></a>
35
+ </p>
36
+ </td>
37
+ </tr>
38
+ </tbody>
39
+ </table>
40
+ <h2 class="title"><?php esc_html_e( 'Premium Features', 'wp-recipe-maker' ); ?></h2>
41
+ <p>
42
+ <?php if ( ! WPRM_Addons::is_active( 'premium' ) ) : ?>
43
+ <?php esc_html_e( 'These features are only available in', 'wp-recipe-maker' ); ?> <a href="https://bootstrapped.ventures/wp-recipe-maker/get-the-plugin/" target="_blank">WP Recipe Maker Premium</a>.
44
+ <?php else : ?>
45
+ <?php esc_html_e( 'Choose the Premium features you want to use on your website.', 'wp-recipe-maker' ); ?>
46
+ <?php endif; ?>
47
+ </p>
48
+ <table class="form-table">
49
+ <tbody>
50
+ <tr>
51
+ <th scope="row">
52
+ <?php esc_html_e( 'Adjustable Servings', 'wp-recipe-maker' ); ?>
53
+ </th>
54
+ <td>
55
+ <label for="features_adjustable_servings">
56
+ <?php $checked = WPRM_Settings::get( 'features_adjustable_servings' ) ? ' checked="checked"' : ''; ?>
57
+ <input name="features_adjustable_servings" type="checkbox" id="features_adjustable_servings"<?php echo esc_html( $checked ); ?> />
58
+ <?php esc_html_e( 'Visitors can adjust the serving size of your recipes', 'wp-recipe-maker' ); ?>
59
+ </label>
60
+ <p class="description">
61
+ <a href="http://bootstrapped.ventures/wp-recipe-maker/adjustable-servings/" target="_blank"><?php esc_html_e( 'Learn more', 'wp-recipe-maker' ); ?></a>
62
+ </p>
63
+ </td>
64
+ </tr>
65
+ </tbody>
66
+ </table>
67
+ <?php submit_button( __( 'Save Changes', 'wp-recipe-maker' ) ); ?>
68
+ </form>
templates/recipe/clean-print-with-image/clean-print-with-image.min.css CHANGED
@@ -1 +1 @@
1
- .wprm-recipe-clean-print-with-image{font-size:.9em}.wprm-recipe-clean-print-with-image .wprm-recipe-image{float:left;margin:0 15px 15px 0}.wprm-recipe-clean-print-with-image h2.wprm-recipe-name,.wprm-recipe-clean-print-with-image h3.wprm-recipe-header,.wprm-recipe-clean-print-with-image h4.wprm-recipe-group-name{font-variant:normal;text-transform:none;letter-spacing:normal;margin:0}.wprm-recipe-clean-print-with-image h2.wprm-recipe-name{clear:none;font-size:1.8em}.wprm-recipe-clean-print-with-image .wprm-recipe-details-container,.wprm-recipe-clean-print-with-image .wprm-recipe-summary{margin-bottom:15px}.wprm-recipe-clean-print-with-image .wprm-recipe-details-name{display:inline-block;font-weight:700;min-width:130px}.wprm-recipe-clean-print-with-image .wprm-recipe-details-unit{font-size:.8em}.wprm-recipe-clean-print-with-image h3.wprm-recipe-header{margin-top:10px;font-size:1.2em}.wprm-recipe-clean-print-with-image ol,.wprm-recipe-clean-print-with-image ul{margin:0 0 8px}.wprm-recipe-clean-print-with-image ol li,.wprm-recipe-clean-print-with-image ul li{margin:0 0 0 32px}.wprm-recipe-clean-print-with-image h4.wprm-recipe-group-name{margin-top:5px!important;font-weight:300;font-size:1em}.wprm-recipe-clean-print-with-image .wprm-recipe-ingredient-notes{color:#999}.wprm-recipe-clean-print-with-image .wprm-recipe-instructions .wprm-recipe-instruction{margin-bottom:5px}.wprm-recipe-clean-print-with-image .wprm-recipe-instruction-text p{margin:0 0 5px}.wprm-recipe-clean-print-with-image .wprm-recipe-instruction-text p:last-of-type{margin-bottom:0}.wprm-print .wprm-recipe-clean-print-with-image{max-width:750px;margin:0 auto}
1
+ .wprm-recipe-clean-print-with-image{font-size:.9em}.wprm-recipe-clean-print-with-image .wprm-recipe-image{float:left;margin:0 15px 15px 0}.wprm-recipe-clean-print-with-image h2.wprm-recipe-name,.wprm-recipe-clean-print-with-image h3.wprm-recipe-header,.wprm-recipe-clean-print-with-image h4.wprm-recipe-group-name{font-variant:normal;text-transform:none;letter-spacing:normal;margin:0;padding:0}.wprm-recipe-clean-print-with-image h2.wprm-recipe-name{clear:none;font-size:1.8em}.wprm-recipe-clean-print-with-image .wprm-recipe-details-container,.wprm-recipe-clean-print-with-image .wprm-recipe-summary{margin-bottom:15px}.wprm-recipe-clean-print-with-image .wprm-recipe-details-name{display:inline-block;font-weight:700;min-width:130px}.wprm-recipe-clean-print-with-image .wprm-recipe-details-unit{font-size:.8em}.wprm-recipe-clean-print-with-image h3.wprm-recipe-header{margin-top:10px;font-size:1.2em}.wprm-recipe-clean-print-with-image ol,.wprm-recipe-clean-print-with-image ul{margin:0 0 8px}.wprm-recipe-clean-print-with-image ol li,.wprm-recipe-clean-print-with-image ul li{margin:0 0 0 32px}.wprm-recipe-clean-print-with-image h4.wprm-recipe-group-name{margin-top:5px!important;font-weight:300;font-size:1em}.wprm-recipe-clean-print-with-image .wprm-recipe-ingredient-notes{color:#999}.wprm-recipe-clean-print-with-image .wprm-recipe-instructions .wprm-recipe-instruction{margin-bottom:5px}.wprm-recipe-clean-print-with-image .wprm-recipe-instruction-text p{margin:0 0 5px}.wprm-recipe-clean-print-with-image .wprm-recipe-instruction-text p:last-of-type{margin-bottom:0}.wprm-print .wprm-recipe-clean-print-with-image{max-width:750px;margin:0 auto}
templates/recipe/clean-print-with-image/clean-print-with-image.scss CHANGED
@@ -13,6 +13,7 @@
13
  text-transform: none;
14
  letter-spacing: normal;
15
  margin: 0;
 
16
  }
17
 
18
  h2.wprm-recipe-name {
13
  text-transform: none;
14
  letter-spacing: normal;
15
  margin: 0;
16
+ padding: 0;
17
  }
18
 
19
  h2.wprm-recipe-name {
templates/recipe/clean-print/clean-print.min.css CHANGED
@@ -1 +1 @@
1
- .wprm-recipe-clean-print{font-size:.9em}.wprm-recipe-clean-print h2.wprm-recipe-name,.wprm-recipe-clean-print h3.wprm-recipe-header,.wprm-recipe-clean-print h4.wprm-recipe-group-name{font-variant:normal;text-transform:none;letter-spacing:normal;margin:0}.wprm-recipe-clean-print h2.wprm-recipe-name{clear:none;font-size:1.8em}.wprm-recipe-clean-print .wprm-recipe-details-container,.wprm-recipe-clean-print .wprm-recipe-summary{margin-bottom:15px}.wprm-recipe-clean-print .wprm-recipe-details-name{display:inline-block;font-weight:700;min-width:130px}.wprm-recipe-clean-print .wprm-recipe-details-unit{font-size:.8em}.wprm-recipe-clean-print h3.wprm-recipe-header{margin-top:10px;font-size:1.2em}.wprm-recipe-clean-print ol,.wprm-recipe-clean-print ul{margin:0 0 8px}.wprm-recipe-clean-print ol li,.wprm-recipe-clean-print ul li{margin:0 0 0 32px}.wprm-recipe-clean-print h4.wprm-recipe-group-name{margin-top:5px!important;font-weight:300;font-size:1em}.wprm-recipe-clean-print .wprm-recipe-ingredient-notes{color:#999}.wprm-recipe-clean-print .wprm-recipe-instructions .wprm-recipe-instruction{margin-bottom:5px}.wprm-recipe-clean-print .wprm-recipe-instruction-text p{margin:0 0 5px}.wprm-recipe-clean-print .wprm-recipe-instruction-text p:last-of-type{margin-bottom:0}.wprm-print .wprm-recipe-clean-print{max-width:750px;margin:0 auto}
1
+ .wprm-recipe-clean-print{font-size:.9em}.wprm-recipe-clean-print h2.wprm-recipe-name,.wprm-recipe-clean-print h3.wprm-recipe-header,.wprm-recipe-clean-print h4.wprm-recipe-group-name{font-variant:normal;text-transform:none;letter-spacing:normal;margin:0;padding:0}.wprm-recipe-clean-print h2.wprm-recipe-name{clear:none;font-size:1.8em}.wprm-recipe-clean-print .wprm-recipe-details-container,.wprm-recipe-clean-print .wprm-recipe-summary{margin-bottom:15px}.wprm-recipe-clean-print .wprm-recipe-details-name{display:inline-block;font-weight:700;min-width:130px}.wprm-recipe-clean-print .wprm-recipe-details-unit{font-size:.8em}.wprm-recipe-clean-print h3.wprm-recipe-header{margin-top:10px;font-size:1.2em}.wprm-recipe-clean-print ol,.wprm-recipe-clean-print ul{margin:0 0 8px}.wprm-recipe-clean-print ol li,.wprm-recipe-clean-print ul li{margin:0 0 0 32px}.wprm-recipe-clean-print h4.wprm-recipe-group-name{margin-top:5px!important;font-weight:300;font-size:1em}.wprm-recipe-clean-print .wprm-recipe-ingredient-notes{color:#999}.wprm-recipe-clean-print .wprm-recipe-instructions .wprm-recipe-instruction{margin-bottom:5px}.wprm-recipe-clean-print .wprm-recipe-instruction-text p{margin:0 0 5px}.wprm-recipe-clean-print .wprm-recipe-instruction-text p:last-of-type{margin-bottom:0}.wprm-print .wprm-recipe-clean-print{max-width:750px;margin:0 auto}
templates/recipe/clean-print/clean-print.scss CHANGED
@@ -8,6 +8,7 @@
8
  text-transform: none;
9
  letter-spacing: normal;
10
  margin: 0;
 
11
  }
12
 
13
  h2.wprm-recipe-name {
8
  text-transform: none;
9
  letter-spacing: normal;
10
  margin: 0;
11
+ padding: 0;
12
  }
13
 
14
  h2.wprm-recipe-name {
templates/recipe/simple/simple.min.css CHANGED
@@ -1 +1 @@
1
- .wprm-recipe-simple{border-top:1px solid #aaa;background-color:#fafafa;padding:10px;margin:20px 0;font-size:.9em}.wprm-recipe-simple li,.wprm-recipe-simple p{font-size:1em}.wprm-recipe-simple h2.wprm-recipe-name,.wprm-recipe-simple h3.wprm-recipe-header,.wprm-recipe-simple h4.wprm-recipe-group-name{font-variant:normal;text-transform:none;letter-spacing:normal;margin:0}.wprm-recipe-simple .wprm-recipe-image-container{float:right;text-align:center;margin:0 0 10px 10px}.wprm-recipe-simple .wprm-recipe-image-container .wprm-recipe-image{margin:0}.wprm-recipe-simple .wprm-recipe-image-container .wprm-recipe-rating{margin-bottom:5px}.wprm-recipe-simple .wprm-recipe-image-container .wprm-recipe-rating svg{vertical-align:middle;width:16px;height:16px;margin:0}.wprm-recipe-simple .wprm-recipe-image-container .wprm-recipe-rating .wprm-recipe-rating-details{font-size:.8em}.wprm-recipe-simple .wprm-recipe-image-container .wprm-recipe-print{font-size:.9em;cursor:pointer}.wprm-recipe-simple h2.wprm-recipe-name{clear:none;font-size:1.8em}.wprm-recipe-simple .wprm-recipe-details-container,.wprm-recipe-simple .wprm-recipe-summary{margin-bottom:15px}.wprm-recipe-simple .wprm-recipe-details-icon svg{vertical-align:middle;width:16px;height:16px}.wprm-recipe-simple .wprm-recipe-details-name{display:inline-block;font-weight:700;min-width:130px}.wprm-recipe-simple .wprm-recipe-details-unit{font-size:.8em}.wprm-recipe-simple h3.wprm-recipe-header{margin-top:10px;font-size:1.2em}.wprm-recipe-simple ol,.wprm-recipe-simple ul{margin:0 0 8px}.wprm-recipe-simple ol li,.wprm-recipe-simple ul li{margin:0 0 0 32px}.wprm-recipe-simple h4.wprm-recipe-group-name{margin-top:5px!important;font-weight:300;font-size:1em}.wprm-recipe-simple .wprm-recipe-ingredient-notes{color:#999}.wprm-recipe-simple .wprm-recipe-instructions .wprm-recipe-instruction{margin-bottom:5px}.wprm-recipe-simple .wprm-recipe-instruction-text p{margin:0 0 5px}.wprm-recipe-simple .wprm-recipe-instruction-text p:last-of-type{margin-bottom:0}.wprm-recipe-simple .wprm-recipe-instruction-image{margin:5px 0 15px}.wprm-print .wprm-recipe-simple{max-width:750px;margin:0 auto}.wprm-print .wprm-recipe-print{display:none}@media only screen and (max-width:640px){.wprm-recipe-simple .wprm-recipe-image-container{float:none}.wprm-recipe-simple .wprm-recipe-details-name{min-width:0}}
1
+ .wprm-recipe-simple{border-top:1px solid #aaa;background-color:#fafafa;padding:10px;margin:20px 0;font-size:.9em}.wprm-recipe-simple li,.wprm-recipe-simple p{font-size:1em}.wprm-recipe-simple h2.wprm-recipe-name,.wprm-recipe-simple h3.wprm-recipe-header,.wprm-recipe-simple h4.wprm-recipe-group-name{font-variant:normal;text-transform:none;letter-spacing:normal;margin:0;padding:0}.wprm-recipe-simple .wprm-recipe-image-container{float:right;text-align:center;margin:0 0 10px 10px}.wprm-recipe-simple .wprm-recipe-image-container .wprm-recipe-image{margin:0}.wprm-recipe-simple .wprm-recipe-image-container .wprm-recipe-rating{margin-bottom:5px}.wprm-recipe-simple .wprm-recipe-image-container .wprm-recipe-rating svg{vertical-align:middle;width:16px;height:16px;margin:0}.wprm-recipe-simple .wprm-recipe-image-container .wprm-recipe-rating .wprm-recipe-rating-details{font-size:.8em}.wprm-recipe-simple .wprm-recipe-image-container .wprm-recipe-print{font-size:.9em;cursor:pointer}.wprm-recipe-simple h2.wprm-recipe-name{clear:none;font-size:1.8em}.wprm-recipe-simple .wprm-recipe-details-container,.wprm-recipe-simple .wprm-recipe-summary{margin-bottom:15px}.wprm-recipe-simple .wprm-recipe-details-icon svg{vertical-align:middle;width:16px;height:16px}.wprm-recipe-simple .wprm-recipe-details-name{display:inline-block;font-weight:700;min-width:130px}.wprm-recipe-simple .wprm-recipe-details-unit{font-size:.8em}.wprm-recipe-simple h3.wprm-recipe-header{margin-top:10px;font-size:1.2em}.wprm-recipe-simple ol,.wprm-recipe-simple ul{margin:0 0 8px}.wprm-recipe-simple ol li,.wprm-recipe-simple ul li{margin:0 0 0 32px}.wprm-recipe-simple h4.wprm-recipe-group-name{margin-top:5px!important;font-weight:300;font-size:1em}.wprm-recipe-simple .wprm-recipe-ingredient-notes{color:#999}.wprm-recipe-simple .wprm-recipe-instructions .wprm-recipe-instruction{margin-bottom:5px}.wprm-recipe-simple .wprm-recipe-instruction-text p{margin:0 0 5px}.wprm-recipe-simple .wprm-recipe-instruction-text p:last-of-type{margin-bottom:0}.wprm-recipe-simple .wprm-recipe-instruction-image{margin:5px 0 15px}.wprm-print .wprm-recipe-simple{max-width:750px;margin:0 auto}.wprm-print .wprm-recipe-print{display:none}@media only screen and (max-width:640px){.wprm-recipe-simple .wprm-recipe-image-container{float:none}.wprm-recipe-simple .wprm-recipe-details-name{min-width:0}}
templates/recipe/simple/simple.scss CHANGED
@@ -16,6 +16,7 @@
16
  text-transform: none;
17
  letter-spacing: normal;
18
  margin: 0;
 
19
  }
20
 
21
  .wprm-recipe-image-container {
16
  text-transform: none;
17
  letter-spacing: normal;
18
  margin: 0;
19
+ padding: 0;
20
  }
21
 
22
  .wprm-recipe-image-container {
vendor/texthighlighter/TextHighlighter.min.js ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ The MIT License (MIT)
3
+
4
+ Copyright (c) 2011 - 2014 mirz <mirz.hq@gmail.com>
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ of this software and associated documentation files (the "Software"), to deal
8
+ in the Software without restriction, including without limitation the rights
9
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the Software is
11
+ furnished to do so, subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in
14
+ all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22
+ THE SOFTWARE.
23
+ */
24
+ (function(l){function t(a,b){return e(a).color()===e(b).color()}function p(a,b){a=a||{};for(var c in b)b.hasOwnProperty(c)&&void 0===a[c]&&(a[c]=b[c]);return a}function m(a){return a.filter(function(a,c,d){return d.indexOf(a)===c})}function h(a,b){a.sort(function(a,d){return e(b?d:a).parents().length-e(b?a:d).parents().length})}function v(a){var b=[],c={},d=[];a.forEach(function(a){var d=a.getAttribute("data-timestamp");"undefined"===typeof c[d]&&(c[d]=[],b.push(d));c[d].push(a)});b.forEach(function(a){var b=
25
+ c[a];d.push({chunks:b,timestamp:a,toString:function(){return b.map(function(a){return a.textContent}).join("")}})});return d}function w(a,b){a.addEventListener("mouseup",b.highlightHandler.bind(b));a.addEventListener("touchend",b.highlightHandler.bind(b))}function g(a,b){if(!a)throw"Missing anchor element";this.el=a;this.options=p(b,{color:"#ffff7b",highlightedClass:"highlighted",contextClass:"highlighter-context",onRemoveHighlight:function(){return!0},onBeforeHighlight:function(){return!0},onAfterHighlight:function(){}});
26
+ e(this.el).addClass(this.options.contextClass);w(this.el,this)}var u="SCRIPT STYLE SELECT OPTION BUTTON OBJECT APPLET VIDEO AUDIO CANVAS EMBED PARAM METER PROGRESS".split(" "),e=function(a){return{addClass:function(b){a.classList?a.classList.add(b):a.className+=" "+b},removeClass:function(b){a.classList?a.classList.remove(b):a.className=a.className.replace(new RegExp("(^|\\b)"+b+"(\\b|$)","gi")," ")},prepend:function(b){b=Array.prototype.slice.call(b);for(var c=b.length;c--;)a.insertBefore(b[c],a.firstChild)},
27
+ append:function(b){b=Array.prototype.slice.call(b);for(var c=0,d=b.length;c<d;++c)a.appendChild(b[c])},insertAfter:function(b){return b.parentNode.insertBefore(a,b.nextSibling)},insertBefore:function(b){return b.parentNode.insertBefore(a,b)},remove:function(){a.parentNode.removeChild(a);a=null},contains:function(b){return a!==b&&a.contains(b)},wrap:function(b){a.parentNode&&a.parentNode.insertBefore(b,a);b.appendChild(a);return b},unwrap:function(){var b=Array.prototype.slice.call(a.childNodes),c;
28
+ b.forEach(function(a){c=a.parentNode;e(a).insertBefore(a.parentNode);e(c).remove()});return b},parents:function(){for(var b,c=[];b=a.parentNode;)c.push(b),a=b;return c},normalizeTextNodes:function(){if(a){if(3===a.nodeType)for(;a.nextSibling&&3===a.nextSibling.nodeType;)a.nodeValue+=a.nextSibling.nodeValue,a.parentNode.removeChild(a.nextSibling);else e(a.firstChild).normalizeTextNodes();e(a.nextSibling).normalizeTextNodes()}},color:function(){return a.style.backgroundColor},fromHTML:function(a){var c=
29
+ document.createElement("div");c.innerHTML=a;return c.childNodes},getRange:function(){var b=e(a).getSelection(),c;0<b.rangeCount&&(c=b.getRangeAt(0));return c},removeAllRanges:function(){e(a).getSelection().removeAllRanges()},getSelection:function(){return e(a).getWindow().getSelection()},getWindow:function(){return e(a).getDocument().defaultView},getDocument:function(){return a.ownerDocument||a}}};g.prototype.destroy=function(){var a=this.el;a.removeEventListener("mouseup",this.highlightHandler.bind(this));
30
+ a.removeEventListener("touchend",this.highlightHandler.bind(this));e(this.el).removeClass(this.options.contextClass)};g.prototype.highlightHandler=function(){this.doHighlight()};g.prototype.doHighlight=function(a){var b=e(this.el).getRange(),c,d;b&&!b.collapsed&&(!0===this.options.onBeforeHighlight(b)&&(d=+new Date,c=g.createWrapper(this.options),c.setAttribute("data-timestamp",d),c=this.highlightRange(b,c),c=this.normalizeHighlights(c),this.options.onAfterHighlight(b,c,d)),a||e(this.el).removeAllRanges())};
31
+ g.prototype.highlightRange=function(a,b){var c;if(!a||a.collapsed)return[];var d=a.startContainer;c=a.endContainer;var r=a.commonAncestorContainer,n=!0;if(0===a.endOffset){for(;!c.previousSibling&&c.parentNode!==r;)c=c.parentNode;c=c.previousSibling}else 3===c.nodeType?a.endOffset<c.nodeValue.length&&c.splitText(a.endOffset):0<a.endOffset&&(c=c.childNodes.item(a.endOffset-1));3===d.nodeType?a.startOffset===d.nodeValue.length?n=!1:0<a.startOffset&&(d=d.splitText(a.startOffset),c===d.previousSibling&&
32
+ (c=d)):d=a.startOffset<d.childNodes.length?d.childNodes.item(a.startOffset):d.nextSibling;var f=n,n=!1,r=[],k;do f&&3===d.nodeType&&(-1===u.indexOf(d.parentNode.tagName)&&""!==d.nodeValue.trim()&&(f=b.cloneNode(!0),f.setAttribute("data-highlighted",!0),k=d.parentNode,e(this.el).contains(k)||k===this.el)&&(f=e(d).wrap(f),r.push(f)),f=!1),d!==c||c.hasChildNodes()&&f||(n=!0),d.tagName&&-1<u.indexOf(d.tagName)&&(c.parentNode===d&&(n=!0),f=!1),f&&d.hasChildNodes()?d=d.firstChild:d.nextSibling?(d=d.nextSibling,
33
+ f=!0):(d=d.parentNode,f=!1);while(!n);return r};g.prototype.normalizeHighlights=function(a){this.flattenNestedHighlights(a);this.mergeSiblingHighlights(a);a=a.filter(function(a){return a.parentElement?a:null});a=m(a);a.sort(function(a,c){return a.offsetTop-c.offsetTop||a.offsetLeft-c.offsetLeft});return a};g.prototype.flattenNestedHighlights=function(a){function b(){var b=!1;a.forEach(function(c,f){var k=c.parentElement,g=k.previousSibling,h=k.nextSibling;d.isHighlight(k)&&(t(k,c)?(k.replaceChild(c.firstChild,
34
+ c),a[f]=k,b=!0):(c.nextSibling||(e(c).insertBefore(h||k),b=!0),c.previousSibling||(e(c).insertAfter(g||k),b=!0),k.hasChildNodes()||e(k).remove()))});return b}var c,d=this;h(a,!0);do c=b();while(c)};g.prototype.mergeSiblingHighlights=function(a){function b(a,b){return b&&1===b.nodeType&&t(a,b)&&c.isHighlight(b)}var c=this;a.forEach(function(a){var c=a.previousSibling,n=a.nextSibling;b(a,c)&&(e(a).prepend(c.childNodes),e(c).remove());b(a,n)&&(e(a).append(n.childNodes),e(n).remove());e(a).normalizeTextNodes()})};
35
+ g.prototype.setColor=function(a){this.options.color=a};g.prototype.getColor=function(){return this.options.color};g.prototype.removeHighlights=function(a){function b(a){e(a).unwrap().forEach(function(a){var b=a.previousSibling,c=a.nextSibling;b&&3===b.nodeType&&(a.nodeValue=b.nodeValue+a.nodeValue,e(b).remove());c&&3===c.nodeType&&(a.nodeValue+=c.nodeValue,e(c).remove())})}a=this.getHighlights({container:a||this.el});var c=this;h(a,!0);a.forEach(function(a){!0===c.options.onRemoveHighlight(a)&&b(a)})};
36
+ g.prototype.getHighlights=function(a){a=p(a,{container:this.el,andSelf:!0,grouped:!1});var b=a.container.querySelectorAll("[data-highlighted]"),b=Array.prototype.slice.call(b);!0===a.andSelf&&a.container.hasAttribute("data-highlighted")&&b.push(a.container);a.grouped&&(b=v(b));return b};g.prototype.isHighlight=function(a){return a&&1===a.nodeType&&a.hasAttribute("data-highlighted")};g.prototype.serializeHighlights=function(){var a=this.getHighlights(),b=this.el,c=[];h(a,!1);a.forEach(function(a){var e=
37
+ 0,g=a.textContent.length,f=a,k=[],h;do h=Array.prototype.slice.call(f.parentNode.childNodes),k.unshift(h.indexOf(f)),f=f.parentNode;while(f!==b||!f);f=a.cloneNode(!0);f.innerHTML="";f=f.outerHTML;a.previousSibling&&3===a.previousSibling.nodeType&&(e=a.previousSibling.length);c.push([f,a.textContent,k.join(":"),e,g])});return JSON.stringify(c)};g.prototype.deserializeHighlights=function(a){var b,c=[],d=this;if(!a)return c;try{b=JSON.parse(a)}catch(g){throw"Can't parse JSON: "+g;}b.forEach(function(a){try{for(var b=
38
+ a[0],g=a[2].split(":"),h=a[3],m=a[4],l=g.pop(),s=d.el,q,p,r;r=g.shift();)s=s.childNodes[r];s.childNodes[l-1]&&3===s.childNodes[l-1].nodeType&&--l;s=s.childNodes[l];q=s.splitText(h);q.splitText(m);q.nextSibling&&!q.nextSibling.nodeValue&&e(q.nextSibling).remove();q.previousSibling&&!q.previousSibling.nodeValue&&e(q.previousSibling).remove();p=e(q).wrap(e().fromHTML(b)[0]);c.push(p)}catch(t){console&&console.warn&&console.warn("Can't deserialize highlight descriptor. Cause: "+t)}});return c};g.prototype.find=
39
+ function(a,b){var c=e(this.el).getWindow(),d=c.scrollX,g=c.scrollY,h="undefined"===typeof b?!0:b;e(this.el).removeAllRanges();if(c.find)for(;c.find(a,h);)this.doHighlight(!0);else if(c.document.body.createTextRange){var f=c.document.body.createTextRange();for(f.moveToElementText(this.el);f.findText(a,1,h?4:0)&&(e(this.el).contains(f.parentElement())||f.parentElement()===this.el);)f.select(),this.doHighlight(!0),f.collapse(!1)}e(this.el).removeAllRanges();c.scrollTo(d,g)};g.createWrapper=function(a){var b=
40
+ document.createElement("span");b.style.backgroundColor=a.color;b.className=a.highlightedClass;return b};l.TextHighlighter=g})(window);(function(l){function t(l,m){return function(){m.call(this,l)}}l.fn.getHighlighter=function(){return this.data("textHighlighter")};l.fn.textHighlighter=function(p){return this.each(function(){var m=this,h;l.data(m,"textHighlighter")||(h=new TextHighlighter(m,p),h.destroy=t(h.destroy,function(p){p.call(h);l(m).removeData("textHighlighter")}),l.data(m,"textHighlighter",h))})}})(jQuery);
wp-recipe-maker.php CHANGED
@@ -15,7 +15,7 @@
15
  * Plugin Name: WP Recipe Maker
16
  * Plugin URI: http://bootstrapped.ventures/wp-recipe-maker/
17
  * Description: The easy and user-friendly recipe plugin for everyone. Automatic JSON-LD metadata for better SEO will get you more visitors!
18
- * Version: 1.6.0
19
  * Author: Bootstrapped Ventures
20
  * Author URI: http://bootstrapped.ventures/
21
  * License: GPL-2.0+
15
  * Plugin Name: WP Recipe Maker
16
  * Plugin URI: http://bootstrapped.ventures/wp-recipe-maker/
17
  * Description: The easy and user-friendly recipe plugin for everyone. Automatic JSON-LD metadata for better SEO will get you more visitors!
18
+ * Version: 1.7
19
  * Author: Bootstrapped Ventures
20
  * Author URI: http://bootstrapped.ventures/
21
  * License: GPL-2.0+