Membuat Obrolan XMPP Fleksibel untuk Situs Web Berbasis Anggota dengan Flash...
Indonesian (Bahasa Indonesia) translation by Zadip (you can also view the original English article)
Kita akan mencari cara membuat aplikasi obrolan XMPP yang dapat digunakan dalam banyak skenario berbeda. Anda akan belajar bagaimana mengintegrasikan basis data eksternal dengan Openfire Jabber Server dari Ignite Realtime dan cara menggunakan pustaka XIFF untuk membuat ekstensi XMPP khusus yang dapat digunakan untuk mengirim data khusus melalui jaringan.
Anda dapat menggunakan ini untuk membangun aplikasi ruang obrolan standar, dengan halaman yang dikhususkan untuknya, atau Anda bisa menjalankannya bersama konten Flash lainnya, seperti yang dilakukan Kongregate dengan gamenya.
Pratinjau Hasil Akhir
Mari kita lihat interface hasil akhir yang akan dikerjakan (demo ini tidak berfungsi sebagai klien obrolan yang sebenarnya):
Berikut ini demo video yang menunjukkannya dalam aksi:
Langkah 1: Prasyarat
Tutorial ini mengasumsikan bahwa Anda memiliki pengalaman dengan PHP. Anda harus memiliki Wamp Server yang diinstal pada sistem Anda dan Anda juga harus terbiasa dengan PhpMyAdmin. Jika Anda tidak memiliki WAMP, Anda dapat mengunduhnya di sini. Anda juga perlu mengunduh Openfire Jabber Server dan perpustakaan XIFF API dari situs web Ignite Realtime. Saya akan memandu Anda melalui proses instalasi Openfire. Akhirnya Anda akan memerlukan versi terbaru Java yang diinstal pada sistem operasi Anda dan Flash CS4 atau yang lebih baru. Saya akan menggunakan Flash CS5 Professional dalam tutorial ini. Jika Anda tidak memiliki versi Java terbaru, Anda dapat mengunduh versi terbaru di sini.
Langkah 2: Menyiapkan Database Kita
Pastikan bahwa Wamp Server berjalan di komputer Anda dan arahkan ke http://localhost/phpmyadmin/ di browser web Anda.
Membuat database baru bernama MyContentSite
menggunakan PhpMyAdmin.



Buat tabel baru di database MyContentSite
yang disebut myMembers
dengan delapan bidang.



Setelah tabel myMembers
berhasil dibuat oleh PhpMyAdmin, buat bidang-bidang berikut:
- uid
- first_name
- last_name
- my_username
- my_password
- status_message
- country
Layar Anda akan terlihat sebagai berikut:



Biarkan saya memecah setiap bidang. Bidang uid
harus bertipe INT
. Anda bisa mengubah tipe bidang dari kolom Type
. Jadikan bidang ini indeks utama dan setel bidang ini ke peningkatan otomatis sendiri. Lakukan ini dengan memilih opsi PRIMARY
di bawah kolom INDEX
dalam baris bidang ini. Kemudian centang kotak centang di bawah kolom Auto-Increment
. Bidang ini mewakili ID pengguna dari situs web saat ini. first_name
, last_name
, my_username
, my_password
, email
, dan bidang country
harus memiliki tipe data atau VARCHAR
dan Lenght/Value
harus ditetapkan ke 255.
Catatan: Kolom my_password
yang ditunjukkan pada gambar memegang MD5
kata sandi pengguna. Anda dapat memilih untuk menyimpan kata sandi polos tanpa enkripsi apa pun, tetapi untuk tutorial ini saya akan menggunakan kata sandi hash.



Terakhir, bidang status_message
harus bertipe MEDIUMTEXT
.
Setelah Anda membuat semua bidang, klik tombol save
.



Sekarang kita siap membuat dua akun tiruan yang akan digunakan untuk masuk ke situs web dan bergabung dengan ruang obrolan nanti dalam tutorial ini. Klik pada tab Insert
. Anda akan disajikan formulir untuk membuat baris baru di tabel.
Biarkan bidang uid
kosong karena kita ingin bidang ini bertambah secara otomatis. Setel first_name
ke Jane dan bidang last_name
ke Doe. Menetapkan my_username
untuk janedoe dengan semua surat-surat cass yang lebih rendah. Untuk bidang my_password
, kita akan menggunakan nilai hash untuk kata sandi kami yang tutsplus dalam semua huruf kecil. Ketikkan ca28ad0733a3dde9dc1f30e32718d209 ke dalam bidang my_password
. Anda dapat mengatur bidang email
ke alamat email yang Anda pilih dan bidang status_message
ke apa pun yang Anda inginkan. Menetapkan bidang negara apa pun negara yang Anda ingin juga. Setelah selesai klik pada tombol save. Ulangi proses ini untuk membuat akun untuk John Doe dengan bidang my_username
disetel ke johndoe123 dalam semua huruf kecil. Gunakan kata sandi yang sama seperti sebelumnya.









Langkah 2: Menginstal Openfire
Setelah Anda mengunduh Openfire dari situs web Ignite Realtime, jalankan file exe instalasi (atau file dmg jika Anda menggunakan Mac).

Pilih bahasa Anda.

Klik Next untuk melanjutkan.



Terima Perjanjian Lisensi.



Pilih direktori dan klik Next untuk melanjutkan.



Pilih folder Start Menu.



Klik Next untuk memulai instalasi.



Setelah instalasi selesai, klik Finish untuk menjalankan Openfire. Layanan Openfire akan mulai secara otomatis ketika program dijalankan. Klik tombol Launch Admin ketika Openfire selesai booting.









Sekarang kita akan mengatur Openfire. Pilih bahasa pilihan Anda dan klik Continue.

Biarkan bidang Admin Console Port dan bidang Secure Admin Console Port ke nilai standarnya. Untuk tutorial ini, biarkan bidang Domain ke nilai defaultnya juga. Anda dapat mengubahnya nanti ke domain situs web Anda. Klik Continue.



Pilih opsi Standard Database Connection dan klik Continue.



Di bawah Database Driver Presets, pilih MySQL. Ketik com.mysql.jdbc.Driver di bidang JDBC Driver Class. Ubah [host-name]
menjadi localhost dan ubah [database-name]
menjadi mycontentsite di bidang URL Database. Atur Username dan Password menjadi nama pengguna dan kata sandi database MySQL Anda. Untuk tutorial ini saya menggunakan nama pengguna default untuk MySQL yang root dan bidang Password tetap kosong. Klik Continue untuk melanjutkan.






Biarkan Pengaturan Profil ke Default. Klik Continue untuk melanjutkan.



Pilih alamat email untuk Akun Administrator Anda dan password lalu lanjutkan.



Kita sekarang melakukan proses pengaturan. Anda sekarang dapat masuk ke konsol admin. Gunakan admin username default dan password yang Anda pilih saat pemasangan.
Langkah 3: Mengatur Ruang Obrolan
Aplikasi kita memungkinkan pengguna untuk berkomunikasi dalam ruang obrolan. Tetapi agar ini terjadi, pengguna kita harus memiliki ruang obrolan untuk bergabung. Mari kita membuat ruang obrolan menggunakan Openfire Admin Console. Jika Anda belum melakukannya, mulai Server Openfire. Masuk ke Konsol Admin Openfire. Buka halaman Ruang Obrolan Grup dengan mengklik tab Group Chat.



Klik Create New Room di sisi kiri layar. Isi rincian seperti yang Anda lihat pada gambar di bawah ini.



Setelah selesai, klik tombol Save Changes. Jika kamar diciptakan berhasil, Anda akan melihat pesan dan cek hijau.



Ikuti langkah yang sama untuk membuat dua ruang obrolan lagi.









