[JS] Tworzymy aplikację JS z wykorzystaniem Backbone.js, część 2 – Integracja z Twitterem
Tak jak obiecałem wcześniej, zamierzam kontynuować serię wpisów dotyczących tworzenia aplikacji internetowej, której zadaniem będzie wspomaganie zarządzania kontami w sieciach Twitter i Facebook.
W poprzedniej części, omówiłem czym jest framework Backbone.js oraz jakie dodatkowe biblioteki będą potrzebne do poprawnego działania aplikacji.
W tej części, skupimy się na utworzeniu danych dostępowych aplikacji do serwisu Twitter, a następnie przejdziemy do części typowo programistycznej, gdzie utworzymy mechanizm autoryzacji oraz mechanizm pobierania wpisów z Twittera.
Integracja z Twitterem
Pierwszym krokiem jakie należy wykonać, to utworzenie konta w serwisie Twitter (jeśli go jeszcze nie masz), dzięki temu będzie można utworzyć aplikację, która uzyska autoryzowany dostęp do API Twittera. Gdy już posiadamy takie konto, należy się udać na stronę developerów Twittera i zalogować się. Z menu użytkownika klikamy w pozycję My applications (obrazek poniżej).
W nowym widoku znajdziemy przycisk Create a new application w który należy kliknąć. Pojawi się wtedy widok formularza, w którym należy wypełnić pola:
- Name - nazwa aplikacji,
- Description - opis aplikacji,
- Website - adres strony internetowej, gdzie można będzie korzystać z aplikacji.
Opcjonalnie, można wypełnić pole: Callback URL, w którym powinien się znaleźć adres strony, która ma się pojawić po poprawnym zalogowaniu się za pomocą Twittera, możemy dać tam link do strony aplikacji. Zgadzamy się na warunki umowy i wypełniamy (tak bardzo znienawidzone) pole captchy.
Po wykonaniu tych wszystkich kroków pojawia się strona z danymi aplikacji, gdzie znajdziemy informacje dotyczące aplikacji oraz tokeny (zaznaczone na czerwono na obrazku poniżej).
Tym sposobem utworzyliśmy aplikację, która będzie stanowiła nasze źródło danych z Twittera, które będzie można później wykorzystać w naszej aplikacji społecznościowej. Na chwilę obecną, poziom dostępu pozwala tylko na odczyt danych, ale w ustawieniach aplikacji można to zmienić i na potrzeby aplikacji rozszerzyć uprawnienia do poziomu trzeciego, czyli: Read, Write and Access direct messages. Dzięki temu, można będzie odczytywać tweety z tablicy użytkownika oraz wysyłać własne tweety do serwisu Twitter.
Autoryzacja konta użytkownika za pomocą Twittera - sposób działania
Aby móc korzystać ze swojego konta na Twitterze za pomocą naszej aplikacji, użytkownik musi najpierw autoryzować dostęp aplikacji do swoich danych w serwisie Twitter. Dlatego na początku utworzymy prosty formularz logowania się, który będzie się składał z dwóch części:
- standardowych pól logowania: login i hasło,
- pole do wpisania kodu dostępowego, który zostanie wygenerowany przez API Twittera.
W pierwszy momencie będą dostępne tylko dwa pola: login i hasło, a po kliknięciu w przycisk - Zaloguj się, pojawią się dodatkowe pole tekstowe i przyciski. Gdy użytkownik kliknie w przycisk - Autoryzuj aplikację, to zostanie przekierowany do strony logowania na Twitterze (jeśli nie jest już tam zalogowany), a następnie pojawi się okno z kodem, który należy wpisać w odpowiednim polu naszej aplikacji. Na sam koniec, użytkownik będzie musiał kliknąć przycisk - Zweryfikuj kod PIN i jeśli kod zostanie wpisany poprawnie, to aplikacja przeładuje swój widok i pojawią się tweety z tablicy użytkownika.
Backbone.js rusza do akcji - logowanie i autoryzacja za pomocą Twittera
Aby wspomóc i przyspieszyć proces tworzenia aplikacji wykorzystamy bibliotekę dostępną na Githubie - Codebird.js, której zadaniem realizacja żądań pomiędzy aplikacją a API Twittera.
Budowanie aplikacji internetowej zaczynamy od utworzenia pliku index.html, w którym zdefiniujemy bazową strukturę HTML oraz załadujemy niezbędne pliki CSS i JS. Początkowa struktura takiego pliku wygląda następująco:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | // index.html <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Aplikacja społecznościowa</title> <meta name="viewport" content="width=device-width, maximum-scale=1.0"> <meta name="author" content="Piotr Nalepa"> <link rel="stylesheet" href="css/main.css"> <link rel="shortcut icon" href="favicon.png" type="image/png"> </head> <body> <main></main> <script src="lib/js/jquery.2.0.3.min.js"></script> <script src="lib/js/lodash.1.3.1.min.js"></script> <script src="lib/js/backbone.1.0.0.min.js"></script> <script src="lib/js/backbone.localStorage-min.js"></script> <script src="lib/js/codebird.2.4.2.core.js"></script> <script src="lib/js/codebird.2.4.2.sha1.js"></script> <script src="js/app/globals.js"></script> <script src="js/app/model/tweet.js"></script> <script src="js/app/collection/tweets.js"></script> <script src="js/app/view/main.js"></script> <script src="js/app/view/tweet.js"></script> <script src="js/app/view/twitter.js"></script> <script src="js/app/view/topnav.js"></script> <script src="js/app/view/loginPopup.js"></script> <script src="js/app/app.js"></script> </body> </html> |
Tak utworzony plik zapisujemy w folderze projektu. Zapomniałbym wspomnieć, że struktura projektu wygląda następująco:
Backbone.js - model dla pojedynczego tweeta
Jak wiadomo, Backbone.js jest frameworkiem MV*, czyli opiera się na modelach, widokach i pozostałych rzeczach (gwiazdka w nazwie). Dlatego, dla pojedynczego elementu, jakim jest tweet, należy utworzyć jego odwzorowanie w postaci modelu. W naszym przypadku, nie ma tutaj wielkiej logiki i model wygląda nastepująco:
1 2 3 4 5 6 7 8 | // js/app/model/tweet.js (function () { 'use strict'; APP.model = APP.model || {}; APP.model.Tweet = Backbone.Model.extend(); })(); |
Należy jednak zwrócić tutaj uwagę na kilka rzeczy. Zastosowano tutaj dyrektywę "use strict", która wymusza pisanie kodu wg najnowszych standardów. Dzięki temu, przyszłościowo, kod ma być zgodny ze standardem ECMAScript 6.
Ponadto, wykorzystano przestrzenie nazw, dzięki czemu kod aplikacji nie będzie kolidował z kodem rozszerzeń czy bibliotek Javascript.
Poza tym, zastosowano standardowe tworzenie modelu wg specyfikacji Backbone.js bez potrzeby jego rozwinięcia.
Każdy model będzie miał wszystkie własności obiektu JSON, ktory jest zwracany przez Twitter API. Serwis ten zwraca dużą ilość danych, a przykładowa struktura obiektu pojedynczego tweeta (w tym przypadku jest to tweet Romana Kołtonia) wygląda następująco:
1 2 3 4 5 6 7 8 9 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 | { created_at: "Sun Oct 06 07:27:46 +0000 2013", id: 386754638499688450, id_str: "386754638499688448", text: "W "Cafe Futbol" głównie o kadrze. Można zadawać pytania do Piotra Świerczewskiego i prowadzących na Polsatsport.pl! http://t.co/fMEwtgksKv", source: "web", truncated: false, in_reply_to_status_id: null, in_reply_to_status_id_str: null, in_reply_to_user_id: null, in_reply_to_user_id_str: null, in_reply_to_screen_name: null, user: { id: 472275856, id_str: "472275856", name: "Roman Kołtoń", screen_name: "KoltonRoman", location: "Polska", description: "Dziennikarz, reporter i komentator Polsat Sport", url: "http://t.co/SqJltf4RGS", entities: { url: { urls: [{ url: "http://t.co/SqJltf4RGS", expanded_url: "http://POLSATSPORT.PL", display_url: "POLSATSPORT.PL", indices: [0, 22] }] }, description: { urls: [ ] } }, protected: false, followers_count: 23489, friends_count: 110, listed_count: 146, created_at: "Mon Jan 23 19:46:18 +0000 2012", favourites_count: 286, utc_offset: null, time_zone: null, geo_enabled: false, verified: false, statuses_count: 2250, lang: "pl", contributors_enabled: false, is_translator: false, profile_background_color: "C0DEED", profile_background_image_url: "http://abs.twimg.com/images/themes/theme1/bg.png", profile_background_image_url_https: "https://abs.twimg.com/images/themes/theme1/bg.png", profile_background_tile: false, profile_image_url: "http://a0.twimg.com/profile_images/1776192945/IMG_0324_face0_normal.jpg", profile_image_url_https: "https://si0.twimg.com/profile_images/1776192945/IMG_0324_face0_normal.jpg", profile_link_color: "0084B4", profile_sidebar_border_color: "C0DEED", profile_sidebar_fill_color: "DDEEF6", profile_text_color: "333333", profile_use_background_image: true, default_profile: true, default_profile_image: false, following: true, follow_request_sent: null, notifications: null }, geo: null, coordinates: null, place: null, contributors: null, retweet_count: 2, favorite_count: 1, entities: { hashtags: [ ], symbols: [ ], urls: [{ url: "http://t.co/fMEwtgksKv", expanded_url: "http://www.polsatsport.pl/pilka-nozna/swierczewski-gosciem-cafe-futbol.-zadawajcie-pytania-do-niego-i-ekspertow-polsatu-sport.html#.UlEQh4L7uTc", display_url: "polsatsport.pl/pilka-nozna/sw…", indices: [116, 138] }], user_mentions: [ ] }, favorited: false, retweeted: false, possibly_sensitive: false, lang: "pl" } |
Backbone.js - kolekcja modeli
Kolejnym krokiem będzie utworzenie swoistego "kontenera" na modele tweetów. Backbone zapewnia odpowiednie mechanizmy tworzenia kolekcji. W naszym przypadku, wygląda to tak:
1 2 3 4 5 6 7 8 9 10 | // js/app/collection/tweets.js (function () { 'use strict'; APP.collection = APP.collection || {}; APP.collection.Tweets = Backbone.Collection.extend({ model : APP.model.Tweet }); })(); |
Należy pamiętać o tym, aby do każdej kolekcji przypisać model, którego instancje mają być zbierane. W tym przypadku, zaznaczono że chcemy zbierać modele tweetów.
Backbone.js - widok pojedynczego Tweeta
Kolejnym krokiem jest utworzenie widoku pojedynczego tweeta, który będzie stanowił samodzielną, niezależną jednostkę. W aplikacjach internetowych pisanych za pomocą JS, to widoki są tym miejscem gdzie dzieje się najwięcej. Są one odpowiedzialne za wyświetlenie elementu, za przypisanie obsługi odpowiednich zdarzeń na elementach umieszczonych w danym widoku oraz są odpowiedzialne za komunikację z innymi widokami czy serwisami.
1 2 3 4 5 6 7 8 9 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 | // js/app/view/tweet.js (function () { 'use strict'; APP.view = APP.view || {}; APP.view.Tweet = Backbone.View.extend({ tagName : 'article', className : 'tweet clearfix', template : 'tweet', render : function () { var text; var templateVars; var item; var media; var user; // funkcja odpowiada za analizę tekstu zamieszczonego w wiadomości // i wyłuskanie z niej adresu URL // a następnie zastąpienie go linkiem var createUrl = function (text) { var urlRegex = /(https?:\/\/[^\s]+)/g; return text.replace(urlRegex, function (url) { return '<a href="https://blog.piotrnalepa.pl/2013/10/07/js-tworzymy-aplikacje-js-z-wykorzystaniem-backbone-js-czesc-2/" rel="nofollow" target="_blank">' + url + '</a>'; }); }; // funkcja odpowiada za zamianę adresy URL do małego obrazka profilu // na adres URL do większego obrazka var biggerProfileImage = function (imageUrl) { imageUrl = imageUrl.replace('_normal.', '_bigger.'); return imageUrl; }; // pobieramy treść pojedynczej wiadomości text = createUrl(this.model.get('text')); // pobieramy informację o ewentualnych treściach multimedialnych // zawartych w obiekcie wiaodmości, np. obrazki media = this.model.get('entities').media; // pobieramy informacje o użytkowniku user = this.model.get('user'); // tworzymy obiekt z danymi // który zostanie wykorzystany w templatce pojedynczego tweeta templateVars = { text : text, userName : user.name, photoUrl : biggerProfileImage(user.profile_image_url), postDate : this.model.get('created_at'), additionalImage : _.isEmpty(media) ? '' : { src : media[0].media_url, alt : media[0].id, title : media[0].expanded_url } }; // pobieramy wygenerowany widok HTML wiadomości item = APP.loadTemplate(this.template, templateVars); // dodajemy wygenerowany widok wiadomości, do kontenera na wiadomość // należy zwrócić uwagę na to, że this.el prowadzi do obiektu JS // kontenera wiadomości, a this.$el prowadzi do obiektu jQuery kontenera wiadomości. // Jest to zachowanie wymuszone przez framework Backbone.js this.$el.append(item); // zwracamy obiekt widoku wiadomości return this; } }); })(); |
Backbone.js - szablon pojedynczego widoku
W poprzedniej części kodu odnosiliśmy się do szablonu. Do obsługi szablonów wykorzystano mechanizm udostępniany przez bibliotekę Underscore.js/Lodash.js. Widok wygląda następująco:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | // js/app/template/tweet.js <aside> <img class="post-user-image" src="{{= photoUrl }}" alt="{{= userName }}" width="75" height="75"> </aside> <section> <header> <h3>{{= userName }}</h3> <time datetime="{{= postDate }}">{{= postDate }}</time> </header> <p>{{= text }}</p> {{ if (additionalImage) { }} <p><img src="{{= additionalImage.src }}" alt="{{= additionalImage.alt }}" title="{{= additionalImage.title }}"/></p> {{ } }} </section> |
Na pewno zauważyłeś/aś, że w kodzie się pojawiają specyficzny tekst w klamrach. Są to placeholdery na tekst, który jest przesyłany w zmiennej templateVars w kodzie widoku tweeta.
Powyżej, pojawia się jeszcze kilka ciekawych rzeczy. Szablon HTML jest zapisany jako plik JavaScript, dzięki temu może być odpowiednio parsowany przez mechanizm szablonów.
Zastosowane znaczniki placeholderów też nie są standardowe. Domyślne znaczniki systemu szablonów mają inny wygląd, który został nadpisany w ustawieniach aplikacji.
1 2 3 4 5 6 7 8 9 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 | // js/app/globals.js (function () { 'use strict'; // zmiana domyślnych placeholderów na wygodniejsze _.templateSettings = { evaluate : /\{\{(.+?)\}\}/g, // placeholder funkcyjny interpolate : /\{\{=(.+?)\}\}/g, // placeholder treści escape : /\{\{-(.+?)\}\}/g // placeholder parsowany }; window.APP = window.APP || { cache : { templates : {}, views : {} }, twitter : new Codebird(), loadTemplate : function (templateFilePath, templateData) { console.info('Loading: ' + templateFilePath + ' with data:', templateData); // jeśli szablonu nie ma w cache aplikacji if (!APP.cache.templates[templateFilePath]) { // ścieżka dostępu do folderu z szablonem var templateDir = 'js/app/template/'; // ścieżka dostępu do pliku var templateUrl = templateDir + templateFilePath + '.js'; var templateString = ''; $.ajax({ async : false, dataType: 'text', url : templateUrl, success : function (response) { // jeśli załadowano szablon, przypisz wynik zapytania do zmiennej templateString = response; }, error : function (response) { // jeśli się nie udał, wyświetl błąd console.info('Error: ', response); return false; } }); // parsuj szablon i zapisz go w cache APP.cache.templates[templateFilePath] = _.template(templateString); } // zwróć szablon z wypełnionymi placeholderami return APP.cache.templates[templateFilePath](templateData); }, config : function (action, key, value) { var config = { debug : false, // Adres serwera proxy, który będzie odpytywany o nowe tweety. // Jesy wymagany do poprawnego działania aplikacji: https://github.com/jublonet/codebird-cors-proxy/ proxy : 'http://localhost/social/proxy/', // dane dostępowe aplikacji do API Twittera - nie są bezpieczne!! consumer : { key : '[consumer key aplikacji z API Twittera]', secret : '[consumer secret aplikacji z API Twittera]' }, // dane dostępowe użytkownika do aplikacji token : {} }; return { // pobierz wartość wybranego ustawienia get : function (key, all) { console.info('Getting config for', key); if (all) { return config; } else { return config[key]; } }, // zmień wartość ustawienia set : function (key, value) { console.info('Updating config with', key, value); config[key] = value; return this; } }; } }; })(); |
Powyższy kawałek kodu, definiuje kilka ciekawych mechanizmów. Jak zwykle, korzystamy z przestrzeni nazw aplikacji i w tej przestrzeni utworzyliśmy odniesienie do biblioteki Codebird.js, zdefiniowaliśmy funkcję loadTeamplate, ktora wspomaga ładowanie plików z szablonami w naszej aplikacji oraz utworzono mechanizm dostępu do danych konfiguracyjnych. Należy pamiętać o tym, że przedstawiony w powyższym kodzie sposób dostępu aplikacji do danych dostępowych API Twittera nie jest bezpiecznym rozwiązaniem. Ma on na celu uproszczenie aplikacji na potrzeby tego wpisu, ale w wersji produkcyjnej tego typu kody dostępowe powinny być umieszczone po stronie serwera, aby utrudnić dostęp do nich.
Backbone.js - widok panelu Twittera
Mamy już przygotowane modele, kolekcje i widoki dla pojedynczego tweeta. Teraz należy to złożyć w jedną całość, która będzie prezentowana jako ściana wiadomości z Twittera. W tym celu, należy utworzyć kolejny widok odpowiedzialny za wyświetlenie tej ściany.
1 2 3 4 5 6 7 8 9 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 91 92 93 94 95 96 97 98 99 100 101 102 103 104 | // js/app/view/twitter.js (function () { 'use strict'; APP.view = APP.view || {}; // tworzymy widok ściany Twittera APP.view.Twitter = Backbone.View.extend({ // określamy miejsce wstawienia kontenera widoku el : $('body').find('main'), // tworzymy kontener widoku container : $('<div/>').prop({ id : 'twitter' }), collection : {}, // gdy utworzymy funkcję initialize, to będzie ona uruchamiana automatycznie // podczas inicjacji widoku Twittera - jest to domyślne zachowanie frameworka Backbone.js initialize : function () { console.info('Twitter: initializing Twitter timeline view'); // ustawiamy połączenie z serwerem proxy APP.twitter.setProxy(APP.Config.get('proxy')); // konfigurujemy dane dostępowe aplikacji do API Twittera APP.twitter.setConsumerKey(APP.Config.get('consumer').key, APP.Config.get('consumer').secret); // zapisz dane użytkownika w localStorage przeglądarki użytkownika var twitterUserConfig = store.get('twitterUserConfig'); // jeżeli użytkownik jest zalogowany i dokonał autoryzacji aplikacji if ((APP.Config.get('token').key !== undefined && APP.Config.get('token').secret !== undefined) || twitterUserConfig !== undefined) { console.info('Twitter: User is logged in.'); // jeśli localStorage jest zdefiniowany if (twitterUserConfig) { // pobierz ustawienia z locaStorage APP.twitter.setToken(twitterUserConfig.key, twitterUserConfig.secret); } else { // pobierz ustawienia z cache aplikacji APP.twitter.setToken(APP.Config.get('token').key, APP.Config.get('token').secret); } // pobierz wiadomości this.getTweets(); } else { console.info('Twitter: User is not logged in'); // wyświetl okno logowania this.renderPopup(this.twitter); } }, render : function () { var that = this; // usuń zawartość kontenera Twittera that.$el.empty(); // usuń istniejące wiadomości z obiektu that.container.empty(); // pobierz wiadomości z kolekcji tweetów i pokaż je _.each(this.collection.models, function (item) { that.renderTweet(item); }, this); // wstaw wygenerowane wiadomości do kontenera Twittera that.$el.append(that.container); }, getTweets : function () { console.info('Twitter: getting tweets'); var that = this; // pobierz tweety z tablicy użytkownika APP.twitter.__call('statuses_homeTimeline', {}, function (tweets) { // jeśli nie ma błędów if (!tweets.errors) { // utwórz nową kolekcję wiadomości that.collection = new APP.collection.Tweets(tweets); // wyrenderuj je that.render(); } else { console.error('Twitter error', tweets.errors); } }); }, renderTweet : function (item) { // utwórz nowy obiekt pojedynczej wiadomości/tweeta var tweetView = new APP.view.Tweet({ model : item }); // wstaw do kontenera na wiadomości wygenerowany tweet this.container.append(tweetView.render().el); }, renderPopup : function () { console.info('Twitter: loading login popup'); // utwórz nowy obiekt widoku okna logowania do Twittera var popup = new APP.view.LoginPopup(); // wyrenderuj go popup = popup.render(); // wstaw do kontenera Twittera this.$el.empty().append(popup); } }); })(); |
Za pomocą powyższego kodu decydujemy czy mamy pokazać najnowsze wiadomości z konta użytkownika czy też pokazać okno logowania. Widok ten pełni rolę swoistego kontrolera (jeśli można to tak nazwać).
Backbone.js - widok okna logowania do Twittera
Aby zapewnić aplikacji dostęp do twitterowych wiadomości z konta użytkownika, należy autoryzować aplikację w systemie. W tym celu potrzebujemy okno logowania (pierwsza faza) oraz okna autoryzacji (druga faza). Za obydwie fazy będzie odpowiedzialny jeden widok.
1 2 3 4 5 6 7 8 9 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 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 | // js/app/view/loginPopup.jsj (function () { 'use strict'; APP.view = APP.view || {}; // tworzymy widok popupu APP.view.LoginPopup = Backbone.View.extend({ el : $('body').find('main'), container : $('<div/>').prop({ id : 'login-popup', className : 'modal login' }), template : 'loginPopup', // definiujemy zdarzenia na elementach formularza // zdarzenia też możemy umieszczać w przestrzeni nazw events : { 'click.APP #login-submit' : 'submit', 'click.APP #login-verify' : 'verify' }, initialize : function () { console.info('Initializing login popup view'); }, // renderujemy widok i zwracamy kod HTML widoku (nie wyświetlamy go) render : function () { var form = APP.loadTemplate(this.template, {}); var container = this.container.empty().append(form); return container; }, // wysyłanie danych do logowania submit : function (event) { event.preventDefault(); console.info('Submiting data', event); // wysyłamy prośbę o autoryzację dostępu this.authorize(); }, authorize : function () { console.info('Twitter: logging user'); var that = this; // wysyłamy żadanie zalogowania użytkownika APP.twitter.__call( 'oauth_requestToken', { oauth_callback : 'oob' }, function (reply) { // tokeny która otrzymaliśmy w odpowiedzi wstawiamy do kolejnych metod // w celu zapamiętania APP.twitter.setToken(reply.oauth_token, reply.oauth_token_secret); APP.Config.set('token', { key : reply.oauth_token, secret : reply.oauth_token_secret }); // pobierz URL do autoryzacji aplikacji APP.twitter.__call('oauth_authorize', {}, function (authorizationUrl) { console.info('Twitter: authorize application', authorizationUrl); // wyświetl ukryte pola var hiddenFields = that.$el.find('.hidden'); // wstaw pobrany adres URL do przycisku, który otworzy nowe okno ze stroną // z wygenerowanego adresu URL that.$el.find('#verification-link').prop({ href : authorizationUrl }); // odblokuj ukryte przyciski hiddenFields.removeClass('hidden'); hiddenFields.find('#login-pincode').prop({ disabled : false }); }); }); }, // funkcja weryfikująca kod PIN podany przez użytkownika verify : function (event) { event.preventDefault(); console.info('Twitter: verify application PIN code'); var that = this; // wykonaj żądanie potwierdzenia poprawności kodu PIN APP.twitter.__call( 'oauth_accessToken', { oauth_verifier : $('#login-pincode').val() }, function (reply) { // dokonano autoryzacji console.info('Twitter: application verified. Render tweets'); // zachowaj tokeny otrzymane podczas autoryzacji (mogą być różne od tych otrzymanych w fazie logowania) var token = { key : reply.oauth_token, secret : reply.oauth_token_secret }; APP.Config.set('token', token); store.set('twitterUserConfig', token); APP.twitter.setToken(reply.oauth_token, reply.oauth_token_secret); that.container.remove(); // ponowne załadowanie widoku panelu Twittera APP.App.activeView = new APP.view.Twitter(); }); } }); })(); |
W tej części dużo się dzieje i ma miejsce komunikacja z API Twittera w celu weryfikacji użytkownika. Dzięki dwufazowej weryfikacji danych, użytkownik może być pewny że aplikacja jest autoryzowana w systemie.
Oprócz kodu widoku, potrzebny jest nam też szablon okna logowania. Wygląda on następująco:
1 2 3 4 5 6 7 8 9 10 11 | // js/app/template/loginPopup.js <form action="#"> <ul> <li><input type="text" id="login-username" name="username" placeholder="Nazwa użytkownika"/></li> <li><input type="password" id="login-password" name="password" placeholder="Hasło"/></li> <li><button id="login-submit" class="btn">Zaloguj się</button></li> <li class="hidden"><a id="verification-link" href="#" target="_blank" class="btn">Autoryzuj aplikację</a></li> <li class="hidden"><label for="pincode">Wpisz kod PIN: </label><input type="text" name="pincode" id="login-pincode" disabled/></li> <li class="hidden"><button id="login-verify" class="btn">Zweryfikuj kod PIN</button></li> </ul> </form> |
Jak widać, jest prosty to formularz logowania, który jednak nie zawiera żadnych placeholderów na dane z aplikacji. Po prostu nie ma takiej potrzeby.
Podsumowanie
Jak widać, proces tworzenia aplikacji internetowej jest dość pracochłonny. Trzeba rozważyć różne przypadki: czy użytkownik jest zalogowany, czy dobrze podane są dane weryfikacyjne, itd. Lecz dzięki temu, otrzymujemy szkielet aplikacji, który pozwala nam na dalszy rozwój i zapobiega niepotrzebnym nerwom w różnych sytuacjach.
Tak utworzony kod aplikacji, jest odpowiedzialny tylko i wyłącznie za jedną część - za panel dostępu do wiadomości z Twittera. Należy mieć jednak na uwadze, że nie jest to kod z którego można być 100% zadowolonym i uznać go za gotową aplikację. Nie, jest to kod aplikacji, która działa i pokazuje jak można tworzyć aplikacje za pomocą frameworka Backbone.js. Zapraszam do zabawy z tym kodem, bo niewątpliwie kilka rzeczy można ulepszyć i usprawnić. Póki co, jest to kod aplikacji która działa i spełnia postawione jej wymagania.