Welcome to TechNet Blogs Sign in | Join | Help

Рецепт передачи параметров веб-части в ascx, если ascx не компилируется в dll

Разбираем распространенный случай. Вы имеете пользовательский элемент управления (ascx) который лежит у вас в папке ...\Template\CONTROLTEMPLATES и не компилируется в сборку, а компилируется "на лету". Теперь надо малой кровью создать веб-часть, которая загрузит в себя этот элемент управления и передаст ему необходимые параметры.

У меня получился следующий результат:

public class TestWebPart : System.Web.UI.WebControls.WebParts.WebPart{

private int pageSize = 25;

[WebBrowsable(true),

Personalizable(PersonalizationScope.Shared),

DefaultValue("25"),

Category(" "),

FriendlyNameAttribute("- "),

XmlElement(ElementName = "PageSize")]

public int PageSize{get{return pageSize;}set{pageSize = value;}}

protected override void OnInit(EventArgs e){

base.OnInit(e);

this.ExportMode = WebPartExportMode.All;

UserControl ctrl = LoadControl("~/_controltemplates/TestUserControl.ascx", new object[1] { PageSize });

if (ctrl != null){this.Controls.Add(ctrl);}

}

private UserControl LoadControl(string UserControlPath, params object[] constructorParameters){

List<Type> constParamTypes = new List<Type>();

foreach (object constParam in constructorParameters) constParamTypes.Add(constParam.GetType());

UserControl ctl = Page.LoadControl(UserControlPath) as UserControl;

ConstructorInfo constructor = ctl.GetType().BaseType.GetConstructor(constParamTypes.ToArray());

if (constructor == null){throw new MemberAccessException("The requested constructor was not found on : " + ctl.GetType().BaseType.ToString());}else{constructor.Invoke(ctl, constructorParameters);}

return ctl;

}

}

К вопросу про то, как писать код?

Для начала с наступившим вас! После продолжительного перерыва возвращаюсь к записыванию всяких полезностей в блог.

Код надо писать так, чтобы потом небыло мучительно больно.

Как? Описано на MSDN: http://msdn.microsoft.com/en-us/library/ms229042(VS.80).aspx

Ссылка - боян но может кому пригодится.

 

 

Sharepoint User Group

Группы пользователей — очень популярный формат общения людей, работающих в сфере информационных технологий. Он предполагает регулярные встречи участников и обмен опытом. Теперь у нас стало еще на одну группу больше. И эта группа полностью посвящена платформе Sharepoint.

Russian SharePoint User Group – это площадка для собраний единомышленников, работающих с продуктами и технологиями SharePoint.

Это не официальное, то есть не организуемое сотрудниками Microsoft собрание. Организаторы обещают, что встречи RUSUG будут проводиться раз в три-четыре недели. Запланированы доклады MVP по SharePoint, в том числе — из других стран. Кроме того, ожидается участие сотрудников Microsoft Consulting Services, работающих с технологией SharePoint.

Темами ближайших встречь будет новая платформа — SharePoint 2010.

Более точную информацию ищите на сайте организаторов.

Отправлем уведомление о встрече по e-mail (Outlook 2007 meeting appointment)

Недавно столкнулась с задачей отправлять пользователю не обычное письмо, а meeting appointment для Outlook 2007.

Класс MailMessage позволяет добавлять в письмо различные представления. В том числе и календарь и html представление. Но для того, чтобы добавить такое представление нужно сгенерировать хитрый текст. Вот что из этого вышло:

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Net.Mail;

using System.Security.Principal;

using System.DirectoryServices;

using System.Net;

namespace TestProj

