Статьи

Создайте гибкий XMPP-чат для веб-сайта для членов с помощью Flash и PHP

Мы рассмотрим, как создать чат-приложение XMPP, которое можно использовать в самых разных сценариях. Вы узнаете, как интегрировать внешнюю базу данных с OpenFire Jabber-сервером Ignite Realtime и как использовать библиотеку XIFF для создания пользовательских расширений XMPP, которые можно использовать для отправки пользовательских данных по сети.

Вы можете использовать это для создания стандартного стандартного приложения для чата со страницей, посвященной ему, или вы можете запустить его вместе с другим фрагментом содержимого Flash, как Kongregate делает со своими играми.



Давайте посмотрим на интерфейс конечного результата, к которому мы будем стремиться (эта демонстрация не работает как реальный клиент чата):

Вот демонстрация видео, которая показывает это в действии:



В этом руководстве предполагается, что у вас есть некоторый опыт работы с PHP. В вашей системе должен быть установлен Wamp Server, и вы также должны быть немного знакомы с PhpMyAdmin. Если у вас нет WAMP, вы можете скачать его здесь . Вам также необходимо загрузить Openfire Jabber Server и библиотеку API XIFF с веб-сайта Ignite Realtime . Я проведу вас через процесс установки Openfire. Наконец, вам потребуется последняя версия Java, установленная в вашей операционной системе и Flash CS4 или более поздней версии. В этом уроке я буду использовать Flash CS5 Professional. Если у вас нет последней версии Java, вы можете скачать последнюю версию здесь .



Убедитесь, что на вашем компьютере работает Wamp Server, и перейдите по адресу http: // localhost / phpmyadmin / в веб-браузере.

Создайте новую базу данных с именем MyContentSite используя MyContentSite .

создать основную базу данных

Создайте новую таблицу в MyContentSite данных myMembers под названием myMembers с восемью полями.

стол mymembers

После того, myMembers таблица myMembers была успешно создана PhpMyAdmin, создайте следующие поля:

  • UID
  • Имя
  • фамилия
  • my_username
  • мой пароль
  • Эл. адрес
  • статус
  • страна

Ваш экран должен выглядеть следующим образом:

члены поля 1

Позвольте мне разбить каждое поле. Поле uid должно иметь тип INT . Вы можете изменить тип поля из столбца Type . Сделайте это поле первичным индексом и установите для этого поля самоинкремент. Сделайте это, выбрав опцию PRIMARY под столбцом INDEX в этой строке полей. Затем установите флажок в столбце « Auto-Increment . Это поле представляет идентификатор пользователя текущего участника нашего веб-сайта. my_username first_name , last_name , my_username , my_password , email и country должны иметь тип данных или VARCHAR а Length/Value должно быть установлено на 255.

Примечание. Поля my_password показанные на изображениях, содержат MD5 пароля пользователя. Вы можете хранить обычные пароли без шифрования, но для этого урока я буду использовать хешированные пароли.

члены поля 2

Наконец, поле status_message должно иметь тип MEDIUMTEXT .

После того, как вы создали все поля, нажмите кнопку save .

поля сохраненных членов

Теперь мы готовы создать две фиктивные учетные записи, которые мы будем использовать для входа на наш веб-сайт и присоединения к чат-комнатам позже в этом руководстве. Нажмите на вкладку Insert . Вам будет представлена ​​форма для создания новой строки в таблице.

Оставьте поле uid пустым, потому что мы хотим, чтобы это поле автоматически увеличивалось . Установите first_name для Джейн и поле last_name для Doe . Установите для my_username значение janedoe со всеми буквами нижнего регистра. Для поля my_password мы будем использовать хешированное значение для нашего пароля, tutsplus во всех строчных буквах. Введите ca28ad0733a3dde9dc1f30e32718d209 в поле my_password . Вы можете установить в поле электронной почты адрес электронной почты по вашему выбору, а в поле status_message — все, что захотите. Установите поле country на любую страну, которую вы хотите. Когда вы закончите, нажмите на кнопку Сохранить . Повторите этот процесс, чтобы создать учетную запись для John Doe с полем my_username установленным в johndoe123 со всеми строчными буквами. Используйте тот же пароль, что и раньше.

Джейн Доу аккаунт
учетная запись Джона Доу
сохраненные аккаунты


Как только вы загрузили Openfire с веб-сайта Ignite Realtime, запустите установочный exe- файл (или dmg- файл, если вы используете Mac).

инициализация установщика

Выберите свой язык.

Выберите ваш язык

Нажмите Далее, чтобы продолжить.

начать установку

Примите лицензионное соглашение.

Условия эксплуатации

Выберите каталог и нажмите « Далее», чтобы продолжить.

каталог установки

Выберите папку «Пуск».

папка меню Пуск

Нажмите Далее, чтобы начать установку.

добыча

После завершения установки нажмите Готово, чтобы запустить Openfire. Сервис Openfire запустится автоматически при запуске программы. Нажмите кнопку Launch Admin , когда Openfire завершит загрузку.

Установка завершена
Openfire запускается
Openfire работает

Теперь мы настроим Openfire. Выберите предпочитаемый язык и нажмите « Продолжить» .

выберите язык

Оставьте в поле « Порт консоли администратора» и в поле « Порт консоли безопасного администратора» их значения по умолчанию. Для этого урока оставьте в поле Домен также значение по умолчанию. Вы можете изменить это позже на домен вашего сайта. Нажмите Продолжить .

настройки сервера

Выберите опцию « Стандартное соединение с базой данных» и нажмите « Продолжить» .

настройки базы данных

Под пресетами драйверов баз данных выберите MySQL . Введите com.mysql.jdbc.Driver в поле Класс драйвера JDBC . Измените [host-name] на localhost и измените [database-name] на mycontentsite в поле URL-адрес базы данных . Установите для имени пользователя и пароля имя пользователя и пароль базы данных MySQL. Для этого урока я использовал имя пользователя по умолчанию для MySQL, которое является root, а поле Password остается пустым. Нажмите Продолжить, чтобы продолжить.

Настройка MySQL 1
Настройка MySQL 2

Оставьте настройки профиля по умолчанию . Нажмите Продолжить, чтобы продолжить.

настройки профиля

Выберите адрес электронной почты для вашей учетной записи администратора и пароль, а затем продолжите.

пароль администратора

Мы закончили процесс установки. Теперь вы можете войти в консоль администратора. Используйте имя пользователя по умолчанию admin и пароль, который вы выбрали при установке.


Наше приложение позволяет пользователям общаться в чатах. Но для этого наши пользователи должны иметь чаты, чтобы присоединиться. Давайте создадим чаты с помощью консоли администратора Openfire. Если вы еще этого не сделали, запустите Openfire Server. Войдите в консоль администратора Openfire. Перейдите на страницу «Комнаты группового чата», нажав на вкладку « Групповой чат ».

домашний чат

Нажмите Создать новую комнату в левой части экрана. Заполните детали, как вы видите их на изображении ниже.

новый чат

Когда вы закончите, нажмите на кнопку Сохранить изменения . Если комната была создана успешно, вы должны увидеть сообщение и зеленую галочку.

создание комнаты

Выполните те же шаги, чтобы создать еще две комнаты чата.

другая комната
последняя комната
все чаты

В этом руководстве наш искусственный веб-сайт использует базу данных MySQL для хранения данных о каждом пользователе. Openfire может быть интегрирован с внешней базой данных, в данном случае базой данных MySQL. Сначала мы должны настроить Openfire для этого.

Откройте файл openfire.xml с помощью « Блокнота» или, предпочтительно, редактора форматированного текста, такого как « Блокнот ++», как я упоминал ранее. Файл будет находиться в папке Openfire / conf / в папке Program Files на вашем ПК.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
<?xml version=»1.0″ encoding=»UTF-8″?>
 
<!—
    This file stores bootstrap properties needed by Openfire.
    Property names must be in the format: «prop.name.is.blah=value»
    That will be stored as:
        <prop>
            <name>
                <is>
                    <blah>value</blah>
                </is>
            </name>
        </prop>
 
    Most properties are stored in the Openfire database.
    property viewer and editor is included in the admin console.
—>
<!— root element, all properties must be under this element —>
<jive>
  <adminConsole>
    <!— Disable either port by setting the value to -1 —>
    <port>9090</port>
    <securePort>9091</securePort>
  </adminConsole>
  <locale>en</locale>
  <!— Network settings.
      Alternatively, you can specify a specific network interfaces that the server
      will listen on.
       on multi-homed servers.
  <!—
    <network>
        <interface></interface>
    </network>
    —>
  <connectionProvider>
    <className>org.jivesoftware.database.EmbeddedConnectionProvider</className>
  </connectionProvider>
  <database>
    <defaultProvider>
      <driver>com.mysql.jdbc.Driver</driver>
      <serverURL>jdbc:mysql://localhost:3306/mycontentsite</serverURL>
      <username>root</username>
      <password/>
      <testSQL>select 1</testSQL>
      <testBeforeUse>true</testBeforeUse>
      <testAfterUse>true</testAfterUse>
      <minConnections>5</minConnections>
      <maxConnections>25</maxConnections>
      <connectionTimeout>1.0</connectionTimeout>
    </defaultProvider>
  </database>
  <setup>true</setup>
</jive>

Вот как выглядит мой файл openfire.xml . Ваш файл openfire.xml должен выглядеть аналогично моему. Вот ссылка на Руководство по интеграции пользовательских баз данных Openfire на веб-сайте Ignite Realtime. Вы заметите, что в этом руководстве вас попросят внести изменения непосредственно в файл конфигурации openfire.xml .

Не вносите никаких изменений в этот файл, если он не похож на мой.

Примечание. Весьма вероятно, что ваш файл openfire.xml будет использовать DefaultConnectionProvider . Если это так, у вас могут возникнуть проблемы при входе в консоль администратора. Попробуйте сначала войти в систему по умолчанию. Если пароль, указанный вами при установке, не работает, используйте пароль по умолчанию для входа в систему. Имя пользователя по умолчанию — admin а пароль по умолчанию — admin .

Если вы не можете войти в систему, измените DefaultConnectionProvider на EmbeddedConnectionProvider . Затем перезапустите Openfire и попробуйте войти снова. Если проблема не устранена, снова запустите программу установки Openfire. Измените значение тега setup с false на true в файле openfire.xml. Затем перезапустите Openfire, чтобы снова запустить установку. Делайте это в крайнем случае — в этом не должно быть необходимости.

Я следовал инструкциям на сайте Ignite Realtime бесчисленное количество раз, чтобы потом оказаться в дыре. Одна из проблем, с которыми я столкнулся, заключалась в том, что пользователи не могли подключиться к серверу, и когда я попытался исправить проблему в консоли администратора, я не смог войти в систему. Фактически, единственное, что я могу себе представить, это может быть более неприятно чем проблемы, с которыми я столкнулся, застревал внутри ловушки пилы.

Я не хочу, чтобы вы проходили через то, что мне пришлось, поэтому, пожалуйста, внимательно выполните следующие шаги. В Openfire есть замечательный способ редактирования и создания свойств, который, на мой взгляд, намного эффективнее, чем редактирование XML-файла в вашей системе.

Войдите в консоль администратора Openfire. Нажмите на ссылку « Свойства системы» в правой части главной страницы.

свойства системы

Страница свойств системы вашего сервера должна выглядеть примерно так.

Важное замечание: Если на странице «Свойства системы» отсутствуют некоторые или все свойства на изображении ниже, вы можете добавить свойства вручную. Когда мы изменяем свойство в учебнике, если у вас нет свойства, которое мы изменяем, просто используйте те же шаги, что и для изменения свойства, чтобы создать его. В противном случае, если у вас уже есть свойство, которое мы создаем, просто измените свойство со значениями, которые я указываю.

Внизу экрана вы увидите раздел с заголовком Добавить новое свойство . У него есть два поля. Первое поле Имя свойства . Второе поле — Значение свойства . В поле Имя свойства введите jdbcProvider.driver, а в поле Значение свойства введите com.mysql.jdbc.Driver в поле. Нажмите на кнопку Сохранить свойство , когда вы закончите. Вы будете следовать этим шагам, чтобы создать больше свойств, а также изменить существующие свойства.

драйвер jdbcProvider

Создайте свойство с именем jdbcProvider.connectionString со значением jdbc: mysql: // localhost / mycontentsite? User = root & password = .

