Znikające dymki i usuwanie wiadomości jak na Facebooku IGUraport#4

Ostatnio pod jednym wpisów pojawiło się kilka komentarzy w których pojawia się opinia o małej ilości mojego kodu na tym blogu. Postanowiłem więc że tym razem będzie mniej lifestylowo a bardziej merytorycznie. Pokażę jak zaimplementowałem mechanizm usuwania wiadomości w facebookowym stylu.

Jak postępy na stronie?

Mimo że niniejszy artykuł będzie miał nieco inny charakter niż zwykle, pozwolę sobie wtrącić kilka akapitów na temat postępu prac przy mojej aplikacji webowej.

Miniony tydzień udało się zakończyć całkiem niezłym wynikiem, bo prawie 22 godziny dłubania przy kodzie. Początek tygodnia to kończenie modułów ustawień użytkownika(co wciąż jest podpisane jako avatar użytkownika, sic!) i archiwizacji ogłoszeń.

Koniec to już modyfikacja modułu wiadomości. Trzeba było zareagować na możliwość usunięcia konta przez usera, zablokowaniem korespondencji z nim, a także na zarchiwizowanie ogłoszenia, informując o tym rozmówcę w wiadomości.

Na koniec jeszcze okazało się że skoro można usuwać całe konto lub pojedyncze ogłoszenia to przydało by się też usuwać niepotrzebne  wiadomości w skrzynce.

I właśnie ten fragment kodu postaram się przedstawić.

Czas pracy
Czas pracy

Moduł wiadomości

Na wstępie pragnę zaznaczyć że nie jestem zawodowym programistą. Programowania uczę się po godzinach od ponad pół roku, a przedstawiona tutaj wiedza i kod nie była sprawdzona przez nikogo „doświadczonego”.

W mojej aplikacji moduł wysyłania wiadomości między użytkownikami funkcjonuje już od jakiegoś czasu.

Choć początkowo miałem taki zamiar, nie będę opisywał tutaj skryptów wyciągających wiadomości z bazy, dodających je, czy warstwy prezentacyjnej. Wymagało by to obszernego materiału, sporo pracy i przede wszystkim czasu którego za wiele nie mam.

W między czasie postaram się pokrótce opisać działanie całego modułu,  ale głównie skupię się na skrypcie usuwającym wiadomości, czyli tym co dodałem wczoraj. Jeżeli jednak ktoś będzie zainteresowany resztą kodu, to odsyłam do mojego GIT’a