{

public class Appointment

{

public Appointment(){}

public void EmailAppointment(DateTime StartDate, DateTime EndDate, String Location, String OrganizerName, String OrganizerEmail, String Summary, String Subject, MailAddressCollection AttendeeList)

{

MailMessage Mail = new MailMessage();

//html

System.Net.Mime.ContentType HTMLType = new System.Net.Mime.ContentType("text/html");

AlternateView HTMLView = AlternateView.CreateAlternateViewFromString(GetHTML(StartDate, EndDate, Location, OrganizerName, OrganizerEmail, Summary, Subject, AttendeeList), HTMLType);

// calendar

System.Net.Mime.ContentType CalendarType = new System.Net.Mime.ContentType("text/calendar");

CalendarType.Parameters.Add("method", "REQUEST");

CalendarType.Parameters.Add("name", "meeting.ics");

AlternateView CalendarView = AlternateView.CreateAlternateViewFromString(GetCalendar(StartDate, EndDate, Location, OrganizerName, OrganizerEmail, Summary, Subject, AttendeeList), CalendarType);

CalendarView.TransferEncoding = System.Net.Mime.TransferEncoding.SevenBit;

Mail.AlternateViews.Add(HTMLView);

Mail.AlternateViews.Add(CalendarView);

Mail.From = new MailAddress(OrganizerEmail);

foreach (MailAddress attendee in AttendeeList)

{

Mail.To.Add(attendee);

}

Mail.Subject = Subject;

SmtpClient Server = new SmtpClient("server1", 25);

Server.Send(Mail);

}

private string GetHTML(DateTime StartDate, DateTime EndDate, String Location, String OrganizerName, String OrganizerEmail, String Summary, String Subject, MailAddressCollection AttendeeList)

{

string bodyHTML = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2//EN\">\r\n<HTML>\r\n<HEAD>\r\n<META HTTP-EQUIV=\"Content-Type\" CONTENT=\"text/html; charset=utf-8\">\r\n<META NAME=\"Generator\" CONTENT=\"MS Exchange Server version 6.5.7652.24\">\r\n<TITLE>{0}</TITLE>\r\n</HEAD>\r\n<BODY>\r\n<!-- Converted from text/plain format -->\r\n<P><FONT SIZE=2>Type:Single Meeting<BR>\r\nOrganizer:{1}<BR>\r\nStart Time:{2}<BR>\r\nEnd Time:{3}<BR>\r\nTime Zone:{4}<BR>\r\nLocation:{5}<BR>\r\n<BR>\r\n*~*~*~*~*~*~*~*~*~*<BR>\r\n<BR>\r\n{6}<BR>\r\n</FONT>\r\n</P>\r\n\r\n</BODY>\r\n</HTML>";

return string.Format(bodyHTML,

Summary,

OrganizerName,

StartDate.ToLongDateString() + " " + StartDate.ToLongTimeString(),

EndDate.ToLongDateString() + " " + EndDate.ToLongTimeString(),

System.TimeZone.CurrentTimeZone.StandardName,

Location,

Summary);

}

private string GetCalendar(DateTime StartDate, DateTime EndDate, String Location, String OrganizerName, String OrganizerEmail, String Summary, String Subject, MailAddressCollection AttendeeList)

{

string DateFormatUsing="yyyyMMddTHHmmssZ";

string bodyCalendar = "BEGIN:VCALENDAR\r\nMETHOD:REQUEST\r\nPRODID:Microsoft CDO for Microsoft Exchange\r\nVERSION:2.0\r\nBEGIN:VTIMEZONE\r\nTZID:(GMT-06.00) Central Time (US & Canada)\r\nX-MICROSOFT-CDO-TZID:11\r\nBEGIN:STANDARD\r\nDTSTART:16010101T020000\r\nTZOFFSETFROM:-0500\r\nTZOFFSETTO:-0600\r\nRRULE:FREQ=YEARLY;WKST=MO;INTERVAL=1;BYMONTH=11;BYDAY=1SU\r\nEND:STANDARD\r\nBEGIN:DAYLIGHT\r\nDTSTART:16010101T020000\r\nTZOFFSETFROM:-0600\r\nTZOFFSETTO:-0500\r\nRRULE:FREQ=YEARLY;WKST=MO;INTERVAL=1;BYMONTH=3;BYDAY=2SU\r\nEND:DAYLIGHT\r\nEND:VTIMEZONE\r\nBEGIN:VEVENT\r\nDTSTAMP:{8}\r\nDTSTART:{0}\r\nSUMMARY:{7}\r\nUID:{5}\r\nATTENDEE;ROLE=REQ-PARTICIPANT;PARTSTAT=NEEDS-ACTION;RSVP=TRUE;CN=\"{9}\":MAILTO:{9}\r\nACTION;RSVP=TRUE;CN=\"{4}\":MAILTO:{4}\r\nORGANIZER;CN=\"{3}\":mailto:{4}\r\nLOCATION:{2}\r\nDTEND:{1}\r\nDESCRIPTION:{7}\\N\r\nSEQUENCE:1\r\nPRIORITY:5\r\nCLASS:\r\nCREATED:{8}\r\nLAST-MODIFIED:{8}\r\nSTATUS:CONFIRMED\r\nTRANSP:OPAQUE\r\nX-MICROSOFT-CDO-BUSYSTATUS:BUSY\r\nX-MICROSOFT-CDO-INSTTYPE:0\r\nX-MICROSOFT-CDO-INTENDEDSTATUS:BUSY\r\nX-MICROSOFT-CDO-ALLDAYEVENT:FALSE\r\nX-MICROSOFT-CDO-IMPORTANCE:1\r\nX-MICROSOFT-CDO-OWNERAPPTID:-1\r\nX-MICROSOFT-CDO-ATTENDEE-CRITICAL-CHANGE:{8}\r\nX-MICROSOFT-CDO-OWNER-CRITICAL-CHANGE:{8}\r\nBEGIN:VALARM\r\nACTION:DISPLAY\r\nDESCRIPTION:REMINDER\r\nTRIGGER;RELATED=START:-PT00H15M00S\r\nEND:VALARM\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n";

bodyCalendar = string.Format(bodyCalendar,

StartDate.ToUniversalTime().ToString(DateFormatUsing),

EndDate.ToUniversalTime().ToString(DateFormatUsing),

Location,

OrganizerName,

OrganizerEmail,

Guid.NewGuid().ToString("B"),

Summary,

Subject,

DateTime.Now.ToUniversalTime().ToString(DateFormatUsing),

AttendeeList.ToString());

return bodyCalendar;

}

}

}

Posted by tvsmetanina | 1 Comments
Filed under: , ,

Материалы со встречи разработчиков в офисе Microsoft

Как и обещала, выкладываю материалы. 

Встреча прошла не без технических проблем. Но в позитивном ключе. Это был мой первый раз на UserGroup. Думаю, что не последний.

Надеюсь вы узнали для себя что-то новое )

Презентацию и демо проект можно скачать тут: http://www.zshare.net/download/656659556d798df7/