Строка подключения jdbcProvider

Теперь мы собираемся сделать нашу первую модификацию существующего свойства. Нажмите на ссылку Изменить, которая соответствует свойству provider.auth.className . Измените его значение на org.jivesoftware.openfire.auth.JDBCAuthProvider, используя таблицу свойств редактирования . Нажмите кнопку Сохранить свойство , когда вы закончите.

имя провайдера

Создайте новое свойство с именем jdbcAuthProvider.passwordSQL . Присвойте ему значение SELECT my_password FROM mymembers ГДЕ my_username =? , Значением этого свойства является строка запроса MySQL, которая будет использоваться для аутентификации пользователя.

Примечание: обратите внимание, что он содержит знак вопроса (?). Вопросительный знак будет заменен значением внутри поля имени пользователя.

jdbcAuthProvider passwordSQL

Создайте новое свойство с именем jdbcAuthProvider.passwordType . Дайте ему значение md5 .

jdbcAuthProvider passwordType

Примечание. Свойства jdbcAuthProvider будут скрыты, если вы правильно выполнили эти шаги.

свойство сохранено

Создайте новое свойство с именем admin.authorizedUsernames . Значение должно быть jid имени пользователя, с которым вы хотите войти в консоль администратора .

Примечание: посмотрите на изображение ниже. Обратите внимание, что jid Джейн и Джона Доу — это имена пользователей, соединенные знаком @ и доменом сервера XMPP.

авторизованные имена пользователей

Измените свойство provider.user.className , изменив его значение на org.jivesoftware.openfire.user.JDBCUserProvider .

Пользователь провайдера className

Создайте новое свойство с именем jdbcUserProvider.loadUserSQL со значением SELECT first_name, email FROM mymembers ГДЕ my_username =? ,

jdbcUserProvider loadUserSQL

Создайте новое свойство с именем jdbcUserProvider.userCountSQL и присвойте ему значение SELECT COUNT (*) FROM mymembers .

jdbcUserProvider userCountSQL

Создайте новое свойство с именем jdbcUserProvider.allUsersSQL . Установите значение SELECT my_username ОТ mymembers .

jdbcUserProvider allUsersSQL

Создайте новое свойство с именем jdbcUserProvider.searchSQL . Присвойте ему значение SELECT my_username ОТ mymembers .

jdbcUserProvider searchSQL

Создайте новое свойство с именем usernameField . Установите его значение в my_username .

поле имени пользователя

Создайте новое свойство с именем nameField . Установите его значение first_name .

поле имени

Создайте новое свойство с именем emailField . Установите его значение для электронной почты .

поле электронной почты

Теперь, когда мы добавили и изменили необходимые свойства, мы можем выйти из консоли администратора . Перезапустите Openfire и попытайтесь снова войти в консоль администратора с реальным пользователем.

Доступ запрещен!

Теперь попробуйте войти с именем пользователя, admin .

Доступ снова запрещен! Что тут происходит?

Давайте посмотрим на файл openfire.xml . Ваш должен выглядеть так же, как и раньше. Нам нужно добавить модификации в XML-файл. Я обнаружил, что внесение изменений сначала в консоль администратора , а затем изменение файла openfire.xml более согласованно, чем просто внесение изменений в xml. Как я уже говорил ранее, я просто не мог войти в систему с помощью клиента или в консоли администратора после внесения этих изменений.

Измените файл openfire.xml так, чтобы он выглядел следующим образом.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
<?xml version=»1.0″ encoding=»UTF-8″?>
 
<!—
    This file stores bootstrap properties needed by Openfire.
    Property names must be in the format: «prop.name.is.blah=value»
    That will be stored as:
        <prop>
            <name>
                <is>
                    <blah>value</blah>
                </is>
            </name>
        </prop>
 
    Most properties are stored in the Openfire database.
    property viewer and editor is included in the admin console.
—>
<!— root element, all properties must be under this element —>
<jive>
  <adminConsole>
    <!— Disable either port by setting the value to -1 —>
    <port>9090</port>
    <securePort>9091</securePort>
  </adminConsole>
  <locale>en</locale>
  <!— Network settings.
      Alternatively, you can specify a specific network interfaces that the server
      will listen on.
       on multi-homed servers.
  <!—
    <network>
        <interface></interface>
    </network>
    —>
  <connectionProvider>
    <className>org.jivesoftware.database.DefaultConnectionProvider</className>
  </connectionProvider>
  <database>
    <defaultProvider>
      <driver>com.mysql.jdbc.Driver</driver>
      <serverURL>jdbc:mysql://localhost:3306/mycontentsite?user=root&amp;password=</serverURL>
      <username>root</username>
      <password/>
      <testSQL>select 1</testSQL>
      <testBeforeUse>true</testBeforeUse>
      <testAfterUse>true</testAfterUse>
      <minConnections>5</minConnections>
      <maxConnections>25</maxConnections>
      <connectionTimeout>1.0</connectionTimeout>
    </defaultProvider>
  </database>
  <jdbcProvider>
    <driver>com.mysql.jdbc.Driver</driver>
    <connectionString>jdbc:mysql://localhost/mycontentsite?user=root&amp;password=</connectionString>
  </jdbcProvider>
  <provider>
    <auth>
      <className>org.jivesoftware.openfire.auth.JDBCAuthProvider</className>
    </auth>
    <user>
      <className>org.jivesoftware.openfire.user.JDBCUserProvider</className>
    </user>
  </provider>
  <jdbcAuthProvider>
    <passwordSQL>SELECT my_password FROM mymembers WHERE my_username=?</passwordSQL>
    <passwordType>md5</passwordType>
  </jdbcAuthProvider>
  <jdbcUserProvider>
    <loadUserSQL>SELECT first_name,email FROM mymembers WHERE my_username=?</loadUserSQL>
    <userCountSQL>SELECT COUNT(*) FROM mymembers</userCountSQL>
    <allUsersSQL>SELECT my_username FROM mymembers</allUsersSQL>
    <searchSQL>SELECT my_username FROM mymembers WHERE</searchSQL>
    <usernameField>my_username</usernameField>
    <nameField>first_name</nameField>
    <emailField>email</emailField>
  </jdbcUserProvider>
  <setup>true</setup>
  <admin>
    <authorizedUsernames>janedoe, johndoe123</authorizedUsernames>
  </admin>
</jive>

Убедитесь, что вы используете DefaultConnectionProvider вместо EmbeddedConnectionProvider затем закройте Openfire и перезапустите его. Попытайтесь войти в консоль администратора в качестве члена базы данных вашего сайта. Я вошел как Джон Доу. Если все сделано правильно, вы должны вернуться в консоль администратора, а имя пользователя должно быть в верхнем правом углу домашней страницы.

Примечание. Прежде чем двигаться дальше, убедитесь, что все свойства в файле openfire.xml отображаются в системных свойствах консоли администратора. Если это не так, теперь вы знаете, как добавить их вручную.

вошли в систему

Нам нужно использовать PHP для сбора данных из базы данных MySQL и представления данных во Flash. Для тех из вас, кто плохо знаком с PHP, я кратко объясню, что выполняет каждый скрипт. Давайте начнем с класса MySQLConnection .

Класс MySQLConnection подключается и отключается от базы данных MySQL.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class MySQLConnection {
 
    private $db_host = «localhost»;
    private $db_user = «root»;
    private $db_pass = «»;
    private $db_name = «mycontentsite»;
    private $connected = 0;
     
    public function connect() {
     
        mysql_connect($this->db_host, $this->db_user, $this->db_pass) or die ( «Error: Script aborted. Could not connect to database.» );
        mysql_select_db($this->db_name) or die ( «Error: Script aborted. No database selected.» );
        $this->connected = 1;
        session_start();
    }
     
    public function close() {
     
        mysql_close();
        $this->connected = 0;
    }
     
    public function get_connected() {
         
        return $this->connected;
    }
}

Класс LoginManager обрабатывает логины пользователей. Пользователь может пройти аутентификацию, а затем войти и выйти с этим классом.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
require_once «MySQLConnection.php»;
 
class LoginManager {
     
    public function __construct() {
         
    }
     
    public function login( $username, $password ) {
         
        $username = strip_tags( $username );
     
        $username = stripslashes( $username );
     
        $username = mysql_real_escape_string( $username );
     
        $passHash = md5( $password );
         
        $connection = new MySQLConnection();
        $connection->connect();
         
        $sql = «SELECT * FROM mymembers WHERE my_username = ‘$username’ AND my_password = ‘$passHash’ LIMIT 1»;
        $query = mysql_query( $sql );
     
        if ($query) {
         
            $count = mysql_num_rows( $query );
        }
        else {
             
            die ( mysql_error() );
        }
         
        if ( $count > 0 ) {
         
            while ( $row = mysql_fetch_array( $query ) ) {
             
                $_SESSION[‘username’] = $username;
                $_SESSION[‘pw’] = $password;
                $uid = $row[‘uid’];
                session_name( $username . $uid );
                setcookie( session_name(), », time() + 42000, ‘/’ );
                $connection->close();
                die ( «login=1» );
            }
         
            die ( «login=0&error=Invalid username or password» );
 
        }
        else {
         
            $connection->close();
            die ( «login=0&error=Invalid username or password» );
        }
    }
     
    public function checkLogin() {
         
        if ( isset ( $_SESSION[‘username’] ) && isset ( $_SESSION[‘pw’] ) ) {
             
            $user = $_SESSION[‘username’];
            $pw = $_SESSION[‘pw’];
            die ( «login=1&username=$user&password=$pw» );
        }
        else {
         
            die ( «login=0» );
        }
    }
     
    public function logout() {
         
        setcookie(session_name(), », time() — 42000, ‘/’);
        if ( isset( $_SESSION[‘username’] ) ) unset( $_SESSION[‘username’] );
        if ( isset( $_SESSION[‘pw’] ) ) unset( $_SESSION[‘pw’] );
         
        //Destroy session
        session_destroy();
         
        //return result to Flash (swf)
        die («logout=1»);
    }
}

Мы вызываем login.php для входа в систему и logout.php для выхода из системы с LoginManager класса LoginManager . Чтобы проверить, вошел ли пользователь в систему, мы вызываем скрипт check_login.php .

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
// login.php
 
require_once «classes/LoginManager.php»;
 
if (isset($_POST[‘username’]) && $_POST[‘password’]) {
     
    login();
}
 
function login() {
 
    $username = $_POST[‘username’];
    $password = $_POST[‘password’];
    unset($_POST[‘username’]);
    unset($_POST[‘password’]);
     
    $login = new LoginManager();
    $login->login( $username, $password) ;
}
1
2
3
4
5
6
7
// logout.php
 
require_once «classes/LoginManager.php»;
 
$login = new LoginManager();
$login->logout();
01
02
03
04
05
06
07
08
09
10
// check_login.php
 
require_once «classes/LoginManager.php»;
 
session_start();
 
$login = new LoginManager();
$login->checkLogin();
exit();

Последний скрипт, который вызывается из ActionScript, — это скрипт grab_user_data.php, который используется для выбора данных пользователя из нашей базы данных MySQL.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
require_once «classes/MySQLConnection.php»;
 
if ( isset( $_POST[‘username’] ) ) {
     
    $connection = new MySQLConnection();
    $connection->connect();
    $username = $_POST[‘username’];
    $sql = «SELECT * FROM mymembers WHERE my_username = ‘$username’ LIMIT 1»;
    $query = mysql_query( $sql );
     
    while ( $row = mysql_fetch_array( $query ) ) {
         
        $uid = $row[‘uid’];
        $xml = ‘<user id=»‘ . $uid . ‘»>’ .
        $xml .= » <firstName>» .
        $xml .= » <lastName>» .
        $xml .= » <country>» .
        $xml .= » <statusMessage>» .
        $xml .= «</user>\n»;
    }
     
    echo $xml;
    $connection->close();
    exit();
}

Эти PHP-скрипты играют очень важную роль в нашем приложении, но они очень просты.


Откройте Flash Professional. Установите класс документа в ChatApp . Установите размер stage на 550 х 400 .

setup_flash

Примечание. Мне нравится использовать частоту кадров 30 кадров в секунду, но в нашем приложении нет анимации, поэтому вы можете использовать любую частоту кадров, которая вам больше подходит.


