Can't get homemade CKEditor file uploader working in web2py - ckeditor

There's something arcane going on with my custom CKEditor uploader. The image or whatever file I try to upload to the server is correctly uploaded but no matter what I do it's link won't show up in the editor. It looks like the callback to CKEditor in my upload_file.html view doesn't work as it should. The documentation of CKEditor is really sparse about these things, so I could really use some guidance here.
In my controller I have the following upload function:
def upload_file():
upload = request.vars.upload
if upload != None:
if hasattr(upload, 'file'):
old_filename = upload.filename
new_filename = db.files.uploaded_data.store(upload.file, upload.filename)
result = db.files.insert(filename = old_filename,
uploaded_data = new_filename,
created_on = datetime.today())
if not result:
message = T('An error has occured during upload.')
url = ''
else:
message = T('File uploaded succesfully.')
url = URL(r = request, f = 'download', args = new_filename)
return dict(form = None, cknum = request.vars.CKEditorFuncNum, url = url, message = message)
else:
raise HTTP(401, T('Upload is not proper type.'))
else:
form = SQLFORM(db.files, fields = ['uploaded_data'])
upload = request.vars.uploaded_data
if upload != None:
form.vars.filename = upload.filename
form.vars.created_on = datetime.today()
if form.process().accepted:
response.flash = T('File uploaded successfully!')
elif form.errors:
response.flash = T('form has errors')
else:
response.flash = T('please fill out the form')
return dict(form = clean_form(form))
The view for this function looks like this:
{{if form != None:}}
{{extend 'layout.html'}}
{{=form}}
{{else:}}
<html>
<body>
<script type="text/javascript">
window.opener.CKEDITOR.tools.callFunction({{=cknum}}, '{{=url}}', '{{=message}}');
</script>
</body>
</html>
{{pass}}
I have a test view with a form containing several textareas all of which are properly converted to editors:
{{extend 'layout.html'}}
<script type="text/javascript">
CKEDITOR.config.filebrowserBrowseUrl = "{{=URL(request.application, c='default', f='upload_file')}}";
CKEDITOR.config.filebrowserUploadUrl = "{{=URL(request.application, c='default', f='upload_file')}}";
CKEDITOR.config.filebrowserWindowHeight = '60%';
CKEDITOR.config.filebrowserWindowWidth = '70%';
</script>
{{=form}}

I finally found the solution. There's a mistake in the view of the upload_file function.
window.opener.CKEDITOR.tools.callFunction({{=cknum}}, '{{=url}}', '{{=message}}');
should be rewritten to this:
window.parent.CKEDITOR.tools.callFunction({{=cknum}}, '{{=url}}', '{{=message}}');
I copied the first version, which caused me quite a lot of headache, from web2pyslices, so I write this answer here in the hope that it will help others trying to integrate CKEditor with Web2py.

Related

How to submit data to Flask from an Ajax call, and return its response in Flask from another Ajax call?