Langkah 4: Mengintegrasikan Openfire dengan MySQL
Dalam tutorial ini, situs web percaya kita menggunakan database MySQL untuk menyimpan data tentang setiap pengguna. Openfire dapat diintegrasikan dengan database eksternal, database MySQL dalam hal ini. Pertama kita harus mengkonfigurasi Openfire untuk melakukan ini.
Buka file openfire.xml menggunakan Notepad atau lebih disukai editor teks kaya seperti Notepad++ seperti yang saya sebutkan sebelumnya. File tersebut akan berada di folder Openfire/conf/ di dalam folder direktori Program Files di PC Anda.
<?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. A 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. By default, Openfire will bind to all network interfaces. Alternatively, you can specify a specific network interfaces that the server will listen on. For example, 127.0.0.1. This setting is generally only useful 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>
Seperti inilah file openfire.xml saya. File openfire.xml Anda akan terlihat seperti milik saya. Berikut ini tautan ke Panduan Integrasi Database Kustom Openfire di situs web Ignite Realtime. Anda akan melihat bahwa Anda diperintahkan untuk membuat perubahan langsung ke file konfigurasi openfire.xml dalam panduan ini.
Jangan membuat perubahan apa pun pada file ini kecuali tidak menyerupai milik saya.
Catatan: Sangat mungkin file openfire.xml Anda akan menggunakan DefaultConnectionProvider
. Jika ya, Anda mungkin mengalami masalah saat masuk ke Konsol Admin. Cobalah masuk dengan default terlebih dahulu. Jika password yang Anda tentukan saat pengaturan tidak berfungsi, gunakan password default untuk masuk. Username default adalah admin
dan password default adalah admin
juga.
Jika Anda tidak dapat masuk, ubah DefaultConnectionProvider
ke EmbeddedConnectionProvider
. Kemudian restart Openfire dan coba masuk lagi. Jika Anda masih mengalami masalah, jalankan pengaturan ke Openfire lagi. Ubah nilai tag setup
dari false ke true dalam file openfire.xml. Kemudian restart Openfire untuk menjalankan pengaturan lagi. Lakukan ini sebagai pilihan terakhir - ini seharusnya tidak perlu.
Saya telah mengikuti langkah-langkah di situs web Ignite Realtime berkali-kali hanya untuk menemukan diri saya dalam lubang di kemudian hari. Salah satu masalah yang saya hadapi adalah bahwa pengguna tidak dapat terhubung ke server dan ketika saya mencoba untuk memperbaiki masalah di konsol admin, saya tidak bisa masuk. Kenyataannya satu-satunya hal yang dapat saya bayangkan yang mungkin lebih membuat frustrasi daripada masalah yang saya hadapi adalah terjebak di dalam perangkap Saw.
Saya tidak ingin Anda menjalani apa yang harus saya lakukan, jadi silakan ikuti langkah-langkah berikut dengan seksama. Openfire memiliki cara yang brilian untuk mengedit dan membuat properti yang menurut saya jauh lebih efisien daripada harus mengedit file xml pada sistem Anda.
Masuk ke Konsol Admin Openfire. Klik tautan System Properties di sisi kanan halaman utama.



Halaman System Properties server Anda akan terlihat seperti ini.
Penting: Jika halaman System Properties Anda kehilangan beberapa atau semua properti dalam gambar di bawah ini, Anda dapat menambahkan properti secara manual. Ketika kita memodifikasi properti di tutorial, jika Anda tidak memiliki properti yang dimodifikasi, cukup gunakan langkah-langkah yang sama seperti Anda memodifikasi properti untuk membuat properti. Jika tidak, jika Anda sudah memiliki properti yang dibuat, cukup modifikasi properti dengan nilai yang saya tentukan.
Di bagian bawah layar Anda akan melihat bagian dengan judul Add new property. It has two fields. Bidang Property Name pertama. Kolom kedua adalah Property Value. Di dalam bidang Property Name, ketik jdbcProvider.driver dan di dalam bidang Property Name, ketik com.mysql.jdbc.Driver ke dalam bidang. Klik pada tombol Save Property ketika Anda selesai. Anda akan mengikuti langkah-langkah ini untuk membuat lebih banyak properti serta memodifikasi properti yang ada.



Buat properti bernama jdbcProvider.connectionString dengan nilai jdbc:mysql://localhost/mycontentsite?user=root&password=.



Sekarang kita akan melakukan modifikasi pertama ke properti yang ada. Klik tautan Edit yang sesuai dengan properti provider.auth.className
. Ubah nilainya ke org.jivesoftware.openfire.auth.JDBCAuthProvider menggunakan tabel Edit property. Klik tombol Save Property ketika Anda selesai.



Buat properti baru dengan nama jdbcAuthProvider.passwordSQL. Berikan nilai SELECT my_password FROM mymembers WHERE my_username=?. Nilai properti ini adalah string pertanyaan MySQL yang akan digunakan untuk mengautentikasi pengguna.
Catatan: Perhatikan bahwa itu berisi tanda tanya (?). Tanda tanya akan diganti dengan nilai di dalam bidang nama pengguna.



Buat properti baru bernama jdbcAuthProvider.passwordType. Memberikan nilai md5.



Catatan: Properti jdbcAuthProvider akan disembunyikan jika Anda telah mengikuti langkah-langkah dengan benar.



Buat properti baru bernama admin.authorizedUsernames. Nilai harus menjadi jid dari username yang Anda ingin dapat masuk ke Konsol Admin dengan.
Catatan: Lihat gambar di bawah ini. Perhatikan bahwa jid milik Jane dan John Doe adalah nama pengguna mereka yang disatukan dengan tanda @ dan domain XMPP server.



Ubah properti provider.user.className
dengan mengubah nilainya menjadi org.jivesoftware.openfire.user.JDBCUserProvider.



Buat properti baru bernama jdbcUserProvider.loadUserSQL dengan nilai SELECT first_name, email FROM mymembers WHERE my_username=?.



Buat properti baru bernama jdbcUserProvider.userCountSQL dan berikan nilai SELECT COUNT(*) FROM mymembers.



Buat properti baru bernama jdbcUserProvider.allUsersSQL. Tetapkan nilai menjadi SELECT my_username FROM mymembers.



Buat properti baru bernama jdbcUserProvider.searchSQL. Berikan nilai SELECT my_username FROM mymembers.



Buat properti baru bernama usernameField. Set nilai my_username.



Buat properti baru bernama nameField. Set nilai first_name.



Buat sebuah properti baru yang disebut emailField. Set nilainya ke email.



Sekarang bahwa kita telah ditambahkan dan diubah sifat diperlukan kita dapat log dari Konsol Admin. Mulai ulang Openfire dan coba masuk kembali ke Konsol Admin dengan pengguna yang sebenarnya.
Akses ditolak!
Sekarang mencoba untuk login dengan username, admin.
Akses ditolak lagi! Apa yang terjadi sini?
Mari kita lihat pada berkas openfire.xml. Milik Anda akan terlihat sama seperti sebelumnya. Kita perlu menambahkan modifikasi xml file. Saya telah menemukan bahwa dengan membuat modifikasi di Konsol Admin pertama, kemudian mengubah berkas openfire.xml setelah, lebih konsisten dari sekadar membuat perubahan ke xml. Seperti yang saya jelaskan sebelumnya, saya tidak bisa login menggunakan klien atau ke Konsol Admin setelah saya membuat modifikasi ini.
Ubah file openfire.xml Anda sehingga terlihat seperti ini.
<?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. A 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. By default, Openfire will bind to all network interfaces. Alternatively, you can specify a specific network interfaces that the server will listen on. For example, 127.0.0.1. This setting is generally only useful 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&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&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>
Pastikan untuk mengubah Anda menggunakan DefaultConnectionProvider
alih-alih EmbeddedConnectionProvider
kemudian tutup Openfire dan mulai kembali. Cobalah masuk ke Konsol Admin sebagai anggota dari basis data situs web Anda. Saya masuk sebagai John Doe. Jika dilakukan dengan benar, Anda harus kembali di Konsol Admin dan username harus di sudut kanan atas halaman beranda.
Catatan: Sebelum pindah, pastikan bahwa semua properti di dalam file openfire.xml muncul di System Properties dari Admin Console. Jika tidak, sekarang Anda tahu cara menambahkannya secara manual.

