• Resolved yabdali

    (@yabdali)


    Hi,
    I am currently testing your plugin to move away from another plugin we are using currently. Your plugin seems to provide majority of features that we need, thanks for this wonderful plugin.
    I am looking to pull the data displayed in every service page in the format of [JSON-LD] ) to enable search engines identify the events in the service calendar data and publish display them as events when people search for certain activities/events as per the image below. I am sure this will help in increasing your plugins popularity compared to the others. For example, I have a sunset tour page, have a service appointment booking which provides the calendar dates and time slots. I want to extract the start and end date of the service, time slots (example: 8:00-12:00) duration 120 minutes and price.

    The basic idea is to add a code snippet that will be executed on every service page, construct the jSON-LD) and inject it to the page code so when search engine indexes the page it will find the structured data of the calendar events. I can see thee different possibilities which can be used to do this but not sure what would be the ideal approach.
    Below is an example of the JSON-LD and a link for the Schema reference if you are interested. Thanks a lot for keeping this plugin evolving and for your continuous support.
    https://schema.org/Schedule

    <script type="application/ld+json">
    {
      "@context": "https://schema.org/",
      "@type": "Event",
      "url": "https://www.example.org/events/1",
      "name": "Tai chi Class",
      "description": "A weekly Tai-Chi class",
      "duration": "PT60M",
      "eventSchedule": {
         "@type": "Schedule",
         "startDate": "2017-01-01",
         "endDate": "2017-12-31",
         "repeatFrequency": "P1W",
         "byDay": "https://schema.org/Wednesday",
         "startTime": "19:00:00",
         "endTime": "20:00:00",
         "scheduleTimezone": "Europe/London"
      }
    }
    </script>