На панели « Компоненты» выберите и перетащите компонент « Button на stage . Расположите кнопку так, чтобы она находилась в верхнем правом углу stage . Установите имя экземпляра кнопки logoutBtn . Добавьте еще одну кнопку в правом нижнем углу stage и установите имя ее экземпляра sendBtn .

компонент кнопки
кнопки на сцене

Добавить компонент List на сцену. Поместите его прямо под logoutBtn и измените размер компонента так, чтобы он logoutBtn между обеими кнопками. Установите его имя экземпляра в list .

список на сцене

Мы будем использовать logoutBtn для выхода из сеанса пользователя и sendBtn чтобы позволить нашим пользователям отправлять сообщения. List отобразит всех онлайн-пользователей в текущей комнате чата. При щелчке элемента в списке будет загружен профиль пользователя.

Теперь нам нужен компонент, который будет отображать входящие и исходящие сообщения чата, а также текстовое поле, которое наши пользователи могут использовать для ввода новых сообщений. Добавьте компонент TextArea на stage . Измените размер и разместите его так, чтобы он занимал большую часть оставшейся части stage , оставляя место для входного текстового поля в нижней части, sendBtn высоте sendBtn . Установите имя экземпляра для displayTxt .

компонент текстовой области
текстовая область на сцене

Наконец, нам нужно добавить компонент TextInput на stage . Расположите компонент непосредственно под displayTxt и слева от sendBtn . Установите имя экземпляра для inputTxt .

компонент ввода текста

Выберите все компоненты на stage . Преобразовать выделение в символ. Символом должен быть MovieClip именем UserInterface . Выберите опцию « Экспорт для ActionScript» . Поле Class должно читаться как UserInterface . Установите имя экземпляра для нашего нового символа в ui . Наконец, назовите текущий слой основного интерфейса временной шкалы. Это поможет вам лучше организовать свой проект.

все компоненты на сцене
символ интерфейса пользователя
UI экземпляр
интерфейсный уровень

Наше приложение чата будет бесполезным, если пользователь не сможет войти в наше приложение. Давайте создадим экран входа в систему. Создайте новый слой на главной временной шкале. Назовите логин слоя.

слой входа

Используя инструмент « Прямоугольник» , нарисуйте прямоугольник на stage того же размера, что и stage . Сток прямоугольника должен быть установлен в 0 . Прямоугольник не должен иметь линию и должен быть закрашен черным с прозрачностью 50% .

темная коробка на сцене
Выбор цвета 1
Выбор цвета 2

Выделите черный прямоугольник и преобразуйте его в новый символ MovieClip под названием DarkBox . Мы будем использовать этот объект для затемнения экрана во время отображения компонентов входа. Установите имя DarkBox объекта darkBox .

символ темного ящика
экземпляр Darkbox

Добавьте на InputText два компонента InputText , два компонента Label и компонент Button . Убедитесь, что вы не добавляете эти объекты поверх слоя интерфейса . Расположите компоненты, как они есть на изображении ниже, с полем имени пользователя, а затем с паролем.

Установите имя экземпляра первой userLabel и установите имя экземпляра второй метки passLabel . Установите имя экземпляра для первого входного текстового поля usernameTxt и установите имя экземпляра второго входного текстового поля passwordTxt . Установите имя экземпляра кнопки loginBtn .

Используйте инструмент « Текст» для добавления динамического текстового поля на stage . Установите размер текста 18 и сделайте цвет текста красным. Установите имя экземпляра в errorTxt . Поместите errorTxt под loginBtn как показано ниже.

элементы экрана входа

Мы собираемся преобразовать все на уровне входа в систему в один новый символ MovieClip именем LoginScreen, но прежде чем мы сможем это сделать, нам нужно заблокировать все на уровне интерфейса, чтобы мы случайно не выбрали объект этого слоя. Заблокируйте интерфейсный слой, нажав кнопку « Слой блокировки» рядом со слоем. Когда слой заблокирован, вы увидите символ замка рядом со слоем.

Теперь вы можете безопасно выбрать все объекты в слое login в login который мы только что создали, и преобразовать выделение в символ со связью LoginScreen . Установите имя экземпляра в loginScreen .

компонент экрана входа
сцена с элементами

Блокировка и скрытие всех текущих слоев на Main Timeline . Создайте новый слой и назовите его информацией профиля .

Примечание: это просто слой, который мы будем использовать для разработки. Вы можете удалить этот слой в конце этого шага, если хотите.

информационный слой профиля

Используя инструмент « Прямоугольник» , нарисуйте прямоугольник с радиусом каждого угла, равным 10,00. Прямоугольник должен иметь заливку, идентичную объекту DarkBox (черный с прозрачностью 50%) и полностью непрозрачную белую линию со значением обводки 4.00 . На изображении ниже я установил цвет сцены «Сожженный апельсин», чтобы вы могли видеть, как все должно выглядеть более четко.

прямоугольник с закругленными углами

Добавьте Dynamic Text на stage прямо над прямоугольником. Расположите и измените размер текстового поля так, чтобы оно занимало большую часть площади прямоугольника, но оставляло место для кнопки. Установите имя экземпляра текстового поля txt . Убедитесь, что цвет текста белый, а размер текста не менее 18px .

текстовое поле
незавершенное окно профиля
добавленная кнопка

Добавьте новый компонент Button поверх прямоугольника и установите имя экземпляра closeBtn .

выделенное окно профиля

Выберите все объекты на информационном слое профиля и преобразуйте их в символ MovieClip именем ProfileWindow . Проверьте поле Export для ActionScript, чтобы этот символ имел связь с ProfileWindow . Теперь удалите объект ProfileWindow со stage . Мы будем создавать этот объект с помощью кода.

символ окна профиля

Создайте новый файл ActionScript и назовите его ChatApp.as . Добавьте следующие строки кода в класс.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
package {
     
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.events.ErrorEvent;
    import flash.display.StageScaleMode;
    import flash.display.StageAlign;
    import flash.events.TimerEvent;
    import flash.utils.Timer;
    import flash.system.Security;
    import flash.external.ExternalInterface;
    import org.igniterealtime.xiff.core.XMPPConnection;
    import org.igniterealtime.xiff.events.ConnectionSuccessEvent;
    import org.igniterealtime.xiff.events.LoginEvent;
    import org.igniterealtime.xiff.events.DisconnectionEvent;
    import org.igniterealtime.xiff.events.XIFFErrorEvent;
    import org.igniterealtime.xiff.events.RoomEvent;
    import org.igniterealtime.xiff.events.IncomingDataEvent;
    import org.igniterealtime.xiff.events.OutgoingDataEvent;
     
    public class ChatApp extends Sprite {
         
        private static const SERVER:String = «tutorial-f5d57edaa»;//= «[host name] // Your server’s host name here
        private static const PORT:Number = 5222;
        private static const RESOURCE:String = «MyContentSite»;//[resource name] // Resource name ex.: ==> MyJabberApp
        private static const DEFAULT_ROOM:String = «Main Lobby»;
         
        private var grabber:LoginCredentialsGrabber;
        private var userData:UserDataGrabber;
        private var connection:XMPPConnection;
        private var requireLogin:Boolean;
        private var roomName:String;
         
        public function ChatApp() {
             
            super();
            if (stage) init()
            else addEventListener(Event.ADDED_TO_STAGE, onAdded);
        }
         
        private function init():void {
             
            stage.align = StageAlign.TOP_LEFT;
            stage.scaleMode = StageScaleMode.NO_SCALE;
            loginScreen.visible = false;
            loginScreen.userLabel.text = «username»;
            loginScreen.passLabel.text = «password»;
            ui.visible = false;
             
            UserDataExtension.enable();
            grabber = new LoginCredentialsGrabber();
            userData = new UserDataGrabber();
             
            var flashVars:Object = this.loaderInfo.parameters;
             
            if ( flashVars.hasOwnProperty( «room» ) ) {
                 
                roomName = flashVars.room;
            }
             
            checkLogin();
         }
         
        private function onAdded( e:Event ):void {
             
            removeEventListener(Event.ADDED_TO_STAGE, onAdded);
            init();
        }
    }
}

В приведенном выше коде мы проверяем, существует ли этап в конструкторе класса. Если этап не существует, мы прослушиваем событие ADDED_TO_STAGE и при onAdded метод обработчика события onAdded . Метод onAdded просто прекращает прослушивание события ADDED_TO_STAGE и вызывает метод init . Если этап существует, мы пропускаем этот первый шаг и просто вызываем метод init который инициализировал наше приложение.

Мы инициализируем stage и loginScreen и делаем объект UserInterface ( ui ) невидимым. Вы можете заметить, что enable метод enable из класса UserDataExtension . Мы напишем этот класс позже, но пока просто знайте, что очень важно помнить, чтобы всегда вызывать этот метод при создании экземпляра приложения. Метод enable регистрирует наше пользовательское расширение (класс) с помощью класса ExtensionClassRegistry в библиотеке XIFF. Мы поговорим об этом позже.

Создайте новый экземпляр класса LoginCredentialsGrabber и назначьте его переменной LoginCredentialsGrabber . Также создайте экземпляр нового экземпляра класса UserDataGrabber и назначьте его переменной userData . Мы напишем эти классы позже также. Когда наш SWF-файл размещается на веб-странице, мы хотим, чтобы наше приложение подключалось к определенной комнате чата, связанной с содержимым на странице. Позже мы собираемся передать имя комнаты чата, к которой должно подключиться наше приложение, в параметр flashVars во время встраивания. Но сейчас мы просто сначала проверим, существует ли переменная, а затем возьмем значение и roomName переменной roomName . Наконец, мы запускаем метод checkLogin который не checkLogin пояснений.


Запишите метод checkLogin в классе документов ( ChatApp ).

1
2
3
4
5
private function checkLogin():void {
     
    grabber.addEventListener( Event.COMPLETE, onLoginCredentials );
    grabber.grab();
}

Как видите, метод очень прост. Это потому, что все функциональные возможности инкапсулированы в классе LoginCredentialsGrabber . Прослушайте событие COMPLETE которое необходимо отправить, чтобы можно было onLoginCredentials метод обработчика события onLoginCredentials . Вызовите метод LoginCredentialsGrabber объекта LoginCredentialsGrabber . Этот метод проверяет, вошел ли пользователь в систему, или, более конкретно, он проверяет, существует ли сеанс пользователя.

Далее мы напишем метод onLoginCredentials .

( Примечание: мы все еще в классе Document.)

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
private function onLoginCredentials( e:Event ):void {
             
    grabber.removeEventListener( Event.COMPLETE, onLoginCredentials );
             
    if ( grabber.isLoggedIn ) {
         
        // Connect to Openfire
        ui.visible = true;
        connect( grabber.username, grabber.password );
    }
    else {
         
        // Display login
        displayLogin();
    }
}

Этот метод также очень прост. LoginCredentialsGrabber проверяет, LoginCredentialsGrabber ли пользователь в систему, захватив куки сеанса с помощью PHP. PHP передает данные объекту LoginCredentialsGrabber и данные анализируются.

Примечание. Далее мы будем писать класс LoginCredentialsGrabber . Все, что нам нужно сделать сейчас, это проверить, вошел ли пользователь в систему. Если это так, мы отображаем пользовательский интерфейс и вызываем метод connect для подключения к Openfire.Мы передаем имя пользователя и пароль в connectметод в качестве обязательных параметров. Если пользователь не вошел в систему, мы отображаем экран входа в систему .


Напишите LoginCredentialsGrabberкласс.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
package {
     
    import flash.events.Event;
    import flash.events.EventDispatcher;
    import flash.net.URLLoader;
    import flash.net.URLRequest;
    import flash.net.URLVariables;
    import flash.net.URLRequestMethod;
     
    public class LoginCredentialsGrabber extends EventDispatcher {
         
        private static const PASSCODE:String = "letmein123";
        private static const SOURCE:String = "http://localhost/mycontentsite/scripts/check_login.php";
         
        private var _data:*;
        private var _username:String;
        private var _password:String;
        private var _isLoggedIn:Boolean;
         
        public function LoginCredentialsGrabber() {
             
            super();
        }
         
        public function grab():void {
             
            var loader:URLLoader = new URLLoader();
            var req:URLRequest = new URLRequest( SOURCE + "?cb=" + new Date().time );
             
            loader.addEventListener( Event.COMPLETE, onComplete );
            loader.load( req );
        }
         
        private function onComplete( e:Event ):void {
             
            e.target.removeEventListener( Event.COMPLETE, onComplete );
            _data = e.target.data;
            var results:URLVariables = new URLVariables( _data.toString() );
             
            if ( results.login == "1" ) {
                 
                _isLoggedIn = true;
                _username = results.username;
                _password = results.password;
            }
            else {
                 
                _isLoggedIn = false;
            }
             
            dispatchEvent( new Event( Event.COMPLETE ) );
        }
         
        public function get data():* {
             
            return _data;
        }
         
        public function get isLoggedIn():Boolean {
             
            return _isLoggedIn;
        }
         
        public function get username():String {
             
            return _username;
        }
         
        public function get password():String {
             
            return _password;
        }
    }
}