Sorry if the title is a little confusing. A kind user here on StackOverflow helped me make my Flask app display some scraped data, only now I have added a parameter in the function so that I can scrape the data I want to search for. I have an input box, and I want to be able to get the data from it, and pass it as a string in my python function in Flask
Current HTML Side
<!DOCTYPE html>
<html lang = "en">
<head>
<meta charset = "utf-8">
<title>NBA Data Web App</title>
</head>
<body>
<script src = "http://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js" crossorigin = "anonymous"></script>
<form id = "nameForm" method = "POST" role = "form">
<input name = "text">
<button id = "searchBtn"> Search </button>
</form>
<div id = "container"></div>
<script type = "text/javascript">
//Function to take place when our search button is clicked
$('button#searchBtn').click(function() {
$.ajax({
url: '/_get_data',
data: $('form').serialize(),
type: 'POST',
success: function(response) {
console.log = response;
},
error: function() {
alert('Failure in first Ajax call');
}
});
/*Everything below this was working before, as I only made one ajax call when a button was pressed. Now, when I press the button, I want to pass its contents as a string to my scrape_data() function in Flask, and return, and display, its contents as shown below. */
//Declare our list so we can print something, and loop through it later
var data_list;
//Variable for our HTML table
var rowMax = 29, html = "<table><tr>";
//Post request
$.post('/_get_data', {
//If done, do this
}).done(function(response) {
/* Assign our scraped data to our variable that was declared earlier,
which is turned into an array here in JS */
data_list = response['data'];
//Declare some variables for making our table
var perRow = 1, count = 0, table = document.createElement("table"),
row = table.insertRow();
//Loop through the data and add it to the cells
for (var i of data_list) {
//Insert a cell for each piece of data
var cell = row.insertCell();
//Add the data to the cell
cell.innerHTML = i;
//Increment our count variable
count++;
//If we have met our set number of items in the row
if (count % perRow == 0) {
//Start a new row
row = table.insertRow();
}
}
//Add the table to our container in our HTML
document.getElementById("container").appendChild(table);
//If request fails
}).fail(function() {
alert("request failed");
});
});
</script>
</body>
</html>
Python (Flask) Side
rom flask import Flask, render_template, jsonify, request, escape, url_for
#Get our lists to post
headers = data_headers()
#Start the flask app
app = Flask(__name__)
#Start page
#app.route('/')
def index():
return render_template('index.html')
#What happens when our button is clicked
#app.route('/_get_data', methods = ['POST'])
def _get_data():
text = request.form['text']
#Here, I am trying to assign the contents of the input box of the form to a variable, so I can pass that variable as a parameter for my function.
data = scrape_data(text)
#Return the json format of the data we scraped
return jsonify({'data' : data})
#Run the app
if __name__ == "__main__":
app.run(debug = True)
I am currently getting error 405 method not allowed. I'm not sure if my syntax in the first Ajax call is incorrect, or if I need to break this up into two different #app.route(urls) since each call is going a different way.
If you use the method attribute of form element and do not specify the action, request will be sent /. What is happening here is when you click on search button it will send two post requests one to '/' and '/_get_data' from ajax. In Flask routing if you do not explicitly provides methods=[] that route will allow GET only. Remove the method attribute from you form, you should not get method not allowed error.

Google App Scripts Function to Open URL [duplicate]

