18
В этот раз я попытаюсь рассмотреть процесс создания модуля для Orchard CMS, который будет пинговать поисковые системы (google, yandex, yahoo, bing, ask) сообщая им о том, что обновилась информация на сайте и нужно проверить файл sitemap.xml.
Функционирование модуля можно описать кратко следующим образом: создадим ContentPart (PingContentPart), которую нужно будет присоединять к типам контента (ContentType), при добавлении которых нужно пинговать сервисы; для пинга придётся реализовать небольшой класс; поисковые сервисы (адреса которые нужно пинговать) будут жёстко прописаны в коде.
Прежде всего начнём с создания заготовки модуля:
Затем тут же добавляем Migration.cs
Открываем проект модуля и изменяем Migration.cs следующим образом:
SchemaBuilder.CreateTable("SitemapPingRecord", table => table.ContentPartRecord());
SchemaBuilder.CreateTable("SitemapPingSettingsRecord", table => table.Column("Id", DbType.Int32, column => column.PrimaryKey().Identity())
.Column("LastUpdateTime", DbType.DateTime, column => column.Nullable()));
ContentDefinitionManager.AlterPartDefinition("SitemapPingPart", cfg => cfg.Attachable());
return 1;
}
В папке Models создаём два класса пустышки: SitemapPingRecord
{
}
SitemapPingPart
{
}
И служебный класс (в нём будем хранить время последнего пинга к сервисам, чтобы не пинговать чаще чем раз в час)
{
public virtual int Id { get; set; }
public virtual DateTime LastUpdateTime { get; set; }
}
Добавляем папку Services и добавляем интерфейс и класс, соответственно, IPingService и PingService.
{
void Ping();
}
{
const int pingTimeout = 5000;
const string GooglePingUrl = "http://www.google.com/webmasters/sitemaps/ping?sitemap={0}";
const string YandexPingUrl = "http://ping.blogs.yandex.ru/ping?sitemap={0}";
const string YahooPingUrl = "http://search.yahooapis.com/SiteExplorerService/V1/ping?sitemap={0}";
const string BingPingUrl = "http://www.bing.com/webmaster/ping.aspx?siteMap={0}";
const string AskPingUrl = "http://submissions.ask.com/ping?sitemap={0}";
IContentManager _contentManager;
IRepository<SitemapPingSettingsRecord> _repository;
public PingService(IContentManager contentManager, IRepository<SitemapPingSettingsRecord> repository)
{
_contentManager = contentManager;
_repository = repository;
}
public void Ping()
{
string siteSitemapUrl = string.Format("http://{0}/sitemap.xml", HttpContext.Current.Request.Url.Host);
bool IsNeedUpdate = false;
var settings = _repository.Table.FirstOrDefault();
if (settings == null)
{
IsNeedUpdate = true;
_repository.Create(new SitemapPingSettingsRecord { LastUpdateTime = DateTime.Now });
}
else
if (Math.Abs(settings.LastUpdateTime.Subtract(DateTime.Now).TotalHours) >= 1)
{
IsNeedUpdate = true;
settings.LastUpdateTime = DateTime.Now;
}
if (IsNeedUpdate)
{
SendPing(string.Format(GooglePingUrl, siteSitemapUrl));
SendPing(string.Format(YahooPingUrl, siteSitemapUrl));
SendPing(string.Format(YandexPingUrl, siteSitemapUrl));
SendPing(string.Format(BingPingUrl, siteSitemapUrl));
SendPing(string.Format(AskPingUrl, siteSitemapUrl));
}
}
private void SendPing(string pingServiceUrl)
{
HttpWebRequest pingRequest = (HttpWebRequest)WebRequest.Create(pingServiceUrl);
pingRequest.Method = "GET";
pingRequest.Timeout = pingTimeout;
using (HttpWebResponse pingResponse = (HttpWebResponse)pingRequest.GetResponse())
{
}
}
}
Добавляем хендлер
{
IPingService _pingService;
public SitemapPingHandler(IRepository<SitemapPingRecord> repository, IPingService pingService)
{
_pingService = pingService;
Filters.Add(StorageFilter.For(repository));
OnPublished<SitemapPingPart>((context, part) => { // когда нажали кнопку Publish и опубликовали версию
if ((context.PreviousItemVersionRecord == null && context.PublishingItemVersionRecord.Published) ||
(context.PreviousItemVersionRecord.Published == false && context.PublishingItemVersionRecord.Published))
{
// сначала, когда заполняем Заголовок (Title) RoutePart идёт ajax-запрос, при том, что ничего не публикуется всё равно отрабатывает OnPublished, поэтому вставляем нижележащие условия, чтобы выделить "настоящий" момент публикации
var route = context.ContentItem.As<RoutePart>();
if(route!=null)
if (!string.IsNullOrEmpty(route.Slug.Trim()))
{
_pingService.Ping();
}
}
});
}
}
И, наконец, добавляем driver:
{
}
Создаём пакет
Вот и всё, теперь при публикации записей с ContentType, к которым присоединена SitemapPingPart, будет происходить пинг поисковых сервисов.
Исходники могут немного отличаться от приведённого кода.
Пакет на сайте Orchard