У нас есть две константы и четыре свойства только для чтения перед методом конструктора. SOURCEКонстанта является , Stringчто представляет местоположение скрипта PHP , который проверяет , чтобы увидеть , если пользователь вошел в систему . Мы также хранить код доступа , необходимый для выполнения PHP — скрипта в константе. Когда вызывается метод grab, URLLoaderобъект загружает PHP-скрипт, передающий код доступа, URLRequestи скрипт возвращает данные обратно во флэш-память. Данные — это набор переменных url, которые мы можем проанализировать с помощью URLVariablesкласса. Если пользователь вошел в систему, сценарий PHP предоставит нам имя пользователя и пароль, чтобы мы могли использовать эту информацию для подключения пользователя к Openfire. Наконец, мы предоставляем методы получения, чтобы предоставить доступ только для чтения к внешнему коду.


Напишите displayLoginметод в классе Document (ChatApp.as).

1
2
3
4
5
6
private function displayLogin():void {
     
    // Displays the login screen
    loginScreen.visible = true;
    loginScreen.addEventListener( LoginManager.LOGIN, onLoggingIn );
}

Мы устанавливаем loginScreenсвойство visible для trueи ожидаем события loginScreenотправки LOGIN. Затем onLoggingInметод вызывается. Давайте напишем этот метод сейчас.

1
2
3
4
5
private function onLoggingIn( e:Event ):void {
             
    ui.visible = true;
    connect( loginScreen.manager.username, loginScreen.manager.password );
}

Мы делаем пользовательский интерфейс видимым, а затем вызываем метод connect.

Важно: Обратите внимание , что мы используем имя пользователя и пароль от loginScreen«s managerобъекта ( loginScreen.manager) вместо grabberимени пользователя и пароля , как мы это делали в объекте onLoginCredentialsметода.


Наконец, мы можем написать connectметод. Метод принимает два обязательных параметра: первый — это имя пользователя пользователя, а второй — пароль пользователя.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
private function connect( username:String, password:String ):void {
     
    connection = new XMPPConnection();
    connection.username = username;
    connection.password = password;
    connection.server = SERVER;
    connection.port = PORT;
    connection.resource = RESOURCE;
    connection.addEventListener( ConnectionSuccessEvent.CONNECT_SUCCESS, onConnected );
    connection.addEventListener( LoginEvent.LOGIN, onLogin );
    connection.addEventListener( DisconnectionEvent.DISCONNECT, onDisconnected );
    connection.addEventListener( XIFFErrorEvent.XIFF_ERROR, onXiffError );
    connection.addEventListener( IncomingDataEvent.INCOMING_DATA, onIncomingData );
    connection.addEventListener( OutgoingDataEvent.OUTGOING_DATA, onOutgoingData );
    connection.connect( XMPPConnection.STREAM_TYPE_FLASH );
}

Создайте XMPPConnectionобъект и назначьте его connectionпеременной. Установите имя пользователя и пароль пользователя вместе с сервером ( SERVER) и resource( RESOURCE). Затем добавьте прослушиватели событий в объект подключения. Наконец мы вызываем connectметод XMPPConnectionобъекта.

Напишите следующие методы:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
private function onConnected( e:ConnectionSuccessEvent ):void {
     
    trace( "connected" );
}
 
private function onLogin( e:LoginEvent ):void {
     
    trace( "logged in" );
    ui.connection = connection;
    grabUserData();
    startTimer();
}
 
private function onDisconnected( e:DisconnectionEvent ):void {
     
    trace( "disconnected" );
    loginScreen.visible = true;
    ui.visible = false;
    loginScreen.displayError( "disconnected" );
}
         
private function onXiffError( e:XIFFErrorEvent ):void {
     
    trace("Error: " + e.errorMessage);
    if ( loginScreen.visible ) loginScreen.displayError( e.errorMessage );
}
 
private function onIncomingData( e:IncomingDataEvent ):void {
     
    trace( e.data.toString() );
}
         
private function onOutgoingData( e:OutgoingDataEvent ):void {
     
    trace( e.data.toString() );
}

onConnected, onIncomingDataИ onOutgoingDataметоды могут иметь множество различных применений , но для этого урока мы будем использовать их только для отслеживания вывода , так что мы можем отладить приложение , когда и если необходимо (особенно , если их проблема подключения к серверу). onDisconnectedМетод делает loginScreenвидимый и выводит сообщение об ошибке пользователя заявляющих им , что их связь была потеряна. onLoginМетод подготавливает пользовательский интерфейс для XMPP чата путем присвоения XMPPConnectionобъекта в connectionсобственность в пределах uiобъекта. Это позволяет UserInterfaceобъекту вызывать методы непосредственно изXMPPConnectionобъект через ссылку. Теперь, когда пользователь вошел в систему на сервере Jabber (Openfire), мы можем начать работу по регистрации пользователя в чате, но сначала нам нужно точно определить, кто наш пользователь. Мы вызываем grabUserDataметод для этого. Наконец мы вызываем startTimerметод.


К настоящему времени cookie сеанса пользователя хранятся в браузере пользователя, и пользователь регистрируется на сервере Jabber. Теперь нам нужно получить основную информацию о нашем пользователе. Мы знаем имя пользователя пользователя, поэтому мы можем использовать его для доступа к дополнительной информации о пользователе, которая хранится в нашей базе данных MySQL. Создайте grabUserDataметод в классе Document.

1
2
3
4
5
private function grabUserData():void {
             
    userData.addEventListener( Event.COMPLETE, joinRoom );
    userData.grab( connection.username );
}

Вся магия происходит в UserDataGrabberклассе. Все, что нам нужно сделать, это вызвать grabметод и прослушать COMPLETEсобытие. Обратите внимание, что grabметод UserDataGrabberобъекта принимает один параметр: имя пользователя пользователя. Используйте имя пользователя из connectionэкземпляра.

У этого класса сейчас не было бы никакой магии, потому что его еще не существует Давайте напишем этот класс сейчас. Создайте новый класс с именем, UserDataGrabberкоторый расширяет flash.events.EventDispatcher.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
package {
     
    import flash.events.Event;
    import flash.events.EventDispatcher;
    import flash.net.URLLoader;
    import flash.net.URLRequest;
    import flash.net.URLVariables;
     
    public class UserDataGrabber extends EventDispatcher {
         
        private static const SOURCE:String = "http://localhost/mycontentsite/scripts/grab_user_data.php"; // Replace with your own php script
         
        private var _data:*;
        private var _uid:String;
        private var _firstName:String;
        private var _lastName:String;
        private var _username:String;
        private var _country:String;
        private var _statusMessage:String;
         
        public function UserDataGrabber() {
             
            super();
        }
         
        public function grab( username:String ):void {
             
            var loader:URLLoader = new URLLoader();
            var req:URLRequest = new URLRequest( SOURCE + "?cb=" + new Date().time );
            var vars:URLVariables = new URLVariables();
             
            _username = username;
            vars.username = username;
            req.data = vars;
            req.method = "POST";
            loader.addEventListener( Event.COMPLETE, onComplete );
            loader.load( req );
        }
         
        private function onComplete( e:Event ):void {
             
            e.target.removeEventListener( Event.COMPLETE, onComplete );
            _data = e.target.data;
            trace( "User Data:\n" + data );
            var user:XML = new XML( _data );
            _uid = [email protected]();
            _firstName = user.firstName.toString();
            _lastName = user.lastName.toString();
            _country = user.country.toString();
            _statusMessage = user.statusMessage.toString();
            dispatchEvent( new Event( Event.COMPLETE ) );
        }
         
        public function get data():* {
             
            return _data;
        }
         
        public function get uid():String {
             
            return _uid;
        }
         
        public function get firstName():String {
             
            return _firstName;
        }
         
        public function get lastName():String {
             
            return _lastName;
        }
         
        public function get username():String {
             
            return _username;
        }
         
        public function get country():String {
             
            return _country;
        }
         
        public function get statusMessage():String {
             
            return _statusMessage;
        }
         
    }
}

Сначала мы создаем const, который хранит местоположение PHP-скрипта в виде строки. Мы создали скрипт grab_user_data.php ранее. Это сценарий, который выполняет запрос в базе данных, используя указанное имя пользователя для извлечения и вывода данных пользователя в виде XML.

Далее мы создаем наши переменные. Я всегда помещаю подчеркивание ( _) перед именем любой закрытой или защищенной переменной (свойства), которая будет доступна только для чтения — o, r в некоторых редких случаях только для записи . Все переменные в этом классе только для чтения . Мы используем методы получения, чтобы разрешить доступ только для чтения к каждой переменной.

Все переменные устанавливаются, когда данные xml, которые выводятся из файла php, анализируются, за исключением _usernameпеременной, которая задается из usernameпараметра grabметода.

Теперь о grabметоде. Ничего сложного здесь нет. Создайте новый URLLoaderобъект, новый URLRequestобъект и новый URLVariablesобъект. URLRequestКонструктор принимает один параметр URL , который вы хотите загрузить данные. В этом случае URL сохраняется в SOURCEконстанте. Теперь я уверен, что вы заметили, что строка ? Cb =, связанная с текущим временем, была объединена с SOURCE. Центибар выступает за кэш попойки. Это предотвращает загрузку нашего скрипта из кеша (памяти).

Инициализируйте URLRequestобъект и URLVariablesобъект. URLVariablesОбъект считает , что usernameпеременная, сценарий РНР должен выполнить запрос в базу данных. Эта переменная передается вместе с URLRequest. Вызов URLLoader«s loadметод и прослушивайте COMPLETEсобытия должны быть отправлены из , loaderтак что onCompleteметод обработчика событий можно назвать.

В onCompleteметоде создайте новый XMLобъект. Передайте данные, назначенные от URLLoaderобъекта ( e.targetв этом случае) в параметр конструктора. Установите переменные класса и отправьте COMPLETEсобытие.


До этого момента мы предполагали, что пользователь уже вошел в систему. Если пользователь не вошел в систему, мы отображаем loginScreen. LoginScreenКласс будет иметь методы , инкапсулированные в нем , которые обрабатывают состояние входа пользователя. Создайте класс LoginScreen. Класс должен расширять MovieClipкласс, поскольку он связан с символом библиотеки этого типа.

