Rails 6: how use send_data to display a binary image and apply styles - ruby-on-rails-6

I'm getting an image file as binary data in a controller action:
def show
#result = QrcodeService.call(id)
send_data #result.body, type: 'image/png', disposition: 'inline'
end
The QrCodeService is just calling an external API to get an image using httparty gem.
The corresponding view template is defines as follows:
<h2>Scan Me</h2>
<%= image_tag #result %>
The problem is that the fetched images is displayed bu there are no styles, - the background is just black:
<body style="margin: 0px; background: #0e0e0e; height: 100%" data-new-gr-c-s-check-loaded="14.991.0" data-gr-ext-installed=""><img style="-webkit-user-select: none;margin: auto;" src="http://localhost:3000/qrcode"></body>
Even if I remove the call to <%= image_tag #result %> the <h2>Scan Me</h2> is not displayed either. What's wrong with the use of send_data?
It seems like the method replace completely the body content? I have nothing at all on the page, just the image...
More of that, when looking at the logs in the console, there the same requests hit twice to get the image:
Started GET "/qrcode" for 127.0.0.1 at 2021-01-12 10:31:33 +0100
Processing by QrcodeController#show as HTML
Rendering text template
Rendered text template (Duration: 0.0ms | Allocations: 1)
Sent data (0.5ms)
Completed 200 OK in 374ms (Views: 0.3ms | ActiveRecord: 0.0ms | Allocations: 2116)
Started GET "/qrcode" for 127.0.0.1 at 2021-01-12 10:31:33 +0100
Processing by QrcodeController#show as HTML
Rendering text template
Rendered text template (Duration: 0.0ms | Allocations: 1)
Sent data (1.0ms)
Completed 200 OK in 319ms (Views: 0.8ms | ActiveRecord: 0.0ms | Allocations: 2126)
and when you try to navigate back in the browser with its navigation buttons, the URL in the URI field changes but not the page content.
How is it possible to display an image fetched as binary data and keep/apply the CSS styles?

