Разработка
19
Окт
4

Картинки на форме CRM, эпизод III

Продолжаем рассматривать способы добавления рисунков на форму (в продолжение постов Картинки на форме CRM, эпизод I и Картинки на форме CRM, эпизод II). Сегодня нас ждут два примера…

Вариант #1

Идея состоит в том, чтобы использовать стандартное диалоговое окно выбора файла для указания пути к файлу рисунка, который следует отобразить в iFrame’е. К тому же диалог выбора файла позволяет просматривать файлы рисунков как миниатюры, что позволяет более «комфортно» подбирать рисунок.

Итак…

  • Для начала расшарьте какую-либо папку в сети, которая будет использоваться как хранилище для рисунков;
  • Добавьте к нужному объекту новый текстовый атрибут с именем new_image;
  • Добавьте на форму (в данном примере это форма Контакта) новую секцию. Задайте для нее форматирование колонок 1:1 и с фиксированной шириной;
  • Перенесите в эту новую секцию поля из основной (т.е. самой верхней) секции и расположите в левой части секции. А саму первую секцию удалите с формы;
  • Затем добавьте на форму iFrame с именем IFRAME_image. А в качестве URL укажите путь к какому-либо рисунку из расшаренной сетевой папки в сетевом формате (т.е. \\<имя_компьютера\<имя_расшаренной_папки>). Этот рисунок будет выполнять роль дефолтного рисунка, отображаемого, когда ни какой-другой явно не указан пользователем. Высоту iFrame’а задайте в соответствии с высотой секции. Поместите iFrame в новую секцию справа;
  • Под iFrame’ом поместите только что созданное поле new_image и отключите его;
  • А на онлоад формы повесьте такой скрипт:
    // Функция добавляет к полю (передаваемому в качестве параметра) кнопку-рисунок (в правой части)
    TextHelperButton = function(fieldId) {
        var fldButton = this;
    
        fldButton.Field = crmForm.all[fieldId];
    
        if (!fldButton.Field) {
            return alert("Поле не найдено: " + fieldId);
        }
    
        fldButton.Click = null;
        fldButton.Image = new ButtonImage();
        fldButton.Paint = function() {
            var field_d = document.all[fldButton.Field.id + "_d"];
            if (field_d) {
                field_d.style.whiteSpace = "nowrap";
                field_d.appendChild(fldButton.Image.ToObject())
            }
        }
    
        fldButton.MouseOver = function() {
            event.srcElement.src = fldButton.Image.MouseOver;
        }
    
        fldButton.MouseOut = function() {
            event.srcElement.src = fldButton.Image.MouseOut;
        }
    
        function ButtonImage() {
            this.MouseOut = "/_imgs/lookupOff.gif";
            this.MouseOver = "/_imgs/lookupOn.gif";
            this.Width = 21
    
            this.ToObject = function() {
                var img = document.createElement("IMG");
                img.onmouseover = fldButton.MouseOver;
                img.onmouseout = fldButton.MouseOut;
                img.onclick = fldButton.Click;
                img.src = this.MouseOut;
    
                var cssText = "vertical-align:bottom;";
                cssText += "position:relative;";
                cssText += "right:" + (this.Width) + "px";
                img.style.cssText = cssText;
                return img;
            }
        }
    }
    
    // Функция вызывает диалоговое окно выбора файла и сохранает выбранный файл в текстовом поле
    function SelectAvatar() {
        var dialog = new ActiveXObject("MSComDlg.CommonDialog");
        if (dialog) { // Диалог выбора файла с помощью MSComDlg.CommonDialog
            // Задаем фильтр на файлы отображаемые диалоговом окне выбора файла
            dialog.Filter = "JPEG|*.jpg;*.jpeg|GIF|*.gif|PNG|*.png";
            // Задаем начальный путь для диалогого окна выбора файла (т.е. текущую картинку)
            dialog.FileName = document.all.IFRAME_image.src;
            dialog.MaxFileSize = 1024; // Ограничение по размеру файлов
            dialog.ShowOpen(); // Открываем диалог
    
            // Сохраняем путь к выбранному рисунку в поле new_image
            crmForm.all.new_image.DataValue = dialog.FileName;
            // Т.к. поле отключено, то принудительно отправляем его данные на сервер
            crmForm.all.new_image.ForceSubmit = true;
            // Изменяем в iFrame'е путь к рисунку
            document.all.IFRAME_image.src = crmForm.all.new_image.DataValue;
        } else { // Диалог выбора файла с помощью <input type=file>
            if (!document.getElementById("imageBrowse")) {
                fileElement = document.createElement("<input type=file id='imageBrowse' style='display: none;' />");
                document.getElementsByTagName("BODY")[0].appendChild(fileElement);
            }
            fileElement.click();
            // Если пользователь выбрал файл, то...
            if (fileElement.value) {
                // Сохраняем путь к выбранному рисунку в поле new_image
                crmForm.all.new_image.DataValue = fileElement.value;
                // Т.к. поле отключено, то принудительно отправляем его данные на сервер
                crmForm.all.new_image.ForceSubmit = true;
                // Изменяем в iFrame'е путь к рисунку
                document.all.IFRAME_image.src = crmForm.all.new_image.DataValue;
            }
        }
    }
    
    // Добавляем к полю new_image кнопку
    var avaterBtn = new TextHelperButton("new_image");
    avaterBtn.Click = SelectAvatar; // При щелчке на ней вызываем функцию SelectAvatar
    avaterBtn.Paint();
    
    // Если в поле new_image есть значение, подставляем его при загрузке формы в iFrame как источник
    if (crmForm.all.new_image.DataValue) {
        document.all.IFRAME_image.src = crmForm.all.new_image.DataValue;
    }
    

    Что тут происходит:

    • В начале мы определяем функцию, которая по переданному ей имени поля, добавляет к нему справа кнопку;
    • Затем идет определение функции SelectAvatar, которая отвечает за отображение диалогового окна выбора файла. Тут возможно два варианта работы диалогового окна: через ActiveX контрол MSComDlg.CommonDialog или через HTML тег
      <input type=»file» />. У того и другого способа есть достоинства и недостатки, а именно:

      • MSComDlg.CommonDialog – не является стандартным элементом управления, а поставляется как доп компонент с такими программами как Visual Basic и т.п. Как следствие установлен не на всех компьютерах, поэтому не всегда может использоваться. Приемуществом является его гибкая настройка http://msdn.microsoft.com/en-us/library/aa155724(office.10).aspx перед вызовом, что дает возможность контролировать действия пользователя (например, задавать какие файлы он может выбирать или исходную папку для диалогового окна).
        З.Ы. Если количество компьютеров, с которых осуществляется доступ к CRM ограничено, то Вы можете установить контрол вручную, установив Visual Basic 5.0 Control Creation Edition. И не забудьте понизить параметры безопасности для ActiveX-компонентов в браузере для группы безопасности в которой находится сайт CRM;
      • <input type=»file» /> — стандартный HTML-тег. Преимюществом его является доступеность с любой маштиный. А недостатком, то, что настройки сильно ограничены по сравнению MSComDlg.CommonDialog (напрмиер, нельзя задавать исходную папку).
        З.Ы.2 Учитите также, что в данном примере через
        <input type=»file» /> не реализована проверка, того, какой файл выбрал пользователь. Поэтому, если он выберет, например, файл с расширением .exe, то получит ошибку при подстановки ссылки на нее в iFrame 🙂 Так что Вам есть куда развивать скрипт 🙂

      В связи с вышесказанным, мы сначала пытается использовать MSComDlg.CommonDialog, а если не получается то переходим к
      <input type=»file» />;

    • После того как юзвер выбрал какой-либо файл мы его записываем в поле new_image и изменяем путь к файлу рисунка в iFrame’е;
    • Ну, а в самом низу мы прикручиваем к полю new_image кнопку и загружаем в iFrmae рисунок, если поле new_image содержит значение.
  • Публикуем и тестируем!