001
002
003
004
005
006
007
008
009
010
011
012
013
014
+015
016
+017
018
019
020
021
022
023
024
025
026
027
028
029
+030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
+055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
package {
     
    import flash.display.MovieClip;
    import flash.events.MouseEvent;
    import flash.events.Event;
    import flash.events.ErrorEvent;
    import flash.events.KeyboardEvent;
    import flash.ui.Keyboard;
     
    public class LoginScreen extends MovieClip {
 
        public var manager:LoginManager;
         
        public function LoginScreen() {
             
            super();
            manager = new LoginManager();
            init();
        }
         
        public function init():void {
             
            userLabel.text = "username:";
            passLabel.text = "password:";
             
            errorTxt.selectable = false;
            passwordTxt.displayAsPassword = true;
             
            loginBtn.label = "Login";
            loginBtn.addEventListener( MouseEvent.CLICK, login );
            stage.addEventListener( KeyboardEvent.KEY_DOWN, login );
            stage.addEventListener( Event.RESIZE, onStageResize );
        }
         
        private function login( e:Event ):void {
             
            loginBtn.removeEventListener( MouseEvent.CLICK, login );
            stage.removeEventListener( KeyboardEvent.KEY_DOWN, login );
             
            if ( e is KeyboardEvent ) {
                 
                var ke:KeyboardEvent = e as KeyboardEvent;
                     
                if ( ke.keyCode != Keyboard.ENTER ) {
                         
                    loginBtn.addEventListener( MouseEvent.CLICK, login );
                    stage.addEventListener( KeyboardEvent.KEY_DOWN, login );
                    return;
                }
            }
             
            if ( usernameTxt.length > 0 && passwordTxt.length > 0 ) {
                 
                if (!manager) manager = new LoginManager();
                manager.addEventListener( Event.COMPLETE , onLogin );
                manager.addEventListener( ErrorEvent.ERROR , onLoginError );
                manager.login( usernameTxt.text, passwordTxt.text );
            }
            else if ( usernameTxt.length == 0 ) {
                 
                // Display error
                errorTxt.text = "Please enter your username";
            }
            else {
                 
                // Display error
                errorTxt.text = "Please enter your password";
            }
             
            loginBtn.addEventListener( MouseEvent.CLICK, login );
            stage.addEventListener( KeyboardEvent.KEY_DOWN, login );
        }
         
        private function onLogin( e:Event ):void {
             
            manager.removeEventListener( Event.COMPLETE , onLogin );
            manager.removeEventListener( ErrorEvent.ERROR , onLoginError );
            stage.removeEventListener( KeyboardEvent.KEY_DOWN, login );
            visible = false;
            dispatchEvent( new Event( LoginManager.LOGIN ) );
        }
         
        private function onLoginError( e:ErrorEvent ):void {
             
            manager.removeEventListener( Event.COMPLETE , onLogin );
            manager.removeEventListener( ErrorEvent.ERROR , onLoginError );
            errorTxt.text = e.text;
            loginBtn.addEventListener( MouseEvent.CLICK, login );
            stage.addEventListener( KeyboardEvent.KEY_DOWN, login );
        }
         
        private function onStageResize( e:Event ):void {
             
            darkBox.width = stage.stageWidth;
            darkBox.height = stage.stageHeight;
        }
         
        public function displayError( error:String ):void {
             
            errorTxt.text = error;
        }
    }
}

Мы начинаем класс с создания нового LoginManagerобъекта. Мы напишем этот класс на следующем шаге, но пока просто знайте, что этот класс управляет входом пользователя в систему.

Функционально этот класс довольно прост. Мы инициализируем отображение, и когда пользователь нажимает клавишу Enter или щелкает, loginBtnмы проверяем, набрали ли они что-нибудь в текстовые поля. Если это так, мы используем их managerдля входа. В противном случае мы отображаем ошибку. Мы отображаем ошибки пользователю, вызывая displayErrorметод и передавая сообщение в качестве Stringпараметра метода. Если LoginManagerобъект отправляет ErrorEvent, сообщение об ошибке будет отображаться пользователю. Напротив, если LoginManagerобъект отправляет COMPLETEсобытие, мы отправляем LOGINсобытие и делаем нашего LoginScreenневидимым.


LoginManagerКласс регистрирует пользователя и выход. Создать LoginManagerкласс.

001
002
003
004
005
006
007
008
009
010
011
012
013
014
+015
016
+017
018
019
020
021
022
023
024
025
026
027
028
029
+030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
+055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
package {
     
    import flash.events.Event;
    import flash.events.EventDispatcher;
    import flash.events.ErrorEvent;
    import flash.net.*;
    import org.igniterealtime.xiff.bookmark.UrlBookmark;
     
    public class LoginManager extends EventDispatcher {
         
        private static const LOGIN_SOURCE:String = "http://localhost/mycontentsite/scripts/login.php"; // Your website's login script location
        private static const LOGOUT_SOURCE:String = "http://localhost/mycontentsite/scripts/logout.php";
        private static const INDEX:String = "http://localhost/mycontentsite/index.php";
         
        private var _data:*;
        private var _isLoggedIn:Boolean;
        private var _username:String;
        private var _password:String;
         
        public static const LOGIN:String = "login";
         
         
        public function LoginManager() {
             
            super();
        }
         
        public function login( username:String, password:String ):void {
             
            var loader:URLLoader = new URLLoader();
            var req:URLRequest = new URLRequest( LOGIN_SOURCE + "?cb=" + new Date().time );
            var vars:URLVariables = new URLVariables();
             
            _username = username;
            _password = password;
            vars.username = username;
            vars.password = password;
            req.data = vars;
            req.method = URLRequestMethod.POST;
            loader.addEventListener( Event.COMPLETE, onLoginComplete );
            loader.load( req );
        }
         
        public function logout():void {
             
            var loader:URLLoader = new URLLoader();
            var req:URLRequest = new URLRequest( LOGOUT_SOURCE );
            loader.addEventListener( Event.COMPLETE, onLogoutComplete );
            loader.load( req );
        }
         
        private function onLoginComplete( e:Event ):void {
             
            e.target.removeEventListener( Event.COMPLETE, onLoginComplete );
            _data = e.target.data;
            var results:URLVariables = new URLVariables( _data.toString() );
             
            if ( results.login == "1" ) {
                 
                _isLoggedIn = true;
            }
            else {
                 
                _isLoggedIn = false;
                dispatchEvent( new ErrorEvent( ErrorEvent.ERROR, false, false, results.error ) );
                return;
            }
             
            dispatchEvent( new Event( Event.COMPLETE ) );
        }
         
        private function onLogoutComplete( e:Event ):void {
             
            e.target.removeEventListener( Event.COMPLETE, onLogoutComplete );
            var req:URLRequest = new URLRequest();
            navigateToURL( new URLRequest( INDEX ), "_self" );
        }
         
        public function get data():* {
             
            return _data;
        }
         
        public function get isLoggedIn():Boolean {
             
            return _isLoggedIn;
        }
         
        public function get username():String {
             
            return _username;
        }
         
        public function get password():String {
             
            return _password;
        }
 
    }
}

Создайте константы класса и переменные экземпляра, но пока не назначайте переменным никаких значений. loginМетод должен иметь два параметра: первый является , Stringчто представляет имя пользователя и второй еще один , Stringкоторый представляет пароль пользователя. Создайте и инициализируйте новый URLLoaderобъект, новый URLRequestобъект и новый URLVariablesобъект.

Инициализируйте URLRequestобъект и URLVariablesобъект. URLVariablesОбъект содержит usernameпеременную POST и passwordпеременную POST , что PHP скрипт требует для выполнения. Эти переменные передаются вместе с URLRequest. Вызов URLLoader«s loadметод и прослушивайте COMPLETEсобытия должны быть отправлены из , loaderтак что onLoginCompleteметод обработчика событий можно назвать.

Сценарий login.php выводит переменную url, которая задает логическое значение (0 или 1). 1 представляет true, конечно. Если это значение равно 1, то пользователь успешно вошел в систему. Если это значение равно 0, произошла ошибка, и мы отправляем ERRORсобытие, содержащее сообщение об ошибке, отображаемое нашим PHP-скриптом. Во всех таких случаях ошибка будет неверной регистрационной информацией .

В logoutметод работает так же, за исключением браузера пользователя будет перейти к главной индексной странице нашего сайта , когда logout.php успешно регистрирует пользователя вне.


Сервер XMPP может быть настроен на разрыв любых соединений idel, которые все еще могут быть открыты. По умолчанию Openfire сделает это. Чтобы предотвратить это, клиент чата должен отправить пинг на сервер. В библиотеке XIFF это делается путем вызова sendKeepAliveметода XMPPConnectionобъекта. Этот метод следует вызывать как минимум раз в минуту. Возможно, вы помните, что мы вызывали метод внутри класса Document ( ChatApp) startTimer. Этот метод не был определен в то время. Давайте создадим это сейчас.

1
2
3
4
5
6
7
private function startTimer():void {
     
    var aliveTimer:Timer = new Timer( 1000 * 60, 0 );
    aliveTimer.addEventListener( TimerEvent.TIMER, keepAlive );
    aliveTimer.start();
    trace( "starting alive timer" );
}

startTimerМетод создает новый Timerобъект, прослушивает TIMERсобытие, затем вызывает startметод на новом Timerобъекте.

Теперь давайте создадим keepAliveметод , который вызывается , когда aliveTimer«S TIMERотправляется событие.

1
2
3
4
private function keepAlive( e:TimerEvent ):void {
             
    connection.sendKeepAlive();
}

Просто вызовите метод XMPPConnectionобъекта keepAlive. Ничего больше.


Создайте joinRoomметод в классе Document.

1
2
3
4
5
6
private function joinRoom( e:Event ):void {
             
    userData.removeEventListener( Event.COMPLETE, joinRoom );
    if ( !roomName ) roomName = DEFAULT_ROOM;
    ui.joinRoom( connection, userData, roomName );
}

Если вы не можете вспомнить, joinRoomметод вызывается, когда пользовательские данные были загружены (когда мы знаем, кто пользователь). У UserInterfaceкласса также будет метод, joinRoomкоторый мы будем вызывать из joinRoomметода класса Document . Прежде чем вызвать метод на uiобъекте. Мы проверяем, что roomNameпеременная была установлена. Если по какой-то причине этого не произошло, мы устанавливаем номер по умолчанию, который в данном случае является главным лобби.

Создайте новый класс и назовите его UserInterface. Убедитесь, что он расширяет класс MovieClip. Добавьте следующее в путь к классу.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
import flash.display.MovieClip;
import flash.events.Event;
import fl.data.DataProvider;
import org.igniterealtime.xiff.core.XMPPConnection;
import org.igniterealtime.xiff.core.EscapedJID;
import fl.controls.List;
import org.igniterealtime.xiff.events.RoomEvent;
import org.igniterealtime.xiff.conference.Room;
import org.igniterealtime.xiff.core.UnescapedJID;
import flash.events.MouseEvent;
import org.igniterealtime.xiff.data.Message;
import org.igniterealtime.xiff.data.Presence;
import flash.events.KeyboardEvent;
import flash.ui.Keyboard;
import flash.display.MovieClip;

Определите следующие переменные.

01
02
03
04
05
06
07
08
09
10
11
private var dp:DataProvider;
private var profileData:Vector.<String>;
private var names:Vector.<String>;
private var usernames:Vector.<String>;
private var items:Vector.<Object>;
private var darkBox:DarkBox;
private var room:Room;
 
public var connection:XMPPConnection;
 
public static const SERVER_NAME:String = "[ your server's name ]";

Нам нужно DataProviderотобразить всех пользователей, которые находятся в текущей комнате чата в пределах Listобъекта. Будут хранить разные наборы данных в Vectorобъектах. DarkBoxРанее мы создали объект, который будет отображаться при открытии окна профиля. Последняя приватная переменная, которая нам нужна, — это org.igniterealtime.xiff.conference.Roomобъект, который, на самом деле, если вы еще не догадались, это наш чат.

XMPPConnectionОбъект ( connection) был установлен в пределах класса документа в onLoginметоде обработчика событий. Это просто ссылка на XMPPConnectionобъект в классе Document.

Создайте конструктор класса и initметод.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public function UserInterface() {
     
    super();
    dp = new DataProvider();
    profileData = new Vector.<String>();
    names = new Vector.<String>();
    usernames = new Vector.<String>();
    items = new Vector.<Object>();
    darkBox = new DarkBox();
    init();
}
 
private function init():void {
     
    UserDataExtensionManager.onUserData = onUserDataExtension;
    disable();
    list.dataProvider = dp;
    list.addEventListener( Event.CHANGE, onGuestSelected );
    displayTxt.editable = false;
    sendBtn.label = "Send";
    logoutBtn.label = "Logout";
    darkBox.visible = false;
    addChild( darkBox );
    positionContents();
    stage.addEventListener( Event.RESIZE, onStageResize );
    logoutBtn.addEventListener( MouseEvent.CLICK, logout );
}

Все довольно просто в конструкторе. Просто создайте новые экземпляры каждого объекта и назначьте их соответствующей переменной. initМетод выполняет несколько задач. Во-первых, мы должны сообщить UserDataExtensionManagerклассу, что мы хотели бы получить и обработать расширение пользовательских данных в текущем экземпляре UserInterfaceкласса. Есть разные способы обработки входящих пользовательских расширений, но мне нравится позволять классу, связанному с пользовательским расширением, обрабатывать пользовательское расширение. Вы также можете прослушивать входящие данные, чтобы вы могли проанализировать данные самостоятельно или проверить, было ли определенное расширение присоединено к входящему XMPPStanza(например, MessageStanza), и извлечь расширение непосредственно из его родительского XMPPStanzaобъекта. Вот пример.