Всем спасибо, кто был с нами!

Встреча разработчиков в офисе Microsoft, посвященная дню программиста

16 сентября с 19.00 до 22.00 абсолютно безвоздмездно в Крылатских холмах в офисе Microsoft состоится очередная встреча Mocковского сообщества .Net разработчиков.

Мой доклад первый по счету и, как всегда, про Sharepoint.

"Разработка веб-сайтов на Sharepoint
В докладе покажут, что не каждый рожденный ползать не может летать. Посмотрите на SharePoint не только со стороны построения внутренних порталов и документооборота, но и со стороны создания привлекательных веб-сайтов, удобных для пользователей и администраторов. Посмотрите на то, как до неузнаваемости изменить такой привычный внешний вид портала и сделать сверкающий и привлекательный веб-сайт."

Подробности ищите тут: http://mdna.ineta.ru/Events/EventMultiSessionInfo.aspx?Id=344d0f01-4ac4-4f5b-89ae-35b66f8f5e3c

Веяния времени. У меня теперь тоже есть twitter

Держалась, держалась и тоже завела себе Twitter. Если что, можно спрашивать меня о чем-нибудь важном или интересном.
Posted by tvsmetanina | 0 Comments

Использование javascript в schema.xml. Некоторые особенности Calculated и Computed полей.

Мультик был такой в интернете (давно и не очень популярный). Про композитора Ушами. К сожалению играть на нервах ушами я не умею, зато ушами отлично получается делать финты.

Все начиналось с простой задачи. Сделать индикатор в виде светофора. Это должно быть после списка в котором отображается картинка (красная, зеленая или желтая).  Если документ просрочен, то отображается красная картинка, если до срока осталось 3 дня -то желтая, иначе зеленая.

Сначала решили сделать в списке Calculated поле. Сделать формулу труда не составило. Все условия есть, функций для работы с числами и датами много. Только вот одно но. Не рекомендуется использовать Today. А следовательно в реальном времени (без пересохрания элемента списка) сменить картинку индикатора нам не удастся при наступлении следующих суток картинка останется прежняя не зависимо от дат.

Бросила я затею с Calculated полем и решила сделать на основе Computed. В нем можно использовать Today. Поле перерисовыввается без пересохранения элемента списка, то есть в реальном времени. Только вот одно но (да, опять и еще одно). Нету у него нормальной работы с условиями. Можно проверять только на "=", а вот например на ">=" или "<=" нельзя.

И тут пришла идея поместить в тэг <HTML><![CDATA[ ]]></HTML> самый настоящий javascript, который бы проводил все рассчеты с датами и выводил бы картинку.

Когда это первый раз заработало описание поля в schema.xml выглядело следующим образом:

<Field  ID="{FC9947C8-F094-45ff-854D-A6F7E9E5CF4C}"

       Name="Indicator"

       StaticName="Indicator"

       Type="Computed"

       ReadOnly = "TRUE"

       DisplayName="$Resources:dplists, dpincdocfieldindicator;"

       Required="TRUE" Filterable="FALSE">

        <FieldRefs>

          <FieldRef Name="ID"/>

          <FieldRef Name="EndDate"/>

       </FieldRefs>

        <DisplayPattern>

              <HTML><![CDATA[<img src="http://blogs.technet.com/_layouts/images/kpidefault-2.gif" name="indicator]]></HTML>

              <Column Name="ID" />

              <HTML><![CDATA[" id="indicator]]></HTML>

              <Column Name="ID" />

              <HTML><![CDATA[" border="0" />]]></HTML>

 

              <HTML><![CDATA[<script type='text/javascript'>]]></HTML>

              <HTML><![CDATA[var myImg         = document.getElementById("indicator"+]]></HTML>

              <Column Name="ID" />

              <HTML><![CDATA[);  ]]></HTML>

              <HTML><![CDATA[var today         = new Date();  ]]></HTML>

              <HTML><![CDATA[var threedaysafter= new Date( (new Date()).setDate(today.getDate() + 3) );  ]]></HTML>

              <HTML><![CDATA[var enddate_arr   = "]]></HTML>

              <Column Name="EndDate" />

              <HTML><![CDATA[".split(".");]]></HTML>

              <HTML><![CDATA[var enddate       = new Date(Date.parse(enddate_arr[2]+"/"+enddate_arr[1]+"/"+enddate_arr[0]));  ]]></HTML>

              <HTML><![CDATA[enddate           = new Date(enddate.setTime(enddate.getTime()+86399990));  ]]></HTML>

 

 

              <HTML><![CDATA[if(enddate >= today && enddate <= threedaysafter ) { ]]></HTML>

              <HTML><![CDATA[myImg.src = "/_layouts/images/kpidefault-1.gif"; } else ]]></HTML>

              <HTML><![CDATA[if(enddate > today )  {]]></HTML>

              <HTML><![CDATA[myImg.src = "/_layouts/images/kpidefault-0.gif";  }]]></HTML>

 

              <HTML><![CDATA[</script>]]></HTML>

        </DisplayPattern>

      </Field>

В результате получилась вот такая вот штуковина:

Ссылки:

Calculated Field Formulas:  http://msdn.microsoft.com/en-us/library/bb862071.aspx

Schema.xml: http://msdn.microsoft.com/en-us/library/ms459356.aspx

Field Element: http://msdn.microsoft.com/en-us/library/ms437580.aspx

 

Разгон страниц Sharepoint: жесткая оптимизация

Кому-то еще на тренинге обещала опубликовать код того IHttpModule с помошью которого я облегчала вес страниц sharepoint. Вообщем вот он.

    /// <summary>
    /// Модуль очистки вывода от ненужных тегов
    /// </summary>
    public class OutputCleanupModule : IHttpModule
    {
        /// <summary>
        /// Инициализация
        /// </summary>
        /// <param name="app"></param>
        public void Init(HttpApplication app)
        {
            app.ReleaseRequestState                    += new EventHandler(InstallResponseFilter);
        }
        /// <summary>
        /// Инсталировать фильтр вывода
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void InstallResponseFilter(object sender, EventArgs e)
        {
            HttpResponse response                    = HttpContext.Current.Response;
            HttpRequest request                        = HttpContext.Current.Request;
            if ("text/html" == response.ContentType && true == request.Url.AbsolutePath.EndsWith(".aspx")
                && false == request.Url.AbsolutePath.Contains("_layouts/"))
            {
                SPWeb web                            = SPContext.Current.Web;
                if(false == web.DoesUserHavePermissions(SPBasePermissions.ManageLists))                                    
                    response.Filter                    = new OutputCleanupFilter(response.Filter);
            }
        }
        /// <summary>
        /// Очистка ресурсов
        /// </summary>
        public void Dispose()
        {
        }
    }

    /// <summary>
    /// Фильтр очистки вывода от ненужных тегов
    /// </summary>
    public class OutputCleanupFilter : Stream
    {
        private static String[] completeTagsToCleanup    = new String[]
        {
            "<meta name=\"GENERATOR\" content=\"Microsoft SharePoint\" />",
            "<input type=\"hidden\" name=\"__SPSCEditMenu\" id=\"__SPSCEditMenu\" value=\"true\" />"+System.Environment.NewLine,
            "<input type=\"hidden\" name=\"MSOWebPartPage_PostbackSource\" id=\"MSOWebPartPage_PostbackSource\" value=\"\" />"+System.Environment.NewLine,
            "<input type=\"hidden\" name=\"MSOTlPn_SelectedWpId\" id=\"MSOTlPn_SelectedWpId\" value=\"\" />"+System.Environment.NewLine,
            "<input type=\"hidden\" name=\"MSOTlPn_View\" id=\"MSOTlPn_View\" value=\"0\" />"+System.Environment.NewLine,
            "<input type=\"hidden\" name=\"MSOTlPn_ShowSettings\" id=\"MSOTlPn_ShowSettings\" value=\"False\" />"+System.Environment.NewLine,
            "<input type=\"hidden\" name=\"MSOGallery_SelectedLibrary\" id=\"MSOGallery_SelectedLibrary\" value=\"\" />"+System.Environment.NewLine,
            "<input type=\"hidden\" name=\"MSOGallery_FilterString\" id=\"MSOGallery_FilterString\" value=\"\" />"+System.Environment.NewLine,
            "<input type=\"hidden\" name=\"MSOTlPn_Button\" id=\"MSOTlPn_Button\" value=\"none\" />"+System.Environment.NewLine,
            "<input type=\"hidden\" name=\"MSOAuthoringConsole_FormContext\" id=\"MSOAuthoringConsole_FormContext\" value=\"\" />"+System.Environment.NewLine,
            "<input type=\"hidden\" name=\"MSOAC_EditDuringWorkflow\" id=\"MSOAC_EditDuringWorkflow\" value=\"\" />"+System.Environment.NewLine,
            "<input type=\"hidden\" name=\"MSOSPWebPartManager_DisplayModeName\" id=\"MSOSPWebPartManager_DisplayModeName\" value=\"Browse\" />"+System.Environment.NewLine,
            "<input type=\"hidden\" name=\"MSOWebPartPage_Shared\" id=\"MSOWebPartPage_Shared\" value=\"\" />"+System.Environment.NewLine,
            "<input type=\"hidden\" name=\"MSOLayout_LayoutChanges\" id=\"MSOLayout_LayoutChanges\" value=\"\" />"+System.Environment.NewLine,
            "<input type=\"hidden\" name=\"MSOLayout_InDesignMode\" id=\"MSOLayout_InDesignMode\" value=\"\" />"+System.Environment.NewLine,
            "<input type=\"hidden\" name=\"MSOSPWebPartManager_OldDisplayModeName\" id=\"MSOSPWebPartManager_OldDisplayModeName\" value=\"Browse\" />"+System.Environment.NewLine,
            "<input type=\"hidden\" name=\"MSOSPWebPartManager_StartWebPartEditingName\" id=\"MSOSPWebPartManager_StartWebPartEditingName\" value=\"false\" />"+System.Environment.NewLine,
            //"_spBodyOnLoadWrapper();",
      };
        private static String[] tagsToCleanup        = new String[]
        {
            "<link rel=\"stylesheet\" type=\"text/css\" href=\"/_layouts/1033/styles/core.css?rev=",
            "<link rel=\"stylesheet\" type=\"text/css\" href=\"/Style%20Library/en-US/Core%20Styles/Band.css",
            "<link rel=\"stylesheet\" type=\"text/css\" href=\"/Style%20Library/en-US/Core%20Styles/Controls.css",
            "<link rel=\"stylesheet\" type=\"text/css\" href=\"/_layouts/1033/styles/HtmlEditorCustomStyles.css?rev=",
            "<link rel=\"stylesheet\" type=\"text/css\" href=\"/_layouts/1033/styles/HtmlEditorTableFormats.css?rev=",

            "<link rel=\"stylesheet\" type=\"text/css\" href=\"/_layouts/1049/styles/core.css?rev=",
            "<link rel=\"stylesheet\" type=\"text/css\" href=\"/Style%20Library/ru-RU/Core%20Styles/Band.css",
            "<link rel=\"stylesheet\" type=\"text/css\" href=\"/Style%20Library/ru-RU/Core%20Styles/Controls.css",
            "<link rel=\"stylesheet\" type=\"text/css\" href=\"/_layouts/1049/styles/HtmlEditorCustomStyles.css?rev=",
            "<link rel=\"stylesheet\" type=\"text/css\" href=\"/_layouts/1049/styles/HtmlEditorTableFormats.css?rev=",

            //"<input type=\"hidden\" name=\"__REQUESTDIGEST\" id=\"__REQUESTDIGEST\"",
            //"<input type=\"hidden\" name=\"__VIEWSTATE\" id=\"__VIEWSTATE\" value=",
      };
        private static string[] scriptsToCleanup    = new String[]
        {
            "<script type=\"text/javascript\" language=\"javascript\" src=\"/_layouts/1033/core.js?rev=",
            "<script type=\"text/javascript\" language=\"javascript\" src=\"/_layouts/1033/init.js?rev=",
            "<script type=\"text/javascript\" language=\"javascript\" src=\"/_layouts/1033/ie55up.js?rev=",
            "<script type=\"text/javascript\" language=\"javascript\" src=\"/_layouts/1033/non_ie.js?rev=",

            "<script type=\"text/javascript\" language=\"javascript\" src=\"/_layouts/1049/core.js?rev=",
            "<script type=\"text/javascript\" language=\"javascript\" src=\"/_layouts/1049/init.js?rev=",
            "<script type=\"text/javascript\" language=\"javascript\" src=\"/_layouts/1049/ie55up.js?rev=",
            "<script type=\"text/javascript\" language=\"javascript\" src=\"/_layouts/1049/non_ie.js?rev=",

            //"<script src=\"/WebResource.axd?",
            "<script> var MSOWebPartPageFormName = 'aspnetForm'",
            "<script type=\"text/javascript\" language=\"javascript\" src=\"/_layouts/portal.js?rev=",
            "<script type=\"text/javascript\">"+System.Environment.NewLine+"<!--"+System.Environment.NewLine+"var __wpmExportWarning",
            "<script type=\"text/JavaScript\" language=\"JavaScript\">"+System.Environment.NewLine+"<!--"+System.Environment.NewLine+"var L_Menu_BaseUrl",
        };

        private Stream responseStream;
        private long position;

        public OutputCleanupFilter(Stream inputStream)
        {
            this.responseStream                        = inputStream;
        }

        public override void Write(Byte[] buffer, Int32 offset, Int32 count)
        {
            StringBuilder html                        = new StringBuilder(System.Text.UTF8Encoding.UTF8.GetString(buffer, offset, count));
            foreach (String completeTagToClean in completeTagsToCleanup)
            {
                this.CleanUp(html, completeTagToClean, null);
            }
            foreach (String tagToClean in tagsToCleanup)
            {
                this.CleanUp(html, tagToClean, ">");
            }
            foreach (String scriptToClean in scriptsToCleanup)
            {
                this.CleanUp(html, scriptToClean, "</script>");
            }
            Byte[] data                                = System.Text.UTF8Encoding.UTF8.GetBytes(html.ToString());
            responseStream.Write(data, 0, data.Length);
        }
        /// <summary>
        /// Удаление патернов
        /// </summary>
        /// <param name="html"></param>
        /// <param name="search"></param>
        /// <param name="endTag"></param>
        private void CleanUp(StringBuilder html, String search, String endTag)
        {
            Int32 startPos                            = html.ToString().IndexOf(search, StringComparison.OrdinalIgnoreCase);
            if (-1 != startPos)
            {
                if (false == String.IsNullOrEmpty(endTag))
                {
                    Int32 endPos                    = html.ToString().IndexOf(endTag, startPos, StringComparison.OrdinalIgnoreCase);
                    if (-1 != endPos)
                        html.Remove(startPos, endPos - startPos + endTag.Length);
                }
                else
                {
                    html.Remove(startPos, search.Length);
                }
            }
        }
//**************************************************
// Stream
//**************************************************
        #region Stream
        public override bool CanRead
        {
            get
            {
                return true;
            }
        }
        public override bool CanSeek
        {
            get
            {
                return true;
            }
        }
        public override bool CanWrite
        {
            get
            {
                return true;
            }
        }
        public override void Close()
        {
            responseStream.Close();
        }
        public override void Flush()
        {
            responseStream.Flush();
        }
        public override long Length
        {
            get
            {
                return 0;
            }
        }
        public override long Position
        {
            get
            {
                return position;
            }
            set
            {
                position = value;
            }
        }
        public override long Seek(long offset, SeekOrigin origin)
        {
            return responseStream.Seek(offset, origin);
        }
        public override void SetLength(long length)
        {
            responseStream.SetLength(length);
        }
        public override int Read(byte[] buffer, int offset, int count)
        {
            return responseStream.Read(buffer, offset, count);
        }
        #endregion Stream
    }


* This source code was highlighted with Source Code Highlighter.

На всякий случай  напоминаю. Модуль просто так не заработает. Сборку надо класть в GAC и прописывать модуль в web.config

Веб-части для Sharepoint

Одной из наиболее распространенных и повседневных задачь разработчика для платформы Sharepoint является разработка веб-частей. Это наиболее гибкий и доступный способ расширения функционала платформы. Несмотря на то, что в интернете сейчас достаточно статей про разработку веб-частей, не многие знают про все возможности и ньюансы.

Кому и зачем это надо?

Разрабатывая интернет сайт или внутренний корпоративный портал программисты стремятся к эллегантности кода и повторному использованию. Вы знаете, что Sharepoint – это платформа и для того, чтобы ваше решение было успешным - необходимо разрабатывать дополнительный функционал, будь то сервис новостей на сайте или сложные системы отчетности и документооборота, который будет обладать легкой и гибкой настройкой. Итак, основные преимущества веб-частей – это повторное использование и  возможность добавления\настройки\использования через веб-интерфейс пользователями, которые могут не являться разработчиками и вообще могут не иметь отношения к IT. С элементами управления и пользовательскими элементами управления это сделать сложнее.

Веб-части прекрасно отчуждаемы от вашего решения и переносимы.

В данный момент тендернция разработки для Sharepoint заключается в том, что из всех компанентиков, элементов управления и т.д. создаются веб-части и могут быть добавлены в любых комбинациях на любую страницу и настроены администратором\редактором.

Например, те веб части, которые показаны на рисунке ниже могут быть использованы в любых похожих сервисах где нужен архив или подписка, или отображение информации из списка. Достигается это созданием правильных свойств для веб-части, позволяющих гибкую настройку через веб-интерфейс.

 

Основы основ

Для разработки нам понадобятся

·         Microsoft® Visual Studio® 2005 или Microsoft® Visual Studio® 2008

·         Visual Studio Extensions for Windows SharePoint Services 3.0 (для VS2005 или VS 2008).

 

В принципе можно разрабатывать и без экстеншна, создавая проект типа Class Library. Но установив extension вам станут доступны новые шаблоны проектов. Один из них – для разработки веб-частей.

 

Для того, чтобы создать веб-часть надо написать класс в Visual Studio .Net - наследник от одного из стандартных классов веб-частей.

Так исторически сложилось, что Sharepoint поддерживает два вида веб-частей.

  • Веб-части ASP.NET - наследуются от класса веб частей из System.Web.dll. Импортируются в .webpart файлы. Это наиболее рекомендуемый вариант.
  • Веб-части Windows SharePoint Services - наследуются от класса веб частей из Microsoft.Sharepoint.dll. Импортируются в .dwp файлы. Поддерживается для обратной связи с Sharepoint v2.

Наша первая веб часть может выглядеть следующим образом:

 

Для того, чтобы веб часть можно было поместить на страницу страница должна содержать зоны веб-частей, а мастер страница менеджер веб-частей. Страница Sharepoint на которой можно размещать веб-части выглядит следующим образом.

Установка веб-части на Sharepoint тоже не является непосильной задачей. Для этого необходимо:

·         Положить сборку с кодом веб-части (.dll файл) в GAC (C:\Windows\assembly) или в bin папку в IIS веб-сайта

·         Прописать веб-часть в web.config

 

 

·         Добавить в библиотеку веб-частей Sharepoint. Для этого на коллекции узлов надо зайти в библиотеку веб-частей и перейти на страницу добавления веб-части. Если в веб конфиге всё было прописано правильно вы должны увидеть namespace вашей веб-части и сможете её добавить в библиотеку

 

 

С основами покончено. Пошаговую инструкцию как создать веб-часть можно найти здесь.  Но это не всё, что я хочу рассказать вам про веб-части. Продолжение последует в ближайшие несколько дней.

Разгоняем Sharepoint до скорости Highload интернет сайта

 Как известно, одиним из не очень приятных моментов при  разработке интернет сайта на Sharepoint является его производительность. Но это не вердикт и с этим можно бороться.

В этой статье я коротко опишу основные возможности по кэшированию, используя которые можно существенно ускорить работу портала.

·         Во-первых можно включать механизмы кеширования страниц Sharepoint через веб интерфейс

·         Возможно включать кэширование объектов и использовать кэширование в разработанных вами веб-частях и элементах управления

·         Существует BLOB cache и сжатие средствами IIS

·         Необходимо делать оптимизацию страниц и мастер страниц, то есть отключать core.js и core.css там, где они не нужны, убирать ненужные элементы управления и т.д. Помещать ваши собственные сборки в GAC и обязательно подписывать их. Использовать <SharePointWebControls:ScriptLink runat="server"/> и   <SharePoint:CssLink ID="CssLink1" runat="server" /> для подключения css и js вместо аналогов из html.

 Теперь давайте по-порядку.

Кэширование возвращаемой страницы (Output caching)

Управляется на уровне коллекции узлов sharepoint. Кэширование вывода страницы может быть прервано для мастер страниц и дочерних узлов. Вы можете иметь разные настройки кэширования для ананимных и аутентифицированных пользователей. В стандартной поставке содержит 4 профиля, которые подходят для большинства сценариев. Страница применения профиля кэширования к коллекции сайтов выглядит следующим образом:

 

А теперь представим, что нам необходимо, чтобы при изменении какого-либо параметра в строке адреса (query string) страница бралась не из кэша, а отображалась и получала информацию заново. Этого можно достичь написав в поле Vary by Query String Parameters символ “*

 

Что делать, если на странице должны быть элементы управления с динамической информацией при этом вся остальная страница должна кэшироваться?  Нужно использовать Post-Cache Substitution control (http://www.nikhilk.net/PostCacheSubstitution.aspx)

Кэширование файлов (Blob cache)

Blob Cache помещает элементы SharePoint на диск на сервере. Опция отключена. Для включения необходимо отредактировать web.config.

 

Кэширование запросов и объектов (Object cache)

Активирован по умолчанию. Используется для быстрого доступа к элементам:

·         Навигация и структура портала

·         Поля и элементы списков

·         Данные отображаемые в Content by Query web part

По умолчинию кэш объектов составлет 100MB и это можно изменить через веб-интерфейс.

 

Сжатие IIS

Добавить сжатие для JS и CSS можно выполнив в командной строке на сервере:

cscript adsutil.vbs set w3svc/filters/compression/gzip/hcfileextensions "htm" "html" "txt" "js" "css"

cscript adsutil.vbs set w3svc/filters/compression/deflate/hcfileextensions "htm" "html" "txt" "js" "css"

iisreset

IHttpModule и жесткая оптимизация

Совет для тех, кого просто бесит много лишнего текста в возвращаемой браузером странице потому что он эстет или гик (ненужное зачеркнуть), или стоит задача сделать страницы совсем маленькими и легкими. Для этого пишется IHttpModule в котором в методе Init страницы убирается из возвращаемого результата всё лишнее. Только убрать надо именно лишнее, т.е. то без чего ваша страница продолжит нормально работать ;)

Заключение

В подготовке статьи участвовали мозг, НЛО и материалы с MSDN:

Output Caching and Cache Profiles

Object Caching

Disk-based Caching for Binary Large Objects

Creating and Registering a Custom HTTP Module

Post-Cache Substitution

Создание многоязычных сайтов на Sharepoint

Sharepoint позволяет быстро и без написания кода создавать и поддерживать различные локализации сайтов.

В Sharepoint есть такое понятие как Variations – варианты сайтов на различных языках. То есть вы можете через веб-интерфейс создать сайт на любом необходимом вам языке, или несколько сайтов на разных языках. При этом сайты могут иметь как одинаковую, так и разную структуру, то есть дублировать друг друга или являться независимыми друг от друга сайтами.

Для того, чтоб это было возможно необходимо:

1.      Установить Language Pack для нужного вам языка

2.      Зайти на страницу администрирования Variations и сделать что-то похожее на картинку ниже. Создать сопоставления для разных языков. При этом надо указать какая из локализаций будет основной и по какому шаблону сайта необходимо содавать локализованные копии сайтов.

 

После нажатия на кнопку “Create Hierarchies” на портале создадутся узлы для каждого из указанных вами языков.

Для того, чтобы поддержка локализации была более полной можно положить различные логотипы для версий сайтов на различных языках в библиотеку Style Library в папку с кодом языка и начать показывать разные логотипы в записимости от языка сайта.

 

Например, положив логотип в папку с кодом языка мы можем добавить его на страницу или мастер страницу следующим образом:

<img src=”<% $SPUrl:~SiteCollection/Style Library/~language/logo.gif %>” border=0 />

Таком образом можно использовать на страницах и  мастер страницах не только изображения, но и например CSS файлы:

<SharePoint:CssRegistration name=”<% $SPUrl:~SiteCollection/Style Library/~language/Core Styles/Band.css%>” runat=”server”/>

После создания локализованной копии сайта, на всем портале в правом верхнем углу появится элемент управления для перехода между языками.

 

Его можно модифицировать, создав свой элемент управления по аналогии со стандартными VariationRootLanding и VariationLabelMenu:

 

А теперь представим, что у нас есть элементы управления, мастер страницы, страницы и веб части. Как заставить их поддерживать многоязычность? Ответ прост. Пользуемся изобретением ASP.NET, то есть ресурсными файлами (.resx)

Пример использования в коде выглядит следующим образом:

Label1.Text = Resources.devtraining_event.devtraining_EventContentFieldName;

Если вы хотите использовать данные из ресурсных файтов в xml файлах фич и шаблонов узлов Sharepoint, сделать это можно следующим образом:

 

Устанавливать ресурсные файлы надо  в папку 12\Resources:

 

Вот такого результата можно достигнуть если долго стараться.

 

Language Packs можно скачать тут:

·         MOSS 2007: скачать (104.1 MB)

·         WSS 3.0: скачать (9.8 MB)

Более подробную информацию по теме можно найти на MSDN:

·         Customizing and Branding Web Content Management-Enabled SharePoint Sites (Part 3 of 3): Creating and Configuring WCM-Enabled Sites

·         How to: Customize the Variation Root Landing Logic

·         How to: Customize the Variations Label Control Logic

Утилита по экспорту\импорту настроек SSP в xml

На codeplex уже несколько месяцев как лежит утилита, умеющая выгружать в xml и загружать xml обратно на Sharepoint настройки SSP, такие как:

  • Managed Properties
  • Crawled properties
  • Content sources
  • Search scopes

Скачать утилиту можно тут: http://www.codeplex.com/SSSPPC/ 

Пример использования:

Экспорт: SSPC.exe -o export ManagedProperties -url http://moss/ssp/admin -file output_managedproperties.xml

Импорт: SSPC.exe -o import ManagedProperties -removeexcessmappings -url http://moss/ssp/admin -file output_managedproperties.xml

Создаём Фичу для изменения дизайна

Cегодня мы будем рассматривать один из способов смены дизайна портала Sharepoint – создание фичи.

Фичи (Features) – это не только основательно задокументированные баги, а еще и механизм, позволяющий добавлять новую функциональность, веб-части, страницы, элементы дизайна на сайт или коллекцию сайтов Sharepoint.

Для того, чтобы создать фичу, необходимо создать несколько файлов в формате xml. Основным из которых является feature.xml.

Более подробно о том, как создавать фичи и что они умеют можно прочитать на сайте Microsoft MSDN по адресу http://msdn.microsoft.com/en-us/library/ms475286.aspx

Создание фичи

Можно встретить различные рекомендации по разработке SharePoint Features из Visual Studio. Я предпочитаю использовать Visual Studio 2008, создавать проект по шаблону Class Library. Многим вероятно понравится VSeWSS - расширение для Visual Studio, позволяющее создавать уже практически готовые проекты для Sharepoint.

В итоге нам надо создать проект со структурой как на картинке слева.

То есть у нас должен быть сss файл, картинки и asp.net мастер страница.

Основным файлом, необходимым для работы Feature является файл feature.xml.

Каждая Feature имеет идентификатор, название, описание и область действия (сайт, коллекция сайтов и ферма).

 

Данная фича имеет еще один xml файл CopyFiles.xml в котором то и происходит копирование картинок, css и мастер страницы c диска на сервере на портал, то есть в базу данных портала.

 

Установка

Созданную Feature скопировать в папку C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\TEMPLATE\FEATURES. Там же можно посмотреть и изучить стандартные Features в Sharepoint.

Далее выполнить в командной строке: stsadm -o installfeature -name ExCustomDesign

Затем перейти на страницу активации возможностей и активировать её.

 

Далее надо применить установленную мастер страницу.

 

Избавляемся от ручной работы

А теперь, давайте избавимся от последнего шага, то есть выбора мастер страницы вручную. Для этого необходимо написать простенький FeatureReceiver:

 

И добавить в Feature.xml описание Receiver:

 

После активации фичи мы получили следующий результат.

 

На этом на сегодня всё. Про создание установочных пакетов и про шаблоны сайтов мы поговорим в следующих статьях.

Удачного сайтостроения!

Создание тем для сайтов Sharepoint используя CSS

Темы – самый простой и быстрый, но наименее гибкий способ изменения внешнего вида сайта Sharepoint. Поэтому, если задача стоит слегка перекрасить портал – сделайте это темами.  Для того, чтобы создать свою первую тему не требуется глубоких знаний в сайтостроении или архитектуре Sharepoint. Достаточно будет знания CSS. Применить готовую тему можно через веб интерфейс Sharepoint. Страница с выбором темы выглядит следующим образом.

 

Темы применяются для каждого сайта Sharepoint отдельно и делается это действие администратором.

Давайте создадим первую в своей жизни тему. На сервере SharePoint перейдите в папку "C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\TEMPLATE\THEMES". В ней располагаются все стандартные темы Sharepoint. Скопируйте например папку с темой "SIMPLE". Переименуйте её на ваше усмотрение. Например "HABR".

В папке " HABR" удалите все изображения и переименуйте файл SIMPLE.INF в  HABR.INF. Откройте HABR.INF и замените везде "Simple" на "HABR".

Перейдите в папку "C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\TEMPLATE\LAYOUTS\1033\" и отредактируете SPTHEMES.XML добавив выделенный текст:

 

Ну вот и всё. Мы создали тему. Теперь вооружаемся чем-нибудь типа Internet Explorer Developer Toolbar или любым другим add-on’ом для браузера, позволяющим работать с CSS. Кстати в IE8 есть встроенная консоль для разработчиков, обладающая нужными нам возможностями.

Итак, давайте изменим логотип и перекрасим выпадающее меню Site Action.

Напишем в theme.css следующее:

#siteactiontd div, .ms-siteactionsmenu div{

background-image: none !important;

      background-color: #86C5EA !important;

      font-family: Arial !important;

      font-size: 12px;

} 

td.ms-sitetitle { 

width: 240px

height: 130px

background: 0 5px no-repeat url(habr_logo.gif)

h1.ms-sitetitle, td.ms-titleimagearea {

display: none;

} 

 

Используйте !important, когда идет речь о переопределении.

Применим тему к сайту зайдя на страницу Site Actions / Site Settings / Site Theme.

Как видите, «раскрасить»  портал – это просто! Спустя не более 20 минут у меня получился такой вот результат:

More Posts Next page »
 
Page view tracker