13 января 2011 г.

Пример создания пользовательского компонента Битрикс: Корпоративные телефоны.

Введение
Приведем пример создание компонента Битрикса на примере создания компонента «Корпоративные телефоны». Т. е. допустим у нас есть несколько телефонов и в разных разделах необходимо их как-то связано представить общественности (Рис. 1). Причем  имеет смысл выбирать не по одному телефону, а к примеру телефон начальника и его помощника.

Рис. 1
Первым делом расмотрим код вызова компонента и структуру директорий компонента.


<?
$APPLICATION->IncludeComponent("ns_ccom:telefon.bank", ".default", array(
"IBLOCK_ID" => "1",
"SELECTTELEFON" => array(
0 => "KS",
1 => "KP",
),
"SELECT_FIRST" => "Y",
"CACHE_TYPE" => "N",
"CACHE_TIME" => "3600"
),
false
);
?>
Листинг 1.
В листенге 1 в литерной переменной "ns_ccom:telefon.bank" передается необходимая информация для запуска компонента  — пространство имен и  имя компонента Битрикс (формат вызова  - «пространство имен: имя компонента»).
В свою очередь эта запись коррелирована с файловой системой компонента(ов) в Битриксе. Так все компоненты битрикса располагаются в папке /bitrix/components/, а с учетом пространства имен и имени компонента путь до файлов компонента  будет иметь следующий вид:
 /bitrix/components/ ns_ccom/telefon.bank/ (1)
Отсюда следует первый вывод — для того, чтобы создать компонент необходимо воспроизвести путь (1) в файловой системе на хосте.
Литерная переменная ".default" – соответствует о вызове необходимого шаблона компонента. Остальные переменные в массиве array(...) дополнительные параметры компонента Битрикса.
Теперь обратим внимание на файловую структуру компонента относительно пути /bitrix/components/:
[ns_ccom]
[date.current]
[templates]
[.default]
template.php
component.php
.parameters.php
.description.php
Листинг 2 (в квадратных скобках указаны имена директорий)
Замечание: В листинге 2 перечислены основные файлы и папки. Так же в директории date.current могут находится (необязательно):
Директория help -  для хранения файлов файлы помощи по компоненту;
Директория install -  для скриптов инсталляции/деинсталляциии компонента (install.php / uninstall.php);
Директория lang - Нужна для поддержки мультиязычности в компоненте.   
В папке templates размещается шаблон отображаемого компонента. Соответственно в папке .default размещен компонент по умолчанию (см. Листинг 1). 
Пройдемся по файлам
component.php -  Основной исполняемый файл компонента, отвечает за определение состояния модели данных компонента. Проще говоря, наполняет переменную $arResult
.parameters.php - Параметры компонента. Используется для определения параметров комонента (рис. 2). Результат работы переменная $arParams. 
description.php — Описание компонента
template.php — Код шаблонна





Рис. 2
component.php
<?
if(!defined("B_PROLOG_INCLUDED")||B_PROLOG_INCLUDED!==true)die();
if ($this->StartResultCache(3600))
{
$iblock_id = $arParams['ID'];
$arFilter = array('IBLOCK_ID'=>$iblock_id);
//Заполняем содержимым:
$arResult["KS"]="+7(812) 498-36-33";
$arResult["KP"]="+7(812) 325-40-06 доб. 120";
$arResult["CALL"]="+7(812) 325-40-06";
$this->IncludeComponentTemplate();
}
?>
Листинг 2
Вызов метода IncludeComponentTemplate инициализирует и подключает шаблон компонента Битрикса, а  вызов метода die в контексте условного оператора if запрещает вызов файла component.php  независимо от контекста страницы размещения компонента (через адресную строку).
 .parameters.php

