How to save a html5 canvas.toDataURL as a png file on the server using Seaside - seaside

I have an image that users can annotate on the browser. I can access the image using
canvas.toDataURL()
...I'd like to add a 'save' option for the user to save the image on the server.
This question has been answered for php...
file_put_contents('test.png', base64_decode(substr($data, strpos($data, ",")+1)));
...what I need is a Seaside callback with the PNG file content.
Is there a way to do this in Seaside?
Johan pointed out that the mine type declaration has to be removed from the value string. This works in VW... (with string hack to remove 'data:image/png;base64,')
html jQuery ajax
callback: [:value |
| writestream string |
writestream := ('c:\data\sketchpad_image.png' asFilename withEncoding: #binary) writeStream.
string := value copyFrom: 23 to: value size.
[writestream nextPutAll: (Seaside.GRPlatform current base64Decode: string) asByteArray]
ensure: [writestream close] ]
value: (Javascript.JSStream on: 'sketchpadCanvas.toDataURL()')

Depending on how you want your website to behave, I guess there are multiple ways of doing it. Here is the raw sample of one possibility I can think of using a jQuery ajax callback:
html jQuery ajax
callback: [:value | (FileStream newFileNamed: 'test.png')
nextPutAll: (value copyFrom: ((value indexOf: $,) + 1 to: value size) base64Decoded ]
value: (JSStream on: 'canvas.toDataURL()')
I did not test this myself. Maybe the filestream needs to be sent the #binary message to make a correct png file. Let me know if there are troubles.
Hope it helps.

Does the file-upload section in the Seaside book solve your problem? Taking the code from the book:
UploadForm>>renderContentOn: html
html form multipart; with: [
html fileUpload
callback: [ :value | self receiveFile: value ].
html submitButton: 'Send File' ]
UploadForm>>receiveFile: aFile
| stream |
stream := (FileDirectory default directoryNamed: 'uploads')
assureExistence;
forceNewFileNamed: aFile fileName.
[ stream binary; nextPutAll: aFile rawContents ]
ensure: [ stream close ]
I've also published a blog post about how to manage file uploads in a production environment using Seaside and Nginx that may be of interest.

Related

Specifying parameters in yml file for Quarto

I am creating a quarto book project in RStudio to render an html document.
I need to specify some parameters in the yml file but the qmd file returns
"object 'params' not found". Using knitR.
I use the default yml file where I have added params under the book tag
project:
type: book
book:
title: "Params_TEst"
author: "Jane Doe"
date: "15/07/2022"
params:
pcn: 0.1
chapters:
- index.qmd
- intro.qmd
- summary.qmd
- references.qmd
bibliography: references.bib
format:
html:
theme: cosmo
pdf:
documentclass: scrreprt
editor: visual
and the qmd file looks like this
# Preface {.unnumbered}
This is a Quarto book.
To learn more about Quarto books visit <https://quarto.org/docs/books>.
```{r}
1 + 1
params$pcn
When I render the book, or preview the book in Rstudio the error I receive is:
Quitting from lines 8-10 (index.qmd)
Error in eval(expr, envir, enclos) : object 'params' not found
Calls: .main ... withVisible -> eval_with_user_handlers -> eval -> eval
I have experimented placing the params line in the yml in different places but nothing works so far.
Could anybody help?
For multi-page renders, e.g. quarto books, you need to add the YAML to each page, not in the _quarto.yml file
So in your case, each of the chapters that calls a parameter needs a YAML header, like index.qmd, intro.qmd, and summary.qmd, but perhaps not references.qmd.
The YAML header should look just like it does in a standard Rmd. So for example, your index.qmd would look like this:
---
params:
pcn: 0.1
---
# Preface {.unnumbered}
This is a Quarto book.
To learn more about Quarto books visit <https://quarto.org/docs/books>.
```{r}
1 + 1
params$pcn
But, what if you need to change the parameter and re-render?
Then simply pass new parameters to the quarto_render function
quarto::quarto_render(input = here::here("quarto"), #expecting a dir to render
output_format = "html", #output dir is set in _quarto.yml
cache_refresh = TRUE,
execute_params = list(pcn = 0.2))
For now, this only seems to work if you add the parameters to each individual page front-matter YAML.
If you have a large number of pages and need to keep parameters centralized, a workaround is to run a preprocessing script that replaces the parameters in all pages. To add a preprocessing script, add the key pre-render to your _quarto.yml file. The Quarto website has detailed instructions.
For example, if you have N pages named index<N>.qmd, you could have a placeholder in the YML of each page:
---
title: This is chapter N
yourparamplaceholder
---
Your pre-render script could replace yourparamplaceholder with the desired parameters. Here's an example Python script:
for filename in os.listdir(dir):
if filename.endswith(".qmd"):
with open(filename, "r") as f:
txt = f.read()
f.replace('yourparamplaceholder', 'params:\n\tpcn: 0.1\n\tother:20\n')
with open(filename, "w") as ff:
ff.write(txt)
I agree with you that being able to set parameters centrally would be a good idea.

Google Cloud DLP - CSV inspection

I'm trying to inspect a CSV file and there are no findings being returned (I'm using the EMAIL_ADDRESS info type and the addresses I'm using are coming up with positive hits here: https://cloud.google.com/dlp/demo/#!/). I'm sending the CSV file into inspect_content with a byte_item as follows:
byte_item: {
type: :CSV,
data: File.open('/xxxxx/dlptest.csv', 'r').read
}
In looking at the supported file types, it looks like CSV/TSV files are inspected via Structured Parsing.
For CSV/TSV does that mean one can't just sent in the file, and needs to use the table attribute instead of byte_item as per https://cloud.google.com/dlp/docs/inspecting-structured-text?
What about for XSLX files for example? They're an unspecified file type so I tried with a configuration like so, but it still returned no findings:
byte_item: {
type: :BYTES_TYPE_UNSPECIFIED,
data: File.open('/xxxxx/dlptest.xlsx', 'rb').read
}
I'm able to do inspection and redaction with images and text fine, but having a bit of a problem with other file types. Any ideas/suggestions welcome! Thanks!
Edit: The contents of the CSV in question:
$ cat ~/Downloads/dlptest.csv
dylans#gmail.com,anotehu,steve#example.com
blah blah,anoteuh,
aonteuh,
$ file ~/Downloads/dlptest.csv
~/Downloads/dlptest.csv: ASCII text, with CRLF line terminators
The full request:
parent = "projects/xxxxxxxx/global"
inspect_config = {
info_types: [{name: "EMAIL_ADDRESS"}],
min_likelihood: :POSSIBLE,
limits: { max_findings_per_request: 0 },
include_quote: true
}
request = {
parent: parent,
inspect_config: inspect_config,
item: {
byte_item: {
type: :CSV,
data: File.open('/xxxxx/dlptest.csv', 'r').read
}
}
}
dlp = Google::Cloud::Dlp.dlp_service
response = dlp.inspect_content(request)
The CSV file I was testing with was something I created using Google Sheets and exported as a CSV, however, the file showed locally as a "text/plain; charset=us-ascii". I downloaded a CSV off the internet and it had a mime of "text/csv; charset=utf-8". This is the one that worked. So it looks like my issue was specifically due the file being an incorrect mime type.
xlsx is not yet supported. Coming soon. (Maybe that part of the question should be split out from the CSV debugging issue.)

CKEDITOR4 Mentions Plugin with ajax : javascript error

I have a javascript error using CKEDITOR 4 and the Mentions Plugin.
I can't solve this problem for 2 days, I'm stuck.
I've used the online builder to get CKEDITOR + Mentions plugin.
See my build here: https://ckeditor.com/cke4/builder/fbe187b32ec7c025e28e01a537c72c62
With the following configuration it works fine: I see the drop down list with the names : Anna, Thomas, John
CKEDITOR.config.mentions = [{feed: ['Anna', 'Thomas', 'John']}];
However, when doing an ajax call to get the data, I got a javascript error:
The script /ajax_mention.php
displays
["Anna", "Thomas", "John"]
with the following configuration :
CKEDITOR.config.mentions = [{feed: '/ajax_mention.php'}];
when I type in the editor "#anna", the names do not display
the /ajax_mention.php script is launched and displays the correct data (when I look at the "network" tab on Chrome. see screenshot)
["Anna", "Thomas", "John"]
However, this triggers a javascript error (looking at the Chrome console tab. see screenshot)
ckeditor.js?1645882460:916 Uncaught TypeError: Cannot read properties of null (reading 'addClass')
at g.selectItem (ckeditor.js?1645882460:916:473)
at d.onSelectedItemId (ckeditor.js?1645882460:912:276)
at f.q (ckeditor.js?1645882460:10:246)
at f.fire (ckeditor.js?1645882460:12:91)
at f.select (ckeditor.js?1645882460:920:294)
at f.selectFirst (ckeditor.js?1645882460:920:371)
at d.open (ckeditor.js?1645882460:910:503)
at d.modelChangeListener (ckeditor.js?1645882460:911:234)
at f.q (ckeditor.js?1645882460:10:246)
at f.fire (ckeditor.js?1645882460:12:91)
See screen copy:
https://polyglotclub.com/bug_ckeditor_mentions.jpg
screen copy
The solution was given by the Ckeditor team : see https://github.com/ckeditor/ckeditor4/issues/5107
When we use a hardcoded data in the array, such as ['Anna, 'Geralt'] the createArrayFeed() function changes the input structure from the mentioned above to:
[
{
id: 1,
name: 'Anna'
},
{
id: 2,
name: 'Geralt'
}
]
I've just adjusted data on the backend side to the structure above.

Ruby Hash and Tumblr API

controller page, Json output from api
I'm trying to display posts from the users tumblr account on my view page using ruby. I have never done anything with api's before. I'm trying to use Hash tables. my controller code is as such:
#Posts = client.posts"zombieprocess1.tumblr.com"
on my view page using html I have
<%=Posts%>
the response is such
{"blog"=>{"title"=>"Untitled", "name"=>"zombieprocess1", "total_posts"=>1, "posts"=>1, "url"=>"URL", "updated"=>1478191052, "description"=>"", "is_nsfw"=>false, "ask"=>false, "ask_page_title"=>"Ask me anything", "ask_anon"=>false, "followed"=>false, "can_send_fan_mail"=>true, "is_blocked_from_primary"=>false, "share_likes"=>true, "likes"=>1, "twitter_enabled"=>false, "twitter_send"=>false, "facebook_opengraph_enabled"=>"N", "tweet"=>"N", "facebook"=>"N", "followers"=>0, "primary"=>true, "admin"=>true, "messages"=>0, "queue"=>0, "drafts"=>0, "type"=>"public", "reply_conditions"=>3, "subscribed"=>false, "can_subscribe"=>false}, "posts"=>[{"blog_name"=>"zombieprocess1", "id"=>152689921093, "post_url"=>"URL", "slug"=>"", "type"=>"photo", "date"=>"2016-11-03 16:37:32 GMT", "timestamp"=>1478191052, "state"=>"published", "format"=>"html", "reblog_key"=>"NCDqGTzW", "tags"=>[], "short_url"=>"URL", "summary"=>"", "recommended_source"=>nil, "recommended_color"=>nil, "followed"=>false, "liked"=>true, "note_count"=>1, "caption"=>"", "reblog"=>{"tree_html"=>"", "comment"=>""}, "trail"=>[], "image_permalink"=>"url", "photos"=>[{"caption"=>"", "alt_sizes"=>[{"url"=>"URL", "width"=>400, "height"=>544}, {"url"=>"URL", "width"=>250, "height"=>340}, {"url"=>"URL", "width"=>100, "height"=>136}, {"url"=>"URL", "width"=>75, "height"=>75}], "original_size"=>{"url"=>"URL", "width"=>400, "height"=>544}}], "can_like"=>false, "can_reblog"=>true, "can_send_in_message"=>true, "can_reply"=>true, "display_avatar"=>true}], "total_posts"=>1}
I have tried many different formats and can't seem to just get the post url. My thought is to get the post_url and embed each of them so it shows as it would in tumblr on my webpage. Can anyone help me?
Try this:
#posts['posts'].first['post_url']
Or if your response contains more than one post you can return them all like this:
(0...#posts['total_posts']).map { |i| #posts['posts'][i]['post_url'] }
EDIT: Fixed capitalization in second line of code. I assume you are using lowercase variable as per Ruby convention, '#posts'. If not, you should change to a lowercase variable as this may be confusing something.

Reading and Writing File Contents From HTTP Post Data In Bash

Problem Statement:
I am trying to upload a file through an HTML form using an HTTP post request and then write it to a file called configuration.xml on my local server. I can only use the stock capabilities of the server, so, as much as I'd love to, I can't use cURL, PHP, Perl, or anything that I'd have to install on the server. What I have tried doing is having the HTML form open a CGI file as the form action and all this CGI file does is run the Bash script with the proper HTML formatting. I would run the Bash script directly from the HTML form, but my research led me to believe that this isn't possible without having to edit .htaccess or other hacky alternatives, which are not roads I want to go down. (If this can be done in a reasonable fashion, please enlighten me!) Regardless, I am able to successfully run the Bash script. I know this because I put a "touch configuration.xml" command in the script and it creates it every time. My script is also able to tell that it is an HTTP Post, as shown by the echoed text in the browser, but then I can't seem to be able to properly read any data from the file. I tried echoing the data as well as redirecting the read data to a file, but nothing appeared in the browser and nothing wrote to the file I specified. This very well may be me not knowing Bash scripting well enough or something silly like that, but I really don't know how to proceed from here.
Code:
UploadToServer.html:
<form action="run_script.cgi" method="POST" enctype="multipart/form-data">
<input type="file" name="file" />
<input type="submit" name="submit" value="Submit">
</form>
run_script.c:
Note: I compile this to a CGI file with the following command: gcc run_script.c -o run_script.cgi
#include <stdlib.h>
#include <stdio.h>
int main() {
system("./test.sh &");
printf("Content-Type: text/html\r\n\r\n");
printf(""); // print blank line for proper HTML header formatting
printf("<html>\n");
printf("</HTML>\n");
}
test.sh:
The non-commented code in the second if statement is from here. The commented code is from here.
#!/bin/bash
touch configuration.xml
if [[ $REQUEST_METHOD = 'POST' ]]; then
echo "this is a post!"
if [ "$CONTENT_LENGTH" -gt 0 ]; then
echo "entered second if statement!"
# read -n $CONTENT_LENGTH POST_DATA <&0
# echo "$CONTENT_LENGTH"
while read line
do eval "echo ${line}"
done
fi
fi
I also tried the approach in the third code block of this post, but didn't get any output. I also looked through this post, but it doesn't seem to grab all the data from the file like I need to. I also tried the approach of just using a CGI file like suggested in this post (_http://blog.purplepixie.org/2013/08/cc-cgi-file-upload/), but, once again, no output. I've been looking through the Apache error log as I try new things and no errors come up.
Anybody have any ideas on what I could possibly be doing wrong? Is there a different approach worth looking into? Any suggestions are greatly appreciated!
I figured out how to do it, with some help from my friends. I ended up doing it all in a CGI script and foregoing the Bash component. While this isn't what I asked for in my original question, it gets the job done for me, which is really what the question was asking.
The following is the C code I'm now using to successfully write the file on the server:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
void print_empty_html_page();
int main() {
char * req_method_str = getenv("REQUEST_METHOD");
if (req_method_str != NULL) {
if (strcmp(req_method_str, "POST") == 0) {
// process POST arguments
char * len_str = getenv("CONTENT_LENGTH");
if (len_str != NULL) {
int len = atoi(len_str);
if (len > 0) {
FILE * fp;
fp = fopen("file.xml", "w");
char * postdata = malloc((len + 1) * sizeof(char));
fread(postdata, sizeof(char), len, stdin);
postdata[len] = '\0';
fprintf(fp, "%s\n", postdata);
free(postdata);
fclose(fp);
}
system("sed -e '/Content/d' -e '/[-][-][*][*][*][*][*]/d' -e '/^[s]*$/d' -e '/WebKitFormBoundary/d' -e '/Submit/d' < file.xml > file_trimmed.xml");
system("rm file.xml");
}
}
}
print_empty_html_page();
return 0;
}
void print_empty_html_page() {
// Send the content type, letting the browser know this is HTML
printf("Content-type: text/html\r\n\r\n");
// Header information that prevents browser from caching
printf(
"<META HTTP-EQUIV=\"CACHE-CONTROL\" CONTENT=\"NO-CACHE, NO-STORE\">\r\n\r\n");
// Top of the page
printf("<html>\n");
printf("<BODY>\n");
// Finish up the page
printf("</BODY></html>\n");
}
Note: This method writes the entire HTTP POST to the file 'file.xml'. The system call to 'sed' is to remove the tags from the HTTP POST that don't correspond to the actual data in the file that was uploaded. If you need to check for additional unwanted lines, just add another -e '/<line_with_expression_to_delete>/d' in that sed call, where line_with_expression_to_delete is the expression you want to match and then delete all lines containing that expression. I couldn't figure out how to delete all the blank lines in the newly uploaded file, even though '/^[s]*$/d' should do that, according to my research. Gonna have to look into that more...
Also note: This method only works for uploading text files. It does not work for other file types, such as JPEGs or OGGs.
Hopefully this helps some other people with the same problem. Let me know if you have any questions.

Resources