Важный: два метода, которые следуют, являются только гипотетическими примерами. Реализуйте их в свои собственные сценарии по своему усмотрению.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
// example
private function onMessage( e:MessageEvent ):void {
     
    // Some block of code to display the message
     
    var msg:Message = e.data as Message;
    var extensions:Array = msg.getAllExtensions();
     
    if ( extensions ) {
     
        for each( var ext:Extension in extensions ) {
         
            switch ( ext.get_NS() ) {
             
                case "mysite:extensions:mycustomextension" :
                break;
                default :
                    // Handle invalid extension
            }
        }
    }
}
01
02
03
04
05
06
07
08
09
10
11
12
13
// another example
private function onMessage( e:MessageEvent ):void {
     
    // Some block of code to display the message
     
    var msg:Message = e.data as Message;
    var ext:Extension = msg.getExtension( "myCustomExtension" );
     
    if ( ext ) {
         
        // Handle extension code
    }
}

Но опять же, в этом уроке мы будем использовать мой метод экспресс-доставки, поскольку одновременно будет происходить только одно соединение. В противном случае мы будем реализовывать один из вышеуказанных методов. Мы будем написание UserDataExtensionи UserDataExtensionManagerклассов в следующем шаге.

Изначально мы хотим, uiчтобы функция была отключена, поэтому мы вызываем disableметод, который отключает все компоненты интерфейса из initметода. Наконец мы инициализируем отображение и DataProviderобъект.

Напишите следующие методы в UserInterfaceклассе.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
private function positionContents():void {
     
    displayTxt.width = stage.stageWidth - list.width - displayTxt.x - 10 - 10 - 10;
    list.x = displayTxt.y + displayTxt.width + 10;
    inputTxt.width = displayTxt.width;
    sendBtn.x = list.x;
    logoutBtn.x = list.x;
             
    displayTxt.height = stage.stageHeight - inputTxt.height - 10 - 10 - 10;
    list.height = displayTxt.height - logoutBtn.height - 10;
    inputTxt.y = displayTxt.height + displayTxt.y + 10;
    sendBtn.y = inputTxt.y;
    logoutBtn.y = displayTxt.y;
     
    darkBox.width = stage.stageWidth;
    darkBox.height = stage.stageHeight;
}
         
private function onStageResize( e:Event ):void {
     
    positionContents();
}
 
public function joinRoom( connection:XMPPConnection, userData:UserDataGrabber, roomName:String ):void {
     
    if ( connection.isLoggedIn() ) {
         
        trace( "joining room..." );
        var id:String = roomName.toLowerCase().replace( " ", "" );
        var ext:UserDataExtension = new UserDataExtension( null, userData );
        room = new Room( connection );
        room.roomJID = new UnescapedJID( id + "@conference." + SERVER_NAME );
        room.nickname = userData.username;
        room.addEventListener( RoomEvent.ROOM_JOIN, onRoomJoin );
        room.addEventListener( RoomEvent.USER_JOIN, onUserJoined );
        room.addEventListener( RoomEvent.GROUP_MESSAGE, onGroupMessage );
        room.addEventListener( RoomEvent.USER_DEPARTURE, onUserLeave );
        room.join( false, [ ext ] );
    }
    else {
             
        trace ( "Must be logged in to enter a chat room." );
    }
}
 
private function enable():void {
     
    list.enabled = true;
    sendBtn.enabled = true;
    inputTxt.enabled = true;
    displayTxt.enabled = true;
    list.dataProvider = dp;
}
 
private function disable():void {
     
    list.enabled = false;
    sendBtn.enabled = false;
    inputTxt.enabled = false;
    displayTxt.enabled = false;
}

Все вышеперечисленные методы довольно понятны. Мы говорили о joinRoomметоде ранее. Теперь, когда у нас есть этот метод написан, мы можем более внимательно посмотреть. После того, как мы проверим, что connectionвсе еще вошли в систему, мы присоединяемся к комнате. Сначала мы изменим roomNameтак, чтобы мы могли сделать правильный jid, чтобы мы могли подключиться к нужной комнате. Затем мы создаем новый UserDataExtensionобъект. Опять же, мы еще не создали его, но конструктор примет UserDataGrabberобъект в качестве обязательного параметра. Подробнее об этом позже.

Создайте Roomобъект, передающий соединение, в качестве параметра, который требуется конструктору. В комнате UnescapedJIDдолжны быть установлены и псевдоним текущего пользователя. Вы можете использовать любое имя, например имя и фамилию пользователя, но я решил использовать только имя пользователя. После прослушивания различных событий мы вызываем joinметод Roomобъекта.

Метод принимает два параметра. Первый — это Booleanпредставление о том, хотите ли вы создать и настроить зарезервированную комнату. Установите это значение falseдля, потому что мы уже создали и настроили наши чаты в Openfire. Второй параметр Arrayсодержит любые пользовательские расширения, которые вы хотели бы передать вместе с Присутствием пользователя при входе в чат-комнату. Передайте массив, содержащий экземпляр UserDataExtensionобъекта, который мы только что создали, в этот параметр.

Давайте закончим урок.

001
002
003
004
005
006
007
008
009
010
011
012
013
014
+015
016
+017
018
019
020
021
022
023
024
025
026
027
028
029
+030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
+055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
private function addMessage( msg:String, from:String ):void {
     
    var now:Date = new Date();
    var nHours:Number = now.hours;
    var sMin:String = now.minutes.toString();
    if ( sMin.length == 1 ) sMin = "0" + sMin;
    var ampm:String = "AM";
     
    if ( nHours > 12 ) {
         
        nHours -= 12;
        ampm = "PM";
    }
    var time:String = String( nHours ) + ":" + sMin + " " + ampm;
    var txt:String = "[ " + from + " ] " + time + " ==> " + msg + "\n";
    displayTxt.appendText( txt );
}
         
private function sendMessage( e:Event ):void {
             
    if ( !visible ) return;
    if ( inputTxt.length > 0 ) {
                 
        if ( e is KeyboardEvent ) {
             
            var ke:KeyboardEvent = e as KeyboardEvent;
             
            if ( ke.keyCode != Keyboard.ENTER ) {
                 
                return;
            }
        }
         
        addMessage( inputTxt.text , room.nickname );
        room.sendMessage( inputTxt.text );
        inputTxt.text = "";
    }
}
 
private function onRoomJoin( e:RoomEvent ):void {
     
    trace( "joined room" );
    enable();
    sendBtn.addEventListener( MouseEvent.CLICK, sendMessage );
    stage.addEventListener( KeyboardEvent.KEY_DOWN, sendMessage );
}
 
private function onUserJoined( e:RoomEvent ):void {
     
    var p:Presence = e.data as Presence;
    trace( "user joined" );
    trace( "Presence: " + p.getNode().toString() );
    trace( e.nickname );
}
 
private function onGroupMessage( e:RoomEvent ):void {
     
    var msg:Message = e.data as Message;
     
    for each( var user:String in usernames ) {
         
        if ( e.nickname == user ) {
             
            addMessage( msg.body, e.nickname );
            return;
        }
    }
}
 
private function addToList( item:Object ):void {
     
    dp.addItem( item );
    trace( "adding " + name + " to list" );
}
 
private function removeFromList( username:String ):void {
     
    var index:int = usernames.indexOf( username );
     
    if ( index > -1 ) {
         
        trace( "removing " + names[ index ] + " from the list" );
        dp.removeItem( items[ index ] );
        profileData[ index ] = null;
        names[ index ] = null;
        usernames[ index ] = null;
        items[ index ] = null;
    }
}
 
private function onGuestSelected( e:Event ):void {
     
    var user:String = list.selectedItem.value.toString();
    var index:int = usernames.indexOf( user );
    trace( "Selected: " + user );
     
    if ( index > -1 ) {
         
        // Display Member Information
        darkBox.visible = true;
        var data:String = profileData[ index ];
        var window:ProfileWindow = new ProfileWindow();
        window.text = data;
        window.addEventListener( ProfileWindow.DESTROYED, onDestroyed );
        addChild( window );
    }
     
    function onDestroyed( e:Event ):void {
         
        window.removeEventListener( ProfileWindow.DESTROYED, onDestroyed );
        window = null;
        darkBox.visible = false;
    }
}
 
private function onUserLeave( e:RoomEvent ):void {
     
    var p:Presence = e.data as Presence;
    var username:String = p.from.toString().replace( room.roomName + "@" + room.conferenceServer + "/", "" );
    removeFromList( username );
}
 
private function onUserDataExtension( ext:UserDataExtension ) {
     
    if ( usernames.indexOf( ext.username ) > -1 || ext.username == connection.username ) return;
     
    var name:String = ext.firstName + " " + ext.lastName;
    var profileText:String = name + "\n\n";
    profileText += "Username: " + ext.username + "\n";
    profileText += "Country: " + ext.country + "\n";
    profileText += "Status: " + ext.statusMessage + "\n";
     
    var item:Object = {};
    item.label = name;
    item.value = ext.username
    profileData.push( profileText );
    names.push( name );
    usernames.push( ext.username );
    items.push( item );
    addToList( item );
}
 
private function logout( e:MouseEvent ):void {
     
    var manager:LoginManager = new LoginManager();
    manager.logout();
}

Хорошо, давайте посмотрим на то, что мы только что написали. Первый метод — это addMessageметод. Этот метод вызывается всякий раз, когда пользователь отправляет или получает сообщение. Сообщение отображается в текстовом поле и объединяется со временем, когда сообщение было отправлено или получено. sendMessageМетод просто отправляет сообщение напечатал пользователь для всех пользователей в текущей комнате чата. Он принимает два параметра: сообщение как String, и имя того, от кого сообщение, как другое String. Мы показываем время, когда сообщение было отправлено или получено, и получатель с сообщением. Надеюсь, ничего сложного.

Теперь о тех таинственных методах обработчика событий, которые вызываются в соответствии с которыми RoomEventбыло отправлено. onRoomJoinedМетод вызывается всякий раз , когда текущий пользователь успешно присоединяется к чат. Все, что мы делаем, это вызываем enableметод, который повторно включает все ранее отключенные компоненты, чтобы пользователь мог взаимодействовать с ними.

onUserJoinedМетод вызывается всякий раз , когда пользователь успешно объединяет текущую комнату. Я создал этот метод для отладки, поэтому он отслеживает только данные. Обычно вы будете прослушивать входящие сообщения и реагировать на изменение данных, но здесь мы хотим отвечать на входящие расширения пользовательских данных через нашу службу экспресс-доставки. Опять же, я обнаружил, что экспресс-доставка здесь намного проще, но вы можете попробовать извлечь пользовательские расширения непосредственно из Presenceобъекта из onUserJoinedметода.

Примечание: объект присутствия e.data as Presenceв этом случае. Вы также можете проявить творческий подход к этому методу, если хотите: возможно, при воспроизведении определенного звука или всплывающем уведомлении, когда в комнату входит другой пользователь. Просто пища для размышлений.

onGroupMessageМетод отправляется , когда входящее сообщение будет получено от другого пользователя. Сама комната (сервер XMPP) может даже отправлять сообщения. Обычно это сообщения о конфигурации или сообщения об ошибках. Вы можете рассматривать эти сообщения как административные сообщения и запретить их отображение в TextAreaкомпоненте как обычное сообщение чата . Может быть, вы можете отобразить эти сообщения в каком-то окне сообщений. Просто еще немного пищи для размышлений. В нашем onGroupMessageметоде мы фактически отфильтровываем любые сообщения, поступающие с сервера. Мы проверяем, что отправитель сообщения e.nickname(в данном случае имя пользователя отправителя) находится в usernamesвекторе, а затем отображаем сообщение, используя addMessageметод.

Теперь о addToListметоде. Добавьте новый объект к Listобъекту, используя DataProvider. Вызовите addItemметод DataProviderобъекта и передайте его itemв параметр. itemОбъект должен быть примитивным Objectи обладает свойством labelString). Значение также должно быть String. Мы будем использовать имя пользователя соответствующего пользователя в качестве значения здесь. removeFromListМетод делает только то , что вы думаете. Удаляет указанного пользователя из list. Ничего сложного — только полная противоположность addItemметода.