<article id="content">
	<div id="userbox">
	<!-- Pojemnik na poszczególnych rozmówców -->
		<script>wczytaj(3,4,1,"Usługi Hydrauliczne","czesiekhydraulik");</script>
		<div class="user" id="3/4">
			<img src="../../ogloszenie/img/3/1.jpg"/>
			<div class="text">
				<span class="add_name">Usługi Hydrauliczne</span>
				<span class="user_name">czesiekhydraulik</span>
			</div>
		</div>
		<div class="user" id="42/14">
			<img src="../../public_profile/avatar/avatar.png"/>
			<div class="text">
				<span class="add_name">usun 1</span>
				<span class="user_name">usun_1</span>
			</div>
		</div>
		<div class="user" id="37/10">
			<img src="../../public_profile/avatar/avatar.png"/>
			<div class="text">
				<span class="add_name">Szukam zespołu na wesele</span>
				<span class="user_name">max_long_nick_na_igu</span>
			</div>
		</div>
		<div class="user" id="34/2">
			<img src="../../ogloszenie/img/34/1.jpg"/>
			<div class="text">
				<span class="add_name">Wykonanie i montaż bramy kutej</span>
				<span class="user_name">Edek</span>
			</div>
		</div>
		<div class="user" id="5/5">
			<img src="../../ogloszenie/img/5/1.jpg"/>
			<div class="text">
				<span class="add_name">Najlepsze suknie ślubne</span>
				<span class="user_name">mondzia</span>
			</div>
		</div>
		<div class="user" id="7/5">
			<img src="../../ogloszenie/img/7/1.jpg"/>
			<div class="text">
				<span class="add_name">Korepetycje z Angielskiego</span>
				<span class="user_name">mondzia</span>
			</div>
		</div>
		<div class="user" id="7/11">
			<img src="../../ogloszenie/img/7/1.jpg"/>
			<div class="text">
				<span class="add_name">Korepetycje z Angielskiego</span>
				<span class="user_name">hilary_clinton</span>
			</div>
		</div>
	<!-- Koniec pojemnika na poszczególnych rozmówców -->
	</div>
		
	<div id="inbox">
	<!-- pojemnik wiadomości ładowany przez AJAX-->
		<div id="message">
		<!-- korespondencja z wybranym rozmówcą-->
			<div class="message right">
				<span class="delete" data-content="4">Usuń</span>
				<button class="options">...</button>
				<span class="text_ms">witam, jaka jest cena za punkt??</span>
				<span class="date">2017-12-06 21:55:48</span>
			</div>
			<div class="message left">
				<img src="../../public_profile/avatar/4.jpg"/>
				<span class="text_ms">130 zł</span>
				<button class="options">...</button>
				<span class="delete" data-content="10">Usuń</span>
				<span class="date">2017-12-12 22:42:14</span>
			</div>
			<div class="message right">
				<span class="delete" data-content="45">Usuń</span>
				<button class="options">...</button>
				<span class="text_ms">no to sporo za tyle to sobie sam zrobię</span>
				<span class="date">2017-12-15 21:34:06</span>
			</div>
			<div class="message left">
				<img src="../../public_profile/avatar/4.jpg"/>
				<span class="text_ms">to sobie kurwa zrób sam i nie zawracaj mi głowy!</span>
				<button class="options">...</button>
				<span class="delete" data-content="72">Usuń</span>
				<span class="date">2018-01-02 10:52:17</span>
			</div>
			<div class="message right">
				<span class="delete" data-content="85">Usuń</span>
				<button class="options">...</button>
				<span class="text_ms">no i żebyś wiedział że zrobię taką robotę jak ty robisz to można się w 15minut na  YT nauczyć</span>
				<span class="date">2018-01-02 15:43:32</span>
			</div>
			<div class="message right">
				<span class="delete" data-content="87">Usuń</span>
				<button class="options">...</button>
				<span class="text_ms">halo jesteś?</span>
				<span class="date">2018-01-26 15:57:46</span>
			</div>
			<div class="message right">
				<span class="delete" data-content="90">Usuń</span>
				<button class="options">...</button>
				<span class="text_ms">zrobiłem sobie ale mam problem</span>
				<span class="date">2018-01-26 21:46:08</span>
			</div>
		<!-- koniec korespondencji z wybranym rozmówcą -->
        </div>
		<div id="send">
			<form id="send_message" method="post">
			<!-- formularz wysyłania wiadomoci -->
				<input class="send" type="text" name="send"/>
				<button id="send_button" class="button_green" >Wyślij</button>
			<!-- koniec formularza wysyłania wiadomoci -->
			</form>
		</div>
	<!-- koniec pojemnika na wiadomości -->
	</div>
</article>

To co widzicie powyżej to kod wynikowy kawałka strony zaznaczonego na czerwono na zdjęciu poniżej. W większości automatycznie wygenerowany przez skrypty PHP i JS

Wiadomości dotyczą konkretnych ogłoszeń, a nie użytkownika. To nieco komplikuje skrypt wyciągający wiadomości z bazy, ponieważ odnośnie jednego ogłoszenia można pisać z kilkoma użytkownikami, a z jednym użytkownikiem odnośnie kilku ogłoszeń.

Wiadomości IGU
Wiadomości IGU

Usuwanie wiadomości

Po wyciągnięciu ogłoszeń z bazy i załadowaniu pierwszego z nich, ukrywam elementy których widać być nie powinno czyli kropeczki $(„.options”) i przycisk usuń $(„.delete”). Teoretycznie mógł bym to zrobić już z poziomu CSS ale użytkownik bez włączonej obsługi JS nie miał by w ogóle możliwości usunąć wiadomości.

