Jump to content

InstagramBasicDisplayApi


nbcommunication

Recommended Posts

Hi,

With the deprecation of Instagram's API and therefore the end of the Instagram Feed module, I've developed a replacement module which uses the Instagram Basic Display API:

https://github.com/nbcommunication/InstagramBasicDisplayApi

To use this module you'll need:

  • ProcessWire >= 2.7
  • A Facebook Developer account
  • Access to the Instagram user account you wish to use

Prior to installation, you'll need to create a Facebook app. The app you will create uses the User Token Generator for authentication - it does not need to be submitted for App Review (and therefore stays in Development mode). 

The README contains full instructions on how to create and set up the app and also how to use the module.

The primary reason for this module's development was to retain functionality on existing websites that use the Instagram Feed module. To assist with upgrading, this module replicates some methods provided by Instagram Feed. I've already upgraded a couple of sites and it was quick and painless ?

Cheers,

Chris

  • Like 12
  • Thanks 3
Link to comment
Share on other sites

  • 3 weeks later...
1 hour ago, nbcommunication said:

The module is now ready for use in production.

Hi, I decided to test this out but it's not working for me. I was able to complete the configuration using your instructions so my IG user account shows up under Authorized Accounts but when I try to load the page, I get this error: Fatal Error: Uncaught Error: Call to a member function getImages() on null in /home/clientname/public_html/site/templates/_sidebar.php:59

Here's my code:

<?php 
   // Get 9 images from the default user
   $images = $instagram->getImages(9);

   // Render the images
   echo "<div data-lightbox='gallery'>" . 
    $images->each("<a href='{href}' data-lightbox='gallery-image'>" . 
      "<img src='{src}' alt='{alt}'>" . 
     "</a>") . 
   "</div>";
?>

 

Link to comment
Share on other sites

46 minutes ago, nbcommunication said:

Pop this before your call:


$instagram = $modules->get("InstagramBasicDisplayApi");

 

Thanks! I can see the images now but I don't see any documentation on how to adjust the thumbnail dimensions. Ideally, I would be able to set the width and height of the thumbnails and then open the {src} in a lightbox/modal, is that possible? Something like this?:

<?php
$instagram = $modules->get("InstagramBasicDisplayApi");
$images = $instagram->getImages(9);
echo "<div data-lightbox='gallery'>" . 
  $images->each("<a href='{src}' data-lightbox='gallery-image'>" . 
                "<img src='{thumb}' alt='{alt}'>" . 
                "</a>") . 
  "</div>";
?>

 

Link to comment
Share on other sites

Thank you so much @nbcommunication! It's an awesome and very needed module! Very useful! ?

 

Only minor thing: Perhaps add to the documentation (which is great otherwise!): 

$instagram = $modules->get("InstagramBasicDisplayApi");

– That ofc needs to be added initially. 

And I also just thought:

Could you also maybe in the readme show an example of pulling an array of both images, image carousels and videos (basically everything the user has posted, of all the types) and showing them all one after the other chronologically, but of course differently formatted depending on if it's an img, video or img carousel ? 

And,

Does it support all https://developers.facebook.com/docs/instagram/embedding/#sample-request properties? Fx video width?

It seems that if I call 

$videos = $instagram->getVideos();
foreach($videos as $video)

and try to use

data-width='{$video->width}'

it doesn't work?

 

All the best,

Jonatan

Link to comment
Share on other sites