<?
if(!defined("B_PROLOG_INCLUDED")||B_PROLOG_INCLUDED!==true)die();
$arComponentParameters = array(
'PARAMETERS' => array(
'IBLOCK_ID' => array(
'NAME' => 'Id инфоблока',
'TYPE' => 'STRING',
'MULTIPLE' => 'N', //Разрешить множественный выбор
'PARENT' => 'BASE',//Раздел настроек
),
'CACHE_TIME' => array('DEFAULT'=>3600),
"SELECTTELEFON" => Array(
"PARENT" => "BASE", //Раздел настроек
"NAME" => "TELPARAM", //Название
"TYPE" => "LIST", //Тип поля
"MULTIPLE" => "Y", //Разрешить множественный выбор
"ADDITIONAL_VALUES" => "N", //Разрешает добавку своих значений к списку
"VALUES" => array(
"KS" => "Кузнецов Сергей", //Пункты для выбора в списке
"KP" => "Кузнецов Павел",
"CALL" => "Официальный телефон банка",
),
"DEFAULT" => "CALL", //Значение по умолчанию
"REFRESH" => "Y", //Отображает кнопку «ОК» для обновления результатов (к примеру при выборе инфоблоков из типов инфоблоков)
),
'SELECT_FIRST' => Array(
'PARENT' => 'BASE',
'NAME' => "Выделять первый номер",
'TYPE' => 'CHECKBOX',
)
)
);
?>

Листинг 3.
В этом файле заполняем хэш таблицу $arComponentParameters которая коррелирует с переменной $arParams, которая доступна для вызова в файлах template.php и component.php.
Ключ 'PARAMETERS' отвечает за наполнение полей на  рис. 1. Как видно для управления доступно 4 опции:
IBLOCK_ID — определяет Id инфоблока (пока в этом компоненте не требуется, но пусть будет ). Тип поля  String;
CACHE_TIME – параметры кэширования;
SELECTTELEFON – Выбор владельца телефона (вообщем чьи телефоны отображать). Тип поля LIST
SELECT_FIRST – параметр для определения выделять ли первый номер телефона из списка. Тип  CHECKBOX.
Пример работы с этими полями будет продемонстрирован, когда дойдем до файла template.php
 .description.php:
В качестве пояснения по этому файлу процитируем из материалов центра разработки (http://dev.1c-bitrix.ru/community/blogs/components2/133.php).
В файле .description.php содержится описание компонента. Это описание применяется для работы с компонентом из среды Битрикс (например, в визуальном редакторе), а так же при работе в режиме редактирования сайта. При работе самого компонента (при обращении к странице, на которой расположен компонент) описание не используется и файл .description.php не подключается.

<?
if (!defined("B_PROLOG_INCLUDED") || B_PROLOG_INCLUDED!==true) die();
$arComponentDescription = array(
"NAME" => 'BankTelefons',
"DESCRIPTION" => 'Список телефонов',
"ICON" => "",
"CACHE_PATH" => "Y",
"PATH" => array(
"ID" => "content", //Главный раздел компонентов, может принимать значения (service, utility, content, communication, e-store)
),
);
?>

Листинг 4
template.php



<style type="text/css">
#telefon_show{
background-color:none;
text-align:center;
}
#telefon_show p.first, #telefon_show p.usual{
font-family: Verdana;
color:#e63500;
}
#telefon_show p.first{
font-size:20px;
font-weight:bold;
padding:2px 0 2px 0;
}
#telefon_show p.usual{
font-size:12px;
font-weight:normal;
}
#telefon_show div.telefon_info{
font-size:15px;
font-weight:bold;
font-family: Verdana;
color:#585858;
}
</style>
<div id="telefon_show">
<div class="telefon_info">Появились вопросы, звоните:</div>

<?
/*Если в $arComponentParameters в качестве типа параметра задавался тип списка ('TYPE'=> 'LIST'),
то получим массив, который можно обработать следующим образом*/
foreach($arParams['SELECTTELEFON'] as $index=>$listParam)
{
//$listParam соответсвует ключу из хэш таблицы VALUES (parameters.php)
$mes="<p";//Сформируем строку которую будем выводить...
if (($arParams['SELECT_FIRST']=='Y')&&($index==0))
$mes.=" class='first'";//Если поставлена метка "Выделить первую строку" и строка первая
else $mes.=" class='usual'";
$mes.=">".$arResult[$listParam]."</p>";
echo $mes;//Печатаем строку
}
?>
<a href="/kontakti/">Как доехать до офиса?</a>
</div>

