Jump to content

Liam88

Members
  • Posts

    64
  • Joined

  • Last visited

Posts posted by Liam88

  1. On 9/28/2024 at 4:22 PM, da² said:

    This should change nothing for the API, field is not editable in admin pages but from the API you can edit it.

    ok, interesting because when I just removed the page-edit access it wouldn't create the tags and save them.

    It shows as guest within the logs which is likely the issue. I'm guessing I need to ensure it has it down as the user that is making the action.

    Here's an example of the log, going to try make it so it's the actual user and not the guest


    Date/Time    User    URL    Text
    18 seconds ago
    2024-09-29 21:22:23    guest    /api/ad-review/    AI processing completed successfully.
    18 seconds ago
    2024-09-29 21:22:23    guest    /api/ad-review/    content_tags field is not editable by the current user.

  2. 15 minutes ago, wbmnfktr said:

    Two things I would try and check:

    1. double check your page reference field allows the creation of new pages and all settings are correct - including parent and template settings
    2. try to save after the tag was added.
      Either with
          $page->save();
      or
          $page->save('your_field_name')

    Yeah page reference allows creation and the tagPages are being created and saved.

    I was thinking should I save after each tag is added or add all then save.

    Will give your suggestion a go. Appreciate it. 

  3. 16 minutes ago, Jan Romero said:

    Any particular reason for this curly brackets syntax? I’m far from a PHP pro but I’ve never seen that and $assetPage->content_tags->add($tagPage) should suffice. I might look at your code again later when I’m on desktop.

    Also, might be obvious, but make sure you save the tagPages before adding them 😅

    Good spot, oversight from me. I don't use them on my other section either. 

    Yeah all the tagPages are created and saved before, but will triple check. 

  4. Hey

    Having a struggle with this one.

    My project sends content to openAI, it sends back tags. Where the tags aren't an existing page, it creates them.

    I then want to add those tag pages to the content.

    assetPage is the content
    content_tags is the page reference within that page
    content-tag is the template for the tag pages added.

    I'm getting it to say it's adding the pages to content_tags, but in any form I'm trying it doesn't add them.

    I have popped my example php below and here are an example log. You will see it gets stuck at attempting to save.

    Any advice on where I am going wrong in adding multiple pages to a page reference field at once would be amazing.

    Is it the way I'm trying to save?

    Date/Time    User    URL    Text
    10 seconds ago
    2024-09-27 17:35:13    guest    /api/ad-review/    Inside try block - attempting to save.
    10 seconds ago
    2024-09-27 17:35:13    guest    /api/ad-review/    Attempting to save the asset page with updated content_tags.

    if (!empty($tags)) {
        $log->save('ad-review', 'Processing tags to create or find corresponding pages.');
        $tagPages = processTags($tags, $log);
    
        // Log the tag pages returned from processTags
        $log->save('ad-review', 'Tag pages found or created: ' . implode(', ', array_map(function ($page) {
            return $page->title;
        }, $tagPages)));
    
        // Log the current state of content_tags
        $currentTags = $assetPage->{'content_tags'}->explode('id');
        $log->save('ad-review', 'Current content_tags IDs before update: ' . implode(', ', $currentTags));
    
        // Ensure output formatting is disabled
        $assetPage->of(false);
    
        // Check if the content_tags field exists and is accessible
        if (!$assetPage->getField('content_tags')) {
            $log->save('ad-review', 'Field content_tags does not exist or is not accessible on the asset page.');
            return; // Exit if the field is not found
        } else {
            $log->save('ad-review', 'Field content_tags exists on the asset page and is ready for update.');
        }
    
        // Add each tag page to the content_tags field using add()
        foreach ($tagPages as $tagPage) {
            if ($tagPage && $tagPage->id) {
                try {
                    // Add the tag page directly using the add() method
                    $log->save('ad-review', 'Adding tag page to content_tags: ' . $tagPage->title . ' - ' . $tagPage->id);
                    $assetPage->{'content_tags'}->add($tagPage->id);
                } catch (Exception $e) {
                    $log->save('ad-review', 'Error adding tag page to content_tags: ' . $tagPage->title . '. Error: ' . $e->getMessage());
                }
            } else {
                $log->save('ad-review', 'Failed to add tag page: ' . ($tagPage ? $tagPage->title : 'Unknown'));
            }
        }
    
        // Additional logging before save attempt
        $log->save('ad-review', 'Attempting to save the asset page with updated content_tags.');
    
        // Explicitly try saving the asset page
        try {
            // Another log entry just before the save method is called
            $log->save('ad-review', 'Inside try block - attempting to save.');
    
            // Execute the save operation
            if ($assetPage->save()) {
                $log->save('ad-review', 'Asset page saved successfully with updated content_tags.');
    
                // Re-fetch and log the content_tags after saving to confirm
                $savedTags = $assetPage->{'content_tags'}->explode('id');  
                $log->save('ad-review', 'Content_tags IDs after save: ' . implode(', ', $savedTags));
            } else {
                $log->save('ad-review', 'Save method returned false, indicating save was not successful.');
            }
        } catch (Exception $e) {
            $log->save('ad-review', 'Failed to save asset page with updated content_tags. Error: ' . $e->getMessage());
        }
    } else {
        $log->save('ad-review', 'No tags to process.');
    }

     

  5. Looking for some advice.

    I need to create a page, insert some basic info, create a repeater and save either a video or image.

    This is where I'm at so far.

    I'm finding that the repeater doesn't save. So when I access the main page after the process, there is no repeater inside.

    The logging gets stuck on video uploading to repeater

    This is an example url - https://video-lhr6-2.xx.fbcdn.net/v/t42.1790-2/359544611_1299622917599542_1081641833156909957_n.mp4?_nc_cat=104&ccb=1-7&_nc_sid=c53f8f&_nc_ohc=v3DnXymDRrMQ7kNvgGY2g0m&_nc_ht=video-lhr6-2.xx&_nc_gid=Avf0ALrpn8YGUKRdqZmpwGi&oh=00_AYCqXW4qteSLwgQKol8SbVAgvR-O3-9R1QVqmDjjZwiUjw&oe=66ECD5B4

    Any advise would be welcomed.

    // Continue handling the data if everything is present
    $mediaUrl = $data['media_url'];
    $mediaType = $data['media_type'];
    $description = isset($data['description']) ? $data['description'] : '';
    
    $title = 'Test Ad Title';
    
    try {
        // 1. Create the main page
        $page = new Page();
        $page->template = 'asset'; // Use the correct template
        $page->parent_id = 1029;  // Adjust this ID for the correct parent page
        $page->title = $title;
        $page->content_type = $mediaType; // Assuming 'content_type' is a valid field
        $page->save();
    
        // Log the creation of the page
        $log->save('save-ad', 'Created new page with title: ' . $title);
    
        // 2. Create a repeater item for the "content" repeater field
        $log->save('save-ad', 'Creating a new repeater item.');
        $contentRepeater = $page->content->getNew(); // Create a new repeater item
    
        if (!$contentRepeater) {
            $log->save('save-ad', 'Error: Failed to create new repeater item.');
            echo json_encode(['status' => 'error', 'message' => 'Failed to create repeater item.']);
            exit;
        }
    
        // 3. Handle media file and add it to the correct field in the repeater
        $filename = basename($mediaUrl); // Extract the filename from the URL
    
        // Prepare the file to be added
        if ($mediaType == 'video') {
            $log->save('save-ad', 'Adding video file to repeater.');
           
            // Add the file directly to the file field 'video_1'
            $contentRepeater->video_1->add($mediaUrl); // Assuming 'video_1' is the field
            $log->save('save-ad', 'Video file added successfully.');
        } elseif ($mediaType == 'image') {
            $log->save('save-ad', 'Adding image file to repeater.');
    
            // Add the image directly to the image field 'image_1'
            $contentRepeater->image_1->add($mediaUrl); // Assuming 'image_1' is the field
            $log->save('save-ad', 'Image file added successfully.');
        } else {
            $log->save('save-ad', 'Error: Unsupported media type: ' . $mediaType);
            echo json_encode(['status' => 'error', 'message' => 'Unsupported media type: ' . $mediaType]);
            exit;
        }
    
        // 4. Save the repeater item
        $log->save('save-ad', 'Saving repeater item.');
        $contentRepeater->save();
    
        // 5. Add the repeater item to the main page
        $page->content->add($contentRepeater);  // Add the repeater item to the page
    
        // 6. Save the main page with the repeater
        $page->save();
        $log->save('save-ad', 'Repeater item added to page and page saved successfully.');
    
        // Log success
        $log->save('save-ad', 'Media saved successfully to page.');
        echo json_encode(['status' => 'success', 'message' => 'Ad saved successfully!']);

     

  6. 42 minutes ago, Matzn said:

    @Liam88

    Check your header settings, maybe is set to deny  https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options

    Do you really want to use iframe? It's obsolete.

    I'd prefer to not use iframe but when searching it was flagged as the best option. 

     

    Any suggestions instead of iframe. I'm not a dev so just learn as I go, so any direction to go would be appreciated. 

  7. 2 hours ago, wbmnfktr said:

    I have some ideas here but they would make things even worse.

    I can imagine that it's nice to have a real page included and scroll around in it BUT... there so many downsides and probably legal issues here.

    Maybe rethink the idea and either only save those ads or make screenshots like everyone else.

    Yeah the purpose is to have a page but without the function. However, it will still adjust to device etc.

    Like you said, I may need to rethink it as I can't find distinct yes or no's on this usage. 

  8. I'm hoping someone may be able to guide on this.

    One of my side projects stores ads for ecommerce brands for inspiration. I want to now add in their landing pages.

    I have used SingleFile to get a download of a page and then uploaded it to the page as a file.

    I am then looking to output as an iframe with the expectation this won't interfere with any css/html on my page.

    This leads to a basic output of this

    <iframe src='<?=$page->landing_page_html?>' width='100%' height='600px'></iframe>

    However, I'm getting a 403 response on the page. Does anyone have any guidance if my method is on the right path or should I leave this for another?

    I'm guessing it's wrong as no other site like awwwards or say Shopify templates do this and lean on a screenshot that links to the page.

    I have attached an example file for context.

    Big thank you in advance.

    image.thumb.png.759cd1ccf665a6a46621b0f738be9a5b.png

    Collagenic Burn Elite – Obvi (11_04_2024 17_38_24).html

  9. 21 minutes ago, flydev ?? said:

    I am used to use the Delayed Output Strat so I can't tell you right now what's going on for real

    Yeah I'll have to search if there are any other threads for markup regions strat.

    Getting the same output with the suggestion above so did a bit of searching and found this - https://processwire.com/blog/posts/processwire-2.6.8-brings-new-version-of-reno-admin-theme-and-more/#new-this-gt-halt-method-for-use-in-template-files

    Adding $this->halt to the end of each case prevents the continued render when using it within _init.php (the prepended file).

    This does not work if just adding to _main.php. 

    This is the action area now - 

    <?php
    if($config->ajax && $input->post->bookmark){
    
      bd($input->post->bookmark); // debug with tracy, uncomment if you have it
      $bookmarkid = $sanitizer->int($input->post->bookmark);
      $action = $sanitizer->name($input->post->action);
      $bookmarks = $user->getUnformatted('bookmarks');
      switch($action) {
        case 'save': // save logic
          if(!$bookmarks->has($bookmarkid /* this was bookmark, now bookmarkid */)) {
            // add bookmark
            $bookmarks->add($bookmarkid /* same change here */);
            $user->setAndSave('bookmarks', $bookmarks);
            $message = 'Borat saved bookmark "'. $bookmark->id .'" ?';
    		$this->halt(); 
          }
          else {
            $message = 'Nothing done';
          }      
          $success = true;
          break;
        case 'remove': // remove logic
          if($bookmarks->has($bookmarkid)) {
            $bookmarks->remove($bookmarkid);
            $user->setAndSave('bookmarks', $bookmarks);
            $message = 'Borat removed bookmark "'. $bookmark->id .'" ';
    		$this->halt(); 
          } else {
            $message = 'Nothing done';
          }
          $success = true;
          break;
        default:
          $message = 'error';
          $success = false;
      }
        
     $json = array( 
        'id' => $bookmarkid,
        'action' => $action,
        'message' => $message,
        'success' => $success
      ); 
      header('Content-Type: text/json; charset=utf-8');
      echo json_encode($json);
      return;
    }
    ?>

    Now I get this within preview:

    image.thumb.png.024869a10ad8db0943aaf9b03916a625.png

    and FINALLY it now saves into the user area. 

    Celebrate Meme Discover more interesting Birthday, Celebrate ...

    A huge thank you @flydev ??. Learnt so much through this thread and hoping it helps others!

    I'll even keep in borat in recognition of your input.

    • Like 1
  10. So another update

    Script looks like this - 

    jQuery(document).ready(function($) {
        // when clicked, send ajax request to server
        $('button.bookmark').on('click', function(e) {
          var btn = $(this).hasClass('clicked') ? false : $(this).addClass('clicked');
          if (!btn) {
            console.warn(`⚠️ Bookmark already saved to profile`);
            return false;
          }
          $.post('<?=$pages->get('/')->url?>', { action: $(this).val(), bookmark: btn.data('id') })
            .done(function(data) {
              console.log(data, "? response from ajax req");
              console.info(` Bookmark id => ${data.id} ${data.action} `);
            });
        });        
      }); 

    I have put this in _main.php (all template is appended to this via config.) at the top so before anything. I just included it within the namespace opening within the template. I left out the if user is logged in as this feature will only be available for logged in users.

    <?php namespace ProcessWire;
    if($config->ajax && $input->post->bookmark){
    
      bd($input->post->bookmark); // debug with tracy, uncomment if you have it
      
      $bookmarkid = $sanitizer->int($input->post->bookmark);
      $action = $sanitizer->name($input->post->action);
      $bookmarks = $user->getUnformatted('bookmarks');
      switch($action) {
        case 'save': // save logic
          if(!$bookmarks->has($bookmark)) {
            // add bookmark
            $bookmarks->add($bookmark);
            $user->setAndSave('bookmarks', $bookmark);
            $message = 'Borat saved bookmark "'. $bookmark->id .'" ?';
          }
          else {
            $message = 'Nothing done';
          }      
          $success = true;
          break;
        case 'remove': // remove logic
          if($bookmarks->has($bookmark)) {
            $bookmarks->remove($bookmark);
            $user->setAndSave('bookmarks', $bookmarks);
            $message = 'Borat removed bookmark "'. $bookmark->id .'" ';
          } else {
            $message = 'Nothing done';
          }
          $success = true;
          break;
        default:
          $message = 'error';
          $success = false;
      }
        
      // build the response data array that will be returned in `data` from `.done(function(data) {`
      $json = array( 
        'id' => $bookmarkid,
        'action' => $action,
        'message' => $message,
        'success' => $success
      ); 
      // convert data and send as JSON
      header('Content-Type: text/json; charset=utf-8'); // forgot this line
      echo json_encode($json);
      return;
    }
    ?>

    So now I get this within preview (video below). Still full html render output (Any thoughts why would this be?) but now the JSON is appended. I have also tried placing the if AJAX within the _init.php file. This just prepends it (expected) to before the HTML output. So I suppose my issue is I'm rendering the full HTML output as the JSON just shows as part of it.

     

     

  11. 4 minutes ago, flydev ?? said:

    You need to show us what is inside the "preview" tab to see what's the answer returned (the payload is what you send, the preview is the answer you get)

    The preview just renders the page HTML. It isn't giving a JSON breakdown. 

    So you're getting the JSON response, I'm getting a full page render. Once back on later I'll update with a. Screenshot.

  12. Thanks again @flydev ??

    Correct, all is within a single template. This is then appended to _main.php. The full template is in my reply above.
    To be honest I'm stuck on this so instead of this seeming like a game of circles I'll take some time out to get to grips with it.

    From what I can tell, I can not see anything wrong but again that is my lower knowledge barrier.

    On button click, I do not get anything firing in console. When I click a second time I then get "⚠️ Bookmark already saved to profile"

    A quick question, should any of this part be included and should it be within the loop? 

    <?php
    if($user->isLoggedin() && $action) {
    $bookmarks = $user->getUnformatted('bookmarks');
    if($action === 'remove') {
    // remove bookmark
    if($bookmarks->has($posts)) $bookmarks->remove($posts);
    } else if($action === 'save' && !$bookmarks->has($posts)) {
    // add bookmark
    $bookmarks->add($posts);
    }
    // save bookmarks
    $user->setAndSave('bookmarks', $bookmarks);
    $this->halt();
    }
    ?>

    Within console -> network I get this when clicking a button and within network -> preview I just get the full page html which I'm guessing shouldn't be the case.

    This is the page in question - https://ad-bank.co.uk/all-ads/

     

    Anyway, I'll take some time out to get to grips on this but appreciate all the info and direction on this. 

  13. 10 hours ago, flydev ?? said:

    Get rid of writing the $action = $input->post->option('bookmark', [ 'save', 'remove'] );  block in a loop, you do not need that.

    If you look at my example (made some corrections for consistency), you are capturing the action made by the user in the `$input->bookmark->action`  and the id in `$input->bookmark->id` (form data built in the $.post() ajax jquery code):

    (I don't know what is the `$input->post->option('a', ['b', 'c'])`) thing, but we dont care)

    bd($input->post); // debug with tracy
      /** contain:
       *  action: 'save'
       *  bookmark: '1024'
       */

     

    Then just handle the request :

    <?php 
    /** threat ajax (pw) => https://processwire.com/api/ref/config/#pwapi-methods-runtime
     *  Better to write a dedicated template which will receive the request.
     *  That is a good candidate and exercise to try the module AppApi ?
     */
    if($config->ajax && $input->post->bookmark) {
    
      // bd($input->post->bookmark); // debug with tracy, uncomment if you have it
      /** contain:
       *  action: 'save' or 'remove'
       *  bookmark: '1024'
       */
      
      $bookmarkid = $sanitizer->int($input->post->bookmark);
      $action = $sanitizer->name($input->post->action);
      switch($action) { // which action ?
        case 'save': // save logic
          $message = 'Borat saved bookmark "'. $bookmarkid .'" ?';
          $success = true;
          break;
        case 'remove': // remove logic
          $message = 'Borat removed bookmark "'. $bookmarkid .'" ';
          $success = true;
          break;
        default:
          $message = 'error';
          $success = false;
      }
        
      // build the response data array that will be returned in `data` from `.done(function(data) {`
      $json = array( 
        'id' => $bookmarkid,
        'action' => $action,
        'message' => $message,
        'success' => $success
      ); 
      // convert data and send as JSON
      header('Content-Type: text/json; charset=utf-8'); // forgot this line
      echo json_encode($json);
      return;
    }
    ?>
    
    
    <!-- the only one loop -->
    
    <!-- you dont really need a form -->
    <!-- <form action='<?=$page->url?>' method='post'> -->
    
    <?php // dummy bookmarks loop logic for the show
    $bookmarks = $pages->find("template=dummypage, limit=5, sort=sort");
    foreach ($bookmarks as $bookmark):
    ?>
    <?php if($user->isLoggedin()): ?>
        <!-- show button to save and remove this page to bookmarks -->
    	<!-- of course, make your own logic to show one or other button -->
        <!-- add a data attribut to each button -->
        <button class="button bookmark" name='bookmark' value='save' data-id='<?= $bookmark->id ?>'>Save <?= $bookmark->id ?></button>
        <button class="button bookmark" name='bookmark' value='remove' data-id='<?= $bookmark->id ?>'>Remove <?= $bookmark->id ?></button>
    <?php endif; ?>
    <?php endforeach; ?>
    
    <!-- </form> -->
    
    
    
    <script>
      jQuery(document).ready(function($) {
        // when clicked, send ajax request to server
        $('button.bookmark').on('click', function(e /* add event arg */) {
          var btn = $(this).hasClass('clicked') ? false : $(this).addClass('clicked');
          if (!btn) {
            console.warn(`⚠️ Bookmark already saved to profil`);
            return false;
          }
          $.post('<?= $page->url /* send to the page which have the logic and return json answer */ ?>', { action: $(this).val(), bookmark: btn.data('id') })
            .done(function(data) {
              console.log(data, "? response from ajax req");
              // $(this).removeClass('clicked'); // remove `clicked` class, can be added on the foreach loop if the bookmark is already saved and then locked, or whatever..
              console.info(` Bookmark id => ${data.id} ${data.action} `);
            });        
            
          //e.preventDefault(); // you didn't need a <form>, and if you really want it, add this line to prevent default behavior and stop the redirection
        });        
      }); 
    </script>

     

    I will make you an example to save the page, but if I were you, I would just save a "page (ID)" in a page-reference field in the profile.

     

    ajx.gif

    As always, thank you @flydev ??

    Straight implementing your snip I'm getting undefined response - 

    image.png.6e2cfa36d435cbbad59604840bcf72d4.png

    This is the whole template file for /all/ url mentioned above. From what I can see it's implemented right?... So where you mention the "only one loop" is where I have placed the main template build. 

    <?php namespace ProcessWire;
    $items = $pages->find($selector); //selector built in _func template
    $total = $items->getTotal();
    
    if($config->ajax && $input->post->bookmark){
    
      bd($input->post->bookmark); // debug with tracy, uncomment if you have it
      
      $bookmarkid = $sanitizer->int($input->post->bookmark);
      $action = $sanitizer->name($input->post->action);
      switch($action) { // which action ?
        case 'save': // save logic
          $message = 'Borat saved bookmark "'. $bookmarkid .'" ?';
          $success = true;
          break;
        case 'remove': // remove logic
          $message = 'Borat removed bookmark "'. $bookmarkid .'" ';
          $success = true;
          break;
        default:
          $message = 'error';
          $success = false;
      }
        
      // build the response data array that will be returned in `data` from `.done(function(data) {`
      $json = array( 
        'id' => $bookmarkid,
        'action' => $action,
        'message' => $message,
        'success' => $success
      ); 
      // convert data and send as JSON
      header('Content-Type: text/json; charset=utf-8'); // forgot this line
      echo json_encode($json);
      return;
    }
    ?>
    
    <div id="content-main" class="adbank" pw-append>
      <!-- start of page header -->
    <header class="small-header">
    	<div class="full-width">
    		<div class="row flex header-content">
    			<h1><?php echo $page->headline ?></h1>
    			<?php 
    			if ($page->summary != ''){
    				echo''.$page->summary.'';
    			}
    			?>
    		</div>
    	</div>
    </header>
      <!-- end of page header -->
    <div class="bread-plus-filter">
        <div class="full-width">
            <div class="row flex">';
                <!-- Breadcrumbs for all pages except home page -->
                <?php include_once "inc/breadcrumb.php"; ?>
                <div class="filter-surround">
                    <div class="filter-row">
                    <p><?=$total?> Results</p>
                      <!-- filter button -->
                    <button class="button filter-toggle open-menu" type="button" aria-label="Toggle filter">
                        Filter		
                    </button>
                    </div>
                </div><!-- close of filter surround -->
            </div>
        </div>
    </div>
    
    <!-- Main blog post section -->
    <section id="main-post-area" class="ab-post-area">
    	<div class="full-width">
    	<?php if(count($items) > 0){
    	}else{
    		echo'<h2>Sorry, no results match that filter</h2>';
    	}
        ?>
        <div class="row">
        <!-- main creative grid -->
    			<div class="creative-posts-container grid">
                    <!-- colcade columns -->
                    <div class="grid-col grid-col--1"></div>
                    <div class="grid-col grid-col--2"></div>
                    <div class="grid-col grid-col--3"></div>
    				<!-- loop out the posts from the selector -->
                    <?php foreach($items as $posts): ?>
                        <div class="creative-post grid-item <?=$posts->ab_channels->title?> <?=$posts->ab_placement->title?> <?=$posts->ab_content->title?> <?=($posts->check_1 ? ' no-title' : '')?> <?=($posts->image_1->height / $posts->image_1->width >= 1.5 ? 'long' : 'short')?> ">
                            <div class="creative-post-options">
                              <!-- posts buttons including view brand, view post and save -->
                                <div class="actions">
                                    <a href="<?=$posts->adbank_brands->{httpUrl}?>" class="action" role="link" aria-label="View more of <?=$posts->adbank_brands->{title}?>" title="View more of <?=$posts->adbank_brands->{httpUrl}?>">View Brand</a>
                                    <a href="<?=$posts->httpUrl?>" class="action" role="link" aria-label="see more info about this advert" title="see more info about this advert">View Ad</a>
                                    <?php if($user->isLoggedin()): ?> <!-- if user is logged in -->
                                        <?php if($user->bookmarks->has($posts)): ?> <!-- if user has saved then show this button -->
                                            <button class="button action" type='submit' name='saved' value='saved' disabled>Saved</button>
                                        <?php else: ?>
                                            <!-- if they haven't saved then show this button to add this page to bookmarks -->
                                            <button class="button bookmark action" type='submit' name='bookmark' value='save' data-id='<?=$posts->id?>'>Save</button> <!-- changed to $posts->id from bookmarksId to match the foreach output -->
                                        <?php endif; ?>
                                    <?php else: ?>
                                  <!-- if user is not logged in then show this button to fire a login modal -->
                                        <button class="button action save-modal" role="button" title="save this ad bank post" aria-label="save this ad bank post" href="">Save</button>
                                    <?php endif; ?>
                                    </div>
                                </div>
                          <!-- loop through posts type and include the relevant markup -->
                            <?php
    							if($posts->ab_channels->title === "facebook-ads"){
    								include "inc/adbank/facebook-ads.php";
    							}else if($posts->ab_channels->title === "instagram-ads"){
    								include "inc/adbank/instagram-ads.php";
    							}else if($posts->ab_channels->title === "pinterest-ads"){
    								include "inc/adbank/pinterest-ads.php";
    							}else if($posts->ab_channels->title === "youtube-ads"){
    								include "inc/adbank/youtube-ads.php";
    							}else if($posts->ab_channels->title === "display-ads"){
    								include "inc/adbank/display-ads.php";
    							}else if($posts->ab_channels->title === "tiktok-ads"){
    								include "inc/adbank/tiktok-ads.php";
    							}else if($posts->ab_channels->title === "podcast"){
    								include "inc/adbank/podcast.php";
    							}else{
    							}
                            ?>
                        </div>
                    <?php endforeach ?>
                    
    			</div><!-- end of main creative grid -->
    		</div><!-- End row -->
    	</div><!-- End container -->
    </section><!-- End section -->
      
    <!-- pagination -->
    <?php echo pagination($items); ?>
     <!-- AJAX request -->
    <script>
      jQuery(document).ready(function($) {
        // when clicked, send ajax request to server
        $('button.bookmark').on('click', function(e) {
          var btn = $(this).hasClass('clicked') ? false : $(this).addClass('clicked');
          if (!btn) {
            console.warn(`⚠️ Bookmark already saved to profile`);
            return false;
          }
          $.post('<?= $page->url?> /*should this be $posts->URL>*/', { action: $(this).val(), bookmark: btn.data('id') })
            .done(function(data) {
              console.log(data, "? response from ajax req");
              console.info(` Bookmark id => ${data.id} ${data.action} `);
            });
        });        
      }); 
    </script>
      <!-- end of AJAX request -->
    </div> <!-- end of content surround -->

     

  14. Just an update again.

    So I have the AJAX working on the single page. See below woop!

    Next steps is trying to get this to work with a variable input. I have taken your jquery snip @flydev ??, again thank you.

    Right now I'm just trying to get it to work on this page which has a loop of posts - https://ad-bank.co.uk/all-ads/

    This is where I'm at.

    The main button section. Only showing a "save" button if the user has not saved the page.

    <?php if($user->isLoggedin()): ?>
      <?php if($user->bookmarks->has($posts)): ?>
        	<button class="button bookmark action" type='submit' name='bookmark' disabled>Saved</button>
        <?php else: ?>
        <!-- show button to add this page to bookmarks -->
        	<button class="button bookmark action" type='submit' name='bookmark' value='save' data-id='<?=$posts->url?>'>Save</button>
        <?php endif; ?>
    <?php endif; ?>

    Next is the action element shown above. This is also within the foreach loop so it is repeated for each button. I found this was the only way I could get it to capture the button action.

    Is there a better way to do this without having this action snip repeated for each button? Would it be using the data id attribute which could then sit outside of the loop?

    <?php
    $action = $input->post->option('bookmark', [ 'save', 'remove'] );
    if($user->isLoggedin() && $action) {
    $bookmarks = $user->getUnformatted('bookmarks');
    // I have now removed "remove" from the button section. Kept it in just in case I do wish to use it.
    if($action === 'save' && !$bookmarks->has($posts)) {
    // add bookmark
      $bookmarks->add($posts);
    }
    // save bookmarks
    $user->setAndSave('bookmarks', $bookmarks);
    $this->halt();
    }
    ?>

    Then I have the AJAX request here. This now takes the button data id attribute instead of <?= $page->url ?>. This I found means it grabs the correct url instead of the first url on page that is not saved.

    $(document).ready(function($) {
        // when clicked, send ajax request to server
        $('button.bookmark').on('click', function() {
        var btn = $(this).hasClass('clicked') ? false : $(this).addClass('clicked');
    	// I am passing data id instead of page->url as it will capture the button value needed. Not sure if correct, console shows the correct value.
        if(btn) $.post($(this).data('id'), { action: $(this).val(), bookmark: btn.data('id') })
            .done(function(data) {
            //do something
            console.info(` Bookmark id => ${btn.data('id')} saved to user profile`);
            });
        });
    }); 

    So in console I get a successful output (see below) but it's not saving into the user. Any thoughts why this might be? I'm thinking it's the action part of this but not sure.

    image.thumb.png.236c1e529a6adcbad0afed9e2ba99cf6.png

    Overall, feels good to be making progress on this and learning. If anyone has input on the above issues please fire over.

    Cheers

  15. 10 hours ago, flydev ?? said:

    I don't really get what is your issue, the click that still redirect ? If yes, just add `e.preventDefault()` to your js script to prevent default behavior as your are submiting a form.

     

    <?php 
    /** threat ajax (pw) => https://processwire.com/api/ref/config/#pwapi-methods-runtime
     *  Better to write a dedicated template which will receive the request.
     *  That is a good candidate and exercise to try the module AppApi ?
     */
    if($config->ajax && $input->post) {
    
      bd($input->post->bookmark); // debug with tracy
      /** contain:
       *  action: 'add'
       *  bookmark: '1024'
       */
    
      /**
       * you can write some logic here, eg., to grab the post data sent by the ajax request
       */
      $bookmarkid = $sanitizer->int($input->post->bookmark);
      // eg. you can get a page from that
      $bookmarkedPage = $pages->get($bookmarkid);
      // send response data back
      $message = 'Borat added bookmark "'. $bookmarkedPage->title .'" ?';
      $success = true;
      
      // build the response data array
      $json = array(
        'message' => $message,
        'success' => $success
      ); 
      // convert data and send as JSON
      header('Content-Type: text/json; charset=utf-8'); // forgot this line
      echo json_encode($json);
      return;
    }
    ?>
    
    <?php // dummy bookmarks loop logic for the show
    // foreach($bookmarks as $bookmark) : 
      $bookmark = $pages->get(1024); // forgot this line
    ?>
    <!-- you dont really need a form -->
    <!-- <form action='<?=$page->url?>' method='post'> -->
    <?php if($user->isLoggedin()): ?>
        <!-- show button to add this page to bookmarks -->
        <!-- add a data attribut to each button -->
        <button class="button bookmark" name='bookmark' value='add' data-id='<?= $bookmark->id ?>'>Save bm #1</button>
    <?php endif; ?>
    <?php //endforeach; ?>
    <!-- </form> -->
    <script>
      jQuery(document).ready(function($) {
        // when clicked, send ajax request to server
        $('button.bookmark').on('click', function(e /* add event arg */) {
          var btn = $(this).hasClass('clicked') ? false : $(this).addClass('clicked');
          if (!btn) {
            console.warn(`⚠️ Bookmark already saved to profil`);
            return false;
          }
          $.post('<?= $page->url /* send to the page which have the logic and return json answer */ ?>', { action: $(this).val(), bookmark: btn.data('id') })
            .done(function(data) {
              console.log(data, "? response from ajax req");
              // $(this).removeClass('clicked'); // remove `clicked` class, can be added on the foreach loop if the bookmark is already saved and then locked, or whatever..
              console.info(` Bookmark id => ${btn.data('id')} saved to user profil`);
            });        
            
          //e.preventDefault(); // you didn't need a <form>, and if you really want it, uncomment this line to prevent default behavior and stop the redirection
        });        
      }); 
    </script>

     

    ajx.gif.1b5b743b02c04fda2b1c2b9fefb61b09.gif

     

    giphy.gif?cid=ecf05e47uqm52d4j5pif2nid1o

    Thanks @flydev ??

     

    Tbh I need to spend time to learn more about AJAX and implementation.

    I'll try implement this on the single option page so /all/page-name/ and then work on the /all/.

    Unless based on the above, I don't need dedicated versions for each page? Again, will have to get digging into AJAX this week.

    Cheers

  16. To update where I'm at.

    So I have - 

    • Added a page ref field to the user template
    • Created a front end button that adds or removes the page id to/from the page ref field.

    This means the like module is not needed as I'm not looking to allow anon users to save, only logged in users.

    This is the button. I will most likely remove the "remove" button and just have a "saved" item to signify it's saved. I'll allow users to remove in their profile area which outputs their saved posts.

    <?php if($user->isLoggedin()): ?>
    <form action='<?=$page->url?>' method='post'>
      <?php if($user->bookmarks->has($page)): ?>
      <!-- user already has this page in their bookmarks, show remove button -->
      <button class="button save-button" type='submit' name='bookmark' value='remove'>Remove</button>
      <?php else: ?>
      <!-- show button to add this page to bookmarks -->
      <button class="button save-button" type='submit' name='bookmark' value='add'>Save</button>
      <?php endif; ?>
    </form>  
    <?php endif; ?>

    This is then be actioned by the below. 

    $action = $input->post->option('bookmark', [ 'add', 'remove'] );
    if($user->isLoggedin() && $action) {
    $bookmarks = $user->getUnformatted('bookmarks');
    if($action === 'remove') {
    // remove bookmark
    if($bookmarks->has($page)) $bookmarks->remove($page);
    } else if(!$bookmarks->has($page)) {
    // add bookmark
    $bookmarks->add($page);
    }
    // save bookmarks
    $user->setAndSave('bookmarks', $bookmarks);
    // always good to redirect back to self after POST save request
    $session->redirect($page->url, false);
    }

    This works great for the actual post page. So url example being website.com/all/page-name/

    However, I have a foreach on website.com/all/ and have the save button on each foreach output.

    These in the foreach are called $posts.

    I have been able to get it to work by adjusting the above to be $posts instead of $page. However, I find that it just sends the user to the $posts URL but I'd want them to stay on the current page. I did this posts suggestion:

    However, I find it doesn't redirect to the /all/ page but goes to /all/page-name/

    I also saw a suggested version below to AJAX it which would remove the redirect issue in /all/. So removal of the form element and I'm doing the button show/hide through PHP instead of JS. The snip below doesn't do anything right now and I'm not too sure why.

    Any input from someone with JS/AJAX knowledge is appreciated.

    <?php if($user->isLoggedin()): ?>
      <?php if($user->bookmarks->has($posts)): ?>
        <!-- user already has this page in their bookmarks, show remove button -->
        <button class="button save-button" name='bookmark' value='remove'>Remove</button>
      <?php else: ?>
        <!-- show button to add this page to bookmarks -->
        <button class="button save-button" name='bookmark' value='add'>Save</button>
      <?php endif; ?>
    <?php endif; ?>
    
    <script>
      jQuery(document).ready(function($) {
            // when clicked, send ajax request to server
            $('button.bookmark').on('click', function() {
            var btn = $(this).hasClass('clicked') ? false : $(this).addClass('clicked');
            if(btn) $.post('<?= $posts->url ?>', { bookmark: btn.val() })
                .done(function(data) {
                });
            });
        }); 
    </script>

    Feels good to get this working but just need to hone in the user experience and behaviour. If anyone wants to see the page in question it's here

    Liam

  17. 1 hour ago, flydev ?? said:

    I understand, and my point to suggest you to ask @ryan directly if his pro module can fit your needs, is because ryan is used to give more flexibility instead of locking the user to a finished functionality. I mean, there is maybe some hooks that can be used to link things to an user. Without speaking about that you benefit ryan's code and insight ?

    Anyway, I will might buy it for a project and could give more feedbacks. Stay tuned.

    Yeah, at the end of my reply to yourself I gave ryan an @ but I'll pop him a PM.

    Appreciate all the direction on this by the way. It's a fun side project to get more into processwire and luckily the forum is full of great info and direction.

    • Like 1
×
×
  • Create New...