Viewing 15 replies - 1 through 15 (of 15 total)
  • Thread Starter yabdali

    (@yabdali)

    Just an additional note, I was able to identify wp-json api as listed below but there list of methods and required fields/parameters especially for the post methods are not clearly defined.

    /wp-json/wbk/v2/

    /wp-json/wbk/v1/

    Plugin Author WebbaPlugins

    (@webba-agency)

    Hello,

    I’ll be happy to help.

    If you need to output service parameters in JSON format the easiest way will be to create custom shortcode using add_sortcode function.

    In the shortcode function use the code like this to get service parameters:

    $service = new WBK_Service($service_id);
     $business_hours = $service->get_business_hours();

    Hope this helps.

    Thread Starter yabdali

    (@yabdali)

    Thanks alot for your response. Just to clarify few things. I tried the above using code snippet by just setting a static value of one of the services and I can see the output into the html code of the page. Also, the intention is that code will run on each page that has a service form, it needs to detect the service ID or the service IDs of the services on the form, then pull the data (dates and time slots/or repetition frequency) and accordingly construct the JSON-LD.

    So far, when trying to get the service IDs on a page with a form which has more than one service, the code isn’t able to get except one service. I am outputting the results into debug.log so I can have better understanding of what is going on with the code. Can you shed light how would I be able to detect the service IDs if more than service is on the form? The the output comes from code that checks for which blocks get loaded, not sure if there is a webba hooks or functions that can be used to check for the service IDs of the services populated by the form.

    The rationale behind this is that we a search engine hits the page, the services data will be available in a syntax that can be parsed for events or tours. This will definitely make your plugin more attractive for people since most of the booking plugins lack such feature.

    [03-Jun-2024 20:37:02 UTC] Checking block: webba-booking/form
    [03-Jun-2024 20:37:02 UTC] Webba Booking Form block found. Full block content: Array
    (
    [blockName] => webba-booking/form
    [attrs] => Array
    (
    [serviceId] => 1
    [categoryId] => 3
    )

    [innerBlocks] => Array
    (
    )

    [innerHTML] =>
    <div>[webbabooking category=3]</div>

    [innerContent] => Array
    (
    [0] =>
    <div>[webbabooking category=3]</div>

    )

    )

    [03-Jun-2024 20:37:02 UTC] Webba Booking Form block attributes: Array
    (
    [serviceId] => 1
    [categoryId] => 3
    )

    [03-Jun-2024 20:37:02 UTC] Detected services: Array
    (
    [0] => 1
    )

    [03-Jun-2024 20:37:02 UTC] Only one Webba Booking service found.

    Plugin Author WebbaPlugins

    (@webba-agency)

    Hello,

    Please, clarify – do you need to make output based on shortcode attributes, correct? So, there can be single service and set of services.

    Also, please let me know – where on the page script needs to be added? Should be it be in the head section or it can be below the booking form?

    Thread Starter yabdali

    (@yabdali)

    Hi,

    Yes, but it could be a Webba booking block or a shortcode. The website admin will add either webba blocks or shortcode on any page. There could be one service, or multiple services based on a category filter.

    I want to check and extract the service ID or IDs if there are more than one service. When I read the IDs, I want to output the JSON LD code into the WP Footer. Section.

    Thread Starter yabdali

    (@yabdali)

    Hi, further information to clear any doubts..
    I add the booking form to the tour pages using the blocks editor as shown in the screen in the link, https://imgur.com/zPsVwOf and not using the [webbabooking service=52] in case this what you were asking about above. In case this is not what you meant, I will assume you are referring to how I obtain the information based on the booking form. In this case, yes I want to get the attributes but in my case I don’t use shortcode when adding the form and wasn’t able to find a way to get the attributes of the Booking form Block which could be due to my lack of knowledge. Lets say I have a form with one or more services, I want to be able to get the attributes of those services.

    The output of the (final) JSON-LD can be anywhere within the body tag of the page, below the booking form should be fine.

    I tried your suggested approach about using add_shortcode function. I used code snippet plugin and created a test code with a static variable for the service ID, did data transformation compliant with JSON-LD schema. Then used the shortcode name (debug_log_shortcode) in a test page and I can see the output as shown in the link below. Not sure if this is the optimal approach, let me know if I can get the same result in a better way. I haven’t figured yet how to get the service name and the description yet, if you can point out on how to do this. Please ignore the naming convention, it is a quick mock up.
    https://imgur.com/YUvzzZP

    // Define custom shortcode
    function custom_debug_log_shortcode() {
      // Assuming WBK_Service class is already available
    
      // Set service ID (replace with your actual service ID)
    // Needs improvement: fetching service ID or IDs, and doing iteration for generating JSON-LD for each service
    
      $service_id = 8;
    
      // Create an instance of WBK_Service class
      $service = new WBK_Service($service_id);
    
      // Get business hours data
      $business_hours = $service->get_business_hours();
    
      // Reformat business hours data (assuming it's JSON)
      if (json_decode($business_hours) !== null) {
        $reformatted_data = reformat_business_hours($business_hours);
      } else {
        return "<!-- Business hours data is not in JSON format. -->";
      }
    
      // Get availability range
      $availability_range = $service->get_availability_range();
    
      // Get duration
      $duration = $service->get_duration();
    
      // Get price
      $price = $service->get_price();
    
      // Generate JSON-LD schema
      $json_ld_schema = generate_json_ld_schema($availability_range, $duration, $price, $reformatted_data);
    
      // Return the JSON-LD schema inside a script tag
      return '<script type="application/ld+json">' . $json_ld_schema . '</script>';
    }
    
    // Function to reformat business hours data
    function reformat_business_hours($business_hours_json) {
      // Weekday names dictionary
      $weekdays = [
        "1" => "Sunday",
        "2" => "Monday",
        "3" => "Tuesday",
        "4" => "Wednesday",
        "5" => "Thursday",
        "6" => "Friday",
        "7" => "Saturday"
      ];
    
      // Function to convert seconds to time string (HH:MM)
      function convertsecondsToTime($seconds) {
        $hours = intval($seconds / 3600);
        $minutes = intval(($seconds % 3600) / 60);
        return sprintf("%02d:%02d", $hours, $minutes);
      }
    
      // Decode JSON data
      $business_hours_data = json_decode($business_hours_json);
    
      // Reformat data structure
      $new_data = ["business_hours" => []];
      foreach ($business_hours_data->dow_availability as $item) {
        $dayOfWeek = $weekdays[$item->day_of_week];
        $startTime = convertsecondsToTime($item->start);
        $endTime = convertsecondsToTime($item->end);
        
        // Include timezone (replace with your desired timezone)
        $timezone = "Asia/Muscat"; // Or any valid timezone
    
        $new_data["business_hours"][] = [
          "day_of_week" => $dayOfWeek,
          "day_of_week_number" => $item->day_of_week,
          "start" => $startTime,
          "end" => $endTime,
          "timezone" => $timezone
        ];
      }
    
      // Encode to JSON string with indentation
      $json_string = json_encode($new_data, JSON_PRETTY_PRINT);
    
      return $json_string;
    }
    
    // Function to generate JSON-LD schema
    function generate_json_ld_schema($availability_range, $duration, $price, $reformatted_data) {
      // Sample event name and description
      $event_name = "Daily Boat Tour";
      $event_description = "A daily boat tour that runs between 08:00 and 12:00 for " . intval($duration / 60) . " hours and " . ($duration % 60) . " minutes each, offering a scenic experience at Bandar Rowdha marina.";
    
      // Decode reformatted business hours data
      $business_hours_data = json_decode($reformatted_data, true);
    
      // Calculate schedules and group identical schedules
      $schedule_groups = [];
      foreach ($business_hours_data['business_hours'] as $day) {
        $start_time_seconds = strtotime($day['start']);
        $end_time_seconds = strtotime($day['end']);
        $service_duration_seconds = $duration * 60;
    
        for ($i = 0; $i + $service_duration_seconds <= $end_time_seconds - $start_time_seconds; $i += $service_duration_seconds) {
          $schedule_start = date("H:i", $start_time_seconds + $i);
          $schedule_end = date("H:i", $start_time_seconds + $i + $service_duration_seconds);
    
          $schedule_key = $schedule_start . '-' . $schedule_end . '-' . $day['timezone'];
          if (!isset($schedule_groups[$schedule_key])) {
            $schedule_groups[$schedule_key] = [
              "@type" => "Schedule",
              "startDate" => $availability_range[0],
              "endDate" => $availability_range[1],
              "repeatFrequency" => "P1D",
              "startTime" => $schedule_start,
              "endTime" => $schedule_end,
              "duration" => "PT" . intval($duration / 60) . "H" . ($duration % 60) . "M",
              "scheduleTimezone" => $day['timezone']
            ];
          }
        }
      }
    
      // Convert schedule groups to array
      $schedules = array_values($schedule_groups);
    
      // JSON-LD schema structure
      $json_ld = [
        "@context" => "https://schema.org",
        "@type" => "Event",
        "name" => $event_name,
        "description" => $event_description,
        "startDate" => $availability_range[0],
        "endDate" => $availability_range[1],
        "location" => [
          "@type" => "Place",
          "name" => "Bandar Rowdha Marina",
          "address" => [
            "@type" => "PostalAddress",
            "streetAddress" => "Bandar Rowdha marina",
            "addressLocality" => "Muscat",
            "addressRegion" => "Muscat",
            "addressCountry" => "Oman"
          ]
        ],
        "offers" => [
          "@type" => "Offer",
          "price" => $price,
          "priceCurrency" => "OMR",
          "availability" => "https://schema.org/InStock",
          "validFrom" => $availability_range[0] . "T08:00:00+04:00",
          "validThrough" => $availability_range[1] . "T12:00:00+04:00"
        ],
        "eventSchedule" => $schedules
      ];
    
      // Encode JSON-LD schema to string
      return json_encode($json_ld, JSON_PRETTY_PRINT);
    }
    
    // Register shortcode
    add_shortcode('debug_log_shortcode', 'custom_debug_log_shortcode');
    
    Plugin Author WebbaPlugins

    (@webba-agency)

    Hi,

    Given the task requirements, your approach of creating a custom shortcode is correct, as retrieving attributes from an existing shortcode or block can be challenging.

    To get name and description of service please use the following methods:

    $service->get_name();
     $service->get_description();


    The get_description method may accept parameter (boolean, false by default) which manage if description is returned escaped or not.

    Regarding your code, to reflect the time zone used in Webba Booking, I’d recommend to avoid usage of the date function. Use wp_date function instead.

    Here it the example of usage:

    date_default_timezone_set(get_option('wbk_timezone', 'UTC'));
    wp_date($time_format, $time, new DateTimeZone(date_default_timezone_get()));
    date_default_timezone_set('UTC');

    $time_format – described in the wp_date doc.
    $time – integer timestamp.

    Hope this helps.

    Thread Starter yabdali

    (@yabdali)

    Thanks again, what are the options to get the service ID or services IDs? I The first and most important part is reading those, my code was using a manual static and hard-coded approach which doesn’t fulfill production deployment requirements. I need a way to read the IDs whenever a page which has a form is loaded!

    Plugin Author WebbaPlugins

    (@webba-agency)

    Webba Booking stores blocks as shortcode in the database. So what you see in the block editor actually is saved as shortcode in the page / post content. Given this, possible approach will be to get the content of the post / page and parse attributes (service id or category id).

    I just got an example of the code which do that from ChatGPT:

    // Get post content by ID
    $post_id = 123; // Replace with your post ID
    $post = get_post($post_id);
    $content = $post->post_content;
    
    // Shortcode to search for
    $shortcode = 'my_shortcode';
    
    // Function to parse shortcode attributes
    function parse_shortcode_atts($content, $shortcode) {
        // Regular expression to match the shortcode and its attributes
        $pattern = get_shortcode_regex(array($shortcode));
    
        // Find all matching shortcodes
        preg_match_all('/'.$pattern.'/s', $content, $matches);
    
        // Attributes array
        $shortcode_atts = array();
    
        // Loop through each match
        foreach ($matches[0] as $match) {
            // Extract attributes from the shortcode
            $atts = shortcode_parse_atts($match);
    
            // Save attributes
            $shortcode_atts[] = $atts;
        }
    
        return $shortcode_atts;
    }
    
    // Parse attributes for the given shortcode
    $atts = parse_shortcode_atts($content, $shortcode);
    
    // Output the attributes
    print_r($atts);
    
    Thread Starter yabdali

    (@yabdali)

    Thanks, What would the value or the pattern for the $shortcode = ‘my_shortcode‘; be? Is it something like [webbabooking service=52]? Can you please elaborate more?

    Plugin Author WebbaPlugins

    (@webba-agency)

    I guess it should be: ‘webbabooking’.

    $shortcode = 'webbabooking';

    Thread Starter yabdali

    (@yabdali)

    Hi, thanks for your inputs… I used the code below, I am able to get the webba shortcode parameters as shown here: the found is the actual shortcode, shortcode attributes are the attributes extracted.

    Can you please advise me on how to get the list of services for a category?

    Found webbabooking shortcode: [webbabooking  category=4]
    Shortcode attributes: {"category":"4"}
    Found webbabooking shortcode: [webbabooking service=9]
    Shortcode attributes: {"service":"9"}
    function extract_webbabooking_shortcode_attributes() {
    if (is_single() || is_page()) {
    global $post;
    $pattern = get_shortcode_regex();
    $content = $post->post_content;
    $shortcode_found = false;

    if (preg_match_all('/' . $pattern . '/s', $content, $matches, PREG_SET_ORDER)) {
    foreach ($matches as $match) {
    if ($match[2] == 'webbabooking') {
    $shortcode_found = true;
    $shortcode = $match[0]; // The full shortcode
    $atts = shortcode_parse_atts($match[3]);
    $json_atts = json_encode($atts);

    // Log the original shortcode content and parameters
    error_log('Found webbabooking shortcode: ' . $shortcode);
    error_log('Shortcode attributes: ' . $json_atts);

    // Print the JSON content at the footer
    add_action('wp_footer', function() use ($json_atts) {
    echo '<script type="application/json" id="webbabooking-shortcode-attributes">' . $json_atts . '</script>';
    });
    }
    }
    }

    if (!$shortcode_found) {
    add_action('wp_footer', function() {
    echo '<script type="application/json" id="webbabooking-shortcode-attributes">{}<\/script>';
    });
    }
    }
    }
    add_action('wp', 'extract_webbabooking_shortcode_attributes');
    Plugin Author WebbaPlugins

    (@webba-agency)

    Hi,

    Please, see example below:

    WBK_Model_Utils::get_services_in_category($category_id);
    Thread Starter yabdali

    (@yabdali)

    Thanks, I used the following code, I think it extends WBK_ModelUtils with some additional functions/methods…

    $service_category = new WBK_Service_Category($category_id);
    Plugin Author WebbaPlugins

    (@webba-agency)

    Yes, your approach will work as well.

    If you have any other questions, feel free to reach out.

Viewing 15 replies - 1 through 15 (of 15 total)
  • You must be logged in to reply to this topic.