I was inspired by the answer provided by Anna and replace the use of send_data in the controller with the instance variable as follows:
def show
#result = QrcodeService.call(server_id)
end
Then create a helper method in ApplicationHelper class:
def qrcode_preview(blob)
if blob.code == 200
('<img src="data:image/jpg;base64,%s">' % Base64.encode64(blob)).html_safe
else
'<b>Image not yet ready</b>'
end
end
Sure, it can be modified as you want (styles, text, etc.).
And, finally use the helper method in the show view template as follows:
<h2>Your QR Code to sign in</h2>
<%= qrcode_preview(#result) %>
Voilà.

Related

How can I render contentful images dynamically in my gatsby portfolio website

I have been struggling to render images from contentful in my gatsby site. I have used gatsby-plugin-image to render the image from contentful. I cannot dynamically render the images. please help me.
I import
import { GatsbyImage, getImage } from "gatsby-plugin-image"
my graphQL query is.
const data = useStaticQuery(graphql`
query {
allContentfulBooks(sort: { bookTitle: ASC }) {
edges {
node {
bookTitle
author
date(formatString: "MMMM Do, YYYY")
type
bookCover {
gatsbyImageData(
width: 300
placeholder: NONE
quality: 75
layout: CONSTRAINED
)
}
slug
summary
}
}
}
contentfulBookHeading {
heading
mainText {
raw
}
}
}
`)
I try to render the book Cover here
<ol>
{data.allContentfulBooks.edges.map((edge) => {
const image = getImage(edge.node.bookCover.gatsbyImageData)
return (
<li className={bookStyles.books}>
<div className={bookStyles.bookThumbnail}>
<div className={bookStyles.bookCover}>
<GatsbyImage src={image} alt="Book Cover" />
</div>
<div>
<Link to={`/book/${edge.node.slug}`}>
<h3 className={bookStyles.title}>{edge.node.bookTitle}</h3>
</Link>
<h5 className={bookStyles.author}>
Author: {edge.node.author}
</h5>
<h6 className={bookStyles.type}>Type: {edge.node.type}</h6>
<p className={bookStyles.date}>Date Read: {edge.node.date}</p>
<p className={bookStyles.summary}> {edge.node.summary}</p>
<Link to={`/book/${edge.node.slug}`}>
<p className={bookStyles.fullnotes}>Read full book notes</p>
</Link>
</div>
</div>
<hr />
</li>
)
})}
</ol>
I tried to render the book cover image from contentful. but it's not working
My first thought when reading your ask was that you are looking to load data dynamically, as in at load time, my original answer would work for that. After rereading your question, I believe you are not having difficulty loading data dynamically at load time but, rather you are not seeing the images display using the useStaticQuery and loading the data at build time.
Steps to ensure you should see the media:
open http://localhost:8000/___graphql in a browser and run your query, ensure you see the media you are expecting.
If you do not see the media:
First, check Contentful and ensure that the media is published.
Next, try stopping your app and restarting it, ctrl c should cancel the running command, then start the site back up. This will ensure that you content is pulled in and ready to render.
If you see the media in graphical, my next suggestion is to debug your site and console.log the data. Try putting in console logs
console.log("gatsbyImageData", edge.node.bookCover.gatsbyImageData)
const image = getImage(edge.node.bookCover.gatsbyImageData)
console.log("image", image)
next open up the developer menu in your browser and click on console
reload the page and verify the output of the console logs. This should help you ensure that data is present.
If you are having a dynamic rendering issue the answer is, useStaticQuery, grabs all of your Contentful data at build time. This is by design and is part of why Gatsby is so fast. You will need to use something like Apollo in order to grab dynamic data in Gatsby. Check out this article that provides more details and a possible solution.

In-Page href in QtWebView fails to display anything

So, to try to boil this down to the simple bits.
I have a href entries like this:
<a href="#section_8">
linktext
</a>
These In-Page links work if I load the page via Qts setContent, however, as my content often exceeds 2MB, I have to use the urlhandler methods. So, quick overview, looks something like this:
scheme.SchemeHandler.register(lambda: webpage, "reportstext")
self.loader = QWebEngineView()
self.loader.setUrl(QUrl("conapp://reportstext"))
SchemeHandler keeps a dictionary of application internal content and it keeps it all behind conapp://<contentname>
Now, this works. I can serve web content to the WebView. That is, until I try to use in-page links as mentioned at the start. What I get then is simply a blank page, no debug messages, no crash, no anything. Just blank. There is also no request going out to the schemehandler.
How do I get these to work?
If I export the content to a .html file and open it in a browser it works as expected, so there's something on the Qt end of things not working as I expect it to.
Complete Example, needs PyQt5:
from PyQt5.QtWidgets import *
from PyQt5.QtCore import QSize, QUrl
from PyQt5.QtWebEngineWidgets import QWebEngineView
from PyQt5.QtCore import QBuffer, QIODevice
from PyQt5.QtWebEngineCore import (QWebEngineUrlSchemeHandler,
QWebEngineUrlRequestJob)
from PyQt5.QtWebEngineWidgets import QWebEngineProfile
import sys
app = QApplication(sys.argv)
class SchemeHandler(QWebEngineUrlSchemeHandler):
def __init__(self):
super(SchemeHandler, self).__init__()
QWebEngineProfile.defaultProfile().installUrlSchemeHandler(b'conapp', self)
self._handlers = {}
def requestStarted(self, job):
url = job.requestUrl()
print("Got request for {}".format(url.toDisplayString()))
request = url.toString().split("//")[1]
buf = QBuffer(parent=self)
buf.open(QIODevice.WriteOnly)
buf.write(self._handlers[request]().encode("utf-8"))
buf.seek(0)
buf.close()
job.reply("text/html".encode("ascii"), buf)
def register(self, contentgenerator, contentname):
self._handlers[contentname] = contentgenerator
return contentname
SchemeHandler = SchemeHandler()
class ReportMenu(QMainWindow):
def __init__(self):
QMainWindow.__init__(self)
self.setMinimumSize(QSize(800, 600))
self.late_init()
def late_init(self):
def webpage():
return """<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title>
ConParser Output
</title>
<meta content="width=device-width, initial-scale=1" name="viewport"/>
</head>
<body>
<div class="navbar">
<a href="#section_0">
MVZ Arztfallzähler
</a>
</div>
</body>
</html>"""
SchemeHandler.register(webpage, "reportstext")
self.loader = QWebEngineView()
self.loader.setUrl(QUrl("conapp://reportstext"))
self.report_loader = QWebEngineView()
self.gridLayout = QGridLayout(self)
self.gridLayout.addWidget(self.loader, 0, 0)
centralWidget = QWidget(self)
self.setCentralWidget(centralWidget)
centralWidget.setLayout(self.gridLayout)
self.showMaximized()
self.loader.loadFinished.connect(self.load_completed)
def load_completed(self, *args):
if not args[0]:
print("Load failed:", args)
menu = ReportMenu()
ret_code = app.exec_()
sys.exit(ret_code)
If you now take this and switch out
self.loader.setUrl(QUrl("conapp://reportstext"))
for
self.loader.setHtml(webpage())
This will give the intended effect of in-page urls working, however, this has 2 MB content limit, that I frequently exceed.
This seems to be caused by a problem with how the base-url is handled by the underlying Chrome engine. The bookmark anchor in your example is relative, but if you make it absolute, like this:
<a href="conapp://reportstext#section_0">
MVZ Arztfallzähler
</a>
<p style="margin-top: 1500px">
<a name="section_0">HELLO WORLD</a>
</p>
your example will work as expected. In light of this, I was hoping it would be possible to get relative bookmarks to work automatically by adding a <base> tag like this:
<head>
<base href="conapp://reportstext">
</head>
But sadly it seems Chrome's implementation of the <base> tag is broken. It can be partially worked around by adding a trailing slash to the href:
<base href="conapp://reportstext/">
Then your custom scheme-handler will get requests for all relative urls on the page. But an unwanted side-effect of this is that Chrome then interprets the hrefs of relative bookmark anchors like this: conapp://reportstext/#section_0. So instead of navigating to the bookmark, your scheme-handler will get a request for an invalid url.
AFAICS, there doesn't seem to be any other way of intercepting navigation requests for bookmark anchors. I tried the QWebEngineUrlRequestInterceptor class and reimplementing QWebEnginePage.acceptNavigationRequest(), but as with custom scheme-handlers, they just don't get called for bookmark anchors. It seems all the handling for that happens within Chrome.
A somewhat hackish way to way to work around the above issues would be to run some javascript on page load which simply rewrites all the bookmark anchors so they have absolute hrefs.
Here is a basic implementation that works with your example:
class ReportMenu(QMainWindow):
...
def load_completed(self, ok):
if ok:
page = self.loader.page()
page.runJavaScript("""
url = window.location.href
links = document.querySelectorAll('a')
for (index = 0; index < links.length; ++index) {
link = links[index]
href = link.getAttribute('href')
if (href && href[0] == '#') {
link.href = url + href
}
}
""")

How to update style when a window is scaled in angular

I have a chat window on my app and I want to update the size of an image in this chat window when the chat window is less than a certain width. Is there a way I can update the css style or class based on the width?
I'm using typescript and have the value of my cat window passed in:
#Input()
public chatWidth: number;
In my html, I was attempting to do something like this where I would apply a css class if the chatWidth property was less than 400:
<img *ngIf="upsell?.image?.url; let url" [src]="url" ng-class="{bigger-img : chatWidth < 400}">
However, this doesn't work and I don't seem to even get an error in my console
Use
<img *ngIf="upsell?.image?.url; let url" [src]="url" [ngClass]="{'bigger-img': chatWidth < 400}">
More info here on ngClass.
UPDATE
You can, I believe, wrap the condition in a 'method' that returns a boolean defined in your respective component and call it in the template instead of directly declaring it in the template. Here, in your case,
in component.ts,
checkChatWidth(): boolean {
return this.chatWidth < 400;
}
then, in your template,
<img *ngIf="upsell?.image?.url; let url" [src]="url" [ngClass]="{'bigger-img': checkChatWidth()}">
You have to take care of the possible 'null' checks within your 'method' that may arise due to not having a value for the 'chatWidth' input property based on your code setup.

How to change a link_to hover image in Rails 4 with an asset pipeline

Note: This explanation is targeted for beginners.
I was facing the problem of not being able to change the image in a link_to when I hovered over the image. Nothing I did in CSS seemed to solve the problem. I present my solution to this problem below.
This is my link_to:
<%= link_to image_tag('1487789814_back.png'),
{controller: #controller_action_hash_from_params[:controller],
action: #controller_action_hash_from_params[:action]},
sql_text: #sql_text,
:id => "ralph-back-button-black-img", :class => "ralph-fieldset-back-button" %>
1487789814_back.png is an ordinary png in app/assets/images. It happens to be a black arrow pointing left. Similarly, app/assets/images/1487789814_back_red.png (used below) is a red arrow pointing left. I wanted the red arrow to appear when I hovered over a fieldset containing the black arrow.
The source of the problem:
The link_to above generates/generated the following HTML:
<img src="https://localhost:3000/assets/1487789814_back-731afaae70e04062b25988079e4ef8ab4e6b523f4ac11ff7d142a9ff01e63455.png" alt="1487789814 back">
Note:
The file https://localhost:3000/assets/1487789814_back-731afa...3455.png" will not exist for you. It's a filename automatically generated by Rails
Analysis:
The problem comes, in part, because (I believe) css cannot override HTML. That is, if you specify an attribute (e.g. width) in your HTML, you cannot override that in css. Similarly, (I believe) you cannot override the src="https: ..." in css. Any attempt (I believe) to use :hover will fail. To make this work, you'll need a bigger hammer: javascript or jquery.
My solution:
Please note: I am not claiming this solution is the best solution or even a good solution. I am claiming it works in my Rails 4 environment.
If you are using the asset pipeline in Rails 4 (Rails 3? Rails 5?), your "friendly" image names (in my case 1487789814_back.png and 1487789814_back_red.png) will be converted by the asset pipeline system into names with a cryptographic hash appended. In other words, to get access to the red arrow image you need to know what name Rails assigned to your image. It is beyond the cope of this little article to explain why Rails renames your image file; just know that it does. (Of course, your original file will still be there with its original name.)
So somehow we must "map" the friendly name (e.g. "1487789814_back.png") to the name in the asset pipeline.
I accomplished this by creating a div with "display:none" css.
erb:
<%# This div has display:none.
We do this in order to preload the images_path as well as create a "map"
between the friendly name and the asset pipeline name
%>
<div class='ralph_preload'>
<%= image_tag('1487789814_back.png', options={class:'ralph_preload_class', id:'1487789814_back'}) %>
<%= image_tag('1487789814_back_red.png', options={class:'ralph_preload_class', id:'1487789814_back_red'}) %>
</div>
The erb above generated the following HTML:
<div class="ralph_preload">
<img class="ralph_preload_class" id="1487789814_back" src="/assets/1487789814_back-731afaae70e04062b25988079e4ef8ab4e6b523f4ac11ff7d142a9ff01e63455.png" alt="1487789814 back">
<img class="ralph_preload_class" id="1487789814_back_red" src="/assets/1487789814_back_red-bbd0f2e34f3401cc46d2e4e1e853d472d8c01c9a75f96d78032738bd01b2133b.png" alt="1487789814 back red">
</div>
The associated css is:
.ralph_preload {
display: none;
}
I created some jQuery to change the black arrow to red when the user hovers:
<script type="text/javascript">
function get_precache_myStruct_array(outer_div_class){
var myStruct_array = [];
$('.'+outer_div_class).find(".ralph_preload_class").each(function(){
var myStruct = {
id: this.id,
src: this.src
};
// myStruct_array.push(myStruct);
myStruct_array[this.id] = myStruct;
});
return myStruct_array;
}
// See http://stackoverflow.com/questions/15352803/how-to-check-if-an-image-was-cached-in-js
function is_cached(img_url){
var imgEle = document.createElement("img");
imgEle.src = img_url;
return imgEle.complete || (imgEle.width+imgEle.height) > 0;
}
$(document).ready(function(){
var precache_myStruct_array = get_precache_myStruct_array("ralph_preload");
$.each(precache_myStruct_array,
function (index, value) {
if (!is_cached(value.src)) {
alert("Not cached!: " + value.src);
};
});
<% if true %>
$('#ralph-back-button-filedset').hover(function(){
var precache_myStruct_array = get_precache_myStruct_array("ralph_preload");
var imageID = '1487789814_back_red';
$('#ralph-back-button-black-img > img:nth-child(1)').attr("src", precache_myStruct_array[imageID].src);
}, function(){
var precache_myStruct_array = get_precache_myStruct_array("ralph_preload");
var imageID = '1487789814_back';
$('#ralph-back-button-black-img > img:nth-child(1)').attr("src", precache_myStruct_array[imageID].src);
});
<% end %>
});
</script>
You might notice a "<% if true %>" / <% end %> block in the jQuery. Since the jQuery is "inlined" into the .erb file, the jQuery goes through erb processing. By changing the true to false, it can make using browser debuggers easier to use.
Conclusion:
I did a search on the web to find the answer to my problem. I found none that is, as the lawyers say, on point.
I am, of course, enthusiastically open to corrections and comments.
If you put those images in your public folder, they will not get the extra hash added to the image name.
With that said, I ran a loop in my controller that got me all the "Rails" file names for my images and stored them somewhere I could access them easily.

Adding a "news item" style thumbnail image to a Dexterity content type

I'm working on a listing page for a custom Dexterity content type, and I'd like to pull one of the images to display in the listing, kind of how a listing page for a folder full of news items shows the thumbnails alongside the title and description (i.e. folder_summary_view).
What's the best way to go about this? I tried customizing the folder_summary_view template to change the thumbnail code to look instead for 'item_object/thumbnail' however it returns an error because it's a blob image or something:
<a href="#" tal:condition="exists:item_object/thumbnail" tal:attributes="href python:test(item_type in use_view_action, item_url+'/view', item_url)">
<img src="" alt="" tal:replace="structure python: path('nocall:item_object/thumbnail')(scale='original', css_class='tileImage')" />
</a>
Returns:
Module Products.PageTemplates.ZRPythonExpr, line 48, in __call__
__traceback_info__: path('nocall:item_object/thumbnail')(scale='original', css_class='tileImage')
Module PythonExpr, line 1, in <expression>
TypeError: 'NamedBlobImage' object is not callable
I guess I'd also be interested in finding out how to call any of the other fields. It pulls the title and description automatically. Is there an easy way to call moar fields? i.e. if there's a text field called: developer_name — how would I display that after Title in the folder summary view?
I saw this question in case that helps, but it seemed to be more related to migration and not displaying content.
The folder_summary_view still needs to be updated for Plone's default news item, like this (works with both; Plone's default Archetype- and Dexterity-newsitem):
<a href="#"
tal:condition="exists:item_object/##images/image"
tal:attributes="href python:test(item_type in use_view_action, item_url+'/view', item_url)">
<img src="" alt=""
tal:replace="structure item_object/##images/image/mini" />
</a>
Where 'image' is the fieldname, in case yours differs.
See plone.app.imaging's README for complete reference.
I hope to find some time soon, to commit this, unless someone else is heading to do it ;)
Note: I think I remember it's not recommended to use python:test() though, maybe that part should be adjusted, too.
For generally rendering your field, you can use good ol':
<div tal:content="structure context/developer_name" />
Tested with a TTW-created Dexterity-CT and a text-field.
See also (with a grokked example):
http://developer.plone.org/reference_manuals/external/plone.app.dexterity/custom-views.html#simple-views
if you look closely to the folder_summary_view template you will find that all you need to do is adding some helper methods to you class:
<a href="#"
tal:condition="exists:item_object/image_thumb"
tal:attributes="href python:test(item_type in use_view_action, item_url+'/view', item_url)">
<img src="" alt=""
tal:replace="structure python: path('nocall:item_object/tag')(scale='thumb', css_class='tileImage')" />
</a>
we did something similar in collective.nitf by creating a getImage, imageCaption, tag and image_thumb functions (you probably won't need them all).
also, note the image attribute that will map the getImage function.
class NITF(Container):
implements(INITF)
...
# The purpose of these methods is to emulate those on News Item
def getImage(self):
"""Return the first Image inside the News Article."""
content_filter = {'portal_type': 'Image'}
images = self.listFolderContents(content_filter)
return images[0] if len(images) > 0 else None
image = getImage # XXX: a hack to support summary_view
def imageCaption(self):
image = self.getImage()
if image is not None:
return image.Description()
def tag(self, **kwargs):
# tag original implementation returns object title in both, alt and
# title attributes
image = self.getImage()
if image is not None:
scales = image.restrictedTraverse('##images')
if 'scale' in kwargs:
scale_id = kwargs.get('scale')
del kwargs['scale']
else:
scale_id = 'thumb'
kwargs['alt'] = image.Description()
kwargs['title'] = image.Title()
scale = scales.scale(fieldname='image', scale=scale_id)
return scale.tag(**kwargs)
def image_thumb(self):
"""Return a thumbnail."""
image = self.getImage()
if image is not None:
view = image.unrestrictedTraverse('##images')
# Return the data
return view.scale(fieldname='image', scale='thumb').data
take a look at that code.

Resources