Preview Of Gallery in Django Admin - ajax

I want to display the selected gallery in my admin. I'm not very capable of writing custom fields and couldn't find any well documented guidelines about it.
As for my question, I've written basic classes such as:
class GalleryViewWidget(forms.TextInput):
def render(self,name,value,attrs):
rendered = super(GalleryViewWidget, self).render(name, value, attrs)
return rendered + mark_safe(....)
class ProductModelForm(forms.ModelForm):
information = forms.CharField(widget=forms.Textarea)
gallery = GalleryViewWidget
class Media:
css = {
'all': (settings.MEDIA_URL + 'css/preview.css',)
}
js=(
"http://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js",
settings.MEDIA_URL + 'js/preview.js',
)
class Meta:
model = Product
In my preview.js file, I want to send an ajax request, the problem is I don't know where to handle this ajax call. In my ProductModelForm ?
I'd really appreciate that if anyone gives me any knowledge about how to handle this ajax thing or another way to display selected gallery in my admin ?

Here I saw a bit outdated tutorial about it...
It creates your own thumbnails. You need to use "sorl-thumbnail" now-days for thumbnails generation and storing it's a bit easier and more right way IMHO...
Nevertheless it's a tutorial of how to build a photo previews in admin. You could use it or enhance it with AJAX calls. But IMHO again it's not necessary...
P.S. It's better to download full sources of this app from the start.
so... article:
Django Tutorial: Photo Organizer and Sharing App Part I. Enhancing Admin.