@gowthamg Do you just need to simply show the images from a user? Because in that case you only need the Instagram Basic Display product (https://developers.facebook.com/docs/instagram-basic-display-api/) (no need to submit anything to fb and no need to provide a website adress). 

 

Did you follow these steps? :

 

https://github.com/nbcommunication/InstagramBasicDisplayApi

Facebook for Developers:

  1. Add new fb app
  2. Add instagram as a product for your app
  3. Go to your app > Products > Instagram > Basic Display and scroll down to "User Token Generator"
    1. Add Instagram Tester
    2. Approve the tester inviter on the instagram profile
    3. Click generate token

2142471174_Skrmbillede2020-03-21kl_17_46_57.thumb.png.1ae88b0d52a894789f51fceba6fc9c32.png

 

Processwire Instagram Basic Display API:

  1. Go to your module, click 'Add an Instagram user account
  2. Fill in the username and the generated token

1236527705_Skrmbillede2020-03-21kl_17_50_01.thumb.png.dd862a24ec5645707292645d3e2ff800.png

 

And then you should be able to use the api?

 

Hope it may help,

Best,

Jonatan

  • Like 1
Link to comment
Share on other sites

Hi @jonatan,

Many thanks for your feedback, and for helping out @gowthamg. You are correct - looks like he's trying to set up the full API which isn't what this module uses.

I've updated the README with the module call in each example, and also added an example for getMedia() which demonstrates how to build a multi-media gallery using UIkit. I've attached a video example of the result ? Here's the code I added to the README:

// Function for rendering items
function renderInstagramItem($src, $alt, $href = null) {
	if(is_null($href)) $href = $src;
	return "<a href='$href' data-caption='$alt' " . ($src !== $href ? "data-poster='$src' " : "") . "class='uk-display-block uk-cover-container'>" .
		"<canvas width='640' height='640'></canvas>" .
		"<img src='$src' alt='$alt' data-uk-cover>" .
	"</a>";
}

// Get the module
$instagram = $modules->get("InstagramBasicDisplayApi");

// Get the 16 most recent items and render them based on type
$items = [];
foreach($instagram->getMedia(16) as $item) {
	switch($item->type) {
		case "VIDEO":
			$items[] = renderInstagramItem($item->poster, $item->alt, $item->src);
			break;
		case "CAROUSEL_ALBUM":
			// If 4 or greater items, display a grid of the first 4 images
			// Otherwise display the main image (no break, moves to default)
			if($item->children->count() >= 4) {
				$items[] = "<div class='uk-grid-collapse uk-child-width-1-2' data-uk-grid>" .
					$item->children->find("limit=4")->each(function($item) {
						return "<div>" . renderInstagramItem($item->src, $item->alt) . "</div>";
					}) .
				"</div>";
				break;
			}
		default: // IMAGE
			$items[] = renderInstagramItem($item->src, $item->alt);
			break;
	}
}

// Render the items as a grid
echo "<div class='uk-grid-collapse uk-child-width-1-2 uk-child-width-1-4@s' data-uk-grid data-uk-lightbox>";
foreach($items as $item) {
	echo "<div>$item</div>";
}
echo "</div>";

The query about data-width - this is a totally separate thing unfortunately. That's the oEmbed implementation from which you get a HTML embed code which normally includes javascript from the provider, and in the case of Instagram includes code which handles a data-width attribute. There's nothing like that here. This API gives you basic data, and it is up to you to render it. I'd actually recommend looking more into oEmbed if you are looking to embed single posts in articles or similar. The main purpose of this module and the API itself is a standalone "feed" or gallery of user media.

Cheers,

Chris

Link to comment
Share on other sites

Hi @gowthamg,

What do you mean by all users?

In the past, getting Instagram data from any user was pretty simple, but it is locked down now. You can only retrieve data from users who have authorised your app.

This module is built around the oAuth token generator provided by facebook, which allows you to add a "test" Instagram user, and then generate a token by logging in as that user and authorising the app. This bypasses the need to submit the app for review, which is intended for apps that would be public facing.

You can only get data from users that have authorised your app, and in this case of this module, you need to login as the user to authorise it. For a number of clients recently we've arranged a time where they change their password to something temporary and let us know the temp password, we run through the steps in the README and get the app/module set up, and then let the client know so they can change their password back. It has worked well so far.

Cheers,

Chris

 

Link to comment
Share on other sites

Hi @gowthamg,

The instructions for this module, for setting up the facebook app are here: https://github.com/nbcommunication/InstagramBasicDisplayApi/blob/master/README.md and relate exclusively to this part of the facebook/instagram docs: https://developers.facebook.com/docs/instagram-basic-display-api/overview#user-token-generator.

I'd advise reviewing the errors displayed on your screenshot - notes and screencast are missing.

If you do have success in getting the app reviewed and approved, please share here, but as I said previously, this isn't in the purview of this module.

Cheers,

Chris

Link to comment
Share on other sites

On 3/21/2020 at 10:05 PM, nbcommunication said:

Hi @jonatan,

Many thanks for your feedback, and for helping out @gowthamg. You are correct - looks like he's trying to set up the full API which isn't what this module uses.
I've updated the README with the module call in each example [...]
The query about data-width [...]
Cheers,
Chris

Awesome @nbcommunication (Criss)! Thanks a lot for all your effort! ?

It has really helped me out! And I'm sure it'll help others as well! No worries about the data-width though, it's working without it actually ? But good to know though about the oEmbeds!
I've just checked out your nbcommunications website btw, and it's really neat!
?

Here's my own take on it (much less sophisticated and uglier ?). But it's because I'm using Bootstrap 4 and Fancybox that I tried to adapt it to that:

<?php
foreach($images = $instagram->getMedia(89) as $post) {

    switch($post->type) {

      case "VIDEO":

          echo"                             
          
            <div class='col-4 p-3 pics'>
            <a data-fancybox='gallery-ig' data-height='600'  data-caption='{$post->href}' href='{$post->src}' >
              <div style='background-image: url({$post->poster})' class=' pics'>
                <div class='overlay-video-icon'></div>                 
              </div>
            </a>
          </div>

          ";

      break;

      case "CAROUSEL_ALBUM":

          echo"

          <div data-interval='3000' class='carousel slide carousel-fade col-4 p-3 pics' data-ride='carousel' id='_{$post->id}'>
                <div class='carousel-inner'>
          ";

                $post->children->each(function($post) {

                    echo"

                    <a class='carousel-item' data-fancybox='gallery-ig' data-height='600'  data-caption='{$post->href}' href='{$post->src}' >
                      <div style='background-image: url({$post->src})' class=' pics'></div>
                    </a>

                    ";

                });

                echo"
                  
                    <a class=\"carousel-control-prev\" href=\"#_{$post->id}\" role=\"button\" data-slide=\"prev\">
                      <span class=\"carousel-control-icon-bg\" aria-hidden=\"true\"></span>
                      <span class=\"carousel-control-prev-icon\" aria-hidden=\"true\"></span>
                      <span class=\"sr-only\">Previous</span>
                    </a>
                    <a class=\"carousel-control-next\" href=\"#_{$post->id}\" role=\"button\" data-slide=\"next\">
                      <span class=\"carousel-control-icon-bg\" aria-hidden=\"true\"></span>
                      <span class=\"carousel-control-next-icon\" aria-hidden=\"true\"></span>
                      <span class=\"sr-only\">Next</span>
                    </a>
                </div>
          </div>
          ";

      break;


      default: // IMAGE

          echo"

          <div class='col-4 p-3 pics'>
            <a data-fancybox='gallery-ig' data-height='600'  data-caption='{$post->href}' href='{$post->src}' >
              <div style='background-image: url({$post->src})' class=' pics'></div>
            </a>
          </div>

          ";

      break;

      }
}
?>

    <script type="text/javascript">

            $(document).ready(function(){
              $('.carousel').each(function(){
                $(this).find('.carousel-item').eq(0).addClass('active');
              });
            });

    </script>

 

Resulting in this:

 

 

– Images as simple square thumbs,
– videos the same but with a video-overlay (dark transparent black box with a play icon),
– and the image carousels as a small carousel / slideshow.

– That's actually also quite good.. Except a feeeew things that you could perhaps help me with...

In general the performance of my instafeed page is awful ?.
It takes an extremely long amount of time to load! ⏱️? – About 30 seconds! 
I am realizing though that I'm also making a large amount of post requests :

$instagram->getMedia(89)

But I can see that what is different from my poorly noob-interpreted version of your php code is that you collect it all into an array, and then serve it all from that array, where as I just echo"..."; away on everything... (as arrays still isn't quite my speciality...)

– Would it performance wise make any difference to use your method of first collecting it all into an array, and then serving it all by rendering all the items in that array, as you do it?...

Which also leads me this...

Pagination (& lazyload?)

I can see that the API has some built in pagination:
https://stackoverflow.com/questions/59814442/instagram-basic-display-api-pagination | https://developers.facebook.com/docs/instagram-basic-display-api/reference/media/children | https://developers.facebook.com/docs/graph-api/using-graph-api#paging

 But with my very sad php skills I can't figure out how to actually implement it? 
My goal is that I wish to serve all and every post available from the user (currently counting 80 posts) as you scroll down the page... But of course here pagination would come into the picture. Could you maybe help me come up with a solution so that when you reach the end of the page the next (9 perhaps) posts would be loaded... (some kind of lazy load? ) Much like on Instagram itself:

– I suppose this pagination / lazy load integration would perhaps also help out a lot of others wishing to display the whole IG feed? Maybe it'd be handy to have in the module's README as well? ?

Also there's something I can't explain.. I'm getting some:

Empty slots
I'm requesting 89 posts. (Because I read on stack overflow that the maximum request is 90.. that might have changed though?)
But there are only 80 posts all in all.
The API feed is only showing 75 posts though. – Which is also really weird? – Can't figure why so...
But for the remaining (89-75 = ) 14 posts it shows empty slots:...
 

– Why is that? Do you have any idea? I supposed that if there weren't actually as many posts to get as requested it would only get the available amount of posts, within the requested limit?

And one more quite crucial thing...

CRAZY API load amounts... 

Before IG feed page Load:    528738513_Skrmbillede2020-03-25kl_13_11_45.thumb.png.5f8fc74c216c275a3f12c2c72cbfc43f.png   gr:get:Media/children: 1331 gr:get:User:Media: 47 (it seems it's still showing yesterdays request-count?)

After one IG feed page load:   1441052496_Skrmbillede2020-03-25kl_13_15_43.thumb.png.720d0cbfb39697b1e44496fdc008e2a9.png   gr:get:Media/children: 669 gr:get:User:Media: 38  –  60%remaining

One reload:                               544146107_Skrmbillede2020-03-25kl_13_31_46.thumb.png.1c5212748d687ef800b8d2b17260457e.png   gr:get:Media/children: 703 gr:get:User:Media: 38  –  37%remaining

One more reload:                      1670312193_Skrmbillede2020-03-25kl_13_33_58.thumb.png.6da91e3d14f3e2e9114cbc91725d7a3a.png  gr:get:Media/children: 739 gr:get:User:Media: 38  –  21%remaining

One more reload:                      1746739276_Skrmbillede2020-03-25kl_13_48_29.thumb.png.9e8b4ce1de6fad2f3d7794bb95e39385.png  gr:get:Media/children: 779 gr:get:User:Media: 38  –  0%remaining

One more reload:                      269741976_Skrmbillede2020-03-25kl_14_18_19.thumb.png.03f58b82e80dd4fdc93aafead2842fb3.png  gr:get:Media/children: 855 gr:get:User:Media: 38  –  6%remaining

 

 ... It says the total number of calls = 200 * number of users... Shouldn't I worry about this? I mean if I after only 5 page loads have used my quota... That doesn't seem to be enough for a properly functioning live website with multiple users?... 

 

And one more thing...

Media_count

<?php
$data = array('username'=>'');
$account = $instagram->getUserAccount($data["username"]);
$username = isset($account["username"]) ? $account["username"] : "";
$data = array('media_count'=>'');
$mediacount = $instagram->getUserAccount($data["media_count"]);
$postsnum = isset($mediacount["media_count"]) ? $mediacount["media_count"] : "";
?>

<div class="col-12 text-center mb-3">

        <a href="https://instagram.com/<?php echo $username;?>/"  target="_blank" class="marked"><h3 class="marked my-5 instalink">
                <?php echo "@" . $username;?></h3>
        </a>

            <h5 class="marked-dark"><?php echo $postsnum?> POSTS / OPSLAG </h5>

</div>

I'm getting the media_count from the module... to show the number of user posts on the page to give it more of an insta feel. 
When I loaded the page though it ($postnum) hadn't updated to the current amount (80 instead of 78, because the user has added a post 4 hours before I reloaded the page). I went to the processwire admin and the module page and then there it was updated... Then I reloaded the page and it was updated.. Was that only due to the module updating it when visiting the modules page in the processwire admin that it updated it though?

– In short:
can I rely on $mediacount = $instagram->getUserAccount($data["media_count"]);  to fetch the updated post count (without having visited the module page in pw) ? The reason why I'm relying on the module in the first place and not just $profile = $instagram->getProfile("username");  is actually to save on API calls as, as you've seen above, they seem to extremely fast become used up... ? 

Also talking about API calls quickly being used up...

PHP error

I'm getting this when critical error if the php tries to get media but the api doesn't answer due to "maxed out credit":

880913994_Skrmbillede2020-03-25kl_14.47_14.thumb.png.1808457b51ad82fc043a48df5346ad51.png 

How could I prevent that? – And make the page still functional if the API doesn't answer

 

I know that was a lot of questions! – Probably some of it is due to a lack of a lot of basic php knowledge... But if you could help me out it would simply be amazing! And I'm sure others using the module, as php-handicapped as me, could probably benefit from some of the answers as well.

 

Thanks a thousand times in advance! ☺️☺️☺️

All the very best,

Jonatan 

Skærmbillede 2020-03-25 kl. 13.48.29.png

Link to comment
Share on other sites

Hi @jonatan,

I'll provide a fuller response later but just a couple of things now:

The API doesn't actually allow for querying a specific number of items like the old one did. The module actually uses the built in pagination to retrieve as many items as requested in the call. That's why it is taking a while. You should have a cache time of at least 3600 seconds (an hour) if you are requesting this many items. This will prevent maxing out the limits (which is a lot easier to do when developing anyway!).

If I were in your position, I'd be weighing whether so many items need to be displayed. I'd probably just display the first batch (e.g. $images = $instagram->getMedia()) which returns 24 items normally.

For the PHP error:

<?php
foreach($images = $instagram->getMedia(89) as $post) {
	...
}

// I'd change this to:
$images = $instagram->getMedia(89);
if(count($images)) {
	foreach($images as $post) {
        ...
	}
}

Cheers,

Chris

Link to comment
Share on other sites

Thank you so much already Criss @nbcommunication! And thank you for such a quick reply! I'm so surprised by the helpfullness of this forum! 
I really appreciate that you're helping out that much! ??

1 hour ago, nbcommunication said:

// I'd change this to: $images = $instagram->getMedia(89); if(count($images)) { foreach($images as $post) { ... } }

– Thank you! That's of course just all that's needed! ? 

1 hour ago, nbcommunication said:

If I were in your position, I'd be weighing whether so many items need to be displayed

– I did consider whether I should just display fewer items - like the standard 24 fx - and hence make it easier, but I really want the page to have a wholehearted instagram feed feel...  

I can't really figure out how it'd had to be done though, implementing a "lazyload"... so that I don't get all 80 or whatever images served all at once... Should I try to go for something like on page load making the php request, say, 9 posts, then when the user scrolls to the bottom, make it call for 9 more? 

Reg cachetime,

1097397725_Skrmbillede2020-03-25kl_16_46_18.thumb.png.717826dd7cf58959985967b1f436e967.png  – it seems it's already set to 3600 in the .module? ? Or is it another cachetime you mean? 

Thank yo again!

All the very best,

Jonatan

Link to comment
Share on other sites

Hi @jonatan,

I'll have a think about how best to implement a lazy-load. I'll need to provide a way to return the "next" link to do this.

The cache time is set in the module configuration, should be 3600 by default. Quite possible that the single request is maxing out the limit, will look at that later.

Cheers,

Chris

Link to comment
Share on other sites

@nbcommunication Awesome!! I'll be excitedly waiting ☺️

I implemented:

$images = $instagram->getMedia(89);
if(count($images)) {
    foreach($images as $post) {
		...
}

And It's works. And I suppose that the page would render fine now even with the API being maxed out. But I'm still getting "empty slots..." ?

155935194_Skrmbillede2020-03-25kl_17_01_24.thumb.png.8a6218c8e01993f3bfac4a2d9f3a3415.png

And also.. I'm getting these PHP notices from tracy: 

1324145922_Skrmbillede2020-03-25kl_17_01_34.thumb.png.6925adcf5bb994f0dd2f6f076a5e19c3.png

Referring to this line in the .module:    148692137_Skrmbillede2020-03-25kl_17_07_58.thumb.png.720f34dbeb91c7c91f120808c2df7d68.png

– And this:                                               1034701756_Skrmbillede2020-03-25kl_17_06_52.thumb.png.63f48af58a093b1df39e267e9709cf06.png

I'm not sure about the first one about the caption, but maybe that's because there's no caption for some of the posts? And I guess since it's just a notice it doesn't need fixing?

But about the other errors regarding undefined offsets... It counts from offset 75 (meaning actually offset 76 counting including offset 0 as one). 
– I guess this is actually basically just saying that the last posts – post nr 77 to post nr 80 –   which for some reason aren't being pulled (again, why aren't they...? ?) (out of the requested amount of posts (89)) are not there so it just displays an empty slot. Can I make like a "

case :
break;

"? perhaps differently (properly) formatted? Or something smarter? Would that work to prevent generating empty slots?

Thank you again!!

All the very best, 

Jonatan

Link to comment
Share on other sites

Hi @jonatan,

6 hours ago, jonatan said:

– Would it performance wise make any difference to use your method of first collecting it all into an array, and then serving it all by rendering all the items in that array, as you do it?...

No, I think echoing as you are doing would probably be faster if anything. Basically no difference - I just like to use the array so I don't have to wrap with <div></div> in three separate places.

6 hours ago, jonatan said:

I suppose this pagination / lazy load integration would perhaps also help out a lot of others wishing to display the whole IG feed? Maybe it'd be handy to have in the module's README as well?

I'm going to look into making the pagination more available through the module tomorrow. Might be tricky, and it probably will need to be batches of 24, but I definitely think you've made a good case that it should be available.

6 hours ago, jonatan said:

I'm requesting 89 posts. (Because I read on stack overflow that the maximum request is 90.. that might have changed though?)

I think that may have been from the previous API. This one returns the first 24 items, and a link for getting the next 24. You can keep going on until all items have been received or until the API limits have been maxed out. Indeed, this is what would happen if you were to call something like InstagramBasicDisplayApi::getMedia(10000), although I think there would be a timeout before completion.

7 hours ago, jonatan said:

But there are only 80 posts all in all.
The API feed is only showing 75 posts though. – Which is also really weird? – Can't figure why so...
But for the remaining (89-75 = ) 14 posts it shows empty slots:...

If you request 89 and there are only 80, it should return 80, but I don't think I actually tested that. I don't have access to an account at the moment with a relatively small amount of items, but should do soon, so will test this when I can. I suspect there is an issue and this is why you are getting the undefined offset errors and the blank items. For debugging, I'd add the following to your code:

<?php
$images = $instagram->getMedia(89);
echo count($images) . "<br>" . print_r($images, 1);
7 hours ago, jonatan said:

It says the total number of calls = 200 * number of users... Shouldn't I worry about this? I mean if I after only 5 page loads have used my quota... That doesn't seem to be enough for a properly functioning live website with multiple users?...

If you are requesting 80 items, and 10 of those are album carousels, that should be 14 calls to the API (24, 24, 24, 24[8] + a call for each carousel). How many carousels do you have? When I was developing and testing I didn't budge the limit off 0%. Will have another look at this tomorrow. I think it may be useful to have an option to not get the full carousel when using getMedia() as this does seem to be the issue.

7 hours ago, jonatan said:

can I rely on $mediacount = $instagram->getUserAccount($data["media_count"]);  to fetch the updated post count (without having visited the module page in pw) ? The reason why I'm relying on the module in the first place and not just $profile = $instagram->getProfile("username");  is actually to save on API calls as, as you've seen above, they seem to extremely fast become used up... ? 

I'm not following this. I think I know what you mean now I've gone through the following...

I'll tweak your code to what I think you are trying to achieve and that'll maybe get us closer:

<?php

$username = "a_username";
$account = $instagram->getProfile($username);
$postsnum = $account["media_count"] ?? 0;
// If the API request is unsuccessful, the post count below will not be displayed

?>

<div class="col-12 text-center mb-3">
	<a href="https://instagram.com/<?= $username ?>/"  target="_blank" class="marked">
		<h3 class="marked my-5 instalink">@<?= $username ?></h3>
	</a>
	<?php if($postsnum): ?><h5 class="marked-dark"><?= $postsnum ?> POSTS / OPSLAG </h5><?php endif; ?>
</div>

getProfile() is a single call which will be cached for either an hour or however long you have the cache time set to so I wouldn't consider this a resource issue. Given that the cache time will be the same for getMedia(), the count and the number of items you get if you were to get them all should be the same, but the count won't update until the cache has expired.

getUserAccount() should have the same value for media_count, but it is only updated when getProfile() is called (and an API request is made) so I don't think it is worth using in this situation. It is a public method but only so it could be used in the config - I can't think of a situation where it should be used over getProfile().

The errors you've pointed out in your last post are related to the total number issue which I'll get fixed as soon as I can.

I'll hopefully have some progress on this tomorrow! Thanks again for your feedback!

Cheers,

Chris

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...