Листинг 5
В листинге 5 следует обратить внимание на цикл foreach, где происходит обращение к элементу $arParams['SELECTTELEFON'] и $arParams['SELECT_FIRST'] из .parameters.php
В качестве вывода:
Разбиение компонента по решаемым задачам на отдельные файлы до конца не решает  проблему сопровождения кода. Как видно из файлов components.php и template.php в шаблоне не удается вынести логику поведения за рамки представления. Файл components.php не имеет возможности выбора необходимого представления и делегирует эти полномочия пользователю при выборе шаблона компонента.

12 января 2011 г.

puts("there's more than one way to do it. ")

Небольшой пост на тему необходимости использовать плюшек языка(ов) на примере использования унарного амперсанда в Ruby.
Не будем брызгать слюной, сразу перейдем к примерам:
# a) перебираем элементы массива (1..10)
# b) применение лямбда функции ко всем элементам этого массива при помощи &
# Подопытная лямбда
test_lam=lambda{|inPut|
  puts("#{inPut}: #{inPut**2}");
  inPut**2 # В качестве возвращаемого значения
  }
# синтаксис с &
buf=(1..10).to_a.map &test_lam
# классический эквивалент
buf2=(1..10).to_a.map{|input| test_lam.call(input)}

Результат, предсказуем:
1: 1
2: 4
3: 9
4: 16
5: 25
6: 36
7: 49
8: 64
9: 81
10: 100
# Еще один пример
buf2=[1, 2, 3].map(&:to_s)
p buf2 # => ["1", "2", "3"]
# Эквивалент:
p [1, 2, 3].map { |i| i.to_s }
Что лучше? В сети нашел наводящий на мысли бенчмарк на эту тему
# Небольшой Бенчмарк: (http://blog.hasmanythrough.com/2006/3/7/symbol-to-proc-shorthand,
# http://m.onkey.org/let-s-start-with-wtf )
# traditional calls (with a block as in obj.each {|e| e.method_name }) 
# И Унарный амперсаендd to a Proc (like obj.each(&:method_name)).
require 'benchmark'
require 'rubygems'
require 'active_support'
array = [ 1, 'ab', :dog, 3.8 ]
COUNT = 100_000
Benchmark.bmbm(2) do |test|
  test.report('Using Symbol#to_proc') do
    COUNT.times do
      array.map(&:to_s)
    end
  end
  test.report('Standard call') do
    COUNT.times do
      array.map {|e| e.to_s }
    end
  end
end


Полученный результат
=begin
Rehearsal --------------------------------------------------------
Using Symbol#to_proc   1.125000   0.000000   1.125000 (  1.125000)
Standard call                 0.578000   0.000000   0.578000 (  0.578000)
----------------------------------------------------- total: 1.703000sec


                                          user     system      total        real
Using Symbol#to_proc   1.015000   0.000000   1.015000 (  1.015000)
Standard call                0.563000   0.000000   0.563000 (  0.563000)
=end

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

23 декабря 2010 г.

Геттеры и сеттеры в Ruby

Пример работы сеттеров и геттеров в Ruby.
attr_reader  - реализует только геттер для заданного поля
attr_accessor - реализует геттер и сеттер
В примере приведу только на чтение/ запись и чтение (на запись можно сделать по аналогии):
#Пример работы Геттеров и Сеттеров в Ruby
class Setter_and_getter
# Объявляю геттеры и сеттеры
 attr_reader :var_readOnly #Доступ только для чтения
 attr_accessor :var_readWrite #Доступ чтение/записи
 def setData
  @var_readOnly="set" # Устанавливаю значение
  @var_readWrite="r/w";
  puts self.var_readOnly, self.var_readWrite #Проверяю
 end
end
testObj=Setter_and_getter.new
testObj.setData
puts "_________"
#testObj.var_readOnly="fff"; # Получаю ошибку при попытке записи
testObj.var_readWrite="read and write"
puts "I have: #{testObj.var_readOnly}, #{testObj.var_readWrite}" # "Считываю значение"

19 декабря 2010 г.

Добавляем свое меню в Microsoft Bing Maps Silverlight Control

