=SEO Field registration and formatting
| | |
| | | return property_exists($property, 'value') && !empty($property->value); |
| | | }); |
| | | } |
| | | if (!empty($this->additionalProperty)) { |
| | | if (!is_array($this->additionalProperty)) { |
| | | $this->additionalProperty = [$this->additionalProperty]; |
| | | } |
| | | $additionalProperty = array_merge($this->additionalProperty, $additionalProperty); |
| | | } |
| | | $this->additionalProperty = $additionalProperty; |
| | | } |
| | | |
| | |
| | | |
| | | use JVBase\base\SchemaHelper; |
| | | use JVBase\managers\SEO\render\Thing\Intangible\Rating\AggregateRating; |
| | | use JVBase\meta\Meta; |
| | | use JVBase\registrar\Fields; |
| | | |
| | | if (!defined('ABSPATH')) { |
| | | exit; |
| | |
| | | $this->aggregateRating = $aggregateRating; |
| | | } |
| | | |
| | | public function getAggregateRatingFieldConfig():array |
| | | public function setAggregateRatingField(Fields $fields):void |
| | | { |
| | | return [ |
| | | 'type' => 'group', |
| | | 'label' => 'Aggregate/Average Rating', |
| | | 'wrap' => 'details', |
| | | 'fields' => [ |
| | | 'ratingCount' => [ |
| | | 'type' => 'number', |
| | | 'label' => 'The total number of ratings (without text)', |
| | | ], |
| | | 'reviewCount' => [ |
| | | 'type' => 'number', |
| | | 'label' => 'The total number of reviews (with text)' |
| | | ], |
| | | 'bestRating' => [ |
| | | 'type' => 'number', |
| | | 'label' => 'The best rating', |
| | | 'default'=> 5, |
| | | ], |
| | | 'worstRating' => [ |
| | | 'type' => 'number', |
| | | 'label' => 'The worst rating', |
| | | 'default'=> 1, |
| | | ] |
| | | $fields->addField('average_rating', [ |
| | | 'type' => 'select', |
| | | 'label' => __('Average Rating', 'jvb'), |
| | | 'options' => [ |
| | | 'none' => 'None', |
| | | '0.5' => '0.5', |
| | | '1' => '1', |
| | | '1.5' => '1.5', |
| | | '2' => '2', |
| | | '2.5' => '2.5', |
| | | '3' => '3', |
| | | '3.5' => '3.5', |
| | | '4' => '4', |
| | | '4.5' => '4.5', |
| | | '5' => '5', |
| | | ], |
| | | 'default' => 'none', |
| | | ]); |
| | | $fields->addField('ratingCount', [ |
| | | 'type' => 'number', |
| | | 'label' => __('The total number of ratings', 'jvb'), |
| | | 'condition' => [ |
| | | 'field' => 'average_rating', |
| | | 'operator' => '!=', |
| | | 'value' => 'none', |
| | | ] |
| | | ]; |
| | | ]); |
| | | |
| | | $fields->addField('reviewCount', [ |
| | | 'type' => 'number', |
| | | 'label' => __('The total number of reviews (with text)', 'jvb'), |
| | | 'condition' => [ |
| | | 'field' => 'average_rating', |
| | | 'operator' => '!=', |
| | | 'value' => 'none', |
| | | ] |
| | | ]); |
| | | |
| | | $fields->addField('bestRating', [ |
| | | 'type' => 'number', |
| | | 'label' => __('The best possible rating value (top of scale)', 'jvb'), |
| | | 'default' => 5, |
| | | 'condition' => [ |
| | | 'field' => 'average_rating', |
| | | 'operator' => '!=', |
| | | 'value' => 'none', |
| | | ] |
| | | ]); |
| | | $fields->addField('worstRating', [ |
| | | 'type' => 'number', |
| | | 'label' => __('The worst possible rating value (bottom of scale)', 'jvb'), |
| | | 'default' => 1, |
| | | 'condition' => [ |
| | | 'field' => 'average_rating', |
| | | 'operator' => '!=', |
| | | 'value' => 'none', |
| | | ] |
| | | ]); |
| | | } |
| | | |
| | | public function formatAggregateRatingField(Meta $meta):void |
| | | { |
| | | [$average, $ratingCount, $reviewCount, $bestRating, $worstRating] = $meta->getAll([ |
| | | 'average_rating', |
| | | 'ratingCount', |
| | | 'reviewCount', |
| | | 'bestRating', |
| | | 'worstRating' |
| | | ]); |
| | | |
| | | if (!empty($average)) { |
| | | $rating = new AggregateRating(); |
| | | $rating->setRatingValue((float)$average); |
| | | if (!empty($ratingCount)) { |
| | | $rating->setRatingCount($ratingCount); |
| | | } |
| | | if (!empty($reviewCount)){ |
| | | $rating->setReviewCount($reviewCount); |
| | | } |
| | | if ($bestRating !== 5) { |
| | | $rating->setBestRating($bestRating); |
| | | } |
| | | if ($worstRating !== 1) { |
| | | $rating->setWorstRating($worstRating); |
| | | } |
| | | $this->setAggregateRating($rating); |
| | | } |
| | | } |
| | | } |
| | |
| | | namespace JVBase\managers\SEO\render\Traits\_Properties; |
| | | |
| | | use JVBase\managers\SEO\render\Traits\_Helpers\arrayHelper; |
| | | use JVBase\registrar\Fields; |
| | | |
| | | if (!defined('ABSPATH')) { |
| | | exit; |
| | |
| | | } |
| | | $this->alternateName = $alternateName; |
| | | } |
| | | |
| | | public function setAlternateNameField(Fields $fields):void |
| | | { |
| | | $fields->addField( |
| | | 'alternate_name', |
| | | [ |
| | | 'type' => 'repeater', |
| | | 'label' => 'Alternate Name', |
| | | 'fields' => [ |
| | | 'name' => [ |
| | | 'type' => 'text', |
| | | 'label' => 'Name', |
| | | ] |
| | | ] |
| | | ] |
| | | ); |
| | | } |
| | | } |
| | |
| | | namespace JVBase\managers\SEO\render\Traits\_Properties; |
| | | |
| | | use JVBase\managers\SEO\render\Thing\Intangible\StructuredValue\LocationFeatureSpecification; |
| | | use JVBase\managers\SEO\render\Thing\Intangible\StructuredValue\PropertyValue; |
| | | use JVBase\managers\SEO\render\Traits\_Helpers\arrayHelper; |
| | | use JVBase\meta\Meta; |
| | | use JVBase\registrar\Fields; |
| | | |
| | | if (!defined('ABSPATH')) { |
| | | exit; |
| | |
| | | $this->amenityFeature = $amenityFeature; |
| | | } |
| | | |
| | | public function getAmenityFeatureFieldConfig():array |
| | | public function setAmenityFeatureField(Fields $fields):void |
| | | { |
| | | return [ |
| | | 'type' => 'repeater', |
| | | 'label' => 'Amenity Feature', |
| | | 'hint' => 'An amenity feature (e.g. a characteristic or service) of the Accommodation. This generic property does not make a statement about whether the feature is included in an offer for the main accommodation or available at extra costs.', |
| | | 'fields' => [ |
| | | |
| | | $fields->addField('amenityFeature', [ |
| | | 'type' => 'set', |
| | | 'label' => __('Amenities', 'jvb'), |
| | | 'options' => [ |
| | | 'Wheelchair Accessible' => 'Wheelchair Accessible', |
| | | 'Free Parking' => 'Free Parking', |
| | | 'Private Rooms' => 'Private Rooms', |
| | | 'Air Conditioning' => 'Air Conditioning', |
| | | 'WiFi' => 'WiFi', |
| | | 'Gender Neutral Restroom' => 'Gender Neutral Restroom', |
| | | 'LGBTQ+ Friendly' => 'LGBTQ+ Friendly', |
| | | 'Sterilization Room' => 'Sterilization Room', |
| | | 'Refreshments Available' => 'Refreshments Available', |
| | | 'Street Level Access' => 'Street Level Access', |
| | | 'Single Use Needles' => 'Single Use Needles', |
| | | 'Consultation Room' => 'Consultation Room', |
| | | 'Aftercare Products Available' => 'Aftercare Products Available', |
| | | 'Walk-Ins Welcome' => 'Walk-Ins Welcome', |
| | | ] |
| | | ]); |
| | | } |
| | | |
| | | public function formatAmenityFeatureField(Meta $meta):void |
| | | { |
| | | $amenities = $meta->get('amenityFeature'); |
| | | |
| | | $properties = [ |
| | | 'Walk-Ins Welcome', |
| | | 'LGBTQ+ Friendly', |
| | | ]; |
| | | |
| | | if (!empty($amenities)) { |
| | | $out = []; |
| | | $prop = []; |
| | | foreach ($amenities as $amenity) { |
| | | if (in_array($amenity, $properties)) { |
| | | $pr = new PropertyValue(); |
| | | $pr->setName($amenity); |
| | | $pr->setValue(true); |
| | | $prop[] = $pr; |
| | | } else { |
| | | $am = new LocationFeatureSpecification(); |
| | | $am->setName($amenity); |
| | | $am->setValue(true); |
| | | $out[] = $am; |
| | | } |
| | | } |
| | | if (!empty($out)) { |
| | | $this->setAmenityFeature($out); |
| | | } |
| | | if (!empty($prop)) { |
| | | $this->setAdditionalProperty($prop); |
| | | } |
| | | } |
| | | } |
| | | } |
| | |
| | | namespace JVBase\managers\SEO\render\Traits\_Properties; |
| | | |
| | | use JVBase\managers\SEO\render\Traits\_Helpers\arrayHelper; |
| | | use JVBase\meta\Meta; |
| | | use JVBase\registrar\Fields; |
| | | |
| | | if (!defined('ABSPATH')) { |
| | | exit; |
| | |
| | | } |
| | | $this->award = $award; |
| | | } |
| | | public function getAwardFieldConfig():array |
| | | |
| | | public function setAwardField(Fields $fields):void |
| | | { |
| | | return [ |
| | | 'type' => 'repeater', |
| | | 'label' => 'Award(s)', |
| | | 'hint' => 'List any awards won by this Thing.', |
| | | $fields->addField('awards', [ |
| | | 'type' => 'tagList', |
| | | 'label' => __('Award(s)', 'jvb'), |
| | | 'hint' => 'List any awards won by this Thing.', |
| | | 'fields' => [ |
| | | 'award' => [ |
| | | 'name' => [ |
| | | 'type' => 'text', |
| | | 'label' => 'Award', |
| | | 'hint' => 'Include the award name, year, and presenter (if any)' |
| | | 'label' => __('Award Name', 'jvb'), |
| | | 'required' => true, |
| | | ], |
| | | 'presenter' => [ |
| | | 'type' => 'text', |
| | | 'label' => __('Presenter', 'jvb'), |
| | | ], |
| | | 'year' => [ |
| | | 'type' => 'number', |
| | | 'label' => __('Year', 'jvb') |
| | | ] |
| | | ] |
| | | ]; |
| | | ]); |
| | | } |
| | | |
| | | public function formatAwardField(Meta $meta):void |
| | | { |
| | | $awards = $meta->get('awards'); |
| | | $out = []; |
| | | if (!empty($awards)) { |
| | | foreach ($awards as $a) { |
| | | $award = false; |
| | | if (!empty($a['name'])) { |
| | | $award = '"'.$a['name'].'"'; |
| | | }else { |
| | | continue; |
| | | } |
| | | if (!empty($a['presenter'])) { |
| | | $award .= ', presented by '.$a['presenter']; |
| | | } |
| | | if (!empty($a['year'])) { |
| | | $award .= ' - '.$a['year']; |
| | | } |
| | | if ($award) { |
| | | $out[] = $award; |
| | | } |
| | | } |
| | | } |
| | | if (!empty ($out)) { |
| | | $this->setAward($out); |
| | | } |
| | | } |
| | | } |
| | |
| | | namespace JVBase\managers\SEO\render\Traits\_Properties; |
| | | |
| | | use JVBase\managers\SEO\render\DataType\Date; |
| | | use JVBase\meta\Meta; |
| | | use JVBase\registrar\Fields; |
| | | |
| | | if (!defined('ABSPATH')) { |
| | | exit; |
| | |
| | | 'hint' => 'IMPORTANT: Do not fill this out, unless your business has actually closed.' |
| | | ]; |
| | | } |
| | | public function setDissolutionDateField(Fields $fields):void |
| | | { |
| | | $fields->addField( |
| | | 'permanently_close', |
| | | [ |
| | | 'type' => 'true_false', |
| | | 'label' => __('Permanently Close', 'jvb'), |
| | | 'hint' => '*IMPORTANT* This signals to search engines that this business is no longer in business. Use only if your shop is closing!', |
| | | ] |
| | | ); |
| | | $fields->addField( |
| | | 'dissolution_date', |
| | | [ |
| | | 'type' => 'date', |
| | | 'label' => __('Dissolution Date', 'jvb'), |
| | | 'condition' => [ |
| | | 'field' => 'permanently_close', |
| | | 'operator' => '==', |
| | | 'value' => true, |
| | | ] |
| | | ] |
| | | ); |
| | | } |
| | | |
| | | public function formatDissolutionDateField(Meta $meta):void |
| | | { |
| | | [$closed, $dissolution] = $meta->getAll(['permanently_close', 'dissolution_date']); |
| | | if (!empty($closed) && $closed === true) { |
| | | if (!empty($dissolution)) { |
| | | $this->setDissolutionDate($dissolution); |
| | | } |
| | | } |
| | | } |
| | | } |
| | |
| | | |
| | | use JVBase\base\SchemaHelper; |
| | | use JVBase\inc\managers\SEO\render\Thing\Intangible\ItemList\OfferCatalog; |
| | | use JVBase\managers\SEO\render\Thing\Intangible\Offer; |
| | | use JVBase\managers\SEO\render\Thing\Intangible\Service; |
| | | use JVBase\managers\SEO\render\Thing\Intangible\StructuredValue\PriceSpecification; |
| | | use JVBase\managers\SEO\render\Thing\Product\Product; |
| | | use JVBase\meta\Meta; |
| | | use JVBase\registrar\Fields; |
| | | use JVBase\registrar\Registrar; |
| | | |
| | | if (!defined('ABSPATH')) { |
| | |
| | | ] |
| | | ]; |
| | | } |
| | | |
| | | public function setHasOfferCatalogField(Fields $fields):void |
| | | { |
| | | $fields->addField( |
| | | 'hasOfferCatalog', |
| | | [ |
| | | 'type' => 'repeater', |
| | | 'label' => 'Main Products & Services', |
| | | 'fields' => [ |
| | | 'type' => [ |
| | | 'type' => 'radio', |
| | | 'label' => __('Select Type', 'jvb'), |
| | | 'options' => [ |
| | | 'product' => 'Product', |
| | | 'service' => 'Service' |
| | | ], |
| | | 'required' => true, |
| | | ], |
| | | 'name' => [ |
| | | 'type' => 'text', |
| | | 'label' => __('Name of Product or Service', 'jvb'), |
| | | 'required' => true, |
| | | ], |
| | | 'description' => [ |
| | | 'type' => 'textarea', |
| | | 'label' => __('Description (optional)', 'jvb'), |
| | | ], |
| | | 'price' => [ |
| | | 'type' => 'text', |
| | | 'subtype' => 'number', |
| | | 'label' => __('Price', 'jvb'), |
| | | ], |
| | | 'unitText' => [ |
| | | 'type' => 'radio', |
| | | 'label' => 'Price per unit', |
| | | 'options' => [ |
| | | 'hour' => 'Hour', |
| | | 'unit' => 'Unit', |
| | | ], |
| | | 'default' => 'unit', |
| | | ] |
| | | ], |
| | | ] |
| | | ); |
| | | } |
| | | |
| | | public function formatHasOfferCatalogField(Meta $meta):void |
| | | { |
| | | $catalog = $meta->get('hasOfferCatalog'); |
| | | if (!empty($catalog)) { |
| | | $offerCatalog = []; |
| | | $name = ''; |
| | | $services = array_filter($catalog, function ($item) { |
| | | return $item['type'] === 'service'; |
| | | }); |
| | | $products =array_filter($catalog, function ($item) { |
| | | return $item['type'] === 'product'; |
| | | }); |
| | | if (count($products) > 0) { |
| | | $name = 'Products'; |
| | | if (count($services) > 0) { |
| | | $name .= ' & '; |
| | | } |
| | | } |
| | | if (count($services) > 0) { |
| | | $name .= 'Services'; |
| | | } |
| | | foreach ($catalog as $row) { |
| | | $offer = new Offer(); |
| | | |
| | | $item = match($row['type']) { |
| | | 'product' => new Product(), |
| | | 'service' => new Service(), |
| | | }; |
| | | |
| | | $item->setName($row['name']); |
| | | if (!empty($row['description'])) { |
| | | $item->setDescription($row['description']); |
| | | } |
| | | if (!empty($row['price'])) { |
| | | $price = new PriceSpecification(); |
| | | $price->setPrice($row['price']); |
| | | $price->setPriceCurrency('CAD'); |
| | | $price->setUnitText($row['unitText']); |
| | | $offer->setPriceSpecification($price); |
| | | } |
| | | $offer->setItemOffered($item); |
| | | $offerCatalog[] = $offer; |
| | | } |
| | | if (!empty(!$offerCatalog)) { |
| | | $final = new OfferCatalog(); |
| | | $final->setName($name); |
| | | $final->setItemListElement($offerCatalog); |
| | | $this->sethasOfferCatalog($final); |
| | | } |
| | | } |
| | | } |
| | | } |
| | |
| | | |
| | | use JVBase\managers\SEO\render\Thing\Intangible\DefinedTerm; |
| | | use JVBase\managers\SEO\render\Traits\_Helpers\arrayHelper; |
| | | use JVBase\meta\Meta; |
| | | use JVBase\registrar\Fields; |
| | | |
| | | if (!defined('ABSPATH')) { |
| | | exit; |
| | |
| | | } |
| | | $this->keywords = $keywords; |
| | | } |
| | | |
| | | public function setKeywordsField(Fields $fields):void |
| | | { |
| | | $fields->addField( |
| | | 'keywords', |
| | | [ |
| | | 'type' => 'repeater', |
| | | 'label' => 'Keywords', |
| | | 'fields' => [ |
| | | 'keyword' => [ |
| | | 'type' => 'text', |
| | | 'label' => 'Keyword', |
| | | ], |
| | | ], |
| | | ] |
| | | ); |
| | | } |
| | | |
| | | public function formatKeywordsField(Meta $meta):void |
| | | { |
| | | $keywords = $meta->get('keywords'); |
| | | if (!empty($keywords)) { |
| | | $this->setKeywords($keywords); |
| | | } |
| | | } |
| | | } |
| | |
| | | <?php |
| | | namespace JVBase\managers\SEO\render\Traits\_Properties; |
| | | |
| | | use JVBase\inc\managers\SEO\render\Thing\Intangible\ItemList\OfferCatalog; |
| | | use JVBase\managers\SEO\render\Thing\Intangible\Demand; |
| | | use JVBase\managers\SEO\render\Thing\Intangible\Offer; |
| | | use JVBase\managers\SEO\render\Thing\Intangible\Service; |
| | | use JVBase\managers\SEO\render\Thing\Intangible\StructuredValue\PriceSpecification; |
| | | use JVBase\managers\SEO\render\Thing\Product\Product; |
| | | use JVBase\managers\SEO\render\Traits\_Helpers\arrayHelper; |
| | | use JVBase\meta\Meta; |
| | | use JVBase\registrar\Fields; |
| | | use JVBase\registrar\Registrar; |
| | | use WP_Query; |
| | | |
| | | if (!defined('ABSPATH')) { |
| | | exit; |
| | |
| | | } |
| | | $this->offers = $offers; |
| | | } |
| | | public function getOpeningHoursFieldConfig():array |
| | | public function setOffersField(Fields $fields):void |
| | | { |
| | | return [ |
| | | $fields->addField('generate_offers_from_content', |
| | | [ |
| | | 'type' => 'true_false', |
| | | 'label' => __('Generate Offers from Content', 'jvb'), |
| | | ]); |
| | | $fields->addField('seo_offers', [ |
| | | 'type' => 'repeater', |
| | | 'label' => 'Opening Hours', |
| | | 'label' => __('Offers', 'jvb'), |
| | | 'condition' => [ |
| | | 'field' => 'generate_offers_from_content', |
| | | 'value' => true, |
| | | 'operator' => '==' |
| | | ], |
| | | 'fields' => [ |
| | | 'dayOfWeek' => [ |
| | | 'type' => [ |
| | | 'type' => 'radio', |
| | | 'label' => 'Day(s) of Week', |
| | | 'label' => __('Select Type', 'jvb'), |
| | | 'options' => [ |
| | | 'Mo' => 'Monday', |
| | | 'Tu' => 'Tuesday', |
| | | 'We' => 'Wednesday', |
| | | 'Th' => 'Thursday', |
| | | 'Fr' => 'Friday', |
| | | 'Sa' => 'Saturday', |
| | | 'Su' => 'Sunday' |
| | | ] |
| | | 'product' => 'Product', |
| | | 'service' => 'Service' |
| | | ], |
| | | 'required' => true, |
| | | ], |
| | | 'opens' => [ |
| | | 'type' => 'time', |
| | | 'label' => 'Opens At', |
| | | 'name' => [ |
| | | 'type' => 'text', |
| | | 'label' => __('Name of Product or Service', 'jvb'), |
| | | 'required' => true, |
| | | ], |
| | | 'closes' => [ |
| | | 'type' => 'time', |
| | | 'label' => 'Closes At', |
| | | 'description' => [ |
| | | 'type' => 'textarea', |
| | | 'label' => __('Description (optional)', 'jvb'), |
| | | ], |
| | | 'price' => [ |
| | | 'type' => 'text', |
| | | 'subtype' => 'number', |
| | | 'label' => __('Price', 'jvb'), |
| | | ], |
| | | 'unitText' => [ |
| | | 'type' => 'radio', |
| | | 'label' => 'Price per unit', |
| | | 'options' => [ |
| | | 'hour' => 'Hour', |
| | | 'unit' => 'Unit', |
| | | ], |
| | | 'default' => 'unit', |
| | | ] |
| | | ] |
| | | ]; |
| | | ], |
| | | ]); |
| | | } |
| | | |
| | | public function formatOffersField(Meta $meta):void |
| | | { |
| | | $generate = $meta->get('generate_offers_from_content'); |
| | | if (!empty($generate) && $generate === true) { |
| | | $this->generateOffersFromContent($meta); |
| | | return; |
| | | } |
| | | $catalog = $meta->get('seo_offers'); |
| | | if (!empty($catalog)) { |
| | | $offerCatalog = []; |
| | | $name = ''; |
| | | $services = array_filter($catalog, function ($item) { |
| | | return $item['type'] === 'service'; |
| | | }); |
| | | $products =array_filter($catalog, function ($item) { |
| | | return $item['type'] === 'product'; |
| | | }); |
| | | if (count($products) > 0) { |
| | | $name = 'Products'; |
| | | if (count($services) > 0) { |
| | | $name .= ' & '; |
| | | } |
| | | } |
| | | if (count($services) > 0) { |
| | | $name .= 'Services'; |
| | | } |
| | | foreach ($catalog as $row) { |
| | | $offer = new Offer(); |
| | | |
| | | $item = match($row['type']) { |
| | | 'product' => new Product(), |
| | | 'service' => new Service(), |
| | | }; |
| | | |
| | | $item->setName($row['name']); |
| | | if (!empty($row['description'])) { |
| | | $item->setDescription($row['description']); |
| | | } |
| | | if (!empty($row['price'])) { |
| | | $price = new PriceSpecification(); |
| | | $price->setPrice($row['price']); |
| | | $price->setPriceCurrency('CAD'); |
| | | $price->setUnitText($row['unitText']); |
| | | $offer->setPriceSpecification($price); |
| | | } |
| | | $offer->setItemOffered($item); |
| | | $offerCatalog[] = $offer; |
| | | } |
| | | if (!empty(!$offerCatalog)) { |
| | | $this->setOffers($offerCatalog); |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * This presumes that the Meta instance being passed is for a profile content type. |
| | | * @param Meta $meta |
| | | * @return void |
| | | */ |
| | | public function generateOffersFromContent(Meta $meta):void |
| | | { |
| | | $userID = (int)get_post_meta($meta->id(), BASE.'profile_link', true); |
| | | if (empty($userID)) { |
| | | return; |
| | | } |
| | | $user = get_userdata($userID); |
| | | if (!$user) { |
| | | return; |
| | | } |
| | | $registrar = Registrar::getInstance(jvbUserRole($userID)); |
| | | if (!$registrar) { |
| | | return; |
| | | } |
| | | $types = $registrar->getCreatable(); |
| | | if (empty($types)) { |
| | | return; |
| | | } |
| | | $offers = []; |
| | | $cities = $meta->get('city'); |
| | | $rate = $meta->get('rate'); |
| | | $reviews = $meta->get('reviews'); |
| | | $rating = $meta->get('average_rating'); |
| | | $theCities = $theRate = $theReviews = $theRating = null; |
| | | |
| | | foreach($types as $type) { |
| | | $based = jvbCheckBase($type); |
| | | $hasAny = new WP_Query([ |
| | | 'post_type' => $based, |
| | | 'posts_per_page'=> 1, |
| | | 'fields' => 'ids', |
| | | 'post_author' => $userID |
| | | ]); |
| | | if ($hasAny->have_posts()) { |
| | | $offer = new Offer(); |
| | | $typeRegistrar = Registrar::getInstance($type); |
| | | |
| | | } |
| | | } |
| | | } |
| | | } |
| | |
| | | use JVBase\base\SchemaHelper; |
| | | use JVBase\managers\SEO\render\Thing\Intangible\StructuredValue\OpeningHoursSpecification; |
| | | use JVBase\managers\SEO\render\Traits\_Helpers\arrayHelper; |
| | | use JVBase\meta\Meta; |
| | | use JVBase\registrar\Fields; |
| | | |
| | | if (!defined('ABSPATH')) { |
| | | exit; |
| | |
| | | $this->openingHoursSpecification = $openingHoursSpecification; |
| | | } |
| | | |
| | | public function getOpeningHoursSpecificationFieldConfig():array |
| | | public function setOpeningHoursSpecificationField(Fields $fields):void |
| | | { |
| | | return [ |
| | | $fields->addField('openingHours', [ |
| | | 'type' => 'repeater', |
| | | 'label' => 'Opening Hours', |
| | | 'label' => __('Opening Hours', 'jvb'), |
| | | 'fields' => [ |
| | | 'dayOfWeek' => [ |
| | | 'type' => 'radio', |
| | | 'label' => 'Day(s) of Week', |
| | | 'type' => 'set', |
| | | 'label' => __('Day(s) of Week', 'jvb'), |
| | | 'options' => [ |
| | | 'Mo' => 'Monday', |
| | | 'Tu' => 'Tuesday', |
| | |
| | | 'Fr' => 'Friday', |
| | | 'Sa' => 'Saturday', |
| | | 'Su' => 'Sunday' |
| | | ] |
| | | ], |
| | | 'required' => true |
| | | ], |
| | | 'opens' => [ |
| | | 'type' => 'time', |
| | | 'label' => 'Opens At', |
| | | 'label' => __('Opens at', 'jvb'), |
| | | 'required' => true |
| | | ], |
| | | 'closes' => [ |
| | | 'type' => 'time', |
| | | 'label' => 'Closes At', |
| | | 'label' => __('Closes at', 'jvb'), |
| | | 'required' => true |
| | | ] |
| | | ] |
| | | ]; |
| | | ]); |
| | | $fields->addField('by_appointment', [ |
| | | 'type' => 'true_false', |
| | | 'label' => __('By Appointment Only', 'jvb'), |
| | | ]); |
| | | $fields->addField('allow_walkins', [ |
| | | 'type' => 'true_false', |
| | | 'label' => __('Walk Ins Welcome', 'jvb') |
| | | ]); |
| | | } |
| | | public function formatOpeningHoursSpecificationField(Meta $meta):void |
| | | { |
| | | $openingHours = $meta->get('openingHours'); |
| | | |
| | | if (!empty($openingHours)) { |
| | | $used = [ |
| | | 'Mo' => false, |
| | | 'Tu' => false, |
| | | 'We' => false, |
| | | 'Th' => false, |
| | | 'Fr' => false, |
| | | 'Sa' => false, |
| | | 'Su' => false, |
| | | ]; |
| | | $hours = []; |
| | | foreach ($openingHours as $row) { |
| | | $days = array_filter(explode(',', $row['dayOfWeek']), |
| | | function ($d) use ($used) { |
| | | if ($used[$d] === false) { |
| | | $used[$d] = true; |
| | | return true; |
| | | } |
| | | return false; |
| | | }); |
| | | if (empty($days)) { |
| | | continue; |
| | | } |
| | | $opens = new OpeningHoursSpecification(); |
| | | $opens->setDayOfWeek($days); |
| | | $opens->setOpens($row['opens']); |
| | | $opens->setCloses($row['closes']); |
| | | } |
| | | if (!empty($hours)){ |
| | | $this->setOpeningHoursSpecification($hours); |
| | | } |
| | | } |
| | | } |
| | | } |
| | |
| | | namespace JVBase\managers\SEO\render\Traits\_Properties; |
| | | |
| | | use JVBase\managers\SEO\render\Traits\_Helpers\arrayHelper; |
| | | use JVBase\meta\Meta; |
| | | use JVBase\registrar\Fields; |
| | | |
| | | if (!defined('ABSPATH')) { |
| | | exit; |
| | |
| | | $this->paymentAccepted = $paymentAccepted; |
| | | } |
| | | |
| | | public function getPaymentAcceptedFieldConfig():array |
| | | public function setPaymentAcceptedField(Fields $fields):void |
| | | { |
| | | return [ |
| | | 'type' => 'string', |
| | | 'label' => 'Payment Accepted', |
| | | 'hint' => 'A comma separated list of payment accepted, example: Cash, Credit Card, Cryptocurrency, Local Exchange Tradings System, etc.' |
| | | ]; |
| | | $fields->addField( |
| | | 'payment_accepted', |
| | | [ |
| | | 'type' => 'set', |
| | | 'label' => __('Payment Accepted', 'jvb'), |
| | | 'options' => [ |
| | | 'Cash' => 'Cash', |
| | | 'Credit Card' => 'Credit Card', |
| | | 'Debit' => 'Debit', |
| | | 'Google Pay' => 'Google Pay', |
| | | 'Apple Pay' => 'Apple Pay', |
| | | 'PayPal' => 'PayPal', |
| | | 'Interac' => 'Interac', |
| | | 'AMEX' => 'AMEX', |
| | | ], |
| | | ] |
| | | ); |
| | | } |
| | | public function formatPaymentAcceptedField(Meta $meta):void |
| | | { |
| | | $accepted = $meta->get('payment_accepted'); |
| | | if (!empty($accepted)){ |
| | | $this->setPaymentAccepted($accepted); |
| | | } |
| | | } |
| | | } |
| | |
| | | |
| | | use JVBase\managers\SEO\render\Thing\CreativeWork\MediaObject\ImageObject; |
| | | use JVBase\managers\SEO\render\Thing\CreativeWork\Photograph; |
| | | use JVBase\meta\Meta; |
| | | use JVBase\registrar\Fields; |
| | | |
| | | if (!defined('ABSPATH')) { |
| | | exit; |
| | |
| | | 'hint' => 'A photograph of this place.' |
| | | ]; |
| | | } |
| | | public function setPhotoField(Fields $fields):void |
| | | { |
| | | $fields->addField( |
| | | 'outside_photo', |
| | | [ |
| | | 'type' => 'image', |
| | | 'limit' => 1, |
| | | 'label' => __('Outside Photo', 'jvb') |
| | | ] |
| | | ); |
| | | } |
| | | |
| | | public function formatPhotoField(Meta $meta):void |
| | | { |
| | | $image = $meta->get('outside_photo'); |
| | | if (!empty($image) && $image > 0) { |
| | | $image = $this->createImageFromID($image); |
| | | if ($image) { |
| | | $this->setPhoto($image); |
| | | } |
| | | } |
| | | } |
| | | } |
| | |
| | | namespace JVBase\managers\SEO\render\Traits\_Properties; |
| | | |
| | | use JVBase\managers\SEO\render\Thing\CreativeWork\Review; |
| | | use JVBase\managers\SEO\render\Thing\Intangible\Rating\Rating; |
| | | use JVBase\managers\SEO\render\Thing\Person\Person; |
| | | use JVBase\managers\SEO\render\Traits\_Helpers\arrayHelper; |
| | | use JVBase\meta\Meta; |
| | | use JVBase\registrar\Fields; |
| | | |
| | | if (!defined('ABSPATH')) { |
| | | exit; |
| | |
| | | ] |
| | | ]; |
| | | } |
| | | public function setReviewField(Fields $fields):void |
| | | { |
| | | $fields->addField( |
| | | 'enable_gmb_review_sync', |
| | | [ |
| | | 'type' => 'true_false', |
| | | 'label' => __('Enable Google My Business Sync', 'jvb'), |
| | | 'hint' => 'Automagically fetch your latest positive reviews from your Google Business Listing. |
| | | Note: you must enable your GMB integration in settings.', |
| | | ] |
| | | ); |
| | | $fields->addField('gmb_min_rating', |
| | | [ |
| | | 'type' => 'number', |
| | | 'default' => 4, |
| | | 'min' => 1, |
| | | 'max' => 5, |
| | | 'label' => 'Minimum Rating to fetch', |
| | | 'hint' => 'Defaults to 4 stars and above', |
| | | 'condition' => [ |
| | | 'field' => 'enable_gmb_review_sync', |
| | | 'operator' => '==', |
| | | 'value' => true, |
| | | ] |
| | | ] |
| | | ); |
| | | $fields->addField( |
| | | 'reviews', |
| | | [ |
| | | 'type' => 'repeater', |
| | | 'label' => __('Reviews', 'jvb'), |
| | | 'fields' => [ |
| | | 'author' => [ |
| | | 'type' => 'text', |
| | | 'label' => __('Review Author', 'jvb'), |
| | | 'required' => true, |
| | | ], |
| | | 'reviewRating' => [ |
| | | 'type' => 'number', |
| | | 'min' => 1, |
| | | 'max' => 5, |
| | | 'label' => __('Review Rating', 'jvb'), |
| | | 'required' => true, |
| | | ], |
| | | 'reviewBody' => [ |
| | | 'type' => 'textarea', |
| | | 'label' => __('Review Text Content', 'jvb'), |
| | | ], |
| | | 'url' => [ |
| | | 'type' => 'url', |
| | | 'label' => __('Link to actual review'), |
| | | 'hint' => 'Optional. Link directly to the review source for added authenticity.' |
| | | ] |
| | | ], |
| | | 'condition' => [ |
| | | 'field' => 'enable_gmb_review_sync', |
| | | 'operator' => '!=', |
| | | 'value' => true, |
| | | ] |
| | | ] |
| | | ); |
| | | } |
| | | |
| | | public function formatReviewField(Meta $meta):void |
| | | { |
| | | $gmb = $meta->get('enable_gmb_review_sync'); |
| | | if (!empty($gmb) && $gmb === true) { |
| | | $this->fetchGMBReviews($meta); |
| | | return; |
| | | } |
| | | |
| | | $reviews = $meta->get('reviews'); |
| | | if (!empty($reviews)) { |
| | | $theReviews = []; |
| | | foreach ($reviews as $row) { |
| | | $review = new Review(); |
| | | $author = $row['author']; |
| | | $reviewer = new Person(); |
| | | $reviewer->setName($author); |
| | | $review->setAuthor($reviewer); |
| | | $rating = new Rating(); |
| | | $rating->setRatingValue($row['reviewRating']); |
| | | $review->setReviewRating($rating); |
| | | if (!empty($row['reviewBody'])) { |
| | | $review->setReviewBody($row['reviewBody']); |
| | | } |
| | | if (!empty($row['url'])){ |
| | | $review->setUrl($row['url']); |
| | | } |
| | | $theReviews[] = $review; |
| | | } |
| | | if (!empty($theReviews)) { |
| | | $this->setReview($theReviews); |
| | | } |
| | | } |
| | | } |
| | | |
| | | protected function fetchGMBReviews(Meta $meta):void |
| | | { |
| | | $userID = (int) get_post_meta($meta->id(), BASE.'profile_link', true); |
| | | if ($userID && $userID > 0) { |
| | | $integration = JVB()->connect('gmb', $userID); |
| | | if ($integration && $integration->isSetUp()){ |
| | | $fetched = $integration->getReviews(20); |
| | | |
| | | if (!empty($fetched)) { |
| | | $reviews = []; |
| | | $minRating = $meta->get('gmb_min_rating'); |
| | | foreach ($fetched as $review) { |
| | | $rating = $review['starRating'] ?? 0; |
| | | if ($rating >= $minRating) { |
| | | $reviews[] = $review; |
| | | if (count($reviews) >= 10) { |
| | | break; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | if (!empty($reviews)) { |
| | | $theReviews = []; |
| | | foreach ($reviews as $r) { |
| | | $review = new Review(); |
| | | $author = $r['reviewer']['displayName'] ?? 'Anonymous'; |
| | | $author = strtok($author, ' '); |
| | | $reviewer = new Person(); |
| | | $reviewer->setName($author); |
| | | $review->setAuthor($reviewer); |
| | | $rating = new Rating(); |
| | | $rating->setRatingValue(match($r['starRating']) { |
| | | 'FIVE' => 5, |
| | | 'FOUR' => 4, |
| | | 'THREE' => 3, |
| | | 'TWO' => 2, |
| | | 'ONE' => 1, |
| | | default => 0 |
| | | }); |
| | | $review->setReviewRating($rating); |
| | | if (!empty($r['comment'])) { |
| | | $review->setReviewBody($r['comment']); |
| | | } |
| | | if (!empty($r['updateTime']??'')){ |
| | | $review->setDateCreated($r['updateTime']); |
| | | } |
| | | $theReviews[] = $review; |
| | | } |
| | | if (!empty($theReviews)) { |
| | | $this->setReview($theReviews); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | |
| | | <?php |
| | | namespace JVBase\managers\SEO\render\Traits\_Properties; |
| | | |
| | | use JVBase\registrar\Fields; |
| | | |
| | | if (!defined('ABSPATH')) { |
| | | exit; |
| | | } |
| | |
| | | { |
| | | $this->slogan = $slogan; |
| | | } |
| | | |
| | | public function setSloganField(Fields $fields):void |
| | | { |
| | | $fields->addField( |
| | | 'slogan', |
| | | [ |
| | | 'type' => 'text', |
| | | 'label' => __('Tagline or Slogan', 'jvb') |
| | | ] |
| | | ); |
| | | } |
| | | } |
| | |
| | | <?php |
| | | namespace JVBase\registrar; |
| | | |
| | | use JVBase\managers\SEO\render\Thing\Organization\LocalBusiness\LocalBusiness; |
| | | use JVBase\managers\SEO\render\Thing\Thing; |
| | | use JVBase\registrar\fields\Field; |
| | | use JVBase\registrar\fields\GroupedField; |
| | | use JVBase\registrar\fields\OptionsField; |
| | |
| | | |
| | | protected function addReviewField(?string $label = null):void |
| | | { |
| | | $this->addField( |
| | | 'reviews', |
| | | [ |
| | | 'type' => 'repeater', |
| | | 'add_label' => 'name', |
| | | 'label' => $label ?: 'Reviews', |
| | | 'fields' => [ |
| | | 'name' => [ |
| | | 'type' => 'text', |
| | | 'label' => 'Reviewer Name', |
| | | ], |
| | | 'review' => [ |
| | | 'type' => 'textarea', |
| | | 'quill' => false, |
| | | 'label' => 'Review', |
| | | ], |
| | | 'rating' => [ |
| | | 'type' => 'select', |
| | | 'label' => 'Rating', |
| | | 'options' => [ |
| | | 'none' => 'Not Given', |
| | | '0.5' => '0.5', |
| | | '1' => '1', |
| | | '1.5' => '1.5', |
| | | '2' => '2', |
| | | '2.5' => '2.5', |
| | | '3' => '3', |
| | | '3.5' => '3.5', |
| | | '4' => '4', |
| | | '4.5' => '4.5', |
| | | '5' => '5', |
| | | ], |
| | | 'default' => 'none' |
| | | ], |
| | | 'date' => [ |
| | | 'type' => 'date', |
| | | 'label' => 'Date of Review', |
| | | ], |
| | | 'url' => [ |
| | | 'type' => 'url', |
| | | 'label' => 'Link to Review (optional)', |
| | | ], |
| | | ], |
| | | 'section' => 'seo' |
| | | ] |
| | | ); |
| | | $biz = new LocalBusiness(); |
| | | $biz->setReviewField($this); |
| | | } |
| | | |
| | | protected function addAlternateNameField():void |
| | | { |
| | | $this->addField( |
| | | 'alternate_name', |
| | | [ |
| | | 'type' => 'repeater', |
| | | 'label' => 'Alternate Name', |
| | | 'fields' => [ |
| | | 'name' => [ |
| | | 'type' => 'text', |
| | | 'label' => 'Name', |
| | | ] |
| | | ], |
| | | 'section' => 'seo' |
| | | ] |
| | | ); |
| | | $thing = new Thing(); |
| | | $thing->setAlternateNameField($this); |
| | | } |
| | | protected function addKeywordsField():void |
| | | { |
| | | $this->addField( |
| | | 'keywords', |
| | | [ |
| | | 'type' => 'repeater', |
| | | 'label' => 'Keywords', |
| | | 'fields' => [ |
| | | 'keyword' => [ |
| | | 'type' => 'text', |
| | | 'label' => 'Keyword', |
| | | ], |
| | | ], |
| | | 'default' => $labels ?? [ 'Edmonton tattoos', 'Edmonton tattoo artist', 'Edmonton tattooist' ], |
| | | 'section' => 'seo', |
| | | 'quickEdit' => true, |
| | | ] |
| | | ); |
| | | $thing = new LocalBusiness(); |
| | | $thing->setKeywordsField($this); |
| | | } |
| | | |
| | | protected function addOutsidePhotoField():void |
| | | { |
| | | $this->addField( |
| | | 'outside_photo', |
| | | [ |
| | | 'type' => 'image', |
| | | 'limit' => 1, |
| | | 'label' => __('Outside Photo', 'jvb') |
| | | ] |
| | | ); |
| | | $business = new LocalBusiness(); |
| | | $business->setPhotoField($this); |
| | | } |
| | | |
| | | protected function addSloganField():void |
| | | { |
| | | $this->addField( |
| | | 'slogan', |
| | | [ |
| | | 'type' => 'text', |
| | | 'label' => __('Tagline or Slogan', 'jvb') |
| | | ] |
| | | ); |
| | | $business = new LocalBusiness(); |
| | | $business->setSloganField($this); |
| | | } |
| | | |
| | | protected function addPaymentField():void |
| | | { |
| | | $this->addField( |
| | | 'payment_accepted', |
| | | [ |
| | | 'type' => 'set', |
| | | 'label' => __('Payment Accepted', 'jvb'), |
| | | 'options' => [ |
| | | 'Cash' => 'Cash', |
| | | 'Credit Card' => 'Credit Card', |
| | | 'Debit' => 'Debit', |
| | | 'Google Pay' => 'Google Pay', |
| | | 'Apple Pay' => 'Apple Pay', |
| | | 'PayPal' => 'PayPal', |
| | | 'Interac' => 'Interac', |
| | | 'AMEX' => 'AMEX', |
| | | ], |
| | | ] |
| | | ); |
| | | $business = new LocalBusiness(); |
| | | $business->setPaymentAcceptedField($this); |
| | | } |
| | | |
| | | protected function addAmenitiesField():void |
| | | { |
| | | $business = new LocalBusiness(); |
| | | $business->setAmenityFeatureField($this); |
| | | } |
| | | protected function addCredentialsField():void |
| | | { |
| | | $this->addField( |
| | | 'amenities', |
| | | 'credentials', |
| | | [ |
| | | 'type' => 'set', |
| | | 'label' => __('Amenities', 'jvb'), |
| | | 'options' => [ |
| | | 'Wheelchair Accessible' => 'Wheelchair Accessible', |
| | | 'Free Parking' => 'Free Parking', |
| | | 'Private Rooms' => 'Private Rooms', |
| | | 'Air Conditioning' => 'Air Conditioning', |
| | | 'WiFi' => 'WiFi', |
| | | 'Gender Neutral Restroom' => 'Gender Neutral Restroom', |
| | | 'LGBTQ+ Friendly' => 'LGBTQ+ Friendly', |
| | | 'Sterilization Room' => 'Sterilization Room', |
| | | 'Refreshments Available' => 'Refreshments Available', |
| | | 'Street Level Access' => 'Street Level Access', |
| | | 'Single Use Needles' => 'Single Use Needles', |
| | | 'Consultation Room' => 'Consultation Room', |
| | | 'Aftercare Products Available' => 'Aftercare Products Available', |
| | | 'Walk-Ins Welcome' => 'Walk-Ins Welcome', |
| | | 'label' => __('Credentials', 'jvb'), |
| | | 'options'=> [ |
| | | 'WHMIS 2015' => 'WHMIS 2015', |
| | | 'Tattoo and Piercing Safety Standards' => 'Tattoo and Piercing Safety Standards', |
| | | 'Bloodborne Pathogens and Infection Control' => 'Bloodborne Pathogens and Infection Control', |
| | | 'First Aid Training' => 'First Aid Training', |
| | | ] |
| | | ] |
| | | ); |
| | | } |
| | | protected function addPermanentlyCloseField():void |
| | | { |
| | | $business = new LocalBusiness(); |
| | | $business->setDissolutionDateField($this); |
| | | } |
| | | |
| | | protected function addHoursField():void |
| | | { |
| | | $business = new LocalBusiness(); |
| | | $business->setOpeningHoursSpecificationField($this); |
| | | } |
| | | |
| | | protected function addRateField():void |
| | | { |
| | | $this->addField('rate', |
| | | [ |
| | | 'type' => 'text', |
| | | 'subtype' => 'number', |
| | | 'label' => __('Hourly Rate', 'jvb') |
| | | ]); |
| | | } |
| | | |
| | | protected function addAwardsField():void |
| | | { |
| | | $business = new LocalBusiness(); |
| | | $business->setAwardField($this); |
| | | } |
| | | |
| | | protected function addRatingsField():void |
| | | { |
| | | $business = new LocalBusiness(); |
| | | $business->setAggregateRatingField($this); |
| | | } |
| | | |
| | | protected function addServicesField():void |
| | | { |
| | | $business = new LocalBusiness(); |
| | | $business->setHasOfferCatalogField($this); |
| | | } |
| | | } |