Всякий раз, когда наш пользователь нажимает на элемент в list, мы не хотим отображать окно профиля, содержащее информацию о выбранном пользователе. onUserSelectedМетод делает именно это. Имя пользователя выбранного пользователя — это значение выбранного элемента в list. Мы используем их имя пользователя, чтобы получить местоположение ( index) их данных Stringв profileData Vector.

Примечание: мы будем добавлять данные в этот Vectorобъект в onUserDataExtensionметоде позже.

Индекс имени пользователя в usernames Vectorявляется таким же, как индекс данных пользователя, хранящихся в profileData Vector. Вы можете увидеть, почему в onUserDataExtensionметоде, если вы смотрите вперед. Далее создается новый ProfileWindowи отображается для пользователя. Мы ожидаем windowотправки DESTROYEDсобытия, чтобы мы могли удалить его с дисплея. Мы должны будем написать ProfileWindowкласс позже, но на данный момент ProfileWindowкласс — это просто связь с объектом в библиотеке, которую мы создали ранее.

onUserLeaveМетод отправляется , когда пользователь выходит из комнаты чата. Когда пользователь уходит, мы удаляем его из списка, потому что мы хотим отображать только текущих пользователей. Мы вызываем removeFromListметод для достижения этой цели.

Сейчас мы все так долго ждали. Теперь мы рассмотрим метод получения посылки, доставленной нашей службой экспресс-доставки. onUserDataExtensionМетод принимает один параметр. Десериализованный UserDataExtensionобъект доставлен только что из UserDataExtensionManagerкласса. UserDataExtensionОбъект содержит все данные , которые нам нужно о пользователе , который только вступил в комнату. Мы строим profileData Stringзаранее, чтобы нам не нужно было хранить расширения. Вместо этого вы можете хранить расширения, если хотите, но я построил данные Stringдо факта. Далее нам нужно перенести все данные в соответствующие Vectorи отобразить пользователя в list. Это так просто.

Последний метод — это logoutметод. Этот метод создает новый LoginManagerобъект и вызывает его logoutметод, который выводит текущего пользователя из системы (уничтожая его сеансовые куки-файлы), а затем переходит на главную страницу индекса сайта.

Вот это да!Это было довольно тяжело. Но не слишком тяжелый, я надеюсь. Если вы нашли что-то запутанное, внимательно изучите код, пока у вас не будет четкого понимания того, что происходит, прежде чем двигаться дальше. Я говорю это, потому что это подливка по сравнению со следующими разделами, которые охватывают сериализацию и десериализацию данных. Я постараюсь сделать это как можно проще.


Прежде чем мы создадим наш UserDataExtensionкласс, который занимается сериализацией и десериализацией данных, нам нужно немного поговорить на эту тему. Сериализация в этом контексте относится к получению определенных данных и преобразованию их в форму, которую мы можем использовать для отправки данных через нашу сеть через XMPP. Мы не можем просто взять UserDataGrabberобъект и отправить его по сети как UserDataGrabberобъект и ожидать, что данные будут получены на другом конце в такте. Фактически, если мы даже попытались отправить UserDataGrabberобъект, используя метод XMPPConnectionобъекта send, мы просто получим ошибку аргумента, потому что он только исключает XMPPStanzaв качестве параметра. Метод принимает только XMPPStanzaобъекты ( Message, Presence, IQ) в качестве параметра.

Итак, как мы решаем эту проблему? Ответ с сериализацией данных. Мы должны преобразовать объект или данные в формат, который можно отправить вместе с разделом XMPP. Так в какой формат мы должны преобразовать UserDataGrabberобъект, чтобы мы могли отправить его по нашей сети и получить другим пользователем? Ну, ответ XML. XMPP — это просто потоковый XML. Это не только легко, но и расширяемо. Но мы должны упаковать (сериализовать) данные пользователя в формат XML, чтобы они могли быть отправлены по сети и получены другими пользователями.

Таким образом, сериализация — это процесс преобразования данных в формат, который может быть сохранен / сохранен и / или отправлен по сети. С технической точки зрения, мы не будем на самом деле сериализовывать UserDataGrabberобъект, но вместо этого мы будем сериализовать содержащиеся в нем данные, но мы вполне могли бы сделать это, если бы нам это понадобилось. Все, что нам нужно для этого проекта, это данные пользователя. Я сказал, что буду делать это как можно проще, и я. Мы просто будем сериализовать примитивные данные. Чем более примитивны данные, тем легче их сериализовать. Если бы мы пытались выполнить поиск объекта в библиотеке, скажем, MovieClipс большим количеством графики и специфичных для экземпляра свойств, сериализация была бы намного сложнее.


До получающего конца можно взять сериализованные данные (XML) и преобразовать их обратно в исходный формат. Мы должны полностью изменить процесс. В нашем проекте данные представлены Stringзначениями, поэтому нам будет легко их десериализовать.

Однако десериализация немного сложнее, чем сериализация, поскольку нам нужно сделать клон полученных данных. При получении входящего расширения (в виде данных XML) UserDataExtensionобъект десериализует (преобразует) данные XML обратно в Stringобъекты, так что что мы можем получить к ним доступ ( только для чтения ) через UserDataExtensionобъект через точечный синтаксис. Нам также нужно будет проверить и убедиться, что полученные данные действительны.

Вот основные сведения о том, что мы будем делать:

  • Получать входящие данные (в данном случае XML)
  • Проверьте, соответствуют ли данные типу UserDataExtensionсериализуемых данных (другими словами, проверьте, является ли это действительным расширением пользовательских данных)
  • Разобрать данные и сделать клон удаленного UserDataExtensionобъекта
  • Отправьте расширение при экспресс-доставке текущему экземпляру UserInterfaceкласса, сделав данные доступными для пользователя.

После всего сказанного и сделанного мы можем перейти к написанию UserDataExtensionкласса.


Никакое введение не требуется здесь. Давайте копаться. Создайте новый класс и назовите его UserDataExtension. Класс должен расширяться org.igniterealtime.xiff.data.Extension. Класс должен также реализовать org.igniterealtime.xiff.data.IExtensionи org.igniterealtime.xiff.data.ISerializable. Идем дальше и импортируем XMLNodeкласс и org.igniterealtime.xiff.data.ExtensionClassRegistryкласс вместе с предыдущими классами.

1
2
3
4
5
6
7
import flash.xml.XMLNode;
import org.igniterealtime.xiff.data.Extension;
import org.igniterealtime.xiff.data.IExtension;
import org.igniterealtime.xiff.data.ISerializable;
import org.igniterealtime.xiff.data.ExtensionClassRegistry;
 