Задача очень проста, создать меню которое будет отображаться на карте.

Меню добавляется в отдельный слой на карте, xaml код:

<m:Map NavigationVisibility="Visible">
  <m:Map.CredentialsProvider>
    <m:ApplicationIdCredentialsProvider ApplicationId="{StaticResource bmk}"/>
  </m:Map.CredentialsProvider>
  <m:Map.Children>
    <m:MapLayer x:Name="ControlLayer"><!-- Собственно меню, вот оно -->
      <Border CornerRadius="5" Width="100" HorizontalAlignment="Right" VerticalAlignment="Top" Background="Black" Opacity="0.8">
        <StackPanel Orientation="Vertical" Margin="5" Height="95">
        <RadioButton Content="Стандарт" GroupName="modeSelection" Foreground="White" IsChecked="True" Name="mode0" Checked="RadioButton_Checked"/>
        <RadioButton Content="Метки" GroupName="modeSelection" Foreground="White" Checked="RadioButton_Checked" Name="mode1"/>
        <RadioButton Content="Редактор" GroupName="modeSelection" Foreground="White" Checked="RadioButton_Checked" Name="mode2"/>
        <RadioButton Content="Навигация" GroupName="modeSelection" Foreground="White" Checked="RadioButton_Checked" Name="mode3"/>
        <StackPanel Orientation="Vertical" x:Name="AnotherControls" Visibility="Collapsed">
          <Button Content="Color" x:Name="colorChange" Click="currentPoint_Click" />
          <Button Content="Delete" x:Name="delComponent" Click="redoLineButton_Click"/>
        </StackPanel>
        <Button Content=">" x:Name="mapMenu"/><!-- Кнопка с помощью которой мы будем "раздвигать" меню -->
        </StackPanel>
      </Border>
    </m:MapLayer>
  </m:Map.Children>
</m:Map>

* This source code was highlighted with Source Code Highlighter.

Код на C#, который может пригодится:

//Обрабатываем расширения меню, которое расположенно в слое на карте
this.mapMenu.Click+=(sen,ev)=>{
  Button currentButton = sen as Button;
  //Если дополнительные пункты меню скрыты => открываем
  if (this.AnotherControls.Visibility == Visibility.Collapsed)
  {
    (currentButton.Parent as Panel).Height += 40;
    this.AnotherControls.Visibility = Visibility.Visible;
    currentButton.Content = "<";
  }
  else//Иначе скрываем
  {
    this.AnotherControls.Visibility = Visibility.Collapsed;
    (currentButton.Parent as Panel).Height -= 40;
    currentButton.Content = ">";
  }
};


* This source code was highlighted with Source Code Highlighter.

27 ноября 2010 г.

WebMatrix. Используем Яндекс в качестве поиска по сайту

Построим поиск по сайту при помощи WebMatrix и поисковика Яндекс. Данную задачу можно было реализовать и на любом языке, но я решил примерить синтаксис Razor к интересной задаче.Суть программки в том, что получив запрос пользователя и его пожелания относительно того что он хочет получить (документ или какой-нибудь материал с сайта), мы делаем запрос к поисковику Yandex. В запросе к серверу мы указываем необходимый сайт и пользовательский запрос. Пользователь получает страницу с поисковой выдачей.
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <title></title>
    <link rel="stylesheet" type="text/css" href="main.css"/>
  </head>
  <body>
@using System.Linq;
@using System.Collections.Generic;
        @{
            // У нас есть данные введенные в поле ввода, на основание этого нам нужно сформировать запрос к яндексу
      if(IsPost){
        string text=Request["query"];
        //Проверяем введеную строку, если она не пустая, продолжаем
        if(!String.IsNullOrEmpty(text))
        {
          /*В веденной строке необходимо заменить все пробелы "+"ми.
          Для этих целей используем LINQ */
          IEnumerable<char> textAsChar = text.Select(x=>(x==' ')?'+':x);
          text=new String(textAsChar.ToArray());
          string searchType;метром
          //Определяемся с предпочтениями пользователя, поиск по всему или только по документам
          if (Request["searchtype"]=="all") {searchType="";}
          else {searchType="&mime=pdf&mime=doc&mime=xls";}
          //tetrapolis.ru - сайт на котором производим поиск
          string query=String.Format("http://yandex.ru/yandsearch?date=&text={0}"+
          "&site=tetrapolis.ru&rstr=&zone=all&wordforms=all&lang=ru&mime=all&numdoc=10&lr=2{1}",text,searchType);
          //Делаем переход
          Response.Redirect(query);
        }
      }
    }
  <form method="post" action="">
    <input type="text" name="query" />
    <label for="ContactName"></label>
    <button type="submit">Search</button>
    <br/>
    <input type="radio" name="searchtype" value="all" checked/><label>Общий поиск</label>
    <input type="radio" name="searchtype" value="doc" /><label>По документам</label>
  </form>
  </body>