Langkah 5: PHP
Kita perlu menggunakan PHP untuk mengambil data dari database MySQL dan menyajikan data ke Flash. Bagi Anda yang baru mengenal PHP, saya akan menjelaskan secara singkat apa yang dicapai setiap skrip. Mari kita mulai dengan kelas MySQLConnection
.
Kelas MySQLConnection
terhubung ke dan terputus dari database MySQL.
class MySQLConnection { private $db_host = "localhost"; // Your Websites domain private $db_user = "root"; // Your databases username private $db_pass = ""; // Your databases password private $db_name = "mycontentsite"; // The name of your database 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; } }
Kelas LoginManager
menangani login pengguna. Seorang pengguna dapat diautentikasi lalu masuk dan keluar dengan kelas ini.
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 ); // Applies MD5 encoded hash to the 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"); } }
Kita memanggil login.php untuk log in dan logout.php untuk log out menggunakan kelas LoginManager
. Untuk memeriksa apakah ada pengguna yang masuk, kita memanggil skrip check_login.php.
// 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) ; }
// logout.php require_once "classes/LoginManager.php"; $login = new LoginManager(); $login->logout();
// check_login.php require_once "classes/LoginManager.php"; session_start(); $login = new LoginManager(); $login->checkLogin(); exit();
Script terakhir yang dipanggil dari ActionScript adalah script grab_user_data.php yang digunakan untuk memilih data pengguna dari database MySQL kita.
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 . '">' . "\n"; $xml .= " <firstName>" . $row['first_name'] . "</firstName>\n"; $xml .= " <lastName>" . $row['last_name'] . "</lastName>\n"; $xml .= " <country>" . $row['country'] . "</country>\n"; $xml .= " <statusMessage>" . $row['status_message'] . "</statusMessage>\n"; $xml .= "</user>\n"; } echo $xml; $connection->close(); exit(); }
Skrip PHP ini memiliki peran yang sangat penting dalam aplikasi kita tetapi sangat mendasar.
Langkah 6: Mengatur Flash
Membuka Flash profesional. Set dokumen kelas ChatApp. Mengatur ukuran stage
550 x 400.

Catatan: Saya suka menggunakan framerate 30 FPS tetapi aplikasi kita tidak memiliki animasi sehingga Anda dapat menggunakan framerate apa pun yang paling cocok untuk Anda.
Langkah 7: Bangun Interface Pengguna Klien
Dari panel Components, pilih dan seret komponen Button
ke atas stage
. Posisikan tombol sehingga berada di sudut kanan atas stage
. Tetapkan nama instance tombol ke logoutBtn
. Tambahkan tombol lain di kanan bawah stage
dan atur nama instance ke sendBtn
.


Tambahkan komponen List
ke stage. Posisikan secara langsung di bawah logoutBtn
dan ubah ukuran komponen sehingga pas di antara kedua tombol. Tetapkan nama contohnya ke list
.

Kita akan menggunakan logoutBtn
untuk keluar dari sesi pengguna dan sendBtn
untuk memungkinkan pengguna mengirim pesan. List
akan menampilkan semua pengguna online di dalam ruang obrolan saat ini. Ketika item dalam daftar diklik, profil pengguna akan dimuat.
Sekarang kita membutuhkan komponen yang akan menampilkan pesan obrolan masuk dan keluar serta bidang teks yang dapat digunakan pengguna untuk memasukkan pesan baru. Tambahkan komponen TextArea
ke atas stage
. Ubah ukuran dan posisikan untuk mengambil sebagian besar dari sisa stage
, sisakan ruang untuk bidang teks input di bagian bawah yang tingginya sama dengan sendBtn
. Tetapkan nama instance ke displayTxt
.




Akhirnya kita perlu menambahkan komponen TextInput
ke stage
. Posisikan komponen langsung di bawah displayTxt
dan di sebelah kiri sendBtn
. Tetapkan nama instance ke inputTxt
.

Pilih semua komponen di atas stage
. Ubah pilihan menjadi simbol. Simbol harus berupa MovieClip
bernama UserInterface
. Pilih opsi Export for ActionScript. Bidang Class harus membaca UserInterface. Tetapkan nama instance ke simbol baru kita ke ui
. Akhirnya beri nama layer saat ini dari interface timeline utama. Ini akan membantu Anda mengatur proyek Anda dengan lebih baik.








Langkah 8: Layar Login
Aplikasi obrolan kita tidak akan berguna jika pengguna tidak dapat masuk ke aplikasi. Mari kita membuat layar login. Buat layer baru di Timeline Utama. Beri nama login layer.

Menggunakan Rectangle tool, menggambar persegi panjang di atas stage
yang ukurannya sama dengan stage
. Stroke persegi panjang harus ditetapkan ke 0
. Persegi panjang tidak boleh memiliki garis dan harus diisi hitam dengan transparansi 50%.





Sorot persegi panjang hitam dan mengubahnya menjadi simbol MovieClip
baru yang disebut DarkBox. Kita akan menggunakan objek ini untuk menggelapkan layar saat komponen login sedang ditampilkan. Setel nama instance objek DarkBox
ke darkBox
.




Tambahkan dua komponen InputText
, dua komponen Label
, dan komponen Button
ke stage
. Pastikan Anda tidak menambahkan objek-objek ini di atas layer interface. Posisikan komponen seperti pada gambar di bawah dengan bidang username terlebih dahulu kemudian bidang password.
Atur nama instan label pertama userLabel
dan atur nama instance label kedua untuk passLabel
. Setel nama instance untuk bidang teks input pertama ke usernameTxt
dan setel nama instance bidang teks input kedua ke passwordTxt
. Tetapkan nama instance tombol untuk loginBtn
.
Gunakan Text tool untuk menambahkan Dynamic TextField ke stage
. Atur ukuran teks menjadi 18 dan buat warna teks Merah. Tetapkan nama instance ke errorTxt
. Posisikan errorTxt
di bawah loginBtn
seperti yang terlihat di bawah ini.



Kita akan mengonversi semua yang ada di layer login menjadi simbol MovieClip
baru bernama LoginScreen, tetapi sebelum kita bisa melakukan ini, kita perlu mengunci semua yang ada di layer interface sehingga kita tidak sengaja memilih objek dari layer itu. Kunci layer interface dengan memilih tombol Lock Layer di sebelah layer. Ketika layer terkunci, Anda akan melihat simbol kunci di sebelah layer.
Anda sekarang dapat dengan aman memilih semua objek pada layer login
yang baru saja kita buat, dan mengonversi pilihan menjadi simbol dengan tautan dari LoginScreen
. Atur nama instance untuk loginScreen
.




Langkah 9: Membuat Jendela Profil
Kunci dan sembunyikan semua layer saat ini di Main Timeline
. Buat layer baru dan sebut itu profile info.
Catatan: Ini hanya layer yang akan digunakan untuk pengembangan. Anda dapat menghapus layer ini di akhir langkah ini jika Anda mau.

Menggunakan Rectangle tool, gambarkan sebuah persegi panjang dengan jari-jari setiap sudut diatur ke 10.00. Persegi panjang harus memiliki isian yang identik dengan objek DarkBox
(Hitam dengan transparansi 50%) dan garis putih sepenuhnya buram dengan nilai stroke 4.00
. Saya mengatur warna panggung ke Burnt Orange pada gambar di bawah ini sehingga Anda dapat melihat bagaimana semuanya akan terlihat lebih jelas.



Tambahkan Dynamic Text
ke stage
langsung di atas persegi panjang. Posisikan dan ubah ukuran bidang teks sehingga memakan sebagian besar area persegi panjang tetapi menyisakan ruang untuk tombol. Setel nama instance bidang teks ke txt
. Pastikan bahwa warna teks adalah Putih dan ukuran teks setidaknya 18px
.









Tambahkan komponen Button
baru di atas persegi panjang dan atur nama instance ke closeBtn
.



Pilih semua objek pada layer info profil dan konversikan ke simbol MovieClip
bernama ProfileWindow
. Periksa bidang Export for ActionScript sehingga simbol ini memiliki keterkaitan ProfileWindow
. Sekarang hapus objek ProfileWindow
dari atas stage
. Kita akan instantiate objek ini dengan kode.