Your widget (with extra feature of handling postfix in file name) might look like this:
class ImageThumbnailWidget(forms.FileInput):
def __init__(self, postfix=None, attrs={}):
self.postfix = postfix
super(ImageThumbnailWidget, self).__init__(attrs)
def render(self, name, value, attrs=None):
output = []
if value and hasattr(value, "url"):
img_path = value.url.split('/')[:-1]
img_path = "/".join(img_path)
img_name = value.url.split('/')[-1]
if self.postfix:
name_parts = img_name.split(".")
ext = name_parts.pop()
img_name_start = "_".join(name_parts)
img_name = "%s%s.%s" % (img_name_start, self.postfix, ext)
output.append('%s<br/><img src="%s/%s" /> <br />%s ' %
(_('Currently:'), img_path, img_name, _('Change:')))
output.append(super(ImageThumbnailWidget, self).render(name, value, attrs))
return mark_safe(u''.join(output))
Hope it helps. If it doesn't fit your needs, write some more details and I'll try to figure out something (I'd like to know where exactly do you want to show the preview of gallery - is it "changelist" or "change_view" of product, where you have inline formset with images).

I'll answer the Where do I put my Admin/ModelForm Ajax views? part of your question, for the gallery part maybe have a look at photologue.
As for creating views which are called from the admin forms, I found creating simple custom views the easiest. In your Javascript code you just call {% url my_ajax_view %} with data specific to your app.
For example (a modified version of ajaxy foreignkey search):
class ForeignKeySearchInput(forms.HiddenInput):
"""
A Widget for displaying ForeignKeys in an autocomplete search input
instead in a ``select`` box.
"""
[ ... stuff removed ... ]
def render(self, name, value, attrs=None):
[ ... snip ... ]
context = Context({
'search_url': reverse('tools_autocomplete_search'),
'model_name': self.rel.to._meta.module_name,
'app_label': self.rel.to._meta.app_label,
[ ... snip ... ]
})
template = loader.get_template('admin/widgets/foreignkeysearchinput.html')
return rendered + mark_safe(template.render(context))
The key here is to hand the required data to the widget template, which then uses this data to call the ajax callback correctly.
The actual view then is as simple (or complicated) as your problem.
def ajax_search(request):
"""
Searches in the fields of the given related model and returns the
result as a simple string to be used by the jQuery Autocomplete plugin
"""
query = request.GET.get('q', None)
app_label = request.GET.get('app_label', None)
model_name = request.GET.get('model_name', None)
search_fields = request.GET.get('search_fields', None)
[ ... snip ... ]
return HttpResponse(simplejson.dumps(data, indent=2))
Alternatively you can embedd ajax view into a ModelAdmin subclass (or a Mixin), but if you don't want to muck about with the internals of django.contrib.admin routing the above is way easier.

Related

Coveo card view on mobile with default template

I've been asked to disable the card view for a search result page. I know nothing about Coveo, but i have determined that for mobile the list view is prefered over card view. This is being implemented in a AEM 6.3 template.
In CoveoJsSearch.js, starting at line 17728 I have the following:
if (merged.responsiveComponents.isSmallScreenWidth()) {
templates = _.filter(templates, function (tmpl) { return tmpl.layout == 'card'; });
merged.currentLayout = 'list';
this.layout = 'list';
}
I changed these to list from card and it does what i want, but this looks JS file looks like it's 'compiled' from the front end framework. So what is the best way to ensure mobile users see a list view rather than a card view for search results.

Dynamically adding custom elements to DOM Aurelia [duplicate]

It seems Aurelia is not aware when I create and append an element in javascript and set a custom attribute (unless I am doing something wrong). For example,
const e = document.createElement('div');
e.setAttribute('custom-attr', 'some value');
body.appendChild(e);
Is there a way to make Aurelia aware of this custom attribute when it gets appended?
A little background: I am creating an app where the user can select their element type (e.g. input, select, checkbox etc.) and drag it around (the dragging is done in the custom attribute). I thought about creating a wrapper <div custom-attr repeat.for="e of elements"></div> and somehow render the elements array, but this seemed inefficient since the repeater will go through all the elements everytime I push a new one and I didn't not want to create a wrapper around something as simple as a text input that might be created.
You would have to manually trigger the Aurelia's enhance method for it to register the custom attributes or anything Aurelia related really. And you also have to pass in a ViewResources object containing the custom attribute.
Since this isn't as straight forward as you might think, I'll explain it a bit.
The enhance method requires the following parameters for this scenario:
Your HTML as plain text (string)
The binding context (in our scenario, it's just this)
A ViewResources object that has the required custom attribute
One way to get access to the ViewResources object that meets our requirements, is to require the custom attribute into your parent view and then use the parent view's ViewResources. To do that, require the view inside the parent view's HTML and then implement the created(owningView, thisView) callback in the controller. When it's fired, thisView will have a resources property, which is a ViewResources object that contains the require-d custom attribute.
Since I am HORRIBLE at explaining, please look into the example provided below.
Here is an example how to:
app.js
import { TemplatingEngine } from 'aurelia-framework';
export class App {
static inject = [TemplatingEngine];
message = 'Hello World!';
constructor(templatingEngine, viewResources) {
this._templatingEngine = templatingEngine;
}
created(owningView, thisView) {
this._viewResources = thisView.resources;
}
bind() {
this.createEnhanceAppend();
}
createEnhanceAppend() {
const span = document.createElement('span');
span.innerHTML = "<h5 example.bind=\"message\"></h5>";
this._templatingEngine.enhance({ element: span, bindingContext: this, resources: this._viewResources });
this.view.appendChild(span);
}
}
app.html
<template>
<require from="./example-custom-attribute"></require>
<div ref="view"></div>
</template>
Gist.run:
https://gist.run/?id=7b80d2498ed17bcb88f17b17c6f73fb9
Additional resources
Dwayne Charrington has written an excellent tutorial on this topic:
https://ilikekillnerds.com/2016/01/enhancing-at-will-using-aurelias-templating-engine-enhance-api/

Loading via AJAX a facebook Like button that uses Open Graph tags [duplicate]

I use ajax to render a content page with a Facebook Like Button plugin in it.
The problem is that when the user clics Like, Facebook will extract meta info but I don't know how to assign the meta with ajax.
I tried using append to head int FB.init but it seems to not work and the update isn't reflected when users like the page on Facebook
$('head').append("<meta property="og:title" content="The Rock"/>');
The problem is that facebook like will extract meta info but I don't know how to assign the meta with ajax.
I tried use append to head int FB.init but it seems not work.
Of course this does not work, because Facebook’s scraper requests your URLs from your server – and does not care about what the DOM might currently look like in any user’s browser.
You can not add Open Graph meta data client-side.
Actually you can use such script:
/// Append Meta tags
function setMT(metaName, name, value) {
var t = 'meta['+metaName+'='+name+']';
var mt = $(t);
if (mt.length === 0) {
t = '<meta '+metaName+'="'+name+'" />';
mt = $(t).appendTo('head');
}
mt.attr('content', value);
}
and call this function from body:
setMT('property', 'og:title', 'Title for Facebook');
I have similar on the News Site at http://www.livepage.info
be careful with using '.append()'.
According to the JQuery Docs, this method has a move effect (reads from source, copies to destination and removes the source).
A theoretical way is something like this:
headObj = $("head");
keywordObj = $(headObj).find("meta[name='keywords']");
newKeywords = $(keywordObj).attr("content");
newKeywords += myKeywords;
$(keywordObj).attr("content", newKeywords);
Download and install the plugin FireBug for browser FireFox, so you can check the changes at runtime.

Grails Webflow display image - flow scope

I have saved an image to a byte[] in a command object and wish to display it in the next stage of the createFlow webflow.
I am trying to steer clear of using the file system and / or database system for storing the image during the webflow.
Typically, to view the image, I would call renderImage from the gsp:
class ArtefactController {
def createFlow = {
............
}
def renderImage = {
def ArtefactInstance = Artefact.findById(params.id)
if(ArtefactInstance?.image) {
response.setContentLength(ArtefactInstance.image.length)
response.outputStream.write(ArtefactInstance.image)
}
else {
response.sendError(404)
}
}
however for the webflow when I try to call renderFlowImage from a gsp:
def renderFlowImage = {
if(flow.artefactCommand?.image) {
response.setContentLength(flow.artefactCommand.image.length)
response.outputStream.write(flow.artefactCommand.image)
}
else {
response.sendError(404)
}
}
The flow scope in not accessable.
Any suggestions?
I assume you're hitting the renderFlowImage action in your flow's view with a second http request via an img tag such as:
<img src="${createLink(action:'renderFlowImage')}" />
This won't work because, for one thing, you need to pass in a 'flowExecutionKey' which grails uses to match a request with a flow.
It is possible to work around this, but you might be better off just rendering the image during the rendering phase of your flow's next view with a data URI in the img tag. You could do this easily with the rendering plugin, which provides some handy tags (among other things) to render inline images using a data uri.
So in the next flow view, you could replace the existing img tag with a call to one of the rendering plugin's 'inline' tags:
<rendering:inlineJpeg bytes="${artefactCommand.image}" />
But be careful - there are some limitations to the data URI approach to be aware of (see http://www.7cynics.com/webdesign/css/css-inline-images-data-uri.html).

How to change WebGrid action for getting data (.NET MVC3)

I have a Partial View that renders WebGrid. My controller looks like
public ActionResult Index()
{
return View();
}
public ActionResult GetUserList(int? page, string sort, string sortdir)
{
var model = UserModel.getList(page,sort,sortdir);
return PartialView("_UserList",model);
}
Index.cshtml :
....
#Html.Action("GetUserList")
The problem is that every time I click on grid navigation or sort links it calls Index method. How can I make Webgrid to execute a different action (GetUserList in this case)? I'm sure I can prepend GetUserList to all links in grid using jquery, but I believe it should be a better way.
It's also possible that what I'm doing is completely wrong, so thanks for your suggestions.
After lot of monkeying around and digging (and even fiddling with Reflector with WebGrid's source code), I came to the conclusion that with WebGrid, you cannot control/change the Header link action.
To create the header link URL, the path is taken from HttpContext.Request.Path, so there is no way to customize it to point to a different route.
One very ugly hack would be to tap into to jQuery Ajax's events (since the header link uses jQuery.load to sort) and overwrite the URL:
Album Id
Better solution would be to use:
Telerik Grid which lets you specify custom routes and also offers much more flexibility in rendering your layout
or MvcContrib Grid (not sure if this lets you modify header links but definitely offers more flexibility than WebGrid)
#MrChief had the idea above about the ugly hack...I put that together. Here is the main code that I used to do this. It does, indeed, hijack the ajax call before it is put on the wire. The key is to modify the URL that is getting sent because the grid will grab that URL from HttpContext.Request.Path. and plug it into the onclick for the anchor element.
I put this into my main common.js and will simply attach a function to capture the ajaxSend event which happens just before the data is sent.
// Used to hijack the sending of all AJAX calls. Before it sends the call to the server, it checks to see if the
// active element (the element that prompted the call) is marked with a given class. If so, then it will perform
// the given operation.
$(document).ajaxSend(function (event, jqXHR, ajaxOptions) {
var activeElement = document.activeElement;
if ($(activeElement).attr('redosorturl') != null) {
// If this is a sort anchor link from a grid that needs to have the sort link redone, do it here.
// the code is in the eipGrip.js file.
if ($(activeElement).attr('redosorturl').toString() == 'redoSortURL') {
var newURL = RedoGridSortURL(activeElement, ajaxOptions.url.toString());
ajaxOptions.url = newURL.toString();
}
}
return false;
});
When rendering the page, I have marked the tag in column header that contains the incorrect URL with a class named "redosorturl', so I know when I hijack the ajax call, the operation has to be done on this element. I then call a custom function that gives me the correct URL, then the ajaxOptions.url is then rewritten with that new URL.
I have to pass the activeElement to that rewrite function so I can traverse up the DOM to get the grid information, where I have put data like the controller and action method that is used along with and IDs and other info that I use for the URL. Likewise, I pass in the current url string because the grid will inject a token at the end of the url that I parse off and put on the new url.
Your conclusion isn't right. You just need to wrap your webgrid in a Get form:
using (Html.BeginForm("GetUserList", "ThingaMaBob", System.Web.Mvc.FormMethod.Get))
{
var grid = new WebGrid(
...
));
Html.Hidden(grid.SortFieldName, grid.SortColumn);
Html.Hidden(grid.SortDirectionFieldName, grid.SortDirection == SortDirection.Ascending ? "ASC" : "DESC");
}
The hiddens are so that the sort dir and sort field end up in parseable form in the querystring. You end up with urls like localhost/ThingaMaBob/GetUserList?someotherfields=whatever=&sort=city&sortdir=ASC
If you remove [HttpPost] attribute and let the route come to the same function. you'll find the Request["page"] value in your method. this will allow you to put a check on Request["Page"] value.

Resources