Następnie oczekuję najechania kursorem na dymek z tekstem wiadomości $(„#message .message”) aby wyświetlić trzy kropki  („.options”) po kliknięciu w które wyświetlę przycisk usuń („.delete”).

Po zjechaniu z pola wiadomości, elementy („.options”) i („.delete”) w nim zawarte zostaną usunięte.

$(document).ready(function(){
    $(".options").hide();//ukryj opcje
    $(".delete").hide();//ukryj przycisk usuń
                    
    $("#message .message").hover(
        function(){//funkcja do wykonania po najechaniu myszą na element
			$(this).children(".options").show();//pokaż element opcje
			$(this).children(".options").click(function(){//jeżeli zostanie kliknięty element options
				$(this).siblings(".delete").show();//pokż element delete
			});
		},
		function(){//funkcja do wykonania po opuszczeniu myszą elementu
			$(this).children(".options").hide();//ukryj element options
			$(this).children(".delete").hide();//ukryj element delete
		}
	);//koniec funkcji hover
            
	$(".delete").click(function(){
		var obiect=$(this);//pobierz obiekt wywołujący zdarzenie
		var id = obiect.attr("data-content");//pobieramy id ogłoszenia
		var _class = obiect.parent().attr("class");//pobieramy klasę ogłoszenia
		var reversal = reversal_valid(_class);//ustal czy użytkownik jest nadawcą czy odbiorcą
		if(reversal!==false){
			deleted_valid(id, reversal, obiect);//jeżeli zmienna reversal ma pożądany typ, uruchom funkcję
		}
	});
});

Przekazanie danych do wykonania

Po kliknięciu w usuń $(„.delete”) pobieram jego atrybut data-content który przechowuje id wiadomości oraz ustalam czy wiadomość próbuje usunąć jej nadawca czy odbiorca poprzez sprawdzenie klasy left/right własną funkcją reversal_valid();

Po zdefiniowaniu zmiennych uruchamiam własną funkcję deleted_valid() przekazując jej ustalone wcześniej parametry oraz obiekt zdarzenia.

Funkcja deleted_valid() to nic innego jak przekazanie AJAX’em danych do skryptu PHP  zawartym w pliku usun_wiadomosc.php w celu walidacji zmiennych i ewentualnego dodania zmian w bazie danych.

function deleted_valid(id,removing,obiect){

    $.ajax({
        url: 'usun_wiadomosc.php', //plik php
        type: 'post', //metoda przekazania zmiennych
        data: {message_id:id , removing:removing}, //zmienne
        success: function(response){
            if(response==true){ //jeżeli skrypt php zwróci true usuń wiadomość
                obiect.parent(".message").fadeOut(800);
            }
            else{// jeżeli nie wyświetl komunikat o błędzie i przewiń stronę do dołu
                obiect.parent(".message").append('<div class="blad_user"><p>Jedno z twoich poleceń narusza zasady użytkowania serwisu.<br/> Prowadzenie działań niezgodnych z regulaminem, bądź działających na niekożyść serwisu lub jego użytkowników może skutkować konsekwencjami prawnymi!</p></div>');
                $("#message").scrollTop="1e6";

            }
        }
    }); //koniec ajax
} //koniec deleted_valid()

function reversal_valid(_class){
	var reversal = _class.split(" ");//rozdziel klasę
    if(reversal[1]=="right"){
        return "nadawca";
    }
    else if(reversal[1]=="left"){
        return "odbiorca";
    }
    else return false;
} //koniec reversal_valid()

Tabela wiadomości w bazie danych MySQL składa się z 9 kolumn. Mianowicie: id , id_ogloszenia , id_nadawca , id_odbiorca , tresc , data , odczytano , deleted_nadawca , deleted_odbiorca

Aby usunąć ogłoszenie wystarczy zmienić wartość w jednej z kolumn deleted z NULL na 1.

2 kolumny deleted ponieważ jeżeli jeden z userów usunie wiadomość którą sam wysłał lub odebrał, nie oznacza to że wiadomość ta powinna zostać skasowana także u drugiego użytkownika. Dlatego trzeba ustalić kto ów wiadomość usuwa.

Skrypt PHP

<?php
//uruchamiam sesje i załączam funkcje
session_start();
$DOCUMENT_ROOT = $_SERVER['DOCUMENT_ROOT'];
require_once ($DOCUMENT_ROOT.'/../ini/FunkcjePHP/polacz_z_baza.php');

//walidacja id widomości
if((is_numeric($_POST['message_id'])) && ($_POST['message_id']>0)){
    $message_id = $_POST['message_id'];
}

//walidacja nadawca/odbiorca 
if(($_POST['removing'] == "nadawca") || ($_POST['removing'] == "odbiorca")){
    $removing = $_POST['removing'];
}

//sprawdź czy zmienne z POST przeszły walidację i istnieją
if((isset($message_id)) && (isset($removing))){

    $user = $_SESSION['id'];//ustaw id użytkownika

    //utwórz zapytanie
    $query = 'UPDATE wiadomosci SET deleted_'.$removing.'=1 WHERE id='.$message_id.' AND id_'.$removing.'='.$user;
    $polaczenie->query($query);//wykonaj zapytanie
    $remove = $polaczenie->affected_rows;//pobierz ilość zmodyfikowanych rekordów

    //jeżeli usunięcie się powiedzie wyświetl true w przeciwnym wypadku wyświetl false
    if($remove==1){
        echo true;
    }
    else echo false;
}
else echo false;//zmienne z POST nie przeszły walidacji, operacja usuwania nie powiodła się, wyświetl false
?>

Tak więc skrypt PHP wykonuje prostą walidację zmiennych message_id i removing, odebranych z POST a następnie próbuje wykonać zapytanie w bazie danych. Prawidłowo wykonane zapytanie zmienia wartość jednego wiersza w kolumnie deleted_nadawca lub deleted_odbiorca z NULL na 1.

Zagrożenia

Pierwsze zagrożenie jakie się nasuwa to złośliwy użytkownik, który może próbować usunąć wiadomość nie należącą do niego. Na przykłąd edytując kod html bądź js może próbować zmienić klasę  (.message.right) z right na left.

W takim przypadku skrypt uzna go nie za nadawcę lecz odbiorcę wiadomości i będzie próbował usunąć wiadomość w skrzynce odbiorcy.

Mimo to w takim przypadku skrypt nie wykona się poprawnie ponieważ id użytkownika generowane jest przez kod PHP ze zmiennej $_SESSION[]  więc baza nie znajdzie rekordu  o id_wiadomości=X i id_odbiorcy=Y ponieważ użytkownik Y jest nadawcą wiadomości X a nie jej odbiorcą.

Zwracanie wyniku do AJAX

Jako że skrypt PHP nie może zwrócić żadnej wartości do skryptu JS metodą return, a nam zależy żeby w razie powodzenia wiadomość zniknęła ze strony bez jej przeładowania musiałem zastosować pewną sztuczkę.

PHP w zależności od powodzenia wykonania skryptu lub nie, wyświetla na stronie wynikowej true lub false metodą echo.

Funkcja deleted_valid() w colback’u  succes zamiast wyświetlać zwróconą zawartość w oknie przeglądarki sprawdza czy zwrócona strona zawiera true i w zależności od wyniku usuwa wiadomość z ekranu bądź wyświetla komunikat o błędzie.

Efekt końcowy

Efekt końcowy widać na powyższym filmie. Trzeba będzie do tego dołączyć usuwanie całych konwersacji a nie tylko wiadomości, ale powinna wystarczyć lekka modyfikacja zapytania i dołożenie kilku guzików do warstwy prezentacji.

Przydało by się też coś takiego jak archiwizowanie wiadomości, dodawanie do spamu. Ale jak to mówią nie od razu Kraków zbudowano, mam jeszcze sporo innych ważnych rzeczy do zrobienia/napisania a Marzec coraz bliżej.

Koniec końcem pisanie tego posta zajęło mi chyba dłużej niż pisanie tego kodu, a jest już sporo po północy tak że tymczasem!:)

 

1 666 komentarzy