Вариант #2

Решение заключается в написании кастомной ASPX-страницы, которая будет загружаться в iFrame и выводит перечень рисунков приаттаченных к текущей карточке.

Начнем:

  • Создайте новый ASP.NET веб-сайт, подключите к нему стандартные сборки CRM SDK;
  • На дефолтной .aspx странице, между тегами FORM добавьте такие строчки:
    <asp:Panel ID="panelPictures" runat="server">
    </asp:Panel>
    

    Panel это контрол ASP.NET, эквивалентный разметке DIV в HTML (которой он и будет заменен при рендеринге). Здесь он нужен для того, чтобы помещать в него рисунки, которые мы будем, вытаскивать из примечаний записей;

  • Перейдите на страницу *.aspx.cs и поместите на нее такой код:
    using System;
    using System.Configuration;
    using System.Data;
    using System.Web;
    using System.Web.Security;
    using System.Web.UI;
    using System.Web.UI.HtmlControls;
    using System.Web.UI.WebControls;
    using System.Web.UI.WebControls.WebParts;
    
    using System.Collections.Generic;
    using Microsoft.Crm.SdkTypeProxy;
    using Microsoft.Crm.Sdk.Query;
    using Microsoft.Crm.Sdk;
    
    public partial class _Default : System.Web.UI.Page 
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            Guid id = Guid.Empty;
            int objecttypecode = 0;
    
            try // Вытаскиваем из URL GUID и код типа записи
            {
                id = new Guid(Request.QueryString["id"]);
                objecttypecode = Int16.Parse(Request.QueryString["type"]);
            }
            catch
            {
                // Если таковых нет, то подставляем дефолтные значения (их всегда нет, у новых записей)
                id = new Guid("{02DFFBED-58A6-DF11-AAA7-000C29F6B309}");
                objecttypecode = 2;
            }
            // Подключаемся к CRM-сервису
            CrmAuthenticationToken token = new CrmAuthenticationToken();
            token.AuthenticationType = 0; //Active Directory
            token.OrganizationName = "superfirma";
            CrmService crmService = new CrmService();
            crmService.Url = "http://win-n22hj23d1b1/mscrmservices/2007/crmservice.asmx";
            crmService.UseDefaultCredentials = true;
            crmService.PreAuthenticate = true;
            crmService.CrmAuthenticationTokenValue = token;
    
            // Формируем запрос который возвращает все Примечания текущей формы (GUID и код типа переданы в URL) 
            RetrieveMultipleRequest rmRequest = new RetrieveMultipleRequest();
            QueryByAttribute query = new QueryByAttribute();
            query.Attributes = new string[] { "objectid", "objecttypecode" }; ;
            query.Values = new object[] { id, objecttypecode };
            QueryBase queryBase = query;
            queryBase.EntityName = "annotation";
            queryBase.ColumnSet = new ColumnSet(new string[] { "filesize", "filename" });
            rmRequest.Query = queryBase;
    
            // Отправляем запрос в CRM
            RetrieveMultipleResponse rmResponse = (RetrieveMultipleResponse)crmService.Execute(rmRequest);
            BusinessEntityCollection Annotations = rmResponse.BusinessEntityCollection;
    
            // Просматриваем все возвращенные примечания и ищем среди рисунки
            foreach (annotation Annotation in Annotations.BusinessEntities)
            {
                Guid annotationid = (Guid)Annotation.annotationid.Value;
                string filename = Annotation.filename;
                int filesize;
                filesize = Annotation.filesize.Value;
    
                // Разрешаем для показа только маленькие картинки в формате JPEG
                if (filename.ToLower().EndsWith("jpg") && filesize < 500000)
                {
                    // Для каждого рисунка формируем HTML-объект <IMG>
                    // Сам рисунок формируется страницей ShowImageAttachment.aspx, на нее и ссылается HTML-объект <IMG> передавая в URL GUID Примечания с нужным рисунком
                    string imageUrl = "ShowImageAttachment.aspx?id=" + annotationid.ToString();
                    // Задаем ссылку для скаивания оргинала рисунка; ее также предоставляет страница ShowImageAttachment.aspx, есои в URL передан параметр r=1
                    string navigateUrl = "ShowImageAttachment.aspx?r=1&id=" + annotationid;
                    HyperLink hl = new HyperLink();
                    hl.ImageUrl = imageUrl;
                    hl.NavigateUrl = navigateUrl;
                    hl.Target = "_self";
                    hl.Text = filename + " (" + (filesize / 1000).ToString("#,##0") + " KB)";
                    hl.ToolTip = filename + " (" + (filesize / 1000).ToString("#,##0") + " KB)";
                    // Поочередно добавляем созданные объекты рисунков на страницу
                    panelPictures.Controls.Add(hl);
                }
            }
        }
    }
    

    Что тут мы делаем…

    • Для начала вытаскиваем из URL GUID записи и код ее типа. Если таковых нет, то используем некоторые дефолтные значения (также используется как заглушка для новых записей, у которых по определению нет GUID’а);
    • Далее подключаемся к CRM серверу;
    • Вытаскиваем из CRM все примечания текущей записи;
    • Просматриваем их и отбираем только те, которые содержат файл в формате *.jpg и имею размер менее 500 Кб;
    • Из каждой отобранной записи примечания, создаем объект рисунка и помещаем его в Panel. При этом сам рисунок генерируется страницей ShowImageAttachment.aspx (ее мы создадим далее), которой передается GUID каждого Примечания, содержащего подходящий рисунок. А точнее, генерируется не рисунок, а его миниатура, содержащая гиперссылку на ту же страницу ShowImageAttachment.aspx и при клике на которой пользователю будет предложено скачать полную версию рисунка;
  • Добавьте к проекту новую ASPX-страницу и назвоите ее ShowImageAttachment.aspx;
  • Удалите из ShowImageAttachment.aspx весь код кроме первой строчки;
  • В ShowImageAttachment.aspx.cs добавьте такой код:
    using System;
    using System.Collections;
    using System.Configuration;
    using System.Data;
    using System.Web;
    using System.Web.Security;
    using System.Web.UI;
    using System.Web.UI.HtmlControls;
    using System.Web.UI.WebControls;
    using System.Web.UI.WebControls.WebParts;
    
    using System.Collections.Generic;
    using System.Drawing;
    using System.Drawing.Drawing2D;
    using System.Drawing.Imaging;
    using System.IO;
    using Microsoft.Crm.SdkTypeProxy;
    using Microsoft.Crm.Sdk.Query;
    using Microsoft.Crm.Sdk;
    using System.Text;
    
    public partial class ShowImageAttachment : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            // Подключаемся к CRM-сервису
            CrmAuthenticationToken token = new CrmAuthenticationToken();
            token.AuthenticationType = 0; //Active Directory
            token.OrganizationName = "superfirma";
            CrmService crmService = new CrmService();
            crmService.Url = "http://win-n22hj23d1b1/mscrmservices/2007/crmservice.asmx";
            crmService.UseDefaultCredentials = true;
            crmService.PreAuthenticate = true;
            crmService.CrmAuthenticationTokenValue = token;
    
            // Вытаскиваем из CRM приаттаченый рисунок, GUID которого был передан в URL 
            Guid annotationid = new Guid(Request.QueryString["id"]);
            annotation crmAnnotation = (annotation)crmService.Retrieve("annotation", annotationid, new AllColumns());
    
            // Если среди параметров URL есть "r" равный 1, то возвращаем рисунок с предложением открыть его или сохранить
            if (Request.QueryString["r"] == "1")
            {
                using (FileStream fileStream = new FileStream(crmAnnotation.filename, FileMode.OpenOrCreate))
                {
                    byte[] fileContent = Convert.FromBase64String(crmAnnotation.documentbody);
                    Response.ContentType = crmAnnotation.mimetype;
                    Response.AddHeader("content-disposition", "attachment; filename=" + crmAnnotation.filename);
                    Response.BinaryWrite(fileContent);
                }
            }
            else // Иначе возвращаем миниатюру рисунка с высотой 150px и пропорционально измененной шириной
            {
                byte[] picture = System.Convert.FromBase64String(crmAnnotation.documentbody);
                System.IO.MemoryStream ms = new System.IO.MemoryStream(picture);
                System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(ms);
                int height = 150;
                int width = height * bmp.Width / bmp.Height;
                Response.ContentType = crmAnnotation.mimetype;
                if (bmp.Height > height)
                {
                    System.Drawing.Image imgPhoto = System.Drawing.Image.FromStream(ms);
                    System.Drawing.Bitmap bmPhoto = new System.Drawing.Bitmap(width, height, bmp.PixelFormat);
                    bmPhoto.SetResolution(bmp.HorizontalResolution, bmp.VerticalResolution);
                    Graphics grPhoto = Graphics.FromImage(bmPhoto);
                    grPhoto.SmoothingMode = SmoothingMode.AntiAlias;
                    grPhoto.InterpolationMode = InterpolationMode.HighQualityBicubic;
                    grPhoto.PixelOffsetMode = PixelOffsetMode.HighQuality;
                    grPhoto.DrawImage(imgPhoto, new Rectangle(0, 0, width, height), 0, 0, bmp.Width, bmp.Height, GraphicsUnit.Pixel);
                    MemoryStream mm = new MemoryStream();
                    bmPhoto.Save(Response.OutputStream, System.Drawing.Imaging.ImageFormat.Jpeg);
                    imgPhoto.Dispose();
                    bmPhoto.Dispose();
                    grPhoto.Dispose();
                }
                else
                {
                    bmp.Save(Response.OutputStream, System.Drawing.Imaging.ImageFormat.Jpeg);
                }
                bmp.Dispose();
            }
        }
    }
    

    Здесь происходит следующее:

    • Подключаемся к CRM серверу;
    • Вытаскиваем из URL GUID Примечания содеращего нужный рисунок;
    • Вытаскиваем из CRM само Примечание (а вместе с ним имя рисунка и его содержимое);
    • Если в URL имеется переменная «r» со значение 1, то формируем полный рисунок и возвращаем пользователю с предложением либо сохранить, либо открытье его. Если же переменная «r» отсутствует, то возвращаем миниатюру рисунка (с высотой 150 px);
  • А в web.config, как всегда, добавьте такой код:
    <?xml version="1.0"?>
    <configuration>
      <appSettings/>
      <connectionStrings/>
      <system.web>
        <httpModules>
          <add name="MapOrg" type="Microsoft.Crm.MapOrgEngine, Microsoft.Crm, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
          <add name="CrmAuthentication" type="Microsoft.Crm.Authentication.AuthenticationEngine, Microsoft.Crm, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
        </httpModules>
        <identity impersonate="true"/>
        <compilation debug="true">
          <assemblies>
            <add assembly="Microsoft.Crm.Sdk, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
            <add assembly="Microsoft.Crm.SdkTypeProxy, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
          </assemblies>
        </compilation>
      </system.web>
    </configuration>
    
  • Опубликуйте сайт в папку <сайт CRM>\isv\image (которую предварительно создайте);
  • Создайте на форме Контакта iFrame как описано в предыдущем примере, только укажите у него в URL путь к Вашей дефольной странице только что опубликованного сайта (у меня это \isv\image\Default.aspx), а также поставьте галку Отправить код типа объекта и уникальный идентификатор как параметры и снимите галку Ограничить использование сценариев между кадрами;
  • Зетс ол 🙂 Тестируем: откройте какую-нибудь карточку Контакта, приаттачте к ней несколько небольших рисунков в формате *.jpg, перезагрузите карточку и идите смотрить на iFrаme 🙂







Комментарии (4)
  • Борис 19.10.2010

    На сайте есть много интересного 😉 Есть пост: «Замена представления предварительного просмотра»

    возможно ли вот эту картинку туда перетянуть через fetch запрос либо как то иначе?

    С уважением, Борис.

  • Борис 19.10.2010

    **имею ввиду из предложенного первого варианта, когда у нас есть адрес к картинке. По логике нужно всего лишь вытянуть адрес, и отобразить эту картинку в превью…это реально? направьте пожалуйста, через что…фетч?

  • Борис 19.10.2010
  • slivka_83 19.10.2010

    Здасьте 🙂
    Честно говоря задача не тривиальная 🙂 В «Замена представления предварительного просмотра» используется .Net для подмены представления. Думаю если в это представление вставить iFrame и внего подставить второй вариант, возможно получится. Только нужно как то в iFrame передать GUID записи 🙂

*

code