• 在Dynamics CRM中使用Bootstrap


    我是微软Dynamics 365 & Power Platform方面的工程师罗勇,也是2015年7月到2018年6月连续三年Dynamics CRM/Business Solutions方面的微软最有价值专家(Microsoft MVP),欢迎关注我的微信公众号 MSFTDynamics365erLuoYong ,回复165或者20151023可方便获取本文,同时可以在第一间得到我发布的最新博文信息,follow me!

    知识学会了是用来用的,不用则没有,格物致知,学以致用,这也是我认为儿子罗格名字中格的意思。最近在学习Bootstrap,大致看了一遍,所以想来拿用用练手,替换掉以前使用的jQuery UI,说实话jQuery UI放入到Dynmiacs CRM使用起来稍显麻烦。我这个第一个练习还是选择放到我熟悉的Dynamics CRM中去看看,很可能也是首次有人公开写博客介绍在Dynamics CRM中使用吧,不小心成了第一个吃螃蟹的人,呵呵。关键是吃下了,味道还不错!
    Bootstrap 首页的介绍也毫不谦虚,英文原文是:Bootstrap is the most popular HTML, CSS, and JS framework for developing responsive, mobile first projects on the web. 我翻译过来的意思是这样:网络上用来开发响应式、移动优先项目的最流行的HTML,CSS 和JS框架。各位看官请注意,最流行,没有之一。
     
    首先郑重说明,本人是刚学习Bootstrap的菜鸟,欢迎各位大侠指点,本文若有误导误用之处实属正常,请各位看官阅读本博文前做好心理准备。敬请关注后续文章:在Dynamics CRM中使用Bootstrap之二:定制Bootstrap
     
    闲话少说,我们先到下载页面将其下载下来,我今天下载的是最新的 3.3.5 版本,当然是用第一个下载链接啦。
     
    解压后的 bootstrap-3.3.5-dist 文件夹中有三个文件夹,分别是 css, fonts 和 js ,有些文件是不需要作为Web资源上传到CRM中的。有一个问题是 fonts 中的文件类型是Web资源不支持的文件类型,下面是fonts文件夹中的文件:
     
    而Dynamics CRM中的Web资源类型目前仅支持如下种类,囧。
      
    还有就是有些文件的命名中使用了中划线,而CRM中的Web资源的命名是不允许使用中划线的。
      
    怎么办?中国有句话叫,有条件要上,没有条件创造条件也要上,让我们来创造下!
    文件类型不支持的解决办法可以参考这篇文章:How to work around the Web Resource file type restriction in Microsoft CRM 。我将fonts文件夹中的所有文件都加上一个后缀 .xml ,名称我这里如下,也就是将原来文件名中的中划线改成了下划线,加上了前缀 /common/fonts/ ,然后以 数据(XML)格式上传。
     
     
    我也尝试过用如下的代码来创建Web资源想绕过名称属性中的中划线检查,但是给我报错:Web resource names may only include letters, numbers, periods, and nonconsecutive forward slash characters. 看来这个限制挺严格的,把name属性的中划线改成下划线就可以了。
                var service = GetOrganizationService();
                var entity = new Entity("webresource");
                entity["name"] = "new_/common/fonts/glyphicons-halflings-regular.eot";
                entity["displayname"] = "glyphicons-halflings-regular.eot";
                using (FileStream fs = File.OpenRead(@"C:UsersluoyongDownloadsootstrap-3.3.5-distootstrap-3.3.5-distfontsglyphicons-halflings-regular.eot"))
                {
                    byte[] bytes = new byte[fs.Length];
                    fs.Read(bytes, 0, bytes.Length);
                    entity["content"] = Convert.ToBase64String(bytes);
                }
                entity["webresourcetype"] = new OptionSetValue(4);//XML
                service.Create(entity);
    后来我干脆写了个程序,方便以后上传这些web资源,如下:注意,Bootstrap下载后并不包括jQuery,但是却需要jQuery,所以请自行去 jQuery 官方网站 下载,我这里用的jQuery是支持IE6,7,8的最新版,1.11.3 版本。还有部分文件名字的中划线改成了下划线,CSS文件中引用的名称也要做响应更改,我的代码考虑到这个了。
            static void Main(string[] args)
            {
                var service = GetOrganizationService();
                const string bootstrapBaseDir = @"C:UsersluoyongDownloadsootstrap-3.3.5-distootstrap-3.3.5-dist";
                const string solutionPrefix = "new_";
                Dictionary<string, string> files = new Dictionary<string, string>();//bootstrap依赖的Web资源
                files.Add("glyphicons-halflings-regular.eot", "fonts");
                files.Add("glyphicons-halflings-regular.svg", "fonts");
                files.Add("glyphicons-halflings-regular.ttf", "fonts");
                files.Add("glyphicons-halflings-regular.woff", "fonts");
                files.Add("glyphicons-halflings-regular.woff2", "fonts");
                files.Add("bootstrap.min.js", "js");
                files.Add("jquery-1.11.3.min.js", "js");
                files.Add("bootstrap.min.css", "css");
                files.Add("bootstrap-theme.min.css", "css");
                //处理css文件,记得要替换fonts文件夹中的内容
                foreach (var item in (from item in files where item.Value == "css" select item).ToDictionary(item => item.Key, item => item.Value))
                {
                    var fontEntity = new Entity("webresource");
                    fontEntity["displayname"] = item.Key;
                    string text = File.ReadAllText(@bootstrapBaseDir + item.Value + "\" + item.Key);
                    foreach (var fontitem in (from fontitem in files where fontitem.Value == "fonts" select fontitem).ToDictionary(fontitem => fontitem.Key, fontitem => fontitem.Value))
                    {
                        text = text.Replace(fontitem.Key, fontitem.Key.Replace("-", "_"));
                    }
                    File.WriteAllText(@bootstrapBaseDir + item.Value + "\" + item.Key, text);
                    using (FileStream fs = File.OpenRead(@bootstrapBaseDir + item.Value + "\" + item.Key))
                    {
                        byte[] bytes = new byte[fs.Length];
                        fs.Read(bytes, 0, bytes.Length);
                        fontEntity["content"] = Convert.ToBase64String(bytes);
                    }
                    fontEntity["webresourcetype"] = new OptionSetValue(2);//CSS
                    fontEntity["name"] = solutionPrefix + @"/common/css/" + item.Key.Replace("-", "_");
                    service.Create(fontEntity);
                }
                //处理fonts文件夹
                foreach (var item in (from item in files where item.Value == "fonts" select item).ToDictionary(item => item.Key, item => item.Value))
                {
                    var fontEntity = new Entity("webresource");
                    fontEntity["displayname"] = item.Key;
                    using (FileStream fs = File.OpenRead(@bootstrapBaseDir + item.Value + "\" + item.Key))
                    {
                        byte[] bytes = new byte[fs.Length];
                        fs.Read(bytes, 0, bytes.Length);
                        fontEntity["content"] = Convert.ToBase64String(bytes);
                    }
                    fontEntity["webresourcetype"] = new OptionSetValue(4);//XML
                    fontEntity["name"] = solutionPrefix + @"/common/fonts/" + item.Key.Replace("-", "_");
                    service.Create(fontEntity);
                }
                //处理js文件夹
                foreach (var item in (from item in files where item.Value == "js" select item).ToDictionary(item => item.Key, item => item.Value))
                {
                    var fontEntity = new Entity("webresource");
                    fontEntity["displayname"] = item.Key;
                    using (FileStream fs = File.OpenRead(@bootstrapBaseDir + item.Value + "\" + item.Key))
                    {
                        byte[] bytes = new byte[fs.Length];
                        fs.Read(bytes, 0, bytes.Length);
                        fontEntity["content"] = Convert.ToBase64String(bytes);
                    }
                    fontEntity["webresourcetype"] = new OptionSetValue(3);//javascript
                    var pattern = @"jquery.*min.js";//用正则表达式替换juery版本号
                    Regex rgx = new Regex(pattern);
                    fontEntity["name"] = solutionPrefix + @"/common/js/" + rgx.Replace(item.Key.Replace("-", "_"),"jquery.min.js");
                    service.Create(fontEntity);
                }
                Console.WriteLine("程序运行完成!");
                Console.ReadKey();
            }
    Bootstrap依赖的内容都已Web资源形式上传后,总共9个,如下:
     
     
    我这里是使用文本替换掉CSS中指定的字体文件名称中的下划线,也可以通过定制的方法,具体参考我这篇文章: 在Dynamics CRM中使用Bootstrap之二:定制Bootstrap 。
     
    下面我们就可以在自己新建的HTML Web资源中引用Bootstrap 了。我这里使用的代码如下:
    <!DOCTYPE html>
    <html>
    <head>
        <title>微软MVP罗勇测试注释</title>
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <link rel="stylesheet" href="../common/css/bootstrap.min.css">
    </head>
    <body>
        <div class="container-fluid">
            <table id="notestable" class="table table-striped table-bordered table-hover table-condensed">
                <thead>
                    <tr>
                        <th class="text-nowrap">序号</th>
                        <th>注释标题</th>
                        <th>注释内容</th>
                        <th>创建人</th>
                        <th>创建时间</th>
                        <th>修改人</th>
                        <th>修改时间</th>
                        <th>附件名称</th>
                        <th class="text-nowrap">附件大小(KB)</th>
                    </tr>
                </thead>
                <tbody>
                </tbody>
            </table>
            <blockquote class="pull-right">这是微软MVP罗勇学习Bootstrap后的第一次练习!<small>Powered by Bootstrap</small></blockquote>
            <div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
                <div class="modal-dialog" role="document">
                    <div class="modal-content">
                        <div class="modal-header">
                            <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
                            <h4 class="modal-title" id="myModalLabel">附件 <small>罗勇使用Bootstrap做的效果</small></h4>
                        </div>
                        <div class="modal-body">
                        </div>
                        <div class="modal-footer">
                            <button type="button" class="btn btn-default" data-dismiss="modal">关闭</button>
                        </div>
                    </div>
                </div>
            </div>
        </div>
        <script type="text/javascript" src="../../ClientGlobalContext.js.aspx"></script>
        <script type="text/javascript" src="../common/js/jquery.min.js"></script>
        <script type="text/javascript" src="../common/js/bootstrap.min.js"></script>
        <script type="text/javascript" src="../common/XrmServiceToolkit.min.js"></script>
        <script type="text/javascript">
            Date.prototype.format = function (fmt) {
                var o = {
                    "M+": this.getMonth() + 1,//月份   
                    "d+": this.getDate(),//
                    "h+": this.getHours(),//小时   
                    "m+": this.getMinutes(),//
                    "s+": this.getSeconds()//
                };
                if (/(y+)/.test(fmt))
                    fmt = fmt.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length));
                for (var k in o)
                    if (new RegExp("(" + k + ")").test(fmt))
                        fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ?
                                 (o[k]) : (("00" + o[k]).substr(("" + o[k]).length)));
                return fmt;
            }
    
            function ShowAttachmentByNoteId(NoteId, MimeType) {
                if (MimeType.indexOf("image") > -1) {
    
                    $(".modal-body").children().remove();
                    var loadingBtn = $("<button type='button' class='btn btn-info btn-block'>正在加载...</button>");
                    loadingBtn.appendTo($(".modal-body"));
                    XrmServiceToolkit.Rest.Retrieve(
                        NoteId,
                        "AnnotationSet",
                        "DocumentBody",
                        null,
                        function (result) {
                            $(".modal-body").children().remove();
                            var DocumentBody = result.DocumentBody;
                            var img = $("<img />");
                            img.attr("alt", "Embedded Image");
                            img.addClass("img-thumbnail");
                            img.attr("src", "data:" + MimeType + ";base64," + DocumentBody);
                            img.appendTo($(".modal-body"));
                        },
                        function (error) {
                            alert(error.message);
                        },
                        true
                    );
                }
                else if (MimeType.indexOf("officedocument.presentationml.presentation") > -1 || MimeType.indexOf("officedocument.spreadsheetml.sheet") > -1 || MimeType.indexOf("officedocument.wordprocessingml.document") > -1) {//office文档
                    var warningBtn = $("<button type='button' class='btn btn-info btn-block'>请在新窗口中查看!</button>");
                    $(".modal-body").children().remove();
                    warningBtn.appendTo($(".modal-body"));
                    window.open("https://view.officeapps.live.com/op/view.aspx?src=" + encodeURIComponent("http://mvpluoyong.azurewebsites.net/GetAnnotationDocument.ashx?AnnotationId=" + NoteId));
                }
                else if (MimeType.indexOf("pdf") > -1) {
                    var warningBtn = $("<button type='button' class='btn btn-info btn-block'>请在新窗口中查看!</button>");
                    $(".modal-body").children().remove();
                    warningBtn.appendTo($(".modal-body"));
                    window.open("http://mvpluoyong.azurewebsites.net/GetAnnotationDocument.ashx?AnnotationId=" + encodeURIComponent(NoteId));
                }
                else {
                    var warningBtn = $("<button type='button' class='btn btn-block btn-warning'>暂时不支持这种文件类型附件的查看!</button>");
                    $(".modal-body").children().remove();
                    warningBtn.appendTo($(".modal-body"));
                }
            }
    
            $(function () {
                var clientUrl = GetGlobalContext().getClientUrl();
                //var id = window.parent.Xrm.Page.data.entity.getId(); //这种方法可以获取表单中的很多信息,包括id
                var match = RegExp('[?&]id=([^&]*)').exec(window.location.search);//这里是外接通过url传递id的值过来
                var id = match && decodeURIComponent(match[1].replace(/+/g, ' '));
                match = RegExp('[?&]typename=([^&]*)').exec(window.location.search);
                var typename = match && decodeURIComponent(match[1].replace(/+/g, ' '));
                XrmServiceToolkit.Rest.RetrieveMultiple(
                "AnnotationSet",
                "?$select=AnnotationId,Subject,NoteText,MimeType,FileName,FileSize,IsDocument,CreatedOn,CreatedBy,ModifiedOn,ModifiedBy&$filter=ObjectTypeCode eq '" + typename + "' and ObjectId/Id eq guid'" + id + "'&$orderby=CreatedOn asc",
                function (results) {
                    for (var i = 0; i < results.length; i++) {
                        var tr = $("<tr></tr>");
                        tr.appendTo($("#notestable tbody"));
                        var td = $("<td class='text-center'>" + (i + 1) + "</td>");
                        td.appendTo(tr);
                        td = $("<td>" + (results[i].Subject == null ? "" : results[i].Subject) + "</td>");
                        td.appendTo(tr);
                        td = $("<td><a data-toggle='tooltip' title='点击我在新窗口中查看或者编辑本注释全部内容!' href='" + clientUrl + "/main.aspx?etn=annotation&pagetype=entityrecord&id=%7B" + results[i].AnnotationId + "%7D' target='_blank'>" + results[i].NoteText + (results[i].IsDocument ? " <span class='glyphicon glyphicon-paperclip'></span>" : "") + "</a></td>");
                        td.appendTo(tr);
                        td = $("<td>" + results[i].CreatedBy.Name + "</td>");
                        td.appendTo(tr);
                        td = $("<td>" + results[i].CreatedOn.format('yyyy-MM-ddThh:mm:ssZ') + "</td>");
                        td.appendTo(tr);
                        td = $("<td>" + results[i].ModifiedBy.Name + "</td>");
                        td.appendTo(tr);
                        td = $("<td>" + results[i].ModifiedOn.format('yyyy-MM-ddThh:mm:ssZ') + "</td>");
                        td.appendTo(tr);
                        td = $("<td>" + (results[i].FileName == null ? "" : ("<a href='#' data-toggle='modal' data-target='#myModal' data-annotationid='" + results[i].AnnotationId + "' data-mimetype='" + results[i].MimeType + "'>" + results[i].FileName + "</a>") + "</td>"));
                        td.find("a").click(function () {
                            ShowAttachmentByNoteId($(this).attr("data-annotationid"), $(this).attr("data-mimetype"));
                        });
                        td.appendTo(tr);
                        td = $("<td>" + (results[i].FileSize == null ? "" : Math.round((results[i].FileSize) / 1024)) + "</td>");
                        td.appendTo(tr);
                    }
                },
                function (error) {
                    alert(error.message);
                },
                function () {
                },
                true
                );
            });
        </script>
    </body>
    </html>
    我在这里主要运用了Bootstrap的几个小东西:
    1. tooltip,提示信息:
     
    2. 字体图标,Glyphicons. 可以看到包括附件的注释的注释内容列后面会多了一个附件的图标。
     
    3. 表格效果,完全没有自己写CSS和JavaScript,表格就带了各行换颜色,鼠标聚焦效果还行。
     
    4. 文本的居中,不换行,之前序号列和 附件大小(KB) 列会换行,序号列、表格的列标题的值是靠左的,应用后实现了想要的效果。
    5. 弹出模态窗口效果和图片的缩略图效果。
     
    6.含有警告信息的按钮。
     
     7. 引用效果,引用靠右对齐。
     
     
  • 相关阅读:
    新功能:查看TrackBack过来的文章
    新功能发布: 数据备份
    小题大作:.Text中恶意脚本过滤的新方法
    新增关闭邮件通知功能
    Will Mono Become the Preferred Platform for Linux Development
    Test Driven Development 资源
    收藏夹注意事项
    功能调整:阅读排行、回复排行
    首页增加了到第二书店的链接
    DotGNU Portable.NET
  • 原文地址:https://www.cnblogs.com/luoyong0201/p/Dynamics_365_Customer_Engagement_Using_Bootstrap.html
Copyright © 2020-2023  润新知