Язва-скрипт в ASP.NET.
Этот топик я решил посвятить всевозможным вкраплениям javascript-кода, которые иногда весьма непросто сделать. Порой на эти небольшие вкрапления уходит существенно больше времени, чем собственно на функционал формы на ASP.NET.
В принципе ASP.NET-программирование подразумевает формирование тегов ИСКЛЮЧИТЕЛЬНО на сервере. Это классическое ASP.NET-программирование. Сервер и клиент жестко связаны стандартным скриптом, который можно посмотреть например так - http://www.ros-med.org/WebResource.axd?d=nF4WiI3aH3Y1atrp8nCRuw2. Здесь число D генерится для каждого сайта индивидуально, но протрассировав реквесты от браузера к Web-узлу на ASP.NET вы всегда найдете подобный запрос.
Естественно, для каждого браузера формируемые на сервере теги разные. Или чуть-чуть различающиеся, иногда сильно отличающиеся (например для браузеров мобильников). При этом предполагается, что индивидуальная настройка тегов под каждый браузер осуществляется через Web-config, который конфигурируется примерно вот так. Иначе эти же специфичные теги для каждого браузера можно делать самому, написав свой класс, унаследованный от WebControlAdapter (чего мне лично пока не доводилось делать), вот так - Extreme ASP.NET Control Adapters.
Надо правильно оценивать обьем специфики каждого браузера. Даже по синтаксису javascript скорее похож на разные языки в каждом браузере, чем на один язык. Например чтобы получить элемент массива надо индекс массива указать в квадратных скобках для фаерфокса, а для IE - в круглых. Даже чтобы найти в DOM сам элемент, к кому обращаться, в IE можно воспользоваться функцией GetElementByName - которой нет вообще в ФаерФоксе. Да вообще DOM в каждом браузере совершенно различный, вот например в IE есть свойство document._content, которого нету в Опере. Чуствительность к регистру совершенно в javascript-процессорах разных браузеров тоже разная. Например IE считает, что attributes['x'] и attributes['X'] - это обращение к разным атрибутам, а Опера и ФаерФокс - что к одинаковым. Даже собственно логика построения javascript-выражений совершенно различна. Например, вот такой переход сработает в IE - parent.window.frames.location.href[1]='...', но для Фаерокса он сработает только через промежуточную переменную - var f = parent.window.frames;f[1].location.href='/Asp2/42/Index.htm' И это только в трех основных браузерах. Я уже не говорю о менее распространенных браузерах - например в каждом мобильнике он вообще свой собственный, со своей чудо-версией языка javascript.
В рунете существует множество форумов по специфическим версиям javasctipt в каждом из основных браузеров, где обсуждаются десятки и сотни тысяч проблем - например форум по специфике ФаерФокса - http://forum.mozilla-russia.org/index.php или форум по специфике Оперы - http://operafan.net/
Чтобы проиллюстрировать обьем этих специфических проблем, возьмем например timyMSE. Это такой небольшой форматер текстов, который я ставлю везде. Например вы можете его увидеть на http://foto.votpusk.ru/. Так вот его обьем составляет в уже усеченном для моего сайта виде - 6486 строк. При этом строки такой длины, что их даже не получается открыть в студии. Вот например его первая строка.
Вот еще пример из этой же серии. Есть такой контрол, который проигрывает флеш-ролики - http://www.flash-control.net/. Он использует известный в узких кругах асперов скрипт (http://blog.deconcept.com/swfobject/), цель которого вообще скромная относительно выше упомянутого форматера - просто сформировать вот такой тег object/embed. Текст этого скрипта в некоторой версии выглядел вот так. Видите, какие усилия требуются, чтобы всего-навсего сформировать на javascript один-единственный HTML-тег.
Чтобы немного упростить ASP.NET программистам жизнь - был придуман AJAX. Это некий набор тегов, типа <Triggers>, которые ложатся непосредственно в html-код, и которые в итоге обрабатывается ацким скриптом, по размеру в тысячу раз больше стандартного ASP.NET скрипта. Этот ацкий AJAX-framework скрипт сначала вычитывается браузером из сервера, а потом интерплетируется в один поток построчно браузером. Что из этого получается, останавливатся на этой страничке не буду - резюме внизу этой странички. Аналогично работает и микрософтовский SilverLight.
Итак, я привел вас к той мысли, что отказаться от прямого программирования на javascript не получится никак. Инадо быть готовым к тому, что малейшая задача такого рода программирования, несмотря на кажущуюся простоту - просто даже положить на HTML-страничку тег EMBED - может оказатся АЦКИ сложной и занять многие месяцы. А может быть даже - ее неудастся решить НИКОГДА.
Эта простая мысль никак не доходит до большинства заказчиков сайтов и они продолжают упорствовать в своих заблуждениях - хочу чтобы было как на том сайте - вон у них есть всплывающее окошко, вон там есть анимация, а вон там еще какая-то фишичка прикручена.
А ведь каждая такая фишечка, даже просто всплывающее окно - это яваскрипт. И это выход за пределы стандартной модели программирования ASP.NET. Мало того, что они запрещены в браузерах по умолчанию, во многих браузерах они В ПРИНЦИПЕ невозможны.
Например возьмем стандартный текстовый браузер LINX (некую версию его можно сгрузить у меня с сайта). Как в принципе в нем возможно открыть всплывающее окно? А ведь это не теоретические измышления. Когда я работал у швейцарцев, они мне все уши прозудели про запрет дискриминации слабовидящих. А тестирование сайта на возможность чтения машинками для слепых производится именно в LYNX. У буржуев дискриминация слабовидящих даже как уголовное преступление рассматривается - например вы сделали сайт, на который народ должнен загружать свои налоговые декларации. А слабовидящие это сделать не могут. Например из-за долбаных всплывающих окошек. И попробуйте доказать в суде, что вы ущемили права слабовидящих не умышленно, чтобы поглумится над ними, а просто по своему скудоумию.
Но поскольку народ наш твердолобый и все равно любит эти самые всплывающие окошки и всякие рюшечки на javascript - я решил создать эту страничку, куда буду складывать всякие свои решения на javascript.
1.
Итак, первое решение - в котором мы все сделаем вручную самостоятельно. Нам требуется форма, которая будет всплывать над основной формой сайта и через которую можно например отправить сообщение администратору, сообщение о городе, который отсутсвует в выборе или что-то в этом роде. В постановке задачи также то, что у нас на форме много вызовов подобных форм и хотелось бы обработать эти вызовы неким единым движком.
Это была бы совсем простая задачка, если бы браузера работали хотя бы похоже. Но они работают по-разному. Например в моем любимом браузере - FF - нет activeElement, те это свойство не заполняется при клике по какому-то элементу мышкой. Напротив, микрософт всегда заполняет это свойство странички ссылкой на тот элемент, по которому кликнули мышкой.
Первое предлагаемое решение может выглядеть так. На форме (или даже гна MASTER-page выкладываем вот такой код:
0001: <script language='javascript' type='text/javascript'>
0002: function onElementFocused(e) {
0003: var e, i = 0
0004: while (e = window.document.getElementsByTagName('a')[i++]) {
0005: (function(a) { a.onfocus = function() { focusedElement = a; }; })(e);
0006: }
0007: }
0008: if (window.navigator.appName == 'Netscape' || window.navigator.appName == 'Opera') {
0009: if (document.addEventListener)
0010: document.addEventListener("focus", onElementFocused, true);
0011:
0012: var focusedElement;
0013: }
0014: </script>
Этот код выглядит просто, но написать его мне было непросто. Задача этого кода - подвесить на каждый элемент Listener в виде функции onElementFocused, которая будет проходить по всей иерархии DOM и выставлять в переменную focusedElement именно тот элемент, на котором был клик. Эта функция действует только в ФаерФоксе и по идее является аналогом IE-свойства activeElement.
Теперь, когда мы наконец-то благодаря этому коду узнали, на какой кнопке был клик - нам остается непосредственно сформировать вызов новой формы.
Сделать это можно так. В нужной ветке (IsPostBasck или Not IsPostBack) вписываем вот такой фрагмент:
0001: If HttpContext.Current.User.Identity.Name <> "" Then
0002: Me.Page.ClientScript.RegisterStartupScript(Me.GetType, "MDI_Window", _
0003: "<script language='javascript' type='text/javascript'>" & vbCrLf & _
0004: "function Add_Click()" & vbCrLf & _
0005: "{" & vbCrLf & _
0006: "var AddButton1;" & vbCrLf & _
0007: "if (window.navigator.appName=='Netscape'|| window.navigator.appName=='Opera') {AddButton1=focusedElement} else if (window.navigator.appName=='Microsoft Internet Explorer'|| window.navigator.appName=='Opera') {AddButton1 = document.activeElement};" & vbCrLf & _
0008: "window.open(AddButton1.attributes['X'].value,'','resizable=no,scrollbars=yes,toolbar=no,location=no,menubar=no,width=500,height=200');" & vbCrLf & _
0009: "}" & vbCrLf & _
0010: "</script>")
0011: '
0012: Literal1.Text = "<a href=#A1 onclick='Add_Click();' X='" & "user_add_country.aspx?i=" & Membership.GetUser(HttpContext.Current.User.Identity.Name).ProviderUserKey.ToString & "'><font size='2'>Другая</font></a>"
0013: Literal2.Text = "<a href=#A1 onclick='Add_Click();' X='" & "user_add_city.aspx?i=" & Membership.GetUser(HttpContext.Current.User.Identity.Name).ProviderUserKey.ToString & "'><font size='2'>Другой</font></a>"
0014: End If
Как видите, он включается только для залогиненного юзверя. Здесь для залогиненного юзверя формируется линк, в котором в атрибуте 'X' вписывается что именно нужно вызвать в виде всплывающей формы над сайтом. И метка #A1, куда будет происходить переход при тыкании в это линк.
Фнкция Add_Click, которая вызывается при клике на линке, выставляет переменную AddButton1 либо по activeElement (для IE), либо по focusedElement (для FF) - котрая определена из кода на форме. Далее вычитывается адрес перехода из атрибута X. Основная форма (на заднем плане) передергивается к метке #A. А над сайтом по window.open всплывает маленькое окошко примерно вот так.
2.
Теперь рассмотрим, как сделать такую же всплывающую форму, но используя стандартный javascript-скрипт из состава ASP.NET.
В неком месте контрола у меня стоит вот такой код:
0141: <asp:ImageButton ID="AddMessageButton" runat="server" ImageUrl="~/Images/response.gif" ToolTip="Пообщатся" BorderWidth="5px" BorderColor="White" />
0142: <asp:HyperLink ID="AddImageLink1" onClick="window.open('','Cont','resizable=no,menubar=no,scrollbars=no,width=400,height=600');" name="Cont" Target="Cont" runat="server">
0143: <asp:Image ID="AddContact" ToolTip="Пообщатся" ImageUrl="~/Images/response.gif" runat="server" BorderWidth="5px" BorderColor="White" /></asp:HyperLink><br/>
Изюминка этого кода в том, что новая форма открывается в окне с именем Cont, причем это имя должно быть указано в трех местах именно так, как в моем коде.
Управление видимостью этих линков для анонимногозалогиненного юзверя выглядит вот так:
0221: If HttpContext.Current.User.Identity.Name = "" Then
0222: AddMessageButton.PostBackUrl = CreateAddContactLink(User1.ProviderUserKey.ToString, True)
0223: AddImageLink1.Visible = False
0224: AddContact.Visible = False
0225: ElseIf HttpContext.Current.User.Identity.Name = User1.UserName Then
0226: AddMessageButton.Visible = False
0227: AddImageLink1.Visible = False
0228: AddContact.Visible = False
0229: Else
0230: AddMessageButton.Visible = False
0231: AddImageLink1.Visible = True
0232: AddContact.Visible = True
0233: AddImageLink1.NavigateUrl = CreateAddContactLink(User1.ProviderUserKey.ToString, False)
0234: End If
Функция CreateAddContactLink создает просто требуемый линк, то же самое, что делалось в самом первом фрагменте кода в строках 12-13, только вынесенное в отдельную функцию из-за приличноно количества параметров.
Как видите, использование стандартного ASP.NET-скрипта тут дает неизмеримые преимущества по сравнению с прямым программированием на javascript - только надо знать точно синтаксис, показанный мною в строке 142.
3.
Теперь я опишу еще одну версию всплывающей формы (открывающейся на полный экран). Кроме того, в этом примере мы посмотрим еще одно применение javascript для формирования устаревания страницы. Речь пойдет о моем сайте http://rzd.vb-net.com. Там есть такая форма с тестами, которая открывается на полный экран и на которой тикают часики. В нее можно попасть если тыкнуть в пробный вход для диспетчера, а потом в тест.
Тут использована та же идея таймаута страниц, которая описана у меня в проки-сервере для видеонаблюдения, но сначала о всплывающей форме.
Форму User.aspx я сконструировал так:
0149: <asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False"
0150: DataKeyNames="ToTest" DataMember="DefaultView" DataSourceID="GetTest"
0151: Width="100%" ShowHeader="False">
0152: <Columns>
0153: <asp:TemplateField>
0154: <ItemTemplate>
0155: <asp:HyperLink ID="HyperLink1" runat="server"
0156: NavigateUrl='<%# SchemaUrl( Eval("ToTest").ToString ) %>' Text='<%# Eval("Title") %>'></asp:HyperLink>
0157: </ItemTemplate>
0158: <EditItemTemplate>
0159: <asp:TextBox ID="TextBox1" runat="server" Text='<%# Bind("Title") %>'></asp:TextBox>
0160: </EditItemTemplate>
0161: </asp:TemplateField>
0162: <asp:TemplateField>
0163: <ItemTemplate>
0164: <asp:LinkButton ID="TestButton1" runat="server" CommandName='<%# Eval("ToTest") %>'
0165: CommandArgument='<%# TestUrl( Eval ("ToTest").ToString ) %>' X='<%# TestUrl( Eval ("ToTest").ToString ) %>'
0166: OnClientClick='TestButton_Click();' OnClick="TestButton1_Click"></asp:LinkButton>
0167: </ItemTemplate>
0168: </asp:TemplateField>
0169: <asp:TemplateField>
0170: <ItemTemplate>
0171: <asp:LinkButton ID="TrainButton1" Text="Самоподготовка" runat="server" CommandName='<%# Eval("ToTest") %>'
0172: CommandArgument='<%# TrainUrl( Eval ("ToTest").ToString ) %>' X='<%# TrainUrl( Eval ("ToTest").ToString ) %>'
0073: OnClientClick='TrainButton_Click();' OnClick="TrainButton1_Click"></asp:LinkButton>
0174: </ItemTemplate>
0175: </asp:TemplateField>
0176: <asp:TemplateField>
0177: <ItemTemplate>
0178: <asp:Label ID="lMain" runat="server" Text="Экзамен"></asp:Label>
0179: </ItemTemplate>
0180: </asp:TemplateField>
0181: </Columns>
0182: </asp:GridView>
Это непростая для понимания форма. Прежде всего надо понять, что фнкции TestUrl и TrainUrl я сформировал в коде, а на форме просто выполнил декларативную привязку к этим функциям. В CommandName я загрузил просто номер теста. И этот же номер идет параметром функций привязки TestUrl и TrainUrl. Итоговый адрес перехода формируется в атрибуте X.
0410:
0411: Function SchemaUrl(ByVal ID As String) As String
0412: Return "OneSchema.aspx?i=" & Request.QueryString("i") & "&j=" & ID
0413: End Function
0414:
0415: Function TestUrl(ByVal ID As String) As String
0416: Return "Test.aspx?i=" & Request.QueryString("i") & "&j=" & ID & "&t=1"
0417: End Function
0418:
0419: Function TrainUrl(ByVal ID As String) As String
0420: Return "Test.aspx?i=" & Request.QueryString("i") & "&j=" & ID & "&t=0"
0421: End Function
0422:
0423: Protected Sub TestButton1_Click(ByVal sender As Object, ByVal e As System.EventArgs)
0424: Dim TestButton1 As LinkButton = CType(sender, LinkButton)
0425: AddLogTest.SelectParameters("P1").DefaultValue = TestButton1.CommandName
0426: AddLogTest.Select(New DataSourceSelectArguments)
0427: End Sub
0428:
0429: Protected Sub TrainButton1_Click(ByVal sender As Object, ByVal e As System.EventArgs)
0430: Dim TrainButton1 As LinkButton = CType(sender, LinkButton)
0431: AddLogTrain.SelectParameters("P1").DefaultValue = TrainButton1.CommandName
0432: AddLogTrain.Select(New DataSourceSelectArguments)
0433: End Sub
Когда происходит клик, то срабатывает и серверный и клиенский код сайта. Серверный код (строки 423, 429) - получают в качестве параметра номер теста и записывают его в база (чтобы потом весть сверку клиентского и серверного таймеров - вдруг юзер каким-то образом сумел остановить тикание клиентского таймера устаревания странички - и оставит себе на поиск ответа больше времени, чем положено).
А где же клиентский код? Он похож на тот вызов, что мы видели выше (в строках 6-7 второго фрагмента кода), но в данном случае требуется работа только в IE, поэтому можно работать просто по activeElement:
0008: <script language="javascript" type="text/javascript">
0009: function TestButton_Click()
0010: {
0011: var TestButton1 = document.activeElement;
0012: window.open(TestButton1.attributes('X').value,'','resizable=no,scrollbars=yes,toolbar=no,location=no,menubar=no,fullscreen=yes');
0013: }
0014: ;
0015: function TrainButton_Click()
0016: {
0017: var TrainButton1 = document.activeElement;
0018: window.open(TrainButton1.attributes('X').value,'','resizable=no,scrollbars=yes,toolbar=no,location=no,menubar=no,fullscreen=yes');
0019: }
0020: </script>
В этом случае он тривиален, делается просто переход по адресу, вписанном в атрибуте X.
Сама по себе вызываемая форма выглядит так (фрагмент):
0015: <script language="javascript" type="text/javascript">
0016: function timer_process()
0017: {
0018: var timer = document.getElementById('Timer1');
0019: if(timer!= null && timer.value>0)
0020: {
0021: var lbl_timer = document.getElementById('lbl_timer');
0022: timer.value = timer.value-1;
0023: var m = timer.value/60;
0024: var s = timer.value%60;
0025: lbl_timer.innerText = 'Для ответа осталось ' + Math.floor(m) + ' минут ' + s + ' секунд';
0026:
0027: window.setTimeout("timer_process()", 1000);
0028: }
0029: else if(timer!= null && timer.value<=0)
0030: {
0031: document.getElementById('Button1').click();
0032: }
0033: }
0034: </script>
0035: <asp:HiddenField ID="Timer1" runat="server" />
0036: <table width="100%">
0037: <asp:Panel ID="TestTitle" runat="server">
0038: <tr><td align="center">
0039: <asp:Label ID="lbl_timer" runat="server" ></asp:Label>
0040: </td></tr>
Как видите, это просто функция, которая обновляет метку на форме и запускает сама себя каждый раз, когда вызывается.
Теперь осталось собственно выполнить первичный запуск этой функции устаревания странички. Запуск этот я делаю в коде в зависимости от ряда условий вот так:
0026: ClientScript.RegisterClientScriptBlock(Me.GetType, "StartTimer", "<script language='javascript' type='text/javascript'>" & vbCrLf & _
0027: "window.setTimeout('timer_process()', 1000);" & vbCrLf & _
0028: "</script>")
4.
Рассмотрим еще одну всплывающую форму. На этот раз для залогинивания. В этом случае задача состоит не только в том, чтобы просто открыть в браузере всплывающую форму в момент, когда на сервере возникла такая наобходимость, но и ПЕРЕДЕРНУТЬ основную форму, те выполнить рефреш данных на ней (если юзер удачно залогинился).
Суть этой технологии - взаимная синхронизация форм засчет взведения ТРИГГЕРА ПОСТБЕКА, который выполнит РЕФРЕШ основной формы (которая подождет завершения всплывающей формы). Это как бы такая небольшая промежуточная фишка со всплывающими формами, которая требует понимания и серверного и клиентского программирования - а как показывает практика, хотя в этом коде всего-то несколько строк - в голове эти вещи сочетаются плохо. C другой стороны - эта технология является основой AJAX. Но MS сумела упаковать эту простую идею в сотни тысяч строк JavaScript-кода, создать тысячи AJAX-обьектов в браузере и заставить браузер интепритировать эту монстрообразную обьектную модель AJAX при каждом постбеке - и все якобы для ускорения!!!
Казалось бы, чтобы вызвать всплывающую форму для запроса пароля все просто - есть метод window.showModalDialog - и что еще надо? Однако, кое что надо. Во-первых, практически всегда в браузере стоит параметр - блокировка всплывающих окон - и даже если они не блокируются - то вместо пароля получится вот такое малоприятное сообщение. А во-вторых, еще сообразить надо, куда его вставить и как.
Для начала этой комбинации добавляем на каждой мастер-форме проекта текстбокс, на котором будем взводить триггер:
В каждой мастер-страничке взводим этот триггер так:
Дочернюю форму запроса пароля мы вызываем через прописыванием в браузер клиентского скрипта ее вызова. При необходимых условиях. Это выделенная строчка. Ясно, что в вашем случае эти условия будут совершенно специфичные для вашего проекта. Этот же фрагмент кода можно вызывать и на мастер-страничке.
Теперь браузер откроет второе окно с маленькой формой запроса пароля, причем первое (родительское окно) будет доступно по windows.opener. Код, который я вызвал, чтобы прописать открывающий новое окно в браузере - перед вами. В реале у меня тут огромный класс, но для пояснения концепции нам будет нужен только именно этот небольшой метод:
00001: Imports Microsoft.VisualBasic 00002: 00003: Public Class DigitalSignature 00004: 00005: ''' <summary> 00006: ''' Этот скрипт будучи зарегистрирован: Me.ClientScript.RegisterStartupScript(Me.GetType, "OpenPassWindScript", OpenPassWindScript.ToString) 00007: ''' откроет в браузере отдельное окно ввода пароля - форму GetPinCode.aspx, которая по закрытии выполнить логин в ОСКОРП и рефреш основной формы 00008: ''' </summary> 00009: Public Shared Function OpenPasswordWindowScript() As String 00010: Dim OpenPassWindScript As New StringBuilder 00011: With OpenPassWindScript 00012: .AppendLine("<script language='javascript'>") 00013: 'так не получается - не получается триггер завести по window.opener.document.getElementById('ctl00$PostBackTrigger').value=document.getElementById('txPIN').value; 00014: '.AppendLine("window.showModalDialog('GetPinCode.aspx','','dialogHeight: 100px; dialogWidth: 200px; dialogTop: px; dialogLeft: px; edge: Raised; center: Yes; resizable: No; status: No;');") 00015: 'к тому же на модальные окна почти всегда стоит блокировка 00016: .AppendLine("var Left=window.screenLeft+document.body.clientWidth/2-130; var Top=window.screenTop+document.body.clientHeight/2-40;") 00017: .AppendLine("window.open('GetPinCode.aspx','GetPinCode','titlebar=no,location=no;scrollbars=no,resizable=no,toolbar=no,statusbar=no,width=250,height=100,left='+Left+',top='+Top);") 00018: .AppendLine("</script>") 00019: End With 00020: Return OpenPassWindScript.ToString 00021: End Function
Теперь разберемся с этой самой маленькой формой запроса пароля, которую у нас написана на обычной ASP2 и которую мы загрузили из браузера по windows.open:
Это простейшая форма, с вот-таким кодом, с которой и происходит залогинивание.
00001: ''' <summary> 00002: ''' Нажатие ОК на этой форме вызывает срабатывание триггера на основной форме, который приводит к постбеку основной формы 00003: ''' Логин в ОСКОРП происходит на этой форме 00004: ''' </summary> 00005: Partial Class Moscow_GetPinCode 00006: Inherits System.Web.UI.Page 00007: Dim ResultTransferStatement As String = _ 00008: "window.opener.document.getElementById('ctl00$PostBackTrigger').value=document.getElementById('txPIN').value;" 00009: 00010: Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load 00011: If Not IsPostBack Then 00012: ImageNoRobot.ImageUrl = "~/RandomImage.ashx?N=" & "3" 00013: Me.ClientScript.RegisterOnSubmitStatement(Me.GetType, "ResultTransferStatement", ResultTransferStatement) 00014: Me.SetFocus(txPIN) 00015: End If 00016: End Sub 00017: 00018: Protected Sub OK_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles OK.Click 00019: 'закрываем в любом случае - держать ее открытой нет смысла - рефреш основной формы уже сделан в браузере 00020: Dim CloseScript As New StringBuilder 00021: With CloseScript 00022: .AppendLine("<script language='javascript'>") 00023: .AppendLine("window.close();") 00024: .AppendLine("</script>") 00025: End With 00026: Response.Write(CloseScript) 00027: 'а теперь попробуем войти по цифровой подписи 00028: If CheckCapture.IsTrustImagePassword(txImagePass.Text, "3") Then 00029: If Not GISIS.Billing.Context.Current.IsCurrentUserLogged Then 00030: If Not DigitalSignature.LoginToOscorpByAU004(txPin.Text) Then 00031: My.Log.WriteEntry("Не вошли по цифровой подписи в " & me.GetType.Name) 00032: End If 00033: End If 00034: End If 00035: End Sub 00036: 00037: End Class
Собственно строки 28-34 - это мое специфическое залогинивание в мой класс (с предварительной проверкой капчи, конечно), а вот вся остальная обвязка этой формы - абсолютно шаблонна и будет полностью повторяться в вашем проекте.Обратите внимание, что засчет того, что мы ложили триггер постбека на мастер-форму - имя у него здесь поменялось - и стало с префиксом ctl00$.
Теперь пробежимся по алгоритму этой маленькой формы. Как видите, при нажатии ОК - форма закрывается скриптом, непосредственно прописываемым в браузер. И в 13-й строке тут производится главное действие - собственно рефреш основной родительской формы. Мы можем туда что угодно прописать - хоть просто нолик. ПИН-код я туда просто так прописываю, здесь важно, что на том поле родительской формы, куда мы пишем - висит триггер постбека, который и передернет основную родительскую форму и отобразит на ней факт залогинивания. Здесь есть важный момент - низзя ни в коем случае делать никакую обработку введенного пин-кода на основной форме, иначе клиентским скриптом его туда можно будет вписывать в обход КАПЧИ. Реальную обработку ПИН-кода возможно производить только на самой форме его запроса.
И еще один важный момент. Конечно, форма логина - она маленькая и по идее должна быстрее отработать - и на момент постбека основной формы - которую мы передергивали триггером в браузере, залогинивание уже должно отработать... Должно - но так происходит только в лабораторных условиях. Ведь из браузера постбеки пошли в серверную обработку одновременно. В реале, например SQL вдруг может начать что-то свое делать. И получается что рефреш-то мы сделали на главной форме, но толку от него мало. Юзер ввел логин/пароль - и видит на главной форме, что он не залогинился. Конечно, если он нажмет рефреш на основной форме секунд через 10 - то все будет отлично. Но... откуда ж он знает об этом?
Тут мы вступаем в еще одну область - синхронизация форм вот в таких многооконных сайтах. Когда нам надо ПРИТОРМОЗИТЬ одну форму, до завершения неких событий. В принципе, синхронизация форм - это большая тема, особенно для форумов и сайтов знакомств, где мы можем одновременно открыть формы - переговоры со множеством юзеров. И я обязательно распишу этот вопрос как-нибудь позже подробнее. В данном же случае - я просто ограничился на главной форме циклом, который ждет факта залогинивания, которое происходит на этой маленькой форме запроса пароля:
Этот код мы добавляем в каждую мастер-форму. В итоге весь этот фрагмент с всплывающей формой запроса пароля будет выглядеть всего в несколько строк:
Но учтите, что в однопоточном девелоперском сервере, такая схема синхронизации форм работать, конечно же, не будет.
5.
|