[CLOSED] Extend store's getRecordsValues options to take in whether it should use the json property instead of data?

Page 1 of 2 12 LastLast
  1. #1

    [CLOSED] Extend store's getRecordsValues options to take in whether it should use the json property instead of data?

    Hi,

    Temporarily I ended up creating this code just so I could access the json property of the record, instead of the data property:

    Ext.data.GroupingStore.prototype.getRecordsJsonValues = function (options) {
        options = options || {};
    
        var records = (options.records ? options.records : (options.currentPageOnly ? this.getRange() : this.getAllRange())) || [],
            values = [],
            i;
    
        for (i = 0; i < records.length; i++) {
            var obj = {}, dataR;
                
            dataR = Ext.apply(obj, records[i].json);
    
            if (this.metaId()) {
                obj[this.metaId()] = options.excludeId === true ? undefined : records[i].id;
            }
                            
            dataR = this.prepareRecord(dataR, records[i], options);
    
            if (!Ext.isEmptyObj(dataR)) {
                values.push(dataR);
            }
        }
    
        return values;
    };
    You will notice that it is a copy/paste of your from your data/Store.js getRecordsValues(options) method where the only difference is that instead of using records[i].data it uses records[i].json.

    Would it be acceptable for you to extend getRecordsValues options to take in whether it should get json instead, so that the following line

            dataR = Ext.apply(obj, records[i].data);
    can then become something like this:

            dataR = Ext.apply(obj, options.jsonRecords === true ? records[i].json : records[i].data);
    Reason:

    • I use DataViews from time to time, and some of the XTemplates I use generate HTML in the prepareData phase, such as image tags. This is done by updating the data parameter passed to the prepareData parameter, which is what is used in the above function
    • Normally we would use the correct XTemplate upfront which would have all the HTML we need as part of the template, in which case I would not need to assign HTML to the data item.
    • However, our data is quite dynamic and we cannot know the XTemplate specifics for each row until we get the data, which is why only then do we render the specific HTML. And even per row of data our HTML will subtly vary within the overall XTemplate. E.g., sometimes, depending on the data you may have an image, or sometimes text, or sometimes different HTML. (And XTemplate if statements are also not enough for our scenario as pretty much all of it can be configured and data driven!)
    • But by that time, the only way we can do it - as far as I can tell - is by assigning HTML to the data itself, and hence the issue during the postback (this allows our data driven solutions to be extended simply by providing new script, and not providing any new C#).
    • This all renders and works fine up to this point.
    • However, I also provide the ability to export the data to excel/csv, but the post back this requires now has html in it which asp.net by default will prevent processing of in fear of malicious content.

    I've verified that by using the above json property instead, I get back the non HTML data sent to the server successfully and therefore I can export to Excel without problem.
    Last edited by Daniil; May 20, 2011 at 12:41 PM. Reason: [CLOSED]
  2. #2
    Hi,

    Thanks for the details explanation as usual.

    But could you provide a sample demonstrating the requirement? In other words, I'm not sure how all is configured on your side.
  3. #3
    Hi,

    It is really difficult to create a cut down version that dynamically generates the XTemplate and everything else, but what I can do is create a very contrived/fake example as follows (so do read the caveats at the end :)):

    First the aspx
    <%@ Page Language="C#" %>
    
    <%@ Register Assembly="Ext.Net" Namespace="Ext.Net" TagPrefix="ext" %>
    
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
        
    <script runat="server">
    
    [DirectMethod]
    public static void ExportData(object data)
    {
        // code here to take data, convert to CSV etc and set response type accordingly    
    }
    
    protected void Page_Load(object sender, EventArgs e)
    {
        if (ExtNet.IsAjaxRequest)
            return;
        
        var data = new List<object>(4);
        for (var i = 0; i < 4; i++)
        {
            data.Add(new
            {
                name = "name " + i,
                nickName = "nickname " + i
            });
        }
    
        Store1.DataSource = data;
        Store1.DataBind();
    }
    </script>
    
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head id="Head1" runat="server">
        <title>Untitled Page</title>
        <ext:ResourcePlaceHolder ID="ResourcePlaceHolder1" runat="server" Mode="Script" />
        
        <script type="text/javascript">
            var prepareData = function (data) {
                data.nickName = "<a href='#' class='nickname'>" + data.nickName + "</a>";
    
                return data;
            };
    
            // see this as to why I am exporting this way:
            // http://forums.ext.net/showthread.php?11896-Export-to-excel-csv-etc-by-posting-to-another-page
            function doExport(exportFormat, records) {
                var form = document.createElement('form');
                form.method = 'post';
                form.action = 'Export.ashx';
                form.target='_blank';
     
                var format = document.createElement('input');
                format.type = 'hidden';
                format.name = 'exportFormat';
                format.value = exportFormat;
                form.appendChild(format);
     
                var hidden = document.createElement('input');
                hidden.type = 'hidden';
                hidden.name = 'storeRecords';
                hidden.value = records;
                form.appendChild(hidden);
     
                document.body.appendChild(form);
     
                form.submit();
     
                form.removeChild(format);
                form.removeChild(hidden);
                document.body.removeChild(form);
     
                format = null;
                hidden = null;
                form = null;
            }
        </script> 
    </head>
    <body>
        <form id="Form1" runat="server">
            <ext:ResourceManager ID="ResourceManager1" runat="server" />
            
            <ext:Store runat="server" ID="Store1">
                <Reader>
                    <ext:JsonReader IDProperty="name">
                        <Fields>
                            <ext:RecordField Name="name" />
                            <ext:RecordField Name="nickName" />
                        </Fields>
                    </ext:JsonReader>
                </Reader>
            </ext:Store>   
                           
            <ext:Panel ID="ImagePanel" runat="server" Width="535" Layout="Fit">
                <Items>
                    <ext:DataView ID="DataView1" runat="server" ItemSelector="div.wrap" StoreID="Store1" AutoHeight="true">
                        <Template ID="Template1" runat="server">
                            <Html>
                                <tpl for=".">
                                    <div class="wrap">{name} aka {nickName}</div>
                                </tpl>
                            </Html>
                        </Template>                         
                        <PrepareData Fn="prepareData" />                
                    </ext:DataView>
                </Items>
                
                <Buttons>
                    <ext:Button ID="Button1" runat="server" Text="Export Data Back to Self (this is okay)">
                        <Listeners>
                            <Click Handler="Ext.net.DirectMethods.ExportData(#{Store1}.getRecordsValues({visibleOnly:true, excludeId:true}));" />
                        </Listeners>
                    </ext:Button>
                    <ext:Button ID="Button2" runat="server" Text="Export via ASHX (this will fail)">
                        <Listeners>
                            <Click Handler="doExport('csv', Ext.encode(#{Store1}.getRecordsValues({visibleOnly:true, excludeId:true})));" />
                        </Listeners>
                    </ext:Button>
                </Buttons>
            </ext:Panel>
        </form>
    </body>
    </html>
    Notes:

    • As per the original post, in the prepareData, I have no choice but to create the HTML there sometimes, which is the cause of the problem (the HTML is assigned back to the data, and when exporting to an ashx, ASP.NET will catch that in its http request validation as the HTML looks malicious to it)
    • You might ask why I am exporting to an ashx and not just do this in post back? The real scenario is that almost everything is dynamically generated on demand (the DataView, the XTemplate etc), and as explained further in this post I cannot therefore successfully post back in some occassions: http://forums.ext.net/showthread.php...o-another-page
    • So, as per that above post, I have a way to export a store to another ashx handler which can then generate the CSV/Excel etc


    Now, the ashx (in the same directory)
    using System.Web;
    
    namespace Ext.Net.Tests.Template.TemplateWithHtmlData
    {
        public class Export : IHttpHandler
        {
            public void ProcessRequest(HttpContext context)
            {
                // the moment you request the first thing off the Request, if there is html in it, it will fail
                var data = context.Request["exportFormat"];
            }
    
            public bool IsReusable
            {
                get { return false; }
            }
        }
    }
    So, the moment you export to the handler, ASP.NET 4 will give you this error
    A potentially dangerous Request.Form value was detected from the client (storeRecords="...ickName":"<a href='#' class='n...").

    Description: Request Validation has detected a potentially dangerous client input value, and processing of the request has been aborted. This value may indicate an attempt to compromise the security of your application, such as a cross-site scripting attack. To allow pages to override application request validation settings, set the requestValidationMode attribute in the httpRuntime configuration section to requestValidationMode="2.0". Example: <httpRuntime requestValidationMode="2.0" />. After setting this value, you can then disable request validation by setting validateRequest="false" in the Page directive or in the <pages> configuration section. However, it is strongly recommended that your application explicitly check all inputs in this case. For more information, see http://go.microsoft.com/fwlink/?LinkId=153133.

    Exception Details: System.Web.HttpRequestValidationException: A potentially dangerous Request.Form value was detected from the client (storeRecords="...ickName":"<a href='#' class='n...").
    Notes

    • This is targetting .NET 4 framework
    • App pool is the default ASP.NET 4 Integrated Mode app pool
    • This error now occurs in ashx handlers as well, as of ASP.NET 4, due to the way the processing pipeline has changed (see this for more info: http://www.asp.net/learn/whitepapers...__Toc256770147)
    • I could of course disable http request validation but I'd rather not do that, and hence having the option to get the data records from the json property instead of data property would be really useful.
  4. #4
    Thanks for the additional details and the sample.

    Your case looks rather specific to move us to add this 'json' feature. At least you are the first who requested:)

    One thing to clarify.

    I understand your sample is not real, but could you clarify why don't you want to move all html to templates? I mean:
    data.nickName = "<a href='#' class='nickname'>" + data.nickName + "</a>";
    So, this <a> (and other html in your real sample) would look better in XTemplate's Html. Why not?
  5. #5
    Hi,

    My first comment has more details and reasons why, but as a summary, the reason I can't put that HTML in the XTemplate is because we cannot define the XTemplate upfront - it is all dynamic (the generation of the DataView/Xtemplate is all determined by C# but while that is data driven it is cannot fully predict the UI needs). The best I can do with the XTemplate is provide a skeleton. Then in our different uses/scenarios, we inject different JavaScript to format based on either the data that we have (sometimes different data results in different HTML).

    If this was all, then we can use XTemplate's condition capabilities, but we have scenarios where we deploy the solution (the Xtemplate/DataView is generated by C# not markup) and the solution integrator needs to tweak things with JavaScript only which is why the JavaScript does the kind of thing you noted.

    I appreciate this may be edge case (I could look at dependency injection to allow XTemplate definitions per specific case maybe though that will require the integrators know even more about ExtJs/Ext.Net whereas the current solution only requires them to write simple JavaScript functions), in which case I could continue using my extension function. The only drawback of course is I will need to keep a track on whether the logic for the getRecordsValues changes, to keep mine in sync.

    Thanks,
    Anup
  6. #6
    Hi,

    Well, i suggest to create separate field in the prepareData
    data.anchorNickName = "<a href='#' class='nickname'>" + data.nickName + "</a>";
    and use that field in the template

    in getRecordsValues use filterField method to exclude that field, please see
    https://examples1.ext.net/#/GridPane...s/Save_Filter/
  7. #7
    Thanks for that suggestion; the filterField approach looks useful.

    It does, however, mean that the solution integrator needs to be aware of more things to do now (not just formatting the result, but if they want to keep the export to excel working to make tweaks to support that too, which otherwise "just works" from their perspective). I might be able to encapsulate all that however, so will see if that is a possibility.

    I think one drawback will be that because the XTemplate is also auto generated it cannot be modified to create an additional field. But I could try a variation: update the original data as I am doing now, but keep a backup of that data in another property, then use the filter field to send the "backedup" property. These DataViews/Stores are not editable, so that may be safe in my case.

    I'll measure performance difference of that vs using my own getRecordsJsonValues and decide accordingly. Maybe okay to close this for now, and if I feel strongly that I can't do this and that maintaining getRecordsJsonValues is also not a good idea, then I will try to make the case again :)
  8. #8
    Hi,

    Why not just override the .getRecordsValues() function with your own implementation of .getRecordsValues()?
    Geoffrey McGill
    Founder
  9. #9
    Hi Geoffrey,

    Yes, I could overwrite it, that is true. Whether I have a new method (as I have done in my original post) or overwrite it the original request would still remain as I'd otherwise have to maintain this new or overriding method if you ever had to change the logic.

    Given that it is an edge case for you, I am happy for now to continue with my approach (or override), or try Vladimir's approach unless it starts to become really painful in some way, which I think I'll only see months later.

    (Forgot to add -- it is kinda edge case for me too :) That is, even in our solution we only call this method in certain circumstances and in most cases use your implementation. That being said, if I override it I can get both with the various caveats above)

    Thanks,
    Anup
  10. #10
    Quote Originally Posted by anup View Post
    I'd otherwise have to maintain this new or overriding method if you ever had to change the logic.
    Not sure that we would ever change this method, at least a possible change should not break the existing applications.
Page 1 of 2 12 LastLast

Similar Threads

  1. [CLOSED] Extend timeout for loading a store
    By 78fede78 in forum 1.x Legacy Premium Help
    Replies: 3
    Last Post: Feb 22, 2013, 5:35 AM
  2. [CLOSED] Store: Passing JSON data directly to Server-Side
    By nhg_itd in forum 1.x Legacy Premium Help
    Replies: 2
    Last Post: Feb 09, 2012, 2:08 AM
  3. Replies: 4
    Last Post: Jan 12, 2012, 11:47 AM
  4. [CLOSED] Store Data as JSON
    By amitpareek in forum 1.x Legacy Premium Help
    Replies: 2
    Last Post: Dec 08, 2010, 1:25 AM
  5. Replies: 3
    Last Post: Feb 25, 2010, 10:25 AM

Posting Permissions