Form many months I have been having problems with our main site code and certain images and files not being displayed in Chrome properly. These files where being loaded out of a SQL server with some fairly standard .net code.
Turns out Chrome doesn't like it when you are streaming a file and then use
Response.Close();
to close the connection.
We have a bug open with Google about it, hopefully they fix it.
Hope this helps others!
CKEditor 3: Properly Getting ID of Link or Image textbox to return URL
I'm still hacking away trying to upgrade FCKEditor 2 to CKEditor 3. Today's problem centered around getting the proper ID for the textbox of either the Link Button or Image Button insert window. Turns out these ID's are quite random, not unlike how .Net generates ID's for server controls (unfortunately). Like most things in the CKEditor world, this is quite poorly documented but after many different combinations of searches I finally found this board posting which cleared things up.
I'm reposting my code here so as to help spread the word.
Background:
The Link and Image buttons (as well as others) in CKEditor will show a dialog box asking you for a URL. You have the option to set a Browser Server button in one of these box's in your config file which can point to some kind of file browser, probably some other portion of your system or CMS. The challange is then to return a URL form your code back to the CKEditor window. Normally this is quite easy with something like this.
Solution:
The solution is to use the CKEditor API to do the heavy lifting.
The CKEditorFuncNum isn't something you have to worry about, the CKEditor auto passes the correct value for you.
Hope this helps someone else, let me know if you need any further clarification.
I'm reposting my code here so as to help spread the word.
Background:
The Link and Image buttons (as well as others) in CKEditor will show a dialog box asking you for a URL. You have the option to set a Browser Server button in one of these box's in your config file which can point to some kind of file browser, probably some other portion of your system or CMS. The challange is then to return a URL form your code back to the CKEditor window. Normally this is quite easy with something like this.
strURL += "javascript:window.opener.document."; strURL += "getElementById('" + strReturnControlID + "')"; strURL += ".value='/" + strPageTitle.Replace("'", "\\'"); strURL += "'; window.close()";Where strReturnControlID would contain the ID of the parent textbox from the querystring that I set in the CKEditor Config for the Browser Server button. Problem is, this ID changes, often with each page reload. Lame.
Solution:
The solution is to use the CKEditor API to do the heavy lifting.
strURL += "javascript:window.parent.opener.CKEDITOR.tools.callFunction("; strURL += Request.QueryString["CKEditorFuncNum"].ToString(); strURL += ", '" + strPageTitle.Replace("'", "\\'") + "', '' ); window.close();";I was quite surprised when this worked the first time!
The CKEditorFuncNum isn't something you have to worry about, the CKEditor auto passes the correct value for you.
Hope this helps someone else, let me know if you need any further clarification.
CKEditor 3 Simple Save Button Plug-in
In a previous post I was talking about how hard it is in the new CKEditor to override the default behavior of the Save button. By design the Save button will cause the form to post, what I wanted it to do was fire a JavaScript function that would open my Save Dialog. Turns out you have to write a plug-in to do this. There are several posts out on the CKSource website that give some code on how to do this, but none of them where quite what I wanted. So here's my simple Save plug-in for CKEditor 3.
That's it. At first it looks quite difficult, but once you wrap your head around all the bits it starts to make sense.
For those interested, I am pasting my function that creates my CKEditor instances. This is a work in process and I'm not even close to being done, but I figured it might help someone out there. I'm using the JQuery adapter to load CKEditor.
- Start by creating a folder called ajaxSave in the CKEditor\Plugins folder.
- Create a new JavaScript file called plugin.js in the ajaxSave folder
- Paste the following code:
(function() { var saveCmd = { modes: { wysiwyg: 1, source: 1 }, exec: function(editor) { showSaveDialog(); // my custom dialog function for doing the save. Uses JQuery UI } }; var pluginName = 'ajaxSave'; CKEDITOR.plugins.add(pluginName, { init: function(editor) { var command = editor.addCommand(pluginName, saveCmd); editor.ui.addButton('AjaxSave', { label: editor.lang.save, command: pluginName, icon: "/ckeditor/plugins/ajaxsave/save.png" }); } }); })();
- Notice the
icon: "/ckeditor/plugins/ajaxsave/save.png"
line? So far I haven't figured out how to reference the default save icon in the skin file so I had to make my own and store it in the same directory as the plugin. I did this by opening the skin icons.png file I was interested in a chopping out the save icon. - You have to tell CKEditor to load the plug-in. You can do this in your config section, either during CKEditor creation, or in a custom.js config file you load during CKEditor creation.
The config property looks like this:
extraPlugins : 'ajaxSave' - Lastly, you need to reference the plug-in in the toolbar. For example, in my CKConfig.js file that I load during editer creationg I have this
config.toolbar_Default = [ ['AjaxSave'], ['Cut', 'Copy', 'Paste', . . . .
That's it. At first it looks quite difficult, but once you wrap your head around all the bits it starts to make sense.
For those interested, I am pasting my function that creates my CKEditor instances. This is a work in process and I'm not even close to being done, but I figured it might help someone out there. I'm using the JQuery adapter to load CKEditor.
// load the CKEDITOR in the given div function loadCKEditor(Div) { var CSSFiles = []; CSSFiles[0] = "https://designer.wsu.edu/template/css2.aspx?key=" + $("#hfSiteKey").val(); CSSFiles[1] = "https://files.ba.wsu.edu/styles/additions" + $("#hfAdditionsVerNum").val() + ".min.css"; CSSFiles[2] = "https://files.ba.wsu.edu/styles/PageEditor_CKEditor.css"; $('#' + Div).ckeditor(function() { // this is the callback, // edMain, edSecondary and edAdditional are public vars at the top of the js file, this will assign them the instance of the editor we just created so I can manipulate it later. Note, I'm creating three different editors by calling loadCKEditor 3 times later on. if (Div === 'edMain') { edMain = $('#edMain').ckeditorGet(); } if (Div === 'edSecondary') { edSecondary = $('#edSecondary').ckeditorGet(); } if (Div === 'edAdditional') { edAdditional = $('#edAdditional').ckeditorGet(); } var e = $('#' + Div).ckeditorGet(); $(window).resize(function() { var eH = $(window).height() - winHeightSub; e.resize(e.config.width, eH); }); fckCompleteCount++; if (fckCompleteCount == 3) // since this gets' called for each FCKeditor_OnComplete (3 times) only want to load when compleatly done. { if ($("#hfIsDraft").val() == "true") // there is a draft to load, show the chooser dialog { // set AutoSave = false so it doesn't try to save when dialog is open AutoSave = false; $("#divLoadDraft").dialog("open"); } else { load(false); } // no draft to load, just load the main page text } }, { // settings customConfig: '/Masterpages/myFCKConfig.js', toolbarCanCollapse: false, toolbar: 'Default', sharedSpaces: { top: 'fckToolBar' }, bodyId: 'content', resize_enabled: false, contentsCss: CSSFiles, extraPlugins : 'ajaxSave', height: EditorHeight }); }
Dealing with corrupt xlsx, docx files when reading from SQL Blob
Posted by
Dave Sargent
on Thursday, April 15, 2010
/
Labels:
Corrupt documents,
Office 2007,
Response.OutputStream.Write
/
Comments: (0)
I've had a bug in my bug tracking system for our CMS for a while now. Basically, any Office 2007 files that where uploaded into our CMS came back as corrupt when downloaded. The short term solution was to have everyone down-convert the docs. I finally got around to fixing this today and wanted to share my results with everyone.
My old code looked like this.
From my research, it looks like this will work fine for most anything but Office 2007 docs, for whatever reason, those tend to end up being a single byte too long. Of course the other possability is that many things where a single byte too long but their document formats either didn't care or took care of that extra padding. In any case the solution is to use Response.OutputStream.Write instead of Response.BinaryWrite
Here's the new code:
Remember that datareader.GetBytes relies on the datareader being in SequentialAccess mode, ala
Hope this helps those suffering the same problem.
My old code looked like this.
do { bytesRead = dr.GetBytes(11, readFrom, bytes, 0, bufferSize); System.Web.HttpContext.Current.Response.BinaryWrite(bytes); readFrom += bufferSize; } while (bytesRead == bufferSize);
From my research, it looks like this will work fine for most anything but Office 2007 docs, for whatever reason, those tend to end up being a single byte too long. Of course the other possability is that many things where a single byte too long but their document formats either didn't care or took care of that extra padding. In any case the solution is to use Response.OutputStream.Write instead of Response.BinaryWrite
Here's the new code:
byte[] buffer = new Byte[4096]; try { long startIndex = 0; //Start of the blob Stream outputStream = Context.Response.OutputStream; while ((bytesRead = dr.GetBytes(blobColumnIndex, startIndex, buffer, 0, buffer.Length)) > 0) { // Verify that the client is connected. if (System.Web.HttpContext.Current.Response.IsClientConnected) { outputStream.Write(buffer, 0, (int)bytesRead); // Reposition the start index to the end of the last buffer and fill the buffer. startIndex += bytesRead; } else { //prevent infinite loop if user disconnects startIndex += 10000; } } } catch (Exception ex) { // Trap the error, if any. System.Web.HttpContext.Current.Response.Write("Error : " + ex.Message); } finally { System.Web.HttpContext.Current.Response.Close(); }
Remember that datareader.GetBytes relies on the datareader being in SequentialAccess mode, ala
SqlDataReader dr = cmd.ExecuteReader(CommandBehavior.SequentialAccess);in SequentialAccess mode you must access the columns in order.
Hope this helps those suffering the same problem.
Fighting with CKEditor
Trying to upgrade our CMS from FCKEditor 2.x to CKEditor 3.0. Where somethings are easier to do, others . . . not so much. Like overriding the Save button built into the editor for doing AJAX posts. In 2.x it was one line of code (inside the onComplete function).
They know this is lame as per this item in the bug tracker.
http://dev.fckeditor.net/ticket/4507
but it apparently isn't lame enough for them to actually roll said fix into the production code.
So, I'm working on a plugin. Stay tuned, I shall conquer this beast and then post the code so others won't have to suffer such pain.
function FCKeditor_OnComplete(editorInstance) { // causes save icon to open the custom save dialog editorInstance.LinkedField.form.onsubmit = showSaveDialog; }However in 3.x you apparently have to create a new plugin to replace the current save plugin. To be clear, this is lame, poorly documented and . . . well, lame.
They know this is lame as per this item in the bug tracker.
http://dev.fckeditor.net/ticket/4507
but it apparently isn't lame enough for them to actually roll said fix into the production code.
So, I'm working on a plugin. Stay tuned, I shall conquer this beast and then post the code so others won't have to suffer such pain.