How do you restrict a Wagtail image chooser panel to a collection - wagtail

I have a field in a model called profile_images, how do I restrict the images shown in the image chooser panel to the profile images collection?
I also want to do the same with an inline panel for the gallery collection.
This means I cannot use permissions as the user should have access to both collections.
Thanks for the help

Wagtail has a feature called hooks that lets you modify some of the Wagtail internal logic, this is a very helpful feature that can enable you to do this.
There is a hook called construct_image_chooser_queryset. You will need to create a file wagtail_hooks.py in one of your app folders, this gets run by Wagtail when the app runs.
Once you have the hook running, you can inject custom filtering of the images results for the image chooser modal. This hook will run for various image listings throughout Wagtail so you will want to add some logic to ensure you are not filtering the images everywhere.
A potential approach would be to read the request, which is passed into the hook, to determine if the current request is for a page editing scenario. Once you know this, you can work out what page class is being used and then filter your images based on some class method on the page class.
When the request is made for the image chooser modal, you can read the HTTP_REFERRER and from that URL use Django's resolve to determine where it was called and what page is being edited.
Example
Using the hook described above, we read the HTTP_REFERRER
Use urlparse and resolve to return a Django match object
Read the match data to confirm we are editing a page
If editing a page, we query for that Page and use the specific_class property
Finally, we can get a collection id from the class to filter images
Note: how you get the collection id is 100% up to you, you could hard code it, make it a separate function or even make it a specific class method, just be careful to ensure that it will all work if that method is not available on ALL pages.
Test this across other image choosers to ensure nothing else breaks.
This will not 'hide' the collection drop-down on the image chooser modal, you may just need to do this with CSS or JS unfortunately.
wagtail_hooks.py
from django.urls import resolve
from urllib.parse import urlparse
from wagtail.core import hooks
from wagtail.core.models import Page
#hooks.register('construct_image_chooser_queryset')
def show_images_for_specific_collections_on_page_editing(images, request):
# first - pull out the referrer for this current AJAX call
http_referrer = request.META.get('HTTP_REFERER', None) or '/'
# second - use django utils to find the matched view
match = resolve(urlparse(http_referrer)[2])
# if we are editing a page we can restrict the available images
if (match.app_name is 'wagtailadmin_pages') and (match.url_name is 'edit'):
page = Page.objects.get(pk=match.args[0])
# important: wrap in a try/except as this may not be implemented on all pages
image_collection_id = page.specific_class.get_image_collection()
# return filtered images
return images.filter(collection=image_collection_id)
return images
models.py
from wagtail.core.models import Page
class BlogPage(Page):
# ...
#classmethod
def get_image_collection(cls):
# any logic here to determine what collection to filter by
return 4

Related

Render AEM pages in React dynamically independently of URL path

I have the need to be able to render any of the pages in my AEM model.json dynamically regardless of the current URL in a SPA React app.
My AEM model.json structure has pages following the /<country>/<language>/rest/of/path format, but I want to be able to strip the country/language and just use the rest of the URL path.
I am able to do this when I initialize the ManagerModel with a the desired path like this:
const path = `/path/to/<my_model>.model.json`
/* initialize the ModelManager with the path to the model.json */
ModelManager.initialize({ path })
/*
grab the desired section of the model and render by calling ReactDOM.render
By doing this I am able to render the section of the model that maps /us/en/user-account` for
example, and render the correct content even though the current browser path is `/`
*/
ModelManager.getData(`/us/en/<page_to_load>`).then(render)
When I handle navigation with history.push (I use react-router), I want to be able to render another page following the same logic. By default, having executed ModelManager.getData("/us/en/<page_to_load>"), every page that I navigate to then renders that same portion of the model.
To fix this, I have tried many variations of ModelManager.getData() calls with no success. The only thing that I have been able to have any success with is dynamically passing the path to the next page to render to a callback function that is defined on the index.js level and passed down as a prop to App.js. The callback triggers another ReactDOM.render call and loads the page correctly regardless of what the actual URL path is. That code looks something like this:
<App
reRender={(path) => {
/* manipulate the path so that it properly maps to the correct AEM model data */
const updatedPath = `/us/en/${path}`
/*
this works, but causes another ReactDOM.render call every time that the current page is
changed
*/
ModelManager.getData(updatedPath).then(render)
}}
/>
There are also cases where the page that has been navigated to doesn't have a corresponding path in the modelStore mapping. I am able to handle that like this:
const pathToInsert = `/<country>/<language>/${window.location.pathname}.model.json`
ModelManager.modelStore.insertData(pathToInsert)
ModelManager.getData(pathToInsert).then(render)
/*
I have been having issues with this, but can get the newly inserted model to load properly by
re-routing back to the current path from the current path
*/
this.props.history.push(window.location.pathname)
I have read and re-read the documentation here and I am unable to figure out the correct way to do what I want to do. The above solutions work for the most part, but are pretty hacky and I would like to find out the proper way to accomplish this. Any help is greatly appreciated!
Yes, I found a work around solution. The AEM library can't handle this requirement out of the box unfortunately. My solution was to make a wrapper for my App component. In that wrapper I initialize the model manager, clone the data and store it in local stage. Then you can conditionally modify the model and pass it along as a prop to your App component. If you have a good understanding of how AEM model data is mapped you should be able to figure out a way to display what you want. You can also fetch and insert models into your master model's ":children" prop (think that is the field name, have not looked in a while).