</html>

main.css:
input[name="query"] {
padding:6px 6px 6px 25px;/* Делаем отступы, самый большой - слева */
font-size: 16px;
color: black;
border: 1px solid #b8b8b8;
/*Делаем круглую рамку (html5) */
-webkit-border-radius:10px;
-moz-border-radius:10px;
/*Добавляем лупу */
background: url('lypa2.jpg') no-repeat;
background-position: left center;
}
button[type="submit"]{
padding:6px;
-moz-border-radius:10px;
-webkit-border-radius:10px;
background-color:#d53e07;
color:white;
font-weight:bold;
}


* This source code was highlighted with Source Code Highlighter.

26 ноября 2010 г.

Относительно недавно познакомился с Silverlight компонентом Bing Map Silverlight Control. Контрол этот полезный и интересный. Поэтому я сразу же попытался сотворить с его участием такое приложенице, что то вроде блокнотика на картах, чтобы можно было делать свои пометки и сохранять их в локальном хранилище. Описывать основы работы с контролом не буду, а сразу перейду к реализации. Основная проблема с которойя столкнулся - навигация по карте осуществляется курсором мышки. Как эту опцию отключить, не переопределяя в наследуемом классе обработчик OnMouseLeftButtonDown, я не нашел.
Привожу код нового класса унаследованного от Microsoft.Maps.MapControl.Map.

Рис. Пример работы
Код класса:
public class NonDragableMap : Microsoft.Maps.MapControl.Map
  {
    private Color _strokeColor;//Цвет линии
    //По умолчанию режим редактирования будет отключен и можно будет как
    //и прежде перемещаться по карте курсором мыши
    public bool allowMapMove
    {
      get;
      set;
    }
    public Color strokeColor
    {
      set{
        this._strokeColor = value;
      }
    }
    private double _strokeThickness;
    public double strokeThickness//Толщина линии
    {
      get{
        return this._strokeThickness;
      }
      set{
        this._strokeThickness = value;
      }
    }
    public NonDragableMap()
    {
      strokeColor = Colors.White;//По умолчанию цвет белый
      strokeThickness = 5;
      allowMapMove = true;
    }
    //Объект линии которую рисуем
    private MapPolyline _mapLine;
    //В коллекции координат храним координаты текущей линии:
    private LocationCollection coordinateCollection;
    //Обрабатываем событие нажатие левой клавиши мыши
    //1) Этим методом убирается перемещение карты курсором мыши
    //2) Создаем объекты отвечающие за прорисовку линии
    protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
    {
      if (this.allowMapMove)
      {
        base.OnMouseLeftButtonDown(e);//Разрешаем работу стандартного обработчика
        return;
      }
      this._mapLine = new MapPolyline();
      this._mapLine.Stroke = new System.Windows.Media.SolidColorBrush(this._strokeColor);//Цвет
      this._mapLine.StrokeThickness = this._strokeThickness;
      this._mapLine.StrokeLineJoin = PenLineJoin.Round;//Скругливаем пересечения - устраняет проблему артефактов при пересечении линий
      this.coordinateCollection = new LocationCollection();
      //Записываем собранные курсором координаты в координаты линии
      this._mapLine.Locations = this.coordinateCollection;
      //Добавляем линию на карту
      this.Children.Add(this._mapLine);
      
    }
    protected override void OnMouseMove(MouseEventArgs e)
    {
      if (this.allowMapMove) base.OnMouseMove(e);
      if (this.coordinateCollection != null)
      {
        var d = e.StylusDevice.GetStylusPoints(this);
        foreach (StylusPoint s in d)
        {
          this.coordinateCollection.Add(this.ViewportPointToLocation(new Point(s.X, s.Y)));

        }
        this.coordinateCollection.Distinct();
      }
    }
    //Обрабатываем события отпускания левой кнопки заершаем рисование
    protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e)
    {
      //Завершаем рисование
      this.coordinateCollection = null;
      this._mapLine = null;
      e.Handled=true;
    }
    
  }