public class UserDataExtension extends Extension implements IExtension, ISerializable {

Создайте следующие переменные и константы.

01
02
03
04
05
06
07
08
09
10
11
private var data:UserDataGrabber;
private var _uid:String;
private var _firstName:String;
private var _lastName:String;
private var _username:String;
private var _country:String;
private var _statusMessage;
private var _isDeserialized:Boolean;
 
public static const NS:String = "mycontentsite:xmpp:extensions:userdata";
public static const ELEMENT_NAME = "userData";

Нам нужны переменные для хранения данных, полученных от другого пользователя, и двух констант класса. Один для представления уникального пространства имен ( NS) для расширения, а другой — для имени элемента XML.

Создайте конструктор.

1
2
3
4
5
6
public function UserDataExtension( parent:XMLNode = null, userData:UserDataGrabber = null ) {
     
    super( parent );
     
    if ( userData ) data = userData;
}

Важное замечание: Убедитесь, что для всех параметров в конструкторе задано нулевое значение по умолчанию, поскольку при регистрации нашего пользовательского расширения в ExtensionClassRegistryэкземпляре будет создан экземпляр класса, и он не получит никаких аргументов. Если конструктор не готов к получению каких-либо параметров, будет выдано сообщение об ошибке, и ваше приложение может работать неправильно.

Также передайте parent( XMLNode) в конструктор базового класса ( org.igniterealtime.xiff.data.Extension). Если userDataне ноль , присвойте это dataсвойству. Мы будем сериализовать информацию, содержащуюся в этом объекте, через минуту.

Прежде чем я забуду, давайте создадим enableметод.

1
2
3
4
public static function enable():void {
             
    ExtensionClassRegistry.register( UserDataExtension );
}

Как видите, enableметод прост. Это статический метод, который регистрирует класс / расширение с помощью ExtensionClassRegistry, таким образом, активируя класс / расширение.

Мы будем сериализовать данные в формат XML, как я объяснил ранее. Чтобы быть более конкретным, мы будем создавать XMLNodeобъекты из наших данных. Давайте создадим метод, чтобы упростить этот процесс для нас. Создать generateNodeметод.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
private function generateNode( nodeName:String, nodeValue:String = null, attributes:Object = null ):XMLNode {
     
    var nameNode:XMLNode = new XMLNode( 1, nodeName );
    var valueNode:XMLNode;
     
    if ( nodeValue ) {
         
        valueNode = new XMLNode( 3, nodeValue );
        nameNode.appendChild( valueNode );
    }
     
    if ( attributes ) {
         
        nameNode.attributes = attributes;
    }
     
    return nameNode;
}

Это еще один очевидный метод. На основании заданных параметров он создает XMLNodeобъект, уже упакованный и готовый к работе. Давайте начнем с параметров. Первый параметр, nodeNameкоторый Stringпредставляет собой имя элемента узла, который мы хотим создать. Вторым является другое, Stringкоторое представляет значение, которое должен содержать узел. Последний параметр — это Objectтот, который содержит любые атрибуты (как Strings), которые должен содержать узел. Метод объединяет для нас узел, чтобы нам не приходилось постоянно повторять эти действия.

Теперь самое интересное.Создать serializeметод.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public function serialize( parent:XMLNode ):Boolean {
     
    var attributes:Object = {};
    attributes.xmlns = NS;
    attributes.id = data.uid
    var firstNode:XMLNode = generateNode( "firstName", data.firstName );
    var lastNode:XMLNode = generateNode( "lastName", data.lastName );
    var userNode:XMLNode = generateNode( "username", data.username );
    var countryNode:XMLNode = generateNode( "country", data.country );
    var statusNode:XMLNode = generateNode( "statusMessage", data.statusMessage );
    var mainNode:XMLNode = generateNode( "userData", null, attributes );
    mainNode.appendChild( firstNode );
    mainNode.appendChild( lastNode );
    mainNode.appendChild( userNode );
    mainNode.appendChild( countryNode );
    mainNode.appendChild( statusNode );
    setNode( mainNode );
     
    var node:XMLNode = this.getNode();
     
    if ( node.parentNode != parent ) {
         
        parent.appendChild( node );
    }
     
    return true;
}

Когда наша Extensionдобавляется к XMPPStanza, вызывается этот метод. Первое, что нам нужно сделать, это упаковать наши данные в основную XMLNode. Обратите внимание, что все узлы являются потомками mainNode. Кроме того, если вы еще не заметили, мы только что generateNodeприменили наш метод. Мы назвали элементы и установили значения каждого элемента. Элемент firstName содержит имя пользователя, элемент statusMessage содержит сообщение о статусе пользователя и т. Д. Мы только что взяли данные пользователя и сохранили (сериализовали) их внутри XML.

Чтобы завершить процесс сериализации, вызовите setNodeметод, который является методом базового класса, чтобы установить узел. Также проверьте, parentNodeсовпадает ли наш номер с параметром parent XMLNode. Если это не так, сделайте наш узел дочерним по отношению к параметру parent XMLNode.

Примечание:serialize метод является методом интерфейса , который требует типа возвращаемого ( Boolean). Если данные были успешно сериализованы, вы захотите вернуться true. Если по какой-то неуклюжей причине вы имеете дело с данными, которые могут быть неправильно сериализованы, этот метод всегда должен возвращаться true. В противном случае он должен вернуться false.

Напишите deserializeметод:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
public function deserialize( node:XMLNode ):Boolean {
     
    if ( node.nodeName == ELEMENT_NAME && node.attributes.hasOwnProperty( "xmlns" ) && node.attributes.xmlns == NS ) {
         
        if ( node.attributes.hasOwnProperty( "id" ) ) {
             
            _uid = node.attributes.id;
        }
        else {
            trace("invalid node xmlns: " + node.nodeName );
            return false;
        }
         
        for each( var child:XMLNode in node.childNodes ) {
             
            switch ( child.nodeName ) {
                 
                case "firstName" :
                    var first:XML = new XML( child.toString() );
                    _firstName = first;
                    break;
                case "lastName" :
                    var last:XML = new XML( child.toString() );
                    _lastName = last;
                    break;
                case "username" :
                    var user:XML = new XML( child.toString() );
                    _username = user;
                    break;
                case "country" :
                    var c:XML = new XML( child.toString() );
                    _country = c;
                    break;
                case "statusMessage" :
                    var msg:XML = new XML( child.toString() );
                    _statusMessage = msg;
                    break;
                default :
                    trace("invalid node child: " + node.nodeName );
                    return false;
            }
        }
         
        if ( _firstName && _lastName && _username && _country && _statusMessage ) {
                     
            // Notify the UserDataExtensionManager Class
            setNode ( node );
            _isDeserialized = true;
            UserDataExtensionManager.registerData( this );
            return true;
        }
        else {
             
            trace("invalid missing data: " + node.nodeName );
            var a:Array = [firstName, lastName, username, country, statusMessage];
            for each(var el:* in a) {
                 
                trace(el);
            }
            return false;
        }
    }
     
    return false;
}

В отличие от serializeметода, который вызывается при добавлении данных XMPPStanzaв deserializeметод, метод вызывается, когда пользовательское расширение получено на другом конце. Метод принимает один параметр, XMLNodeкоторый был потомком XMPPStanzaполученного. Наша задача — проверить, был ли узел упакован с помощью нашего собственного механизма. Поэтому, если узел является нашим пользовательским расширением, перейдите к десериализации данных узла, в противном случае верните false.

Мы используем for eachцикл для итерации каждого дочернего узла в параметре XMLNode. Вы можете выбрать, насколько строгим будет ваше расширение. Я решил сделать наше расширение довольно строгим. Если все данные не получены, расширение возвращается false, сообщая библиотеке XIFF, что узел недопустим и не был упакован нашим кодом.

После того, как данные XML проанализированы, мы проверяем, установлены ли все свойства; если они были установлены, мы вызываем registerDataметод UserDataExtensionManagerкласса — класс, который мы создадим следующим — чтобы отправить расширение во время экспресс-доставки нашему пользователю. Наконец мы возвращаемся trueк успеху.

Два метода интерфейса не работают, и два, чтобы пойти. Вот они:

1
2
3
4
5
6
7
8
9
public function getNS():String {
     
    return NS;
}
 
public function getElementName():String {
     
    return ELEMENT_NAME;
}

Очень, очень, очень, очень, просто. Эти методы требуются IExtensionинтерфейсом. getNS()Метод возвращает уникальные имена расширения и getElementName()возвращает имя элемента внутреннего абонента. Как я уже сказал: очень, очень, очень, очень, просто.

Были почти готовы. Завершите работу класса, предоставив доступ только для чтения к данным пользователя и _isDeserializedсвойству.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
public function get uid():String {
     
    if ( data ) {
         
        return data.uid;
    }
    else {
         
        return _uid;
    }
}
 
public function get firstName():String {
     
    if ( data ) {
         
        return data.firstName;
    }
    else {
         
        return _firstName;
    }
}
 
public function get lastName():String {
     
    if ( data ) {
         
        return data.lastName;
    }
    else {
         
        return _lastName;
    }
}
 
public function get username():String {    
     
    if ( data ) {
         
        return data.username;
    }
    else {
         
        return _username;
    }
}
 
public function get country():String {
     
    if ( data ) {
         
        return data.country;
    }
    else {
         
        return _country;
    }
}
 
public function get statusMessage():String {
     
    if ( data ) {
         
        return data.statusMessage;
    }
    else {
         
        return _statusMessage;
    }
}
 
public function get isDeserialized():Boolean {
     
    return _isDeserialized;
}

Как я объяснил на предыдущих этапах, могут быть разные способы обработки входящих расширений, но я предпочитаю метод, который я вызываю методом экспресс-доставки. Концепция проста. Класс Helper получает расширение и отправляет расширение непосредственно определенному методу (функции), который используется для обработки расширения, но только в том случае, если метод был зарегистрирован для этого действия. Давайте создадим UserDataExtensionManagerкласс.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
package {
     
    public class UserDataExtensionManager {
 
        public static var onUserData:Function;
         
        public static function registerData( data:UserDataExtension ) {
             
            if ( data.isDeserialized ) {
                 
                if ( onUserData != null ) {
                     
                    onUserData( data );
                }
            }
        }
    }
}

Этот класс очень легкий: всего 18 строк кода. registerDateМетод вызывается, принимая Десериализованный в UserDataExtensionкачестве параметра, то onUserDataметод вызывается , если он существует. В этом случае onUserDataметод является onUserDataExtensionметодом из UserInterfaceкласса.


Чтобы пользователь мог просматривать информацию о другом пользователе, нам нужен способ отображения информации для пользователя. Я решил использовать простое окно, содержащее текстовое поле, которое мы можем использовать для отображения информации пользователю. Давайте создадим ProfileWindowкласс.

Помните: этот класс связан с MovieClipсимволом в библиотеке.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
package {
     
    import flash.display.MovieClip;
    import flash.events.Event;
    import flash.events.MouseEvent;
    import flash.display.DisplayObject;
     
    public class ProfileWindow extends MovieClip {
         
        public static const DESTROYED:String = "destroyed";
         
        public function ProfileWindow() {
             
            super();
            addEventListener( Event.ADDED_TO_STAGE, onAdded );
        }
         
        private function init():void {
             
            txt.selectable = false;
            txt.wordWrap = true;
            closeBtn.label = "Close";
            closeBtn.addEventListener( MouseEvent.CLICK, destroy );
            stage.addEventListener( Event.RESIZE, onStageResize );
            center();
        }
         
        private function center():void {
             
            x = ( stage.stageWidth - width ) / 2;
            y = ( stage.stageHeight - height ) / 2;
        }
         
        private function onAdded( e:Event ):void {
             
            removeEventListener( Event.ADDED_TO_STAGE, onAdded );
            init();
        }
         
        private function onStageResize( e:Event ):void {
             
            center();
        }
         
        private function destroy( e:Event ) {
             
            removeEventListener( MouseEvent.CLICK, destroy );
            stage.removeEventListener( Event.RESIZE, onStageResize );
             
            if ( this.parent ) {
                 
                for ( var i:int = 0; i < numChildren; i++ ) {
                     
                    removeChild( getChildAt( i ) );
                }
                 
                this.parent.removeChild( this );
                dispatchEvent( new Event( DESTROYED ) );
            }
        }
         
        public function set text( value:String ):void {
             
            txt.text = value;
        }
    }
}

Метод конструктора добавляет и прослушиватель событий, который прослушивает ADDED_TO_STAGEсобытие. Когда окно добавляется в stage, onAddedвызывается метод, затем слушатель удаляется и initвызывается метод.

initМетод инициализации текстового поля и кнопку закрытия. Это также гарантирует, что окно всегда будет находиться в центре сцены.

Вы можете заметить константу класса в начале скрипта. DESTROYEDКонстанта представляет собой тип события , что окно будет рассылать , когда пользователь нажимает на closeBtn. При closeBtnнажатии вызывается метод уничтожения, все дочерние элементы удаляются, а окно удаляется из его родительского элемента. Мы могли бы создать собственный класс событий, например ProfileWindowEvent, расширяемый flash.events.Event, но это не было необходимо. Вместо этого мы отправляем Eventобъект с типом DESTROYED. Это просто и хорошо выполняет свою задачу.

Наконец у нас есть метод установки set text. Это также могло бы быть читаемым и записываемым свойством, но опять же, в этом не было необходимости. Вместо этого это только для записи, потому что нам никогда не нужно читать это свойство ни в одном из остального кода. Метод устанавливает текст текстового поля таким образом, чтобы текст отображался пользователю.


Последнее, что нам нужно сделать, чтобы наше приложение работало, — это настроить страницу HTML. Наше приложение представляет собой динамическое веб-приложение. Ранее мы захватили roomNameпеременную, которая была передана во Flash; единственная проблема здесь в том, что мы еще не передали эту переменную во Flash. Вам нужно будет реализовать это в своем коде самостоятельно. Вот мой код:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
<?php
 
    $flashVars = '"room=mainlobby"';
    $image = '"mainlobby.png"';
     
    if ( isset( $_GET['content'] ) ) {
     
        $content = $_GET['content'];
         
        switch ( $content ) {
         
            case "box2dgame" :
                $roomName = "Box2d Game";
                break;
            case "platformgame" :
                $roomName = "Platform Game";
                break;
            case "mainlobby" :
            case "" :
            case null :
            default :
                $roomName = "Main Lobby";
                break;
        }
         
        $flashVars = '"room=' . $roomName . '"';
        $image = '"' . $content . '.png"';
    }
     
?>
<?
<!DOCTYPE html PUBLIC «-//W3C//DTD XHTML 1.0 Strict//EN» «http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd»>
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
    <head>
        <title>My Content Site</title>
        <meta http-equiv=»Content-Type» content=»text/html; charset=utf-8″ />
        <style type="text/css" media="screen">
        //html, body { height:100%; background-color: #ffffff;}
        padding:0;
        }
        </style>
    </head>
    <body>
         
        <div id="myContent">
            <image src=<?php echo $image; ?> alt="game content" align="middle"/>
        </div>
     
        <div id="flashContent">
            <object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" width="100%" height="50%" id="chat" align="middle">
                <param name="movie" value="chat.swf" />
                <param name=»quality» value=»high» />
                <param name="bgcolor" value="#ffffff" />
                <param name="play" value="true" />
                <param name="loop" value="true" />
                <param name="wmode" value="window" />
                <param name="scale" value="showall" />
                <param name=»menu» value=»true» />
                <param name="devicefont" value="false" />
                <param name="salign" value="" />
                <param name=»allowScriptAccess» value=»sameDomain» />
                <param name="flashVars" value=<?php echo $flashVars; ?> />
                <!--[if !IE]>-->
          <object type="application/x-shockwave-flash" data="chat.swf" width="100%" height="50%">
                    <param name="movie" value="chat.swf" />
                    <param name=»quality» value=»high» />
                    <param name="bgcolor" value="#ffffff" />
                    <param name="play" value="true" />
                    <param name="loop" value="true" />
                    <param name="wmode" value="window" />
                    <param name="scale" value="showall" />
                    <param name=»menu» value=»true» />
                    <param name="devicefont" value="false" />
                    <param name="salign" value="" />
                    <param name=»allowScriptAccess» value=»sameDomain» />
                    <param name="flashVars" value=<?php echo $flashVars; ?> />
                <!—<![endif]—>
                    <a href="http://www.adobe.com/go/getflash">
                        <img src="http://www.adobe.com/images/shared/download_buttons/get_flash_player.gif" alt="Get Adobe Flash player" />
                    </a>
                <!--[if !IE]>-->
                </object>
                <!—<![endif]—>
            </object>
        </div>
</body>
</html>

Приведенный выше PHP-скрипт является лишь примером. Ваш сайт, скорее всего, будет более оригинальным и креативным. contentПеременная GET указывает, к какой комнате чата присоединиться. Затем мы передаем эту информацию во Flash, чтобы наш код знал, к какой комнате чата присоединиться.


Убедитесь, что в вашей системе работают WAMP и Openfire. Если все прошло хорошо, ваше приложение должно работать нормально. Протестируйте приложение в нескольких браузерах как разные пользователи. Попросите одного пользователя следовать за другим в чат-комнату, чтобы увидеть, насколько хорошо наше приложение справляется с изменением данных. Тогда пообщайся с собой. Веселитесь с этим.


Что ж, мы многому научились, и я рад, что поделился этим временем с вами. Библиотека XIFF абсолютно потрясающая. Я надеюсь, что этот урок побудил вас полностью изучить эту невероятную библиотеку. Ты так много можешь с этим сделать. Вы можете создать частный чат и списки пользователей в нашем текущем приложении или создать совершенно новое приложение, которое подключается к другому XMPP-серверу, такому как один из серверов, перечисленных на xmpp.org . Спасибо за настройку. Увидимся в следующий раз.

Важно: Исходные файлы были настроены на основе моей собственной машины. Не забудьте перезагрузить XIFF.swcфайл в chat.flaфайл. Также не забудьте установить настройки вашего сервера в константах класса Document (ChatApp.as).