admin-on-rest Show component with specified id on custom page

I am using admin-on-rest in my React app and wonder how is possible to make a custom page containing Show (or another detail component) with specified resource Id (for example: I want my app to fetch only resource with id = 1)
I don't know if this is canonical or the intended way to do things but you can supply both basePath and record as props to the ShowButton and EditButton components.
You can use this to redirect your user basically anywhere.
It should be possible (using some switch case or dependent input addon) to also add these props to the ListButton in the Show and Edit view and redirect your user back to the originating ListView.
Here is documentation to supply actions to the List view.
https://marmelab.com/admin-on-rest/List.html#actions

Codenameone refresh list of images without reloading the page

I am Working on an app with requirement where we need to create a dynamic image gallery which refreshes after few minutes.When refresh happens three things should happen without reloading the page
1) Obsolete images should be removed
2) New Images should be added
3) Non Obsolete images should stay (not reload)
4) Images should be stacked next to each other as it will be mostly used on tablet
I was looking at Boxlayout or FlowLayout and I can add the image but I am not sure how to delete it dynamically .I was able to set UUID for the image component but was not able to get component based on UUID to remove it . How can I get component based on its UUID added to the form ?
Is this the correct approach ? or there is already build in component that does that .
I saw this
How to add dynamic data in the renderer created using UI builder?
But I also read using List is discouraged
https://www.codenameone.com/javadoc/com/codename1/ui/list/package-summary.html
This is actually pretty simple to do with a BoxLayout.Y_AXIS Container. When you create an image component to add to the box layout do this:
myImageComponent.putClientProperty("imageId", imageId);
Then when you have the callback to refresh the list just do something like this:
for(Component cmp : parentContainer) {
String id = (String)myImageComponent.getClientProperty("imageId");
if(!existsInNewList(id)) {
cmp.remove();
}
}
When you are done updating the container just call animateLayout(200) or revalidate() to refresh the UI.

AngularJS different child components vs child routes

Newbie using AngularJS 1.5 with ES6, a component-based design and the new Component Router.
Let's consider a simple Survey application. Any given Survey has an instructions page and a list of Questions.
When the end user accesses a given Survey, he must be shown the instructions page. He must then navigate through the different Questions, which may span several pages. Upon reaching the last Question page, he must be shown a summary page with all his answers.
Ideally, I would like to have the following route mappings:
/surveys/:id : alias to /surveys/:id/instructions
/surveys/:id/instructions : displays the survey instructions
/surveys/:id/entry : displays the survey for entry by the end user
/surveys/:id/summary : display a summary of all survey answers entered by the end user
Ideally, the SurveyComponent (mapped to /surveys/:id) would load the Survey on route activation ($onRouterActivate method) and expose the retrieved Survey to its child components through its <ng-outlet/> template tag. The child components, mapped to the correct sub-routes, would then bind to this Survey and render the appropriate display.
I have been unable to achieve this, as it seems that <ng-outlet/> cannot be used to send data to child components.
Is my approach correct and am I missing something? Or is the route/sub-route mechanism inappropriate for this?

how to load or use a Model in a Component File especially in Cakephp 2.0?

I need to load a Model in a component to save the Data of that related model in that component itself.
My Component name is ImagemuploadComponent
In this Component I want to load the Attachement Model.
I've tried the following code but it did't worked. (Which works fine in cakephp 1.3)
$Attachment = ClassRegistry::init('Attachment');
$this->loadModel('Attachment');
Try this code. This will import your model and instantiate.
After this you can perform any operation as controller.
App::import('Model','Attachment');
$this->Attachment = &new Attachment();
$this->Attachment->save($your_data);

Resources