Роберт Басыров написал отличную
Особо отмечу, что в этом примере задействован весь спектр технологий нашей CMS: инфоблоки, пользовательские свойства, агенты, обработчики событий, ORM.
Как Вы правильно догадались, CreateFieldPublicEditor и CreateCustomTypePublicEditor - это методы, в которых идет обработка полей. Данные методы должны выглядеть примерно следующим образом:
protected override void OnInit(EventArgs e)
{
base.OnInit(e);
if (Component.IsPermissionDenied)
Bitrix.Security.BXAuthentication.AuthenticationRequired();
//обработчики полей, таких как название элемента, привязка к секциям и описание.
//Нас конечно же интересуют секции.
Component.CreateFieldPublicEditor += new EventHandler<IBlockElementWebFormComponent.FieldPublicEditorEventArgs(CreateFieldPublicEditor);
//обработчики пользовательских свойств инфоблока,
//в нашем случае это необходимо для свойства типа Файл
Component.CreateCustomTypePublicEditor += new EventHandler<IBlockElementWebFormComponent.CustomTypePublicEditorEventArgs>(CreateCustomTypePublicEditor);
}
//переопределяем обработчик для поля типа секцииКласс FileElementSections должен быть унаследован от BXIBlockElementFieldPublicEditor, а класс FileUploadCustomType от BXCustomTypePublicEdit. То есть от стандартных классов редактирования поля элемента и класса редактирования свойства элемента. Теперь поговорим подробнее об этих "родителях". Родители представляю 4 метода которые отвечают за:
protected void CreateFieldPublicEditor(object component, IBlockElementWebFormComponent.FieldPublicEditorEventArgs args)
{
//SECTIONS является идентификатором поля
if (args.ID.ToUpper() != "SECTIONS")
return;
args.PublicEditor = new FileElementSections();
}
//переопределяем обработчик для свойства типа файл
protected void CreateCustomTypePublicEditor(object component, IBlockElementWebFormComponent.CustomTypePublicEditorEventArgs args)
{
//FILE является идентификатором свойства
if (args.ID.ToUpper() != "FILE")
return;
args.PublicEditor = new FileUploadCustomType(BXCustomTypeManager.GetCustomType(args.CustomField.CustomTypeId));
}
public override void Load(BXIBlockElement iblockElement, BXParamsBag<object> settings)В этом методе мы вначале забираем свойства контрола, которые по большей части относятся к свойствам секций инфоблока. В конце метод смотрит - мы редактируем поле или нет, если редактируем, то сохраняем секции.
{
required = settings.ContainsKey("required") ? (bool)settings["required"] : true;
fieldTitle = settings.ContainsKey("fieldTitle") ? (string)settings["fieldTitle"] : String.Empty;
textBoxSize = settings.ContainsKey("textBoxSize") ? (int)settings["textBoxSize"] : 30;
iblockId = settings.ContainsKey("iblockId") ? (int)settings["iblockId"] : 0;
onlyLeafSelect = settings.ContainsKey("onlyLeafSelect") ? (bool)settings["onlyLeafSelect"] : false;
maxSectionSelect = settings.ContainsKey("maxSectionSelect") ? (int)settings["maxSectionSelect"] : 3;
multiple = maxSectionSelect > 1;
categories = GetFileSections("bycategory");
languages = GetFileSections("bylang");
technologies = GetFileSections("bytechnology");
if (iblockElement != null)
{
foreach (BXIBlockElement.BXInfoBlockElementSection section in iblockElement.Sections)
fieldValues.Add(section.SectionId);
}
}
public override string Render(string formFieldName, string uniqueID)
{
/* задаем вспомогательные перменные, которые нам понадобятся для построения select-ов и чекбоксов */
string fieldHeader = @"<tr field field-sections{1}""><td align=right width=30% valign=top><label class=""field-title"">{0}</label></td><td valign=top>";
string fieldFooter = "</td></tr>";
string dropDownStart = @"<select name=""{0}"" class=""custom-field-list"">";
string dropDownOption = @"<option value=""{0}""{1}>{2}</option>";
string dropDownEnd = "</select>";
string checkbox = @"<input name=""{0}"" type=""checkbox"" value=""{1}"" id=""{2}""{3}/> <label for=""{2}"">{4}</label><BR>";
StringBuilder result = new StringBuilder(String.Empty);
//Categories
//формируем список отвечающий за вывод категории в виде DropDown
result.AppendFormat(fieldHeader, @"Категория<span style=""color: red;"">*</span>", isCategorySet ? "" : " field-error");
result.AppendFormat(dropDownStart, HttpUtility.HtmlEncode(formFieldName));
result.AppendFormat(dropDownOption, 0, String.Empty, "(выберите категорию)");
for (int i = 0; i < categories.Count; i++)
{
SectionTreeItem section = (SectionTreeItem)categories[i];
result.AppendFormat(
dropDownOption,
section.Id,
fieldValues.Contains(section.Id) ? " selected=\"selected\"" : String.Empty,
section.Name
);
}
result.Append(dropDownEnd);
result.AppendFormat(fieldFooter);
//Languages
//такой же селект для языков программирования
result.AppendFormat(fieldHeader, @"Язык программирования<span style=""color: red;"">*</span>", isCategorySet ? "" : " field-error");
result.AppendFormat(dropDownStart, HttpUtility.HtmlEncode(formFieldName));
result.AppendFormat(dropDownOption, 0, String.Empty, "(выберите язык)");
for (int i = 0; i < languages.Count; i++)
{
SectionTreeItem section = (SectionTreeItem)languages[i];
result.AppendFormat(
dropDownOption,
section.Id,
fieldValues.Contains(section.Id) ? " selected=\"selected\"" : String.Empty,
section.Name
);
}
result.Append(dropDownEnd);
result.AppendFormat(fieldFooter);
//Technologies
//а технологии сделаем ка мы чек боксами
result.AppendFormat(fieldHeader, @"Технологии", "");
for (int i = 0; i < technologies.Count; i++)
{
SectionTreeItem section = (SectionTreeItem)technologies[i];
result.AppendFormat(
checkbox,
HttpUtility.HtmlEncode(formFieldName),
section.Id,
HttpUtility.HtmlEncode(formFieldName) + section.Id,
fieldValues.Contains(section.Id) ? " checked=\"checked\"" : String.Empty,
section.Name
);
}
result.AppendFormat(fieldFooter);
return result.ToString();
}
public override void Save(string formFieldName, BXIBlockElement iblockElement, BXCustomPropertyCollection properties)Ну и проверка входных данных производится так для нашего элемента:
{
if (iblockElement == null)
return;
BXIBlockElement.BXInfoBlockElementSectionCollection sections = iblockElement.Sections;
sections.Clear();
foreach (int sectionId in fieldValues)
sections.Add(sectionId);
}
public override bool Validate(string formFieldName, ICollection<string> errors)Вспомогательный класс SectionTreeItem хранит в себе нужную нам информацию о секции. Он есть в архиве с шаблоном
{
postValues = HttpContext.Current.Request.Form.GetValues(formFieldName);
if (postValues == null || postValues.Length < 1)
{
fieldValues.Clear();
errors.Add("Не указана категория файла");
return false;
}
isCategorySet = false;
isLangSet = false;
fieldValues = new List<int>(postValues.Length);
foreach (string value in postValues)
{
int sectionId;
if (!int.TryParse(value, out sectionId) || sectionId < 1)
continue;
if (categories.Contains(sectionId))
{
isCategorySet = true;
fieldValues.Add(sectionId);
}
else if (languages.Contains(sectionId))
{
isLangSet = true;
fieldValues.Add(sectionId);
}
else if (technologies.Contains(sectionId))
{
fieldValues.Add(sectionId);
}
}
if (!isCategorySet)
errors.Add(String.Format("Не указана категория файла", fieldTitle));
else if (!isLangSet)
errors.Add(String.Format("Не указан язык программирования", fieldTitle));
return isCategorySet && isLangSet;
}
private BXFile ValidateFile(HttpPostedFile file, ICollection<string> errors)В этом методе сначала идет проверка по размеру файла, затем по расширение и пустоту названия файла. Если все хорошо, то мы сжимаем файл и регистрируем его в системе.
{
bool result = true;
if (maxSize > 0 && file.ContentLength > maxSize)
{
result = false;
errors.Add(HttpUtility.HtmlEncode(string.Format(
"Размер файла {0} превышает {1}",
file.FileName,
BXStringUtility.BytesToString(maxSize)
)));
}
if (allowedExtensions.Count != 0)
{
string ext = Path.GetExtension(file.FileName);
if (ext != null)
ext = ext.TrimStart('.').ToLowerInvariant();
if (!allowedExtensions.Contains(ext))
{
result = false;
errors.Add(HttpUtility.HtmlEncode(string.Format(
"Тип загружаемого файла {0} не является допустимым ({1})",
file.FileName,
string.Join(", ", allowedExtensions.ToArray())
)));
}
}
if (!result)
return null;
BXFile f = null;
string fileName = Path.GetFileName(file.FileName);
if (!String.IsNullOrEmpty(fileName))
{
if (fileName.EndsWith(".zip") || fileName.EndsWith(".rar"))
f = new BXFile(file, "files", "iblock", string.Empty);
else
//сжимаем файл из текущего
f = new BXFile(new BXZip.ZipStream(file.InputStream, fileName, ushort.MaxValue / 2), fileName + ".zip", "files", "iblock", "");
if (!BXSecureIO.CheckUpload(f.FileVirtualPath))
{
errors.Add(HttpUtility.HtmlEncode(string.Format("Недостаточно прав для загрузки файла {0}", fileName)));
return null;
}
}
return f;
}