Langkah 10: Kode ActionScript Pertama Kita
Buat file ActionScript baru dan beri nama ChatApp.as. Tambahkan baris kode berikut ke kelas.
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; // Your servers public port here 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(); } } }
Dalam kode di atas kita memeriksa untuk melihat apakah ada tahapan dalam konstruktor kelas. Jika panggung tidak ada, kita memperhatikan event ADDED_TO_STAGE
dan metode penanganan kejadian onAdded
dipanggil saat panggung tersedia. Metode onAdded
berhenti mendengarkan acara ADDED_TO_STAGE
dan memanggil metode init
. Jika tahap ada, kita lewati langkah pertama ini dan panggil metode init
yang menginisialisasi aplikasi kita.
Kita menginisialisasi stage
dan loginScreen
, dan kita membuat objek UserInterface
(ui
) tidak terlihat. Anda mungkin memperhatikan metode enable
dari kelas UserDataExtension
dipanggil. Kita akan menulis kelas ini nanti tetapi untuk saat ini ketahuilah bahwa sangat penting untuk mengingat untuk selalu memanggil metode ini ketika membuat instance aplikasi. Cara enable
register ekstensi kustom kami (kelas) dengan kelas ExtensionClassRegistry
di Perpustakaan XIFF. Kita akan membicarakan lebih lanjut tentang ini nanti.
Instantiate instance baru dari kelas LoginCredentialsGrabber
dan tetapkan ke variabel grabber
. Juga instantiate instance baru dari kelas UserDataGrabber
dan tetapkan ke variabel userData
. Kita akan menulis kelas-kelas ini nanti juga. Ketika file SWF Anda tertanam di halaman web, kita ingin aplikasi terhubung ke ruang obrolan tertentu yang terkait dengan konten pada halaman tersebut. Kemudian kita akan melewati nama chat room, bahwa aplikasi kita harus terhubung ke, ke dalam parameter flashVars di menanamkan waktu. Tetapi untuk sekarang kita hanya akan memeriksa dulu untuk melihat apakah variabel itu ada dan kemudian kita ambil nilainya dan menugaskannya ke variabel roomName
. Akhirnya kita menjalankan metode checkLogin
yang cukup jelas.
Langkah 11: Memeriksa Apakah Pengguna Masuk
Tulis metode checkLogin
di Kelas Dokumen (ChatApp
).
private function checkLogin():void { grabber.addEventListener( Event.COMPLETE, onLoginCredentials ); grabber.grab(); }
Seperti yang Anda lihat metode ini sangat sederhana. Ini karena semua fungsionalitas dikemas dalam kelas LoginCredentialsGrabber
. Perhatikan event COMPLETE
yang akan dikirim sehingga metode penanganan event onLoginCredentials
dapat dipanggil. Panggil metode grab
pada objek LoginCredentialsGrabber
. Metode ini memeriksa untuk melihat apakah pengguna masuk - atau, lebih khusus, memeriksa untuk melihat apakah sesi pengguna ada.
Selanjutnya, kita akan menulis metode onLoginCredentials
.
(Catatan: Kami masih di kelas Dokumen.)
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(); } }
Metode ini juga sangat sederhana. LoginCredentialsGrabber
memeriksa untuk melihat apakah pengguna masuk dengan mengambil cookie sesi menggunakan PHP. PHP membuat data ke objek LoginCredentialsGrabber
dan data diuraikan.
Catatan: Kita akan menulis kelas LoginCredentialsGrabber
berikutnya. Yang perlu kita lakukan sekarang adalah memeriksa untuk melihat apakah pengguna login. Jika ya, kita menampilkan antarmuka pengguna dan memanggil metode connect
untuk terhubung ke Openfire. Kita meneruskan username dan kata password ke metode connect
sebagai parameter yang diperlukan. Jika pengguna tidak masuk, kita menampilkan Login Screen.
Langkah 12: Meraih Kredensial Masuk
Tulis kelas LoginCredentialsGrabber
.
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; } } }
Kita memiliki dua konstanta dan empat properti read-only sebelum metode konstruktor. Konstanta SOURCE
adalah String
yang mewakili lokasi skrip PHP yang memeriksa untuk melihat apakah pengguna masuk. Kita juga menyimpan passcode yang diperlukan untuk menjalankan skrip PHP secara konstan. Ketika metode ambil disebut objek URLLoader
memuat skrip PHP yang meneruskan kode sandi dengan URLRequest
dan skrip mengembalikan data kembali ke flash. Data adalah satu set variabel url yang dapat kita uraikan menggunakan kelas URLVariables
. Jika pengguna masuk, skrip PHP akan memberikan username dan password pengguna sehingga kita dapat menggunakan informasi ini untuk menghubungkan pengguna ke Openfire. Akhirnya kita menyediakan metode getter untuk memberikan akses read-only ke kode luar.
Langkah 13: Tampilkan Layar Login
Tulis metode displayLogin
di kelas Dokumen (ChatApp.as).
private function displayLogin():void { // Displays the login screen loginScreen.visible = true; loginScreen.addEventListener( LoginManager.LOGIN, onLoggingIn ); }
Kita mengatur properti terlihat loginScreen
menjadi true
dan menunggu loginScreen
untuk mengirimkan acara LOGIN
. Kemudian metode onLoggingIn
dipanggil. Mari kita tulis metode ini sekarang.
private function onLoggingIn( e:Event ):void { ui.visible = true; connect( loginScreen.manager.username, loginScreen.manager.password ); }
Kita membuat interface pengguna terlihat dan kemudian kami memanggil metode koneksi.
Penting: Perhatikan bahwa kami menggunakan username dan password dari objek manager
loginScreen
(loginScreen.manager
) alih-alih username dan password objek grabber
seperti yang dilakukan pada metode onLoginCredentials
.
Langkah 14: Menghubungkan ke Openfire
Akhirnya, kita dapat menulis metode connect
. Metode ini menerima dua parameter yang diperlukan: yang pertama adalah username dan yang kedua adalah password pengguna.
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 ); }
Instantiate objek XMPPConnection
dan tetapkan ke variabel connection
. Tetapkan username dan password pengguna bersama dengan server (SERVER
) dan resource
(RESOURCE
). Kemudian tambahkan listeners event ke objek koneksi. Akhirnya kita memanggil metode connect
pada objek XMPPConnection
.
Tulis metode berikut:
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() ); }
Metode onConnected
, onIncomingData
, dan onOutgoingData
dapat memiliki banyak kegunaan yang berbeda, tetapi untuk tutorial ini kita hanya akan menggunakannya untuk melacak output sehingga kita dapat men-debug aplikasi kita kapan dan jika kita perlu (khususnya jika itu adalah masalah koneksi ke server) . Metode onDisconnected
membuat loginScreen
terlihat dan menampilkan kesalahan kepada pengguna yang memberi tahu mereka bahwa koneksi mereka terputus. Metode onLogin
menyiapkan User Interface untuk obrolan XMPP dengan menetapkan objek XMPPConnection
ke properti connection
dalam objek ui
. Ini memungkinkan objek UserInterface
untuk memanggil metode langsung dari objek XMPPConnection
melalui referensi. Sekarang setelah pengguna masuk ke server Jabber (Openfire), kita dapat mulai bekerja untuk masuk ke ruang obrolan, tetapi pertama-tama kita harus mengidentifikasi dengan tepat siapa pengguna kita. Kita memanggil metode grabUserData
untuk melakukannya. Akhirnya kita memanggil metode startTimer
.
Langkah 15: Meraih Data Pengguna
Saat ini cookie sesi pengguna disimpan di browser pengguna dan pengguna masuk ke server Jabber. Sekarang kita perlu mengambil informasi dasar tentang pengguna. Kita tahu username pengguna, sehingga kita dapat menggunakannya untuk mengakses informasi tambahan tentang pengguna yang disimpan dalam basis data MySQL. Buat metode grabUserData
di kelas Dokumen.
private function grabUserData():void { userData.addEventListener( Event.COMPLETE, joinRoom ); userData.grab( connection.username ); }
Semua keajaiban terjadi di dalam kelas UserDataGrabber
. Yang harus dilakukan adalah grab
metode ambil dan dengarkan acara COMPLETE
. Perhatikan bahwa metode grab
pada objek UserDataGrabber
menerima satu parameter: username pengguna. Gunakan username pengguna dari instance connection
.
Kelas ini tidak akan memiliki sihir apa pun sekarang karena belum ada. Ayo tulis kelas ini sekarang. Buat kelas baru yang disebut UserDataGrabber
yang memperluas flash.events.EventDispatcher
.
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; } } }
Pertama-tama kita membuat const yang menyimpan lokasi ke skrip PHP sebagai string. Kita telah membuat skrip grab_user_data.php sebelumnya. Ini adalah skrip yang melakukan pertanyaan dalam database menggunakan nama pengguna yang ditentukan untuk mengambil dan menggemakan data pengguna sebagai xml.
Selanjutnya buat variabel kita. Saya selalu menempatkan garis bawah (_
) di depan nama variabel pribadi atau dilindungi (properti) yang akan menjadi read-only - o, r dalam beberapa kasus yang jarang terjadi, write-only. Semua variabel di kelas ini hanya read-only. Kita menggunakan metode getter untuk mengizinkan akses read-only ke setiap variabel.
Semua variabel diatur ketika data xml, yang diberikan dari file php, diuraikan dengan pengecualian variabel _username
yang diatur dari parameter username
metode grab
.
Sekarang untuk metode grab
. Tidak ada yang menyulitkan di sini. Buat objek URLLoader
baru, objek URLRequest
baru, dan objek URLVariables
baru. Konstruktor URLRequest
menerima satu parameter, url tempat Anda ingin memuat data. Dalam hal ini, url disimpan dalam konstanta SOURCE
. Saya yakin sekarang Anda telah memperhatikan bahwa string? Cb= digabungkan dengan waktu saat ini telah digabungkan dengan SOURCE
. cb adalah singkatan dari cache buster. Ini mencegah agar skrip kita tidak dimuat dari cache (memori).
Inisialisasi objek URLRequest
dan objek URLVariables
. Objek URLVariables
menyatakan bahwa variabel username
yang diperlukan skrip php untuk melakukan pertanyaan dalam database. Variabel ini diteruskan bersama dengan URLRequest
. Panggil metode load
URLLoader
dan dengarkan acara COMPLETE
yang akan dikirim dari loader
sehingga metode penanganan kejadian onComplete
dapat dipanggil.
Dalam metode onComplete
, buat objek XML
baru. Lewati data yang ditetapkan dari objek URLLoader
(e.target
, dalam kasus ini) masuk ke parameter konstruktor. Tetapkan variabel kelas dan kirim acara COMPLETE
.
Langkah 16: Kelas LoginScreen
Hingga saat ini, kita mengasumsikan bahwa pengguna sudah masuk. Jika pengguna tidak masuk, kita menampilkan loginScreen
. Kelas LoginScreen
akan memiliki metode terenkapsulasi di dalamnya yang menangani status login pengguna. Buat kelas LoginScreen. Kelas harus memperluas kelas MovieClip
karena terkait dengan simbol perpustakaan jenis itu.
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; } } }
Kita memulai kelas dengan membuat objek LoginManager
baru. Kita akan menulis kelas ini pada langkah berikutnya, tetapi untuk sekarang hanya tahu bahwa kelas ini mengelola login pengguna.
Secara fungsional kelas ini cukup sederhana. Kita menginisialisasi layar dan ketika pengguna menekan tombol Enter atau mengklik loginBtn
memeriksa untuk melihat apakah mereka telah mengetikkan sesuatu ke dalam bidang teks. Jika demikian, kita menggunakan manager
untuk login. Kalau tidak, kita menampilkan kesalahan. Kita menampilkan kesalahan kepada pengguna dengan memanggil metode displayError
dan mengirimkan pesan sebagai String
di parameter metode. Jika objek LoginManager
mengirimkan ErrorEvent
, pesan kesalahan akan ditampilkan kepada pengguna. Sebaliknya jika objek LoginManager
mengirimkan acara COMPLETE
, kita mengirimkan acara LOGIN
dan kita membuat LoginScreen
tidak terlihat.
Langkah 17: Kelas LoginManager
Kelas LoginManager
mencatat pengguna masuk dan keluar. Buat kelas LoginManager
.
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; } } }
Buat konstanta kelas dan variabel instan tetapi belum menetapkan variabel nilai apa pun. Metode login
harus memiliki dua parameter: yang pertama adalah String
yang mewakili username pengguna dan yang kedua adalah String
lain yang mewakili password pengguna. Buat dan inisialisasikan objek URLLoader
baru, objek URLRequest
baru, dan objek URLVariables
baru.
Inisialisasi objek URLRequest
dan objek URLVariables
. Objek URLVariables
menampung variabel POST username
dan variabel POST password
yang harus dijalankan oleh skrip php. Variabel-variabel ini diteruskan bersama dengan URLRequest
. Panggil metode load
URLLoader
dan dengarkan acara COMPLETE
yang akan dikirim dari loader
sehingga metode penanganan event onLoginComplete
dapat dipanggil.
Skrip login.php membuat variabel url yang menentukan Boolean (0 atau 1). A 1 mewakili true
, tentu saja. Jika nilai itu adalah 1, maka pengguna telah berhasil masuk. Jika nilai itu adalah 0, kesalahan telah terjadi dan mengirimkan acara ERROR
yang berisi pesan kesalahan yang diulangi oleh skrip PHP. Dalam semua kasus seperti itu kesalahan akan menjadi data login yang tidak valid.
Metode logout
bekerja dengan cara yang sama kecuali browser pengguna akan menavigasi ke halaman indeks utama situs web kita ketika logout.php berhasil mengeluarkan pengguna.
Langkah 18: Tetap Hidup
Server XMPP dapat dikonfigurasikan untuk mengakhiri koneksi idel yang mungkin masih terbuka. Secara default, Openfire akan melakukan ini. Untuk mencegah hal ini terjadi, klien obrolan harus mengirim ping ke server. Di dalam pustaka XIFF, ini dilakukan dengan memanggil metode sendKeepAlive
pada objek XMPPConnection
. Metode ini harus dipanggil setidaknya satu menit sekali. Anda mungkin ingat bahwa kita memanggil metode di dalam kelas Dokumen (ChatApp
) yang disebut startTimer
. Metode ini tidak ditentukan pada saat itu. Mari kita buat sekarang.
private function startTimer():void { var aliveTimer:Timer = new Timer( 1000 * 60, 0 ); aliveTimer.addEventListener( TimerEvent.TIMER, keepAlive ); aliveTimer.start(); trace( "starting alive timer" ); }
Metode startTimer
membuat objek Timer
baru, mendengarkan acara TIMER
, lalu memanggil metode start
pada objek Timer
baru.
Sekarang, mari buat metode keepAlive
yang dipanggil ketika event aliveTimer
TIMER
dikirim.
private function keepAlive( e:TimerEvent ):void { connection.sendKeepAlive(); }
Cukup panggil metode keepAlive
objek XMPPConnection
. Tidak ada lagi.
Langkah 19: Kelas UserInterface
Buat metode joinRoom
dalam kelas Dokumen.
private function joinRoom( e:Event ):void { userData.removeEventListener( Event.COMPLETE, joinRoom ); if ( !roomName ) roomName = DEFAULT_ROOM; ui.joinRoom( connection, userData, roomName ); }
Jika Anda tidak ingat, metode joinRoom
dipanggil saat data pengguna telah dimuat (saat kita tahu siapa pengguna itu). Kelas UserInterface
juga akan memiliki metode yang disebut joinRoom
yang akan kita panggil dari metode joinRoom
kelas Dokumen. Sebelum kita memanggil metode pada objek ui
. Kita memeriksa untuk memastikan bahwa variabel roomName
telah ditetapkan. Jika tidak karena alasan apa pun, kita mengaturnya ke kamar default, yang dalam hal ini adalah lobi utama.
Buat kelas baru dan beri nama, UserInterface
. Pastikan itu memperluas kelas MovieClip. Tambahkan berikut ini ke jalur kelas.
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;
Tentukan variabel berikut.
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 ]";
Kita membutuhkan DataProvider
untuk menampilkan pengguna yang ada di ruang obrolan saat ini dalam objek List
. Akan menyimpan berbagai set data dalam objek Vector
. Kita telah membuat objek DarkBox
sebelumnya yang akan ditampilkan ketika jendela profil terbuka. Variabel pribadi terakhir yang kita butuhkan adalah objek org.igniterealtime.xiff.conference.Room
yang sebenarnya, jika Anda belum menebaknya, ruang obrolan.
Objek XMPPConnection
(connection
) diatur dalam metode penanganan event onLogin
kelas Dokumen. Ini hanya referensi ke objek XMPPConnection
dalam kelas dokumen.
Buat konstruktor kelas dan metode init
.
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 ); }
Semuanya cukup sederhana di dalam konstruktor. Hanya instantiate instance baru dari setiap objek dan tetapkan mereka ke variabel yang sesuai. Metode init
menyelesaikan beberapa tugas. Yang pertama adalah bahwa kita harus memberi tahu kelas UserDataExtensionManager
bahwa kita ingin menerima dan menangani Ekstensi Data Pengguna dalam instance kelas UserInterface
saat ini. Ada berbagai cara untuk menangani ekstensi khusus yang masuk tetapi saya suka membiarkan kelas yang terkait dengan ekstensi khusus, menangani ekstensi khusus. Anda juga dapat mendengarkan data yang masuk sehingga Anda dapat menguraikan data sendiri atau Anda dapat memeriksa untuk melihat apakah ekstensi tertentu telah dilampirkan ke XMPPStanza
yang masuk (mis. Message
Stanza), dan mengambil ekstensi langsung dari objek XMPPStanza
induknya. Ini sebuah contoh.
Penting: Dua metode yang mengikuti hanyalah contoh hipotetis. Terapkan ke dalam skrip Anda sendiri sesuai keinginan Anda.
// 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 } } } }
// 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 } }
Tetapi sekali lagi untuk tutorial ini kita akan menggunakan metode pengiriman ekspres saya karena hanya akan ada satu koneksi yang terjadi pada suatu waktu. Kalau tidak, kita akan menerapkan salah satu metode di atas. Kita akan menulis kelas UserDataExtension
dan UserDataExtensionManager
dalam langkah berikutnya.
Awalnya di dalam kita ingin ui
dinonaktifkan, jadi kita memanggil metode disable
, yang menonaktifkan semua komponen dalam interface, dari metode init
. Terakhir kita menginisialisasi layar dan objek DataProvider
.
Tulis metode berikut di kelas UserInterface
.
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; }
Semua metode di atas cukup jelas. Kita berbicara tentang metode joinRoom
sebelumnya. Sekarang setelah kita menulis metode ini, kita dapat melihat lebih dekat. Setelah kita memeriksa untuk mengkonfirmasi bahwa connection
masih masuk, kita bergabung dengan ruangan. Pertama modifikasi roomName
sehingga kita dapat membuat jid yang valid sehingga kita dapat terhubung ke ruang yang tepat. Lalu kita membuat objek UserDataExtension
baru. Sekali lagi kita belum membuatnya, tetapi konstruktor akan menerima objek UserDataGrabber
sebagai parameter yang diperlukan. Lebih lanjut tentang ini nanti.
Instantiate objek Room
melewati koneksi sebagai parameter yang diperlukan oleh konstruktor. UnescapedJID
ruangan harus diatur dan nickname dari pengguna saat ini. Anda dapat menggunakan nama apa saja seperti nama depan dan belakang pengguna tetapi saya memilih untuk hanya menggunakan username. Setelah kita mendengarkan berbagai acara, kita memanggil metode join
pada objek Room
.
Metode menerima dua parameter. Yang pertama adalah Boolean
yang mewakili apakah Anda ingin membuat dan mengkonfigurasi ruang yang dipesan. Tetapkan nilai ini menjadi false
untuk karena telah membuat dan mengkonfigurasi ruang obrolan di Openfire. Parameter kedua adalah Array
yang berisi ekstensi kustom apa pun yang ingin Anda sampaikan dengan Kehadiran pengguna saat memasuki ruang obrolan. Pass array yang berisi instance dari objek UserDataExtension
, yang baru saja kita buat, ke dalam parameter ini.
Mari kita selesaikan kelasnya.
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(); }
Oke, mari kita lihat apa yang baru saja ditulis. Metode pertama adalah metode addMessage
. Metode ini dipanggil setiap kali pengguna mengirim atau menerima pesan. Pesan tersebut ditampilkan di dalam bidang teks dan disesuaikan dengan waktu saat pesan itu dikirim atau diterima. Metode sendMessage
hanya mengirim pesan yang diketikkan oleh pengguna ke semua pengguna di ruang obrolan saat ini. Ini menerima dua parameter: pesan sebagai String
, dan nama siapa pun pesan berasal dari String
lain. Kita menampilkan waktu pesan dikirim atau diterima dan penerima pesan. Tidak ada yang terlalu rumit, saya harap.
Sekarang untuk metode penanganan peristiwa misterius yang dipanggil sesuai dengan yang telah dikirim RoomEvent
. Metode onRoomJoined
dipanggil setiap kali pengguna saat ini berhasil bergabung dengan ruang obrolan. Yang dilakukan hanyalah memanggil metode enable
yang mengaktifkan kembali semua komponen yang sebelumnya dinonaktifkan agar pengguna dapat berinteraksi dengannya.
Metode onUserJoined
dipanggil setiap kali pengguna berhasil bergabung dengan ruang saat ini. Saya membuat metode ini untuk debugging sehingga hanya melacak data. Biasanya Anda akan mendengarkan kehadiran yang masuk dan merespons perubahan data, tetapi di sini kita ingin menanggapi ekstensi data pengguna yang masuk melalui layanan pengiriman ekspres. Sekali lagi saya menemukan pengiriman ekspres jauh lebih mudah di sini, tetapi Anda dapat mencoba dan mengekstrak ekstensi khusus langsung dari objek Presence
dari metode onUserJoined
.
Catatan: Objek kehadiran adalah e.data as Presence
dalam kasus ini. Anda juga bisa lebih kreatif dengan metode ini, jika Anda suka: mungkin ada permainan suara tertentu atau pesan notifikasi muncul untuk pengguna setiap kali pengguna lain memasuki ruangan. Hanya beberapa makanan untuk dipikirkan.
Metode onGroupMessage
dikirim ketika pesan masuk diterima dari pengguna lain. Ruangan itu sendiri (server XMPP) bahkan dapat mengirim pesan. Ini biasanya pesan konfigurasi atau pesan kesalahan. Anda mungkin ingin memperlakukan pesan ini sebagai pesan administrasi dan melarang pesan tersebut ditampilkan sebagai pesan obrolan biasa di komponen TextArea
. Mungkin Anda dapat menampilkan beberapa pesan ini di jendela pesan. Hanya beberapa makanan untuk dipikirkan. Dalam metode onGroupMessage
, kita benar-benar menyaring semua pesan yang datang dari server. Kita memeriksa bahwa pengirim pesan, e.nickname
(nama pengguna pengirim dalam hal ini), ada dalam vektor username
, dan kemudian tampilkan pesan menggunakan metode addMessage
.
Sekarang untuk metode addToList
. Tambahkan item baru ke objek List
menggunakan DataProvider
. Panggil metode addItem
pada objek DataProvider
dan berikan item
ke parmeter. Objek item
harus menjadi Object
primitif dan memiliki properti yang disebut label
(String
). Nilai juga harus berupa String
. Kita akan menggunakan username pengguna yang sesuai sebagai nilai di sini. Metode removeFromList
melakukan apa yang Anda pikirkan. Ini menghapus pengguna yang ditentukan dari list
. Tidak ada yang rumit sama sekali - hanya kebalikan dari metode addItem
.
Setiap kali pengguna kami mengklik suatu item dalam list
, kita tidak akan menampilkan jendela profil yang berisi informasi tentang pengguna yang dipilih. Metode onUserSelected
tidak hanya itu. Username pengguna yang dipilih adalah nilai dari item yang dipilih dalam list
. Kita menggunakan username mereka untuk mengambil lokasi (index
) dari String
data mereka di profilData
Vector
.
Catatan: Kita akan menambahkan data ke objek Vector
ini dalam metode onUserDataExtension
nanti.
Indeks nama pengguna dalam Vector
usernames
sama dengan indeks profileData
yang disimpan dalam profilData Vector
. Anda dapat melihat mengapa dalam metode onUserDataExtension
jika Anda melihat ke depan. Selanjutnya ProfileWindow
baru dibuat dan ditampilkan kepada pengguna. Kita perhatikan window
untuk mengirim event DESTROYED
sehingga kita dapat menghapusnya dari tampilan. Kita perlu menulis kelas ProfileWindow
nanti, tetapi sampai sekarang kelas ProfileWindow
hanyalah hubungan ke objek di library yang dibuat sebelumnya.
Metode onUserLeave
dikirim bila pengguna meninggalkan ruang chat. Ketika pengguna pergi, hapus dari daftar karenat kita hanya ingin menampilkan pengguna saat ini. Kita panggil metode removeFromList
untuk mencapai ini.
Sekarang untuk saat ini kita semua sudah menunggu. Sekarang kita bisa melihat metode yang menerima paket yang dikirimkan oleh layanan pengiriman ekspres. Metode onUserDataExtension
menerima satu parameter. Objek UserDataExtension
deserialized dikirim segar dari kelas UserDataExtensionManager
. Objek UserDataExtension
berisi semua data yang dibutuhkan tentang pengguna yang baru saja bergabung dengan ruangan. Kita buat profileData
String
terlebih dahulu sehingga tidak perlu menyimpan ekstensi. Anda dapat menyimpan ekstensi sebagai gantinya jika Anda ingin tetapi saya membuat data menjadi sebuah String
sebelum fakta. Selanjutnya kita perlu mendorong semua data ke Vector
yang sesuai dan menampilkan pengguna dalam list
. Ini yang sederhana.
Metode terakhir adalah metode logout
. Metode ini membuat objek LoginManager
baru dan menyebutnya metode logout
yang mencatat pengguna saat ini (menghancurkan cookie sesi mereka) dan kemudian menavigasi ke halaman indeks utama situs.
Wow! Itu cukup berat. Tapi jangan terlalu berat, kuharap. Jika Anda menemukan salah satu dari ini membingungkan, periksa kode dengan hati-hati sampai Anda memiliki pemahaman yang jelas tentang apa yang terjadi sebelum Anda melanjutkan. Saya mengatakan ini karena ini adalah saus dibandingkan dengan bagian selanjutnya yang membahas serialisasi dan deserialisasi data. Saya akan mencoba membuatnya sesederhana mungkin.
Langkah 20: Tinjauan Singkat Serialisasi Data
Sebelum membuat kelas UserDataExtension
kita yang berkaitan dengan serialisasi data dan deserialisasi, kita perlu berbicara sedikit tentang topik tersebut. Serialisasi dalam konteks ini mengacu pada pengambilan data tertentu dan mengubahnya menjadi bentuk yang dapat digunakan untuk mengirim data melalui jaringan kita melalui XMPP. Kita tidak bisa hanya mengambil objek UserDataGrabber
dan mengirimkannya di jaringan sebagai objek UserDataGrabber
dan mengharapkan data yang akan diterima di ujung lain dalam kebijakan. Bahkan jika kita bahkan mencoba mengirim objek UserDataGrabber
menggunakan metode send
objek XMPPConnection
, kita hanya mendapatkan kesalahan argumen karena hanya mengecualikan XMPPStanza
sebagai parameter. Metode ini hanya menerima objek XMPPStanza
(Message
, Presence
, IQ
) sebagai parameter.
Jadi bagaimana kita mengatasi masalah ini? Jawabannya adalah dengan serialisasi data. Kita harus mengonversi objek atau data menjadi format yang dapat dikirim bersama dengan XMPP Stanza. Jadi format apa yang harus kita konversi menjadi objek UserDataGrabber
sehingga kita dapat mengirimnya ke jaringan kita dan diterima oleh pengguna lain? Yah jawabannya adalah XML. XMPP hanya streaming XML. Ini tidak hanya mudah diurai - itu juga dapat diperluas. Namun terserah kepada kita untuk mengemas (membuat serial) data pengguna ke dalam format XML sehingga dapat dikirim di seluruh jaringan dan diterima oleh pengguna lain.
Singkatnya, serialisasi adalah proses mengubah data menjadi format yang dapat disimpan/disimpan, dan/atau dikirim melalui jaringan. Secara teknis kita tidak bisa membuat serialisasi objek UserDataGrabber
, tetapi sebaliknya kita akan membuat serialisasi data yang dikandungnya tetapi kita bisa melakukannya jika perlu. Yang dibutuhkan untuk proyek ini adalah data pengguna. Saya berkata saya akan membuat ini sesederhana mungkin dan memang. Kita hanya akan membuat serialisasi data primitif. Semakin primitif data, semakin mudah untuk diserialisasi. Jika kita mencoba membuat serial objek di perpustakaan, katakanlah MovieClip
dengan banyak grafik dan properti spesifik-instance, serialisasi akan jauh lebih rumit.
Langkah 21: Bagaimana Kita Akan Melakukan Deserialisasi Data
Terserah pihak penerima untuk mengambil data serial (XML) dan mengubahnya kembali ke format aslinya. Kita harus membalikkan prosesnya. Dalam proyek, data diwakili oleh nilai-nilai String
sehingga sebenarnya akan mudah bagi kita untuk deserialize.
Deserialisasi sedikit lebih rumit daripada serialisasi, karena kita harus membuat klon dari data yang diterima. Ketika ekstensi masuk diterima (sebagai data XML), objek UserDataExtension
deserializes (mengubah) data XML kembali menjadi String
objek sehingga kita bisa mengaksesnya (read-only) melalui objek UserDataExtension
melalui sintaks dot. Kita juga perlu memeriksa dan memastikan data yang diterima valid.
Inilah dasar dari apa yang akan dilakukan:
- Terima data yang masuk (XML dalam hal ini)
- Periksa untuk melihat apakah data tersebut cocok dengan jenis data,
UserDataExtension
berseri serial (dengan kata lain, periksa untuk melihat apakah itu adalah ekstensi Data pengguna yang valid) - Mengurai data dan membuat tiruan dari objek
UserDataExtension
jauh - Kirim ekstensi pada pengiriman ekspres ke instance saat ini dari kelas
UserInterface
membuat data tersedia untuk pengguna.
Dengan semua yang dikatakan dan dilakukan sekarang kita dapat beralih ke menulis kelas UserDataExtension
.
Langkah 22: Kelas UserDataExtension
Tidak diperlukan pengenalan di sini. Mari kita menggali. Buat kelas baru dan beri nama UserDataExtension
. Kelas harus memperpanjang org.igniterealtime.xiff.data.Extension
. Kelas juga harus mengimplementasikan org.igniterealtime.xiff.data.IExtension
dan org.igniterealtime.xiff.data.ISerializable
. Silakan impor kelas XMLNode
dan kelas org.igniterealtime.xiff.data.ExtensionClassRegistry
bersama dengan kelas sebelumnya.
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 {
Buat variabel dan konstanta berikut.
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";
Kita membutuhkan variabel untuk menyimpan data yang diterima dari pengguna lain dan dua konstanta kelas. Satu untuk mewakili namespace unik (NS
) untuk ekstensi, dan lainnya untuk mewakili nama elemen XML.
Buat konstruktor.
public function UserDataExtension( parent:XMLNode = null, userData:UserDataGrabber = null ) { super( parent ); if ( userData ) data = userData; }
Penting: Pastikan untuk memberikan nilai default nol ke parameter apa pun di konstruktor karena ketika kita mendaftarkan Ekstensi Kustom kita dengan ExtensionClassRegistry
, turunan kelas akan dipakai dan tidak akan menerima argumen apa pun. Jika konstruktor tidak siap untuk tidak menerima parameter apa pun, kesalahan akan terjadi dan aplikasi Anda mungkin tidak berfungsi dengan benar.
Juga, meneruskan parent
(XMLNode
) ke dalam konstruktor kelas dasar (org.igniterealtime.xiff.data.Extension
). Jika userData
bukan null, tetapkan ke properti data
. Kita akan membuat serialisasi informasi yang terkandung dalam objek ini sebentar lagi.
Sebelum saya lupa, mari kita buat metode enable
.
public static function enable():void { ExtensionClassRegistry.register( UserDataExtension ); }
Seperti yang Anda lihat, metode enable
lurus ke depan. Ini adalah metode statis yang mendaftarkan kelas / ekstensi dengan ExtensionClassRegistry
, sehingga memungkinkan kelas/ekstensi.
Kita akan membuat serial data ke dalam format XML seperti yang saya jelaskan sebelumnya. Untuk lebih spesifik, kita akan membuat objek XMLNode
dari data. Mari kita buat metode untuk menyederhanakan proses ini untuk. Buat metode generateNode
.
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; }
Ini adalah metode penjelasan lain. Berdasarkan parameter yang diberikan itu menciptakan objek XMLNode
yang sudah dikemas dan siap untuk pergi. Mari kita mulai dengan parameter. Parameter pertama adalah nodeName
yang merupakan String
yang mewakili nama elemen dari simpul yang ingin dibuat. Yang kedua adalah String
lain yang mewakili nilai yang dikandung simpul. Parameter terakhir adalah Object
yang berisi atribut apa pun (seperti Strings
) yang harus dikandung oleh simpul tersebut. Metode ini menempatkan node bersama untuk kita sehingga kita tidak perlu mengulangi tindakan ini terus menerus.
Sekarang untuk bagian yang menyenangkan. Buat metode serialize
.
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; }
Ketika Extension
Anda ditambahkan ke XMPPStanza
, metode ini disebut. Hal pertama yang perlu dilakukan adalah mengemas data kita menjadi XMLNode
utama. Perhatikan bahwa semua node adalah turunan dari mainNode
. Juga, jika Anda belum menyadarinya, kita baru saja menggunakan metode generateNode
dengan baik. Kita telah menamai elemen dan mengatur nilai masing-masing elemen. Elemen firstName berisi nama depan pengguna, elemen statusMessage berisi pesan status pengguna, dan sebagainya. Kita baru saja mengambil data pengguna dan menyimpannya (berseri) di dalam xml.
Untuk menyelesaikan proses serialisasi, panggil metode setNode
, yang merupakan metode kelas dasar, untuk mengatur node. Juga periksa untuk melihat apakah parentNode
Anda sama dengan parameter parent
XML Node
. Jika tidak menjadikan simpul kita anak dari parameter parent
XMLNode
.
Catatan: Metode serialize
adalah metode interface yang membutuhkan tipe pengembalian (Boolean
). Jika data berhasil diserialisasi, Anda ingin mengembalikan true
. Kecuali karena alasan yang canggung Anda berurusan dengan data yang mungkin tidak diserialkan dengan benar, metode ini harus selalu mengembalikan true
. Kalau tidak, itu akan kembali false
.
Tulis metode deserialize
:
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; }
Berbeda dengan metode serialize
- yang dipanggil saat data ditambahkan ke XMPPStanza
- metode deserialize
dipanggil ketika ekstensi kustom diterima di ujung lainnya. Metode ini menerima satu parameter, XMLNode
yang merupakan anak dari XMPPStanza
yang diterima. Adalah tugas kita untuk memeriksa apakah node itu dikemas oleh mekanisme kita sendiri. Jadi, jika node adalah ekstensi khusus, lanjutkan deserialisasi data node, jika tidak, kembalikan false
.
Kita menggunakan for each
loop untuk beralih melalui setiap simpul turunan di parameter XMLNode
. Anda dapat memilih seberapa ketat ekstensi Anda. Saya memilih untuk membuat ekstensi kita cukup ketat. Jika semua data tidak diterima, ekstensi mengembalikan false
, memberi tahu perpustakaan DIFF bahwa node tidak valid dan tidak dikemas oleh kode.
Setelah data XML diuraikan, kita periksa untuk melihat apakah semua properti telah disetel; jika telah ditetapkan, panggil metode registerData
di kelas UserDataExtensionManager
- kelas yang akan dibuat selanjutnya - untuk mengirim ekstensi pada perjalanan pengiriman ekspres ke pengguna. Akhirnya kami mengembalikan true
setelah sukses.
Dua metode interface turun, dan dua lagi. Di sini mereka adalah:
public function getNS():String { return NS; } public function getElementName():String { return ELEMENT_NAME; }
Sangat, sangat, sangat, sangat, sederhana. Metode ini diperlukan oleh interface IExtension
. Metode getNS()
mengembalikan nama unik ekstensi dan getElementName()
mengembalikan nama elemen ekstensi. Seperti yang saya katakan: sangat, sangat, sangat, sangat, sederhana.
Hampir selesai. Selesaikan kelas dengan memberikan akses read-only ke data pengguna dan properti _isDeserialized
.
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; }
Langkah 23: Layanan pengiriman Ekspres
Seperti yang saya jelaskan di langkah sebelumnya, mungkin ada berbagai cara untuk menangani ekstensi yang masuk tapi saya lebih suka metode yang saya sebut metode pengiriman ekspres. Konsep ini sederhana. Kelas Helper menerima ekstensi dan mengirim ekstensi langsung ke metode (fungsi) tertentu, yang digunakan untuk menangani ekstensi, tetapi hanya jika metode telah terdaftar untuk tindakan ini. Mari kita buat kelas UserDataExtensionManager
.
package { public class UserDataExtensionManager { public static var onUserData:Function; public static function registerData( data:UserDataExtension ) { if ( data.isDeserialized ) { if ( onUserData != null ) { onUserData( data ); } } } } }
Kelas ini sangat ringan: hanya 18 baris kode. Metode registerDate
disebut, menerima UserDataExtension
deserialized sebagai parameter, maka metode onUserData
dipanggil jika ada. Dalam hal ini metode onUserData
adalah metode onUserDataExtension
dari kelas UserInterface
.
Langkah 24: Kelas Jendela Profil
Agar pengguna dapat melihat informasi tentang pengguna lain, kita memerlukan cara untuk menampilkan informasi kepada pengguna. Saya memilih untuk menggunakan jendela sederhana yang berisi bidang teks yang bisa digunakan untuk menampilkan info kepada pengguna. Mari kita buat kelas ProfileWindow
.
Ingat: Kelas ini ditautkan ke simbol MovieClip
di library.
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; } } }
Metode konstruktor menambahkan dan pendengar acara yang mendengarkan acara ADDED_TO_STAGE
. Ketika jendela ditambahkan ke stage
, metode onAdded
dipanggil, lalu pendengar dihapus dan metode init
disebut.
Metode init
menginisialisasi bidang teks dan tombol tutup. Ini juga memastikan bahwa jendela akan selalu berada di tengah panggung.
Anda dapat melihat konstanta kelas di awal skrip. Konstanta DESTROYED
adalah jenis acara yang akan dikirim oleh jendela ketika pengguna mengklik closeBtn
. Ketika closeBtn
diklik, metode penghancuran dipanggil, semua anak dihapus dan jendela dihapus dari indukannya. Kita bisa membuat kelas acara khusus seperti ProfileWindowEvent
yang memperluas flash.events.Event
, tapi itu tidak perlu. Alih-alih, kita mengirimkan objek Event
dengan tipe DESTROYED
. Ini sederhana dan melayani tujuannya dengan baik.
Akhirnya kita memiliki setter metode set text
. Ini juga bisa menjadi properti yang readable dan writable tetapi sekali lagi itu tidak perlu. Alih-alih write-only karena kita tidak perlu membaca properti ini di salah satu kode kita yang lain. Metode ini mengatur teks dari bidang teks sehingga teks ditampilkan kepada pengguna.
Langkah 25: Langkah terakhir
Hal terakhir yang perlu kita lakukan agar aplikasi kita berfungsi adalah mengatur halaman HTML. Aplikasi kita adalah aplikasi web yang dinamis. Sebelumnya kita mengambil variabel roomName
yang telah dimasukkan ke Flash; satu-satunya masalah di sini adalah kita belum mengirimkan variabel ini ke Flash. Anda harus menerapkan ini ke dalam kode Anda sendiri. Ini kode saya:
<?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"'; } ?> <? xml version="1.0" encoding='utf-8' ?> <!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;} //body { margin:0; padding:0; overflow:hidden; } #flashContent { width:100%; height:100%; align:middle; } </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>
Script PHP di atas hanyalah sebuah contoh. Situs Anda kemungkinan besar akan jauh lebih intuitif dan kreatif. Variabel content
GET menentukan ruang obrolan mana yang akan bergabung. Lalu kita meneruskan informasi ini ke Flash sehingga kode kita tahu ruang obrolan mana yang akan bergabung.
Langkah 26: Uji Aplikasi Kita
Pastikan WAMP dan Openfire berjalan di sistem Anda. Jika semuanya berjalan dengan baik, aplikasi Anda akan berjalan dengan baik. Uji aplikasi di beberapa browser sebagai pengguna yang berbeda. Mintalah satu pengguna mengikuti yang lain ke ruang obrolan untuk melihat seberapa baik aplikasi kita menangani perubahan data. Kemudian ngobrollah dengan diri Anda sendiri. Bersenang-senanglah dengan itu.
Kesimpulan
Yah, kita belajar banyak dan saya senang telah berbagi waktu ini dengan Anda. Library XIFF benar-benar luar biasa. Saya harap tutorial ini mendorong Anda untuk menjelajahi perpustakaan yang luar biasa ini secara keseluruhan. Ada begitu banyak yang dapat Anda lakukan dengannya. Anda dapat membuat obrolan pribadi dan daftar nama dalam aplikasi kita saat ini atau membuat aplikasi yang sama sekali baru yang terhubung ke server XMPP lain seperti salah satu server yang terdaftar di xmpp.org. Terima kasih telah mendengarkan. Lihat waktu Anda berikutnya.
Penting: File sumber dikonfigurasikan berdasarkan mesin saya sendiri. Ingatlah untuk memuat ulang file XIFF.swc
ke file chat.fla
. Juga ingat untuk mengatur pengaturan server Anda dalam konstanta kelas Dokumen (ChatApp.as).
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Update me weekly