* This source code was highlighted with Source Code Highlighter.

21 ноября 2010 г.

Псевдокласс :target и меню на CSS

Работая над редизайном раздела своего сайта, мне в голову пришла идея сделать меню и весь раздел на css. Сейчас поясню подробнее. Одним из нововведений css 3.0, является псевдокласс :target, который применяется к целевому элементу (к идентификатору, указанному в адресной строке браузера) (см. рис. 1).

Рис. 1 Целевой элемент #bk. 
Следовательно, мы можем на страницы разместить якоря, которые будут ссылаться на определенные элементы страницы. Тем самым при переходе по этим ссылкам мы будем применять к ним псевдокласс :target.
Теперь следует сказать, что псевдокласс :target поддерживается почти всеми ныне ходовыми браузерами (Chrome, Safari, FireFox, Opera), кроме Internet Explorerа младше 9.0. Поэтому метод реализации меню средствами css пока лучше заменять JQuery или верстать по старинке с переходом на отдельные страницы, пока не «отомрут» все браузеры не поддерживающие css 3.0.

Представляю Вам отверстанную страницу html:
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta content="text/html; charset=utf-8" http-equiv="Content-Type" />
<title>Untitled 1</title>
<link rel="stylesheet" type="text/css" href="corp.css"/>
</head>
<body>
<--С права меню, а слева контент -->
<div class="part_left">
<ul>
  <li><a href="#bk">Бизнес кредиты</a></li>
  <li><a href="#rko">Расчетно кассовое обслуживание</a></li>
  <li><a href="#dbo">ДБО</a></li>
  <li><a href="">Конверсионные операции</a></li>
  <li><a href="">Корпоративные карты</a></li>
  <li><a href="">Зарплатный проект</a></li>
  <li><a href="">Депозиты</a></li>
  <li><a href="">Векселя</a></li>
</ul>
</div>
<div class="part_right">
  <ul>
    <li id="default">это IE</li>
    <li id="bk">Бизнес кредиты... </li>
    <li id="rko">Расчетно кассовое обслуживание...</li>
    <li id="dbo">Дистанционное банковское обслуживание...</li>
  </ul>
</div>
</body>

<html>

* This source code was highlighted with Source Code Highlighter.
 и сопутствующий corp.CSS файл:
a{
  font-family:Arial, Helvetica, sans-serif;
  font-size:12pt;
  color:black;
  font-weight:bold;
}
a:link{text-decoration:none;}
a:hover{text-decoration:underline;}
a:active{color:#0000CC;}
ul{
  margin:0;
  -webkit-padding-start:0;
  -moz-padding-start:0;
}
li {list-style:none;}
.part_left{
  position:absolute;
  left:0;
  top:0;
  width:300px;
}
.part_right{
  position:absolute;
  left:305px;
  top:0;
  width:auto;
}
/* Задаем параметры для фрагмента #bk, и остальных пунктов меню*/
/* Значения по умолчанию */
li#bk, li#rko, li#dbo{
display:none;
}
/* Когда ссылка активирована */
li#bk:target, li#rko:target, li#dbo:target{
  display:block;
}
/*Возврат к визуализации по умолчанию */
li#bk:not(:target),li#rko:not(:target), li#dbo:not(:target){
  display:none;
}
/* ------- */
/* Дефолтный случай для IE <9.0*/
li#default{
  display:block;
}
/*li#default:target{
  display:block;
}*/
li#default:not(:target){
  display:none;
}


* This source code was highlighted with Source Code Highlighter.