Is there a way to write a google apps script so when ran, a second browser window opens to www.google.com (or another site of my choice)?
I am trying to come up with a work-around to my previous question here:
Can I add a hyperlink inside a message box of a Google Apps spreadsheet
This function opens a URL without requiring additional user interaction.
/**
* Open a URL in a new tab.
*/
function openUrl( url ){
var html = HtmlService.createHtmlOutput('<html><script>'
+'window.close = function(){window.setTimeout(function(){google.script.host.close()},9)};'
+'var a = document.createElement("a"); a.href="'+url+'"; a.target="_blank";'
+'if(document.createEvent){'
+' var event=document.createEvent("MouseEvents");'
+' if(navigator.userAgent.toLowerCase().indexOf("firefox")>-1){window.document.body.append(a)}'
+' event.initEvent("click",true,true); a.dispatchEvent(event);'
+'}else{ a.click() }'
+'close();'
+'</script>'
// Offer URL as clickable link in case above code fails.
+'<body style="word-break:break-word;font-family:sans-serif;">Failed to open automatically. Click here to proceed.</body>'
+'<script>google.script.host.setHeight(40);google.script.host.setWidth(410)</script>'
+'</html>')
.setWidth( 90 ).setHeight( 1 );
SpreadsheetApp.getUi().showModalDialog( html, "Opening ..." );
}
This method works by creating a temporary dialog box, so it will not work in contexts where the UI service is not accessible, such as the script editor or a custom G Sheets formula.
You can build a small UI that does the job like this :
function test(){
showURL("http://www.google.com")
}
//
function showURL(href){
var app = UiApp.createApplication().setHeight(50).setWidth(200);
app.setTitle("Show URL");
var link = app.createAnchor('open ', href).setId("link");
app.add(link);
var doc = SpreadsheetApp.getActive();
doc.show(app);
}
If you want to 'show' the URL, just change this line like this :
var link = app.createAnchor(href, href).setId("link");
EDIT : link to a demo spreadsheet in read only because too many people keep writing unwanted things on it (just make a copy to use instead).
EDIT : UiApp was deprecated by Google on 11th Dec 2014, this method could break at any time and needs updating to use HTML service instead!
EDIT :
below is an implementation using html service.
function testNew(){
showAnchor('Stackoverflow','http://stackoverflow.com/questions/tagged/google-apps-script');
}
function showAnchor(name,url) {
var html = '<html><body>'+name+'</body></html>';
var ui = HtmlService.createHtmlOutput(html)
SpreadsheetApp.getUi().showModelessDialog(ui,"demo");
}
There really isn't a need to create a custom click event as suggested in the bountied answer or to show the url as suggested in the accepted answer.
window.open(url)1 does open web pages automatically without user interaction, provided pop- up blockers are disabled(as is the case with Stephen's answer)
openUrl.html
<!DOCTYPE html>
<html>
<head>
<base target="_blank">
<script>
const url1 ='https://stackoverflow.com/a/54675103';
const winRef = window.open(url1);
winRef ? google.script.host.close() : window.alert('Allow popup to redirect you to '+url1) ;
window.onload=function(){document.getElementById('url').href = url1;}
</script>
</head>
<body>
Kindly allow pop ups</br>
Or <a id='url'>Click here </a>to continue!!!
</body>
</html>
code.gs:
function modalUrl(){
SpreadsheetApp.getUi()
.showModalDialog(
HtmlService.createHtmlOutputFromFile('openUrl').setHeight(50),
'Opening StackOverflow'
)
}
Google Apps Script will not open automatically web pages, but it could be used to display a message with links, buttons that the user could click on them to open the desired web pages or even to use the Window object and methods like addEventListener() to open URLs.
It's worth to note that UiApp is now deprecated. From Class UiApp - Google Apps Script - Google Developers
Deprecated. The UI service was deprecated on December 11, 2014. To
create user interfaces, use the HTML service instead.
The example in the HTML Service linked page is pretty simple,
Code.gs
// Use this code for Google Docs, Forms, or new Sheets.
function onOpen() {
SpreadsheetApp.getUi() // Or DocumentApp or FormApp.
.createMenu('Dialog')
.addItem('Open', 'openDialog')
.addToUi();
}
function openDialog() {
var html = HtmlService.createHtmlOutputFromFile('index')
.setSandboxMode(HtmlService.SandboxMode.IFRAME);
SpreadsheetApp.getUi() // Or DocumentApp or FormApp.
.showModalDialog(html, 'Dialog title');
}
A customized version of index.html to show two hyperlinks
<a href='http://stackoverflow.com' target='_blank'>Stack Overflow</a>
<br/>
<a href='http://meta.stackoverflow.com/' target='_blank'>Meta Stack Overflow</a>
Building of off an earlier example, I think there is a cleaner way of doing this. Create an index.html file in your project and using Stephen's code from above, just convert it into an HTML doc.
<!DOCTYPE html>
<html>
<base target="_top">
<script>
function onSuccess(url) {
var a = document.createElement("a");
a.href = url;
a.target = "_blank";
window.close = function () {
window.setTimeout(function() {
google.script.host.close();
}, 9);
};
if (document.createEvent) {
var event = document.createEvent("MouseEvents");
if (navigator.userAgent.toLowerCase().indexOf("firefox") > -1) {
window.document.body.append(a);
}
event.initEvent("click", true, true);
a.dispatchEvent(event);
} else {
a.click();
}
close();
}
function onFailure(url) {
var div = document.getElementById('failureContent');
var link = 'Process';
div.innerHtml = "Failure to open automatically: " + link;
}
google.script.run.withSuccessHandler(onSuccess).withFailureHandler(onFailure).getUrl();
</script>
<body>
<div id="failureContent"></div>
</body>
<script>
google.script.host.setHeight(40);
google.script.host.setWidth(410);
</script>
</html>
Then, in your Code.gs script, you can have something like the following,
function getUrl() {
return 'http://whatever.com';
}
function openUrl() {
var html = HtmlService.createHtmlOutputFromFile("index");
html.setWidth(90).setHeight(1);
var ui = SpreadsheetApp.getUi().showModalDialog(html, "Opening ..." );
}
I liked #Stephen M. Harris's answer, and it worked for me until recently. I'm not sure why it stopped working.
What works for me now on 2021-09-01:
function openUrl( url ){
Logger.log('openUrl. url: ' + url);
const html = `<html>
<a id='url' href="${url}">Click here</a>
<script>
var winRef = window.open("${url}");
winRef ? google.script.host.close() : window.alert('Configure browser to allow popup to redirect you to ${url}') ;
</script>
</html>`;
Logger.log('openUrl. html: ' + html);
var htmlOutput = HtmlService.createHtmlOutput(html).setWidth( 250 ).setHeight( 300 );
Logger.log('openUrl. htmlOutput: ' + htmlOutput);
SpreadsheetApp.getUi().showModalDialog( htmlOutput, `openUrl function in generic.gs is now opening a URL...` ); // https://developers.google.com/apps-script/reference/base/ui#showModalDialog(Object,String) Requires authorization with this scope: https://www.googleapis.com/auth/script.container.ui See https://developers.google.com/apps-script/concepts/scopes#setting_explicit_scopes
}
https://developers.google.com/apps-script/reference/base/ui#showModalDialog(Object,String) Requires authorization with this scope: https://www.googleapis.com/auth/script.container.ui See https://developers.google.com/apps-script/concepts/scopes#setting_explicit_scopes

Windows IoT Core on Raspberry PI default webserver

Have someone found how to make a similar webserver as the default IoT Core one? The most similar example found is this but when I try to insert some javascript in the page, is not recognized. In the default webserver of the IoT Core there are a lot of js and jQuery scripts that runs very well.
Someone have ideas please?
Thanx a lot
Based on this sample, you can add a HTML file to your project and use this HTML file host the content of the web page, then insert some javascript in it.
HTML file:
<!DOCTYPE html>
<html>
<head>
<title>Background Message</title>
</head>
<body>
Hello from the background process!<br />
<script type="text/javascript">
var myVariable = 'Hello, I come from script!';
window.alert(myVariable);
</script>
</body>
</html>
You need edit part of code like this:
using (var response = output.AsStreamForWrite())
{
string page = "";
var folder = Windows.ApplicationModel.Package.Current.InstalledLocation;
var file = await folder.GetFileAsync("index.html");
var readFile = await Windows.Storage.FileIO.ReadLinesAsync(file);
foreach (var line in readFile)
{
page += line;
}
page += query;
byte[] bodyArray = Encoding.UTF8.GetBytes(page);
var bodyStream = new MemoryStream(bodyArray);
var header = "HTTP/1.1 200 OK\r\n" +
$"Content-Length: {bodyStream.Length}\r\n" +
"Connection: close\r\n\r\n";
byte[] headerArray = Encoding.UTF8.GetBytes(header);
await response.WriteAsync(headerArray, 0, headerArray.Length);
await bodyStream.CopyToAsync(response);
await response.FlushAsync();
}
After deploying your app to Raspberry Pi, while the app running, you can visit the web server. The result will look like this:

Using xmlhttprequest in IE only includes the contents of the body tag

When I try to get the contents of a htm file into a div using a xmlhttprequest object in Firefox it includes everything, but in IE it only includes the contents of the body tag. In other words it ignores all the styling (in the head tag) of the page, rendering it ugly.
Is it possible to get the full page when using xmlhttprequest in internet explorer?
edit:
document.getElementById('divtoreceivetheresponse').innerHTML = xmlHTTP.responseText
This line in FF gets the page contents including the <head></head> section.
In IE it just gets the contents inside the <body></body> section.
I got an answer from elsewhere. Basically it does include all the page (not just the body) but IE chooses not to render it (probably the correct behavour)
I therefore worked out some code to extract the css, place it in the head, and place the body stuff in the target div. So both html and css from the external page would be got.
<html><head>
<script type="text/javascript" language="javascript">
function include(lyr,url)
{
if (document.all)
{
try {
var xml = new ActiveXObject("Microsoft.XMLHTTP");
xml.Open( "GET", url, false );
xml.Send()
}
catch (e) {
var xml = new ActiveXObject("MSXML2.XMLHTTP.4.0");
xml.Open( "GET", url, false );
xml.Send()
}
}
else
{
var xml=new XMLHttpRequest();
xml.open("GET",url,false);
xml.send(null);
}
text = xml.responseText;
text = text.replace("<html>","");
text = text.replace("</html>","");
text = text.replace("<head>","");
text = text.replace("</head>","");
text = text.replace("<body>","");
text = text.replace("</body>","");
splittext = text.split("<style type=\"text/css\">");
splittext = splittext[1].split("</style>");
css = splittext[0];
everythingelse = splittext[1];
addCss(css);
document.getElementById(lyr).innerHTML=everythingelse;
}
function addCss(cssCode) {
var styleElement = document.createElement("style");
styleElement.type = "text/css";
if (styleElement.styleSheet) {
styleElement.styleSheet.cssText = cssCode;
} else {
styleElement.appendChild(document.createTextNode(cssCode));
}
document.getElementsByTagName("head")[0].appendChild(styleElement);
}
</script>
</head>
<body onload="include('adiv','test.htm')">
<div id="adiv">sdfgboui hsdguhwruh o ikuy </div>
</body>
</html>
The code is far from perfect, but it does the job and I will probably improve the code bit by bit now that I know it works

CKeditor integration with FCKeditor filebrowser

I'm using CKEditor 3 and I need to integrate a cost-free filebrowser/uploader. I tried to integrate the one that comes with FCKEditor but I always get this XML error:
The server didn't send back a proper XML response. Please contact your system administrator.
XML request error: OK (200)
Requested URL: http://example.com/admin/filemanager/browser/default/?Command=GetFoldersAndFiles&Type=File&CurrentFolder=%2F&uuid=1260817820353
Response text:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<html>
<head>
<title>Index of /admin/filemanager/browser/default</title>
</head>
<body>
<h1>Index of /admin/filemanager/browser/default</h1>
<table><tr><th><img src="/icons/blank.gif" alt="[ICO]"></th>
<th>Name</th>
<th>Last modified</th>
<th>Size</th>
<th>Description</th></tr>
<!-- edited for brevity -->
I'm trying to do it in this way:
<script type="text/javascript">
window.onload = function(){
CKEDITOR.config.language='es';
CKEDITOR.config.forcePasteAsPlainText = true;
CKEDITOR.config.enterMode = CKEDITOR.ENTER_DIV;
CKEDITOR.replace('ncCont',{
filebrowserBrowseUrl: 'filemanager/browser/default/browser.html',
filebrowserUploadUrl : 'filemanager/connectors/php/upload.php'
});
};
</script>
Can FCKeditor be integrated with CKEditor? If yes, how can this be done? If not, is there a free filebrowser/uploader alternative?
Wanted to piggy back off Penuel whose code helped me a lot.
add this to /filemanager/connectors/php/upload.php
// Get the CKEditor Callback
$CKEcallback = $_GET['CKEditorFuncNum'];
//modify the next line adding in the new param
FileUpload($sType, $sCurrentFolder, $sCommand, $CKEcallback);
add this to /filemanager/connectors/php/io.php
// This is the function that sends the results of the uploading process to CKE.
function SendCKEditorResults ($callback, $sFileUrl, $customMsg = '')
{
echo '<script type="text/javascript">';
$rpl = array( '\\' => '\\\\', '"' => '\\"' ) ;
echo 'window.parent.CKEDITOR.tools.callFunction("'. $callback. '","'.
strtr($sFileUrl, $rpl). '", "'. strtr( $customMsg, $rpl). '");' ;
echo '</script>';
}
modify this /filemanager/connectors/php/commands.php
//line 158
function FileUpload($resourceType, $currentFolder, $sCommand, $CKEcallback = '')
//line 166
if ( (isset($_FILES['NewFile']) && !is_null($_FILES['NewFile']['tmp_name']))
# This is for the QuickUpload tab box
or (isset($_FILES['upload']) and !is_null($_FILES['upload']['tmp_name'])))
{
global $Config ;
$oFile = isset($_FILES['NewFile']) ? $_FILES['NewFile'] : $_FILES['upload'];
...
if($CKEcallback == '')
{
// this line already exists so wrap the if block around it
SendUploadResults( $sErrorNumber, $sFileUrl, $sFileName ) ;
}
else
{
//issue the CKEditor Callback
SendCKEditorResults ($CKEcallback, $sFileUrl,
($sErrorNumber != 0
? 'Error '. $sErrorNumber. ' upload failed. '. $sErrorMsg
: 'Upload Successful'));
}
You'll need to add the upload URL's to your CKEDITOR definition like so:
var filemanager = '/js/ckeditor/filemanager/';
var browser = filemanager + 'browser/default/browser.html';
var connector = filemanager + 'connectors/php/connector.php';
var upload = filemanager + 'connectors/php/upload.php';
CKEDITOR.replace( id,
{
customConfig : this.config,
filebrowserBrowseUrl : browser +'?Connector=' + connector,
filebrowserImageBrowseUrl : browser + '?Type=Image&Connector='
+ connector,
filebrowserFlashBrowseUrl : browser + '?Type=Flash&Connector='
+ connector,
filebrowserUploadUrl : upload + '?type=Files',
filebrowserImageUploadUrl : upload + '?type=Images',
filebrowserFlashUploadUrl : upload + '?type=Flash'
});
I think that covers everything left off by Penuel
To answer your question I have posted one small tutorial on my blog with step by step instructions to integrate the File Browser of FCKEditor in CKEditor. Please goto:
http://www.mixedwaves.com/2010/02/integrating-fckeditor-filemanager-in-ckeditor/
I have done this for PHP connector but should be pretty simple for other languages as well.
You can also download the already done example or view the demo from this article.
I'm using a custom file browser with my implementation of ckeditor, so I don't see why you couldn't use the old file browser. It uses the same javascript to populate the editor.
Just install the old FCK editor in whatever directory, and make sure you have the correct path to that browser in your config. I'm guessing that you have a pathing problem above.

Resources