Итак, появилась необходимость добавить для сайта на базе Orchard CMS, модуль для проигрывания mp3-файлов. Выбранный модуль jPlayer, к сожалению, уронил весь сайт. Не решившись более рисковать решил создать свой модуль на базе плеера Uppod (не факт, конечно, что мой не роняет
). С ним я работаю давно, поэтому долго не выбирал.
Процедура создания модуля отличается от других примеров создания модулей на данном сайте тем, что в рамках модуля мы будем активно использовать шорткоды, функционал которых добавляется на сайт путём добавления моего модуля kosfiz.Shortcodes, создание которого описано в записи «Orchard CMS: добавляем поддержку shortcodes«.
Итак, прежде всего необходимо создать модуль, и затем открыть проект с ним. Далее, в папку Models добавляем класс UppodSettingsRecord описывающий настройки плеера, распространяющиеся на весь сайт:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace kosfiz.Uppod.Models
{
public class UppodSettingsRecord
{
public virtual int Id { get; set; }
public virtual string VideoPlayerStyle { get; set; }
public virtual int VideoPlayerWidth { get; set; }
public virtual int VideoPlayerHeight { get; set; }
public virtual string AudioPlayerStyle { get; set; }
public virtual int AudioPlayerWidth { get; set; }
public virtual int AudioPlayerHeight { get; set; }
public virtual string PhotoPlayerStyle { get; set; }
public virtual int PhotoPlayerWidth { get; set; }
public virtual int PhotoPlayerHeight { get; set; }
public virtual string BackgroundColor { get; set; }
}
}
Данный класс описывает размеры и стили для каждого из видов плееров (видео, аудио, фото), а также цвет фона.
Потом отражаем свойства описанные в классе в базу посредством класса Migrations
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Orchard.Data.Migration;
using System.Data;
using Orchard.ContentManagement.MetaData;
using Orchard.ContentManagement.MetaData.Builders;
using Orchard.Core.Contents.Extensions;
namespace kosfiz.Uppod
{
public class Migrations : DataMigrationImpl
{
public int Create()
{
SchemaBuilder.CreateTable("UppodSettingsRecord", table => table.Column("Id", DbType.Int32, column => column.PrimaryKey().Identity())
.Column("VideoPlayerStyle", DbType.String, column => column.Nullable())
.Column("VideoPlayerWidth", DbType.Int32, column => column.NotNull().WithDefault(400))
.Column("VideoPlayerHeight", DbType.Int32, column => column.NotNull().WithDefault(300))
.Column("AudioPlayerStyle", DbType.String, column => column.Nullable())
.Column("AudioPlayerWidth", DbType.Int32, column => column.NotNull().WithDefault(300))
.Column("AudioPlayerHeight", DbType.Int32, column => column.NotNull().WithDefault(90))
.Column("PhotoPlayerStyle", DbType.String, column => column.Nullable())
.Column("PhotoPlayerWidth", DbType.Int32, column => column.NotNull().WithDefault(400))
.Column("PhotoPlayerHeight", DbType.Int32, column => column.NotNull().WithDefault(300))
.Column("BackgroundColor", DbType.String, column => column.NotNull().WithDefault("ffffff"))
);
return 1;
}
}
}
Указываем имена полей, типы данных, и значения по умолчанию, а также обязательность. Дальше создаём папку Services и в неё добавляем интерфейс IUppodService и класс, реализующий данный интерфейс UppodService. Данный класс содержит методы обновления и получения настроек плеера, а также метод, который будет вызываться на обработку шорткода uppod. Последний и представляет наибольший интерес:
public static string UppodRender
(Dictionary
<string,
object> atts
)
{
string type
= "video";
if (atts
.ContainsKey("type"))
type
= (string)atts
["type"];
string file
= string.Empty;
string playlist
= string.Empty;
if (atts
.ContainsKey("files"))
{
file
= (string)atts
["files"];
if (file
.Contains(","))
{
playlist
= "{ 'playlist' : [" + string.Join(", ", file
.Split(new char[] { ',' }, StringSplitOptions
.RemoveEmptyEntries).Select(x
=> "{" + string.Format(""file
":"{0}"", x
) + "}").ToList()) + "] }";
file
= string.Empty;
}
}
var settings
= GetSettings
();
int width
= 0;
int height
= 0;
string style
= string.Empty;
switch (type
)
{
case "audio":
width
= settings
.AudioPlayerWidth;
height
= settings
.AudioPlayerHeight;
style
= settings
.AudioPlayerStyle;
break;
case "video":
width
= settings
.VideoPlayerWidth;
height
= settings
.VideoPlayerHeight;
style
= settings
.VideoPlayerStyle;
break;
case "photo":
width
= settings
.PhotoPlayerWidth;
height
= settings
.PhotoPlayerHeight;
style
= settings
.PhotoPlayerStyle;
break;
default:
break;
}
if(atts
.ContainsKey("width"))
width
= (int)atts
["width"];
if (atts
.ContainsKey("height"))
height
= (int)atts
["height"];
string Poster
= string.Empty;
if (atts
.ContainsKey("poster"))
Poster
= (string)atts
["poster"];
string Comment
= string.Empty;
if (atts
.ContainsKey("comment"))
Comment
= (string)atts
["comment"];
Random rnd
= new Random
();
string playerId
= "uplayer_" + rnd
.Next(0,
10000);
StringBuilder flashvars
= new StringBuilder
();
flashvars
.Append("{");
flashvars
.AppendFormat(""m
":"{0}","
+ ""uid
":"{1}","
+ ""{2}": "{3}""
+ "{4}"
+ "{5}"
+ "{6}", type, playerId,
string.IsNullOrEmpty(file
) ? "pl" : "file",
string.IsNullOrEmpty(file
) ? playlist
.Replace(""", "'") : file,
string.IsNullOrEmpty(style) ? "" : ", "st" : "" + style + """, string.IsNullOrEmpty(Poster) ? "" : string.Format(", "poster" : "{0}"", Poster),
string.IsNullOrEmpty(Comment) ? "" : string.Format(", "comment" : "{0}"", Comment));
flashvars.Append("}");
StringBuilder flashparams = new StringBuilder();
flashparams.Append("{");
flashparams.AppendFormat("id:"{0}", "
+ "bgcolor:"#{1}", allowFullScreen:"true", allowScriptAccess:"always"", playerId, settings.BackgroundColor);
flashparams.Append("}");
StringBuilder htmlparams = new StringBuilder();
htmlparams.Append("{");
htmlparams.AppendFormat("m: "{0}", comment: "{1}", uid: "{2}", {3} : "{4}"", type, string.IsNullOrEmpty(Comment) ? "" : Comment,
playerId, string.IsNullOrEmpty(file) ? "pl" : "file", string.IsNullOrEmpty(file) ? playlist.Replace(""", "'") : file);
htmlparams.Append("}");
string div = string.Format("<div id
="{0}" style
="width:{1}px; height:{2}px;"></div
>", playerId, width, height);
string script = string.Format("<script type
="text/javascript">init
("{0}",
{1},
{2},
{3},
{4},
{5})</script
>", playerId, flashvars, flashparams, htmlparams, width, height);
return string.Format("{0}<br
/>{1}", div, script);
}
Данный метод добавляет в страницу вместо шорткода div и скрипт вызывающий инициализацию плеера. Сама функция инициализации (и другие) содержится в скрипте и выглядит следующим образом:
function initUppodFlashPlayer(playerId, flashvars, flashparams, width, height) {
swfobject.embedSWF("/Modules/kosfiz.Uppod/Scripts/uppod.swf", playerId, width, height, "9.0.115", false, flashvars, flashparams);
}
function initHtml5Player(htmlparams) {
var v = document.createElement('video');
if (v.canPlayType('video/mp4')) {
this.vplayer = new Uppod(htmlparams);
}
else { }
}
function init(playerId, flashvars, flashparams, htmlparams, width, height) {
var playerVersion = swfobject.getFlashPlayerVersion();
if (playerVersion.major >= 9)
initUppodFlashPlayer(playerId, flashvars, flashparams, width, height);
else
initUppodHtml5Player(htmlparams);
}
Другие файлы необходимые для работы плеера добавляем в папку Scripts: uppod.js (html5-версия), uppod.swf (плеер), swfobject (вдруг ещё не добавлен), uppod_init.js (содержимое приведено выше).
Теперь надо добавить скрипты в код страницы и добавить шорткод uppod и метод для его обработки в словарь шорткодов. Для этого добавляем папку Handlers и в него класс UppodHandler:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Orchard.ContentManagement.Handlers;
using kosfiz.Uppod.Models;
using Orchard.Data;
using Orchard;
using Orchard.UI.Resources;
using Orchard.Caching;
using kosfiz.UppodServices;
using kosfiz.Shortcodes.Models;
namespace kosfiz
.Uppod.Handlers
{
public class UppodHandler
: ContentHandler
{
readonly IWorkContextAccessor _workContextAccessor
;
public UppodHandler
(IWorkContextAccessor workContextAccessor, IRepository
<UppodSettingsRecord
> repository, ICacheManager cacheManager, ISignals signals
)
{
try
{
_workContextAccessor
= workContextAccessor
;
var resourceManager
= _workContextAccessor
.GetContext().Resolve<IResourceManager
>();
var links
= resourceManager
.GetRegisteredLinks();
bool uppod
= false;
bool swfobject
= false;
foreach (var link
in links
)
{
if (link
.Href.ToLower().Contains("uppod"))
uppod
= true;
if (link
.Href.ToLower().Contains("swfobject"))
swfobject
= true;
}
if (!swfobject
)
resourceManager
.RegisterHeadScript("<script type="text
/javascript
" src="/Modules
/kosfiz
.Uppod/Scripts
/swfobject
-2.2.min.js"></script>");
if (!uppod
)
{
resourceManager
.RegisterHeadScript("<script type="text
/javascript
" src="/Modules
/kosfiz
.Uppod/Scripts
/uppod
.js"></script>");
resourceManager
.RegisterHeadScript("<script type="text
/javascript
" src="/Modules
/kosfiz
.Uppod/Scripts
/uppod_init
.js"></script>");
}
}
finally
{
ShortCodeService
.AddShortCode("uppod", UppodService
.UppodRender); //добавляем шорткод в список обрабатываемых, указывая в какой метод передавать параметры
UppodService uppodService
= new UppodService
(repository, cacheManager, signals
);
}
}
}
}
На этом программирование основного функционала можно считать законченным, осталось лишь добавить код для внесения настроек модуля в админку.
Добавляем AdminMenu в корень проекта
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Orchard.UI.Navigation;
using Orchard.Localization;
namespace kosfiz
.Uppod
{
public class AdminMenu
: INavigationProvider
{
public Localizer T
{ get
; set
; }
public AdminMenu
()
{
T
= NullLocalizer
.Instance;
}
public void GetNavigation
(NavigationBuilder builder
)
{
builder
.Add(T
("Uppod"),
"49", menu
=> menu
.Add(T
("Uppod"),
"0", item
=> item
.Action("Index",
"Admin",
new { area
= "kosfiz.Uppod" })));
}
public string MenuName
{
get
{ return "admin"; }
}
}
}
Осталось добавить контроллер и представление, соответственно:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Mvc;
using Orchard.UI.Admin;
using kosfiz.Uppod.ViewModels;
using kosfiz.Uppod.Services;
using Orchard;
using Orchard.Localization;
namespace kosfiz
.Uppod.Controllers
{
[ValidateInput
(false), Admin
]
public class AdminController
: Controller
{
private readonly IUppodService _uppodService
;
public IOrchardServices Services
{ get
; set
; }
public Localizer T
{ get
; set
; }
public AdminController
(IOrchardServices services, IUppodService uppodService
)
{
Services
= services
;
_uppodService
= uppodService
;
}
[HttpGet
]
public ActionResult Index
()
{
var m
= _uppodService
.Get();
UppodSettingsViewModel model
= new UppodSettingsViewModel
{ AudioPlayerHeight
= m
.AudioPlayerHeight, AudioPlayerStyle
= m
.AudioPlayerStyle, AudioPlayerWidth
= m
.AudioPlayerWidth, BackgroundColor
= m
.BackgroundColor, VideoPlayerHeight
= m
.VideoPlayerHeight, VideoPlayerStyle
= m
.VideoPlayerStyle, VideoPlayerWidth
= m
.VideoPlayerWidth, PhotoPlayerHeight
= m
.PhotoPlayerHeight, PhotoPlayerStyle
= m
.PhotoPlayerStyle, PhotoPlayerWidth
= m
.PhotoPlayerWidth };
return View
(model
);
}
[HttpPost
]
public ActionResult Index
(UppodSettingsViewModel model
)
{
if (ModelState
.IsValid)
{
_uppodService
.Set(model
.VideoPlayerStyle, model
.VideoPlayerWidth, model
.VideoPlayerHeight, model
.AudioPlayerStyle, model
.AudioPlayerWidth, model
.AudioPlayerHeight, model
.PhotoPlayerStyle, model
.PhotoPlayerWidth, model
.PhotoPlayerHeight, model
.BackgroundColor);
return RedirectToAction
("Index");
}
return View
();
}
}
}
@model kosfiz.Uppod.ViewModels.UppodSettingsViewModel
<h1>@Html.TitleForPage(@T("Manage Uppod settings").ToString())</h1>
@using (Html.BeginFormAntiForgeryPost())
{
<fieldset>
<table>
<tr>
<td>@T("Background color"):</td>
<td>
@Html.TextBoxFor(model=>model.BackgroundColor)
</td>
</tr>
<tr>
<td colspan="3">
<h2>@T("Video")</h2>
</td>
</tr>
<tr>
<td>@T("Style"):</td>
<td>
@Html.TextBoxFor(model=>model.VideoPlayerStyle)
</td>
</tr>
<tr>
<td>@T("Width"):</td>
<td>
@Html.TextBoxFor(model=>model.VideoPlayerWidth)
</td>
</tr>
<tr>
<td>@T("Height"):</td>
<td>
@Html.TextBoxFor(model=>model.VideoPlayerHeight)
</td>
</tr>
<tr>
<td colspan="3">
<h2>@T("Audio")</h2>
</td>
</tr>
<tr>
<td>@T("Style"):</td>
<td>
@Html.TextBoxFor(model=>model.AudioPlayerStyle)
</td>
</tr>
<tr>
<td>@T("Width"):</td>
<td>
@Html.TextBoxFor(model=>model.AudioPlayerWidth)
</td>
</tr>
<tr>
<td>@T("Height"):</td>
<td>
@Html.TextBoxFor(model=>model.AudioPlayerHeight)
</td>
</tr>
<tr>
<td colspan="3">
<h2>@T("Photo")</h2>
</td>
</tr>
<tr>
<td>@T("Style"):</td>
<td>
@Html.TextBoxFor(model=>model.PhotoPlayerStyle)
</td>
</tr>
<tr>
<td>@T("Width"):</td>
<td>
@Html.TextBoxFor(model=>model.PhotoPlayerWidth)
</td>
</tr>
<tr>
<td>@T("Height"):</td>
<td>
@Html.TextBoxFor(model=>model.PhotoPlayerHeight)
</td>
</tr>
</table>
<input type="submit" value="@T("Save")" title="@T("Save")" />
</fieldset>
}
Остаётся только собрать модуль.
Ссылка на модуль
Оба модуля успешно используются, к примеру, здесь: http://school286.ru/odod-voenno-patrioticheskoe
P.S.: CodeColorer «любезно» во вставках кода подтёр кое-что в тех местах, где идёт работа со строками, так что лучше смотреть код в модуле.