Add files via upload

This commit is contained in:
Anqi Chen 2019-06-01 22:55:48 -07:00 committed by GitHub
parent a7b830012c
commit 2027d63b4a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 944 additions and 0 deletions

12
Yo/ajax/setBroken.php Normal file
View file

@ -0,0 +1,12 @@
<?php
include("../config.php");
if(isset($_POST["src"])) {
$query = $con->prepare("UPDATE images SET broken = 1 WHERE imageUrl=:src");
$query->bindParam(":src", $_POST["src"]);
$query->execute();
} else {
echo "No src passed to page";
}
?>

View file

@ -0,0 +1,12 @@
<?php
include("../config.php");
if(isset($_POST["imageUrl"])) {
$query = $con->prepare("UPDATE images SET clicks = clicks + 1 WHERE imageUrl=:imageUrl");
$query->bindParam(":imageUrl", $_POST["imageUrl"]);
$query->execute();
} else {
echo "No image URL passed to page";
}
?>

View file

@ -0,0 +1,12 @@
<?php
include("../config.php");
if(isset($_POST["linkId"])) {
$query = $con->prepare("UPDATE sites SET clicks = clicks + 1 WHERE id=:id");
$query->bindParam(":id", $_POST["linkId"]);
$query->execute();
} else {
echo "No link passed to page";
}
?>

270
Yo/assets/css/style.css Normal file
View file

@ -0,0 +1,270 @@
* {
font-family: arial, sans-serif;
color: #545454;
}
html,
body {
margin: 0;
height: 100%;
}
.wrapper {
display: flex;
flex-direction: column;
min-height: 100%;
}
.wrapper.indexPage {
justify-content: center;
}
.mainSection {
display: flex;
flex-direction: column;
align-items: center;
}
.mainSection .searchContainer {
margin-top: 20px;
width: 100%;
}
.mainSection .searchContainer form {
display: flex;
flex-direction: column;
align-items: center;
}
.searchContainer .searchButton {
color: #757575;
background-color: #f5f5f5;
border: none;
height: 36px;
width: 125px;
border-radius: 1px;
font-size: 13px;
font-weight: bold;
margin-top: 20px;
cursor: pointer;
outline: none;
}
.mainSection .searchContainer .searchBox {
border: none;
box-shadow: 0 2px 2px 0 rgba(0,0,0,0.16), 0 0 0 1px rgba(0,0,0,0.08);
height: 44px;
border-radius: 2px;
outline: none;
padding: 10px;
box-sizing: border-box;
font-size: 16px;
width: 70%;
max-width: 630px;
color: #000;
}
.mainSection .logoContainer {
width: 220px;
text-align: center;
}
.logoContainer img {
width: 100%;
}
.header {
background-color: #FAFAFA;
border-bottom: 1px solid #ebebeb;
}
.wrapper .headerContent {
display: flex;
align-items: center;
}
.headerContent .logoContainer {
width: 150px;
padding: 5px 20px;
box-sizing: border-box;
}
.headerContent .searchContainer {
flex: 1;
}
.headerContent .searchContainer form {
margin: 15px 0 28px 0;
}
.headerContent .searchBarContainer {
height: 44px;
border-radius: 2px;
background-color: #fff;
box-shadow: 0 2px 2px 0 rgba(0,0,0,0.16), 0 0 0 1px rgba(0,0,0,0.08);
width: 70%;
max-width: 630px;
box-sizing: border-box;
display: flex;
}
.headerContent .searchBarContainer .searchBox {
flex: 1;
border: none;
background-color: transparent;
outline-color: #D0021B;
padding: 12px;
font-size: 16px;
color: #000;
}
.headerContent .searchBarContainer .searchButton {
background-color: #fff;
height: 44px;
margin-top: 0;
width: 44px;
padding-right: 20px;
display: flex;
justify-content: center;
}
.headerContent .searchBarContainer .searchButton img {
width: 22px
}
.tabsContainer {
margin-left: 150px;
}
.tabsContainer .tabList {
padding: 0;
margin: 0;
}
.tabsContainer .tabList li {
display: inline-block;
padding: 0 16px 12px 16px;
color: #777;
font-size: 13px;
}
.tabsContainer .tabList li a {
text-decoration: none;
}
.tabsContainer .tabList li.active {
border-bottom: 3px solid #D0021B;
}
.tabsContainer .tabList li.active a {
font-weight: bold;
color: #D0021B;
}
.mainResultsSection {
flex: 1;
}
.mainResultsSection .resultsCount {
font-size: 13px;
color: #808080;
margin-left: 150px;
}
.mainResultsSection .siteResults {
margin-left: 150px;
}
.resultContainer {
display: flex;
flex-direction: column;
margin-bottom: 26px;
}
.resultContainer .title {
margin: 0;
}
.resultContainer .title a {
color: #1a0dab;
text-decoration: none;
font-weight: normal;
font-size: 18px;
}
.resultContainer .title a:hover {
text-decoration: underline;
}
.resultContainer .url {
color: #006621;
font-size: 14px;
}
.resultContainer .description {
font-size: 12px;
}
.paginationContainer {
display: flex;
justify-content: center;
margin-bottom: 25px;
}
.pageButtons {
display: flex;
}
.pageNumberContainer img {
height: 37px;
}
.pageNumberContainer,
.pageNumberContainer a {
display: flex;
flex-direction: column;
align-items: center;
text-decoration: none;
}
.pageNumber {
color: #000;
font-size: 13px;
}
a .pageNumber {
color: #D0021B;
}
.mainResultsSection .imageResults {
margin: 20px;
}
.gridItem {
position: relative;
}
.gridItem img {
max-width: 200px;
min-width: 50px;
visibility: hidden;
}
.gridItem .details {
visibility: hidden;
position: absolute;
bottom: 0px;
left: 0px;
width: 100%;
overflow: hidden;
background-color: rgba(0,0,0,0.8);
color: #fff;
font-size: 11px;
padding: 3px;
box-sizing: border-box;
white-space: nowrap;
}
.gridItem:hover .details {
visibility: visible;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

BIN
Yo/assets/images/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

BIN
Yo/assets/images/page.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 479 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 479 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 816 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 977 B

90
Yo/assets/js/script.js Normal file
View file

@ -0,0 +1,90 @@
var timer;
$(document).ready(function() {
$(".result").on("click", function() {
var id = $(this).attr("data-linkId");
var url = $(this).attr("href");
if(!id) {
alert("data-linkId attribute not found");
}
increaseLinkClicks(id, url);
return false;
});
var grid = $(".imageResults");
grid.on("layoutComplete", function() {
$(".gridItem img").css("visibility", "visible");
});
grid.masonry({
itemSelector: ".gridItem",
columnWidth: 200,
gutter: 5,
isInitLayout: false
});
$("[data-fancybox]").fancybox({
caption : function( instance, item ) {
var caption = $(this).data('caption') || '';
var siteUrl = $(this).data('siteurl') || '';
if ( item.type === 'image' ) {
caption = (caption.length ? caption + '<br />' : '')
+ '<a href="' + item.src + '">View image</a><br>'
+ '<a href="' + siteUrl + '">Visit page</a>';
}
return caption;
},
afterShow : function( instance, item ) {
increaseImageClicks(item.src);
}
});
});
function loadImage(src, className) {
var image = $("<img>");
image.on("load", function() {
$("." + className + " a").append(image);
clearTimeout(timer);
timer = setTimeout(function() {
$(".imageResults").masonry();
}, 500);
});
image.on("error", function() {
$("." + className).remove();
$.post("ajax/setBroken.php", {src: src});
});
image.attr("src", src);
}
function increaseLinkClicks(linkId, url) {
$.post("ajax/updateLinkCount.php", {linkId: linkId})
.done(function(result) {
if(result != "") {
alert(result);
return;
}
window.location.href = url;
});
}
function increaseImageClicks(imageUrl) {
$.post("ajax/updateImageCount.php", {imageUrl: imageUrl})
.done(function(result) {
if(result != "") {
alert(result);
return;
}
});
}

View file

@ -0,0 +1,31 @@
<?php
class DomDocumentParser {
private $doc;
public function __construct($url) {
$options = array(
'http'=>array('method'=>"GET", 'header'=>"User-Agent: YoBot/0.1\n")
);
$context = stream_context_create($options);
$this->doc = new DomDocument();
@$this->doc->loadHTML(file_get_contents($url, false, $context));
}
public function getlinks() {
return $this->doc->getElementsByTagName("a");
}
public function getTitleTags() {
return $this->doc->getElementsByTagName("title");
}
public function getMetaTags() {
return $this->doc->getElementsByTagName("meta");
}
public function getImages() {
return $this->doc->getElementsByTagName("img");
}
}
?>

View file

@ -0,0 +1,79 @@
<?php
class ImageResultsProvider {
private $con;
public function __construct($con) {
$this->con = $con;
}
public function getNumResults($term) {
$query = $this->con->prepare("SELECT COUNT(*) as total
FROM images
WHERE (title LIKE :term
OR alt LIKE :term)
AND broken=0");
$searchTerm = "%". $term . "%";
$query->bindParam(":term", $searchTerm);
$query->execute();
$row = $query->fetch(PDO::FETCH_ASSOC);
return $row["total"];
}
public function getResultsHtml($page, $pageSize, $term) {
$fromLimit = ($page - 1) * $pageSize;
$query = $this->con->prepare("SELECT *
FROM images
WHERE (title LIKE :term
OR alt LIKE :term)
AND broken=0
ORDER BY clicks DESC
LIMIT :fromLimit, :pageSize");
$searchTerm = "%". $term . "%";
$query->bindParam(":term", $searchTerm);
$query->bindParam(":fromLimit", $fromLimit, PDO::PARAM_INT);
$query->bindParam(":pageSize", $pageSize, PDO::PARAM_INT);
$query->execute();
$resultsHtml = "<div class='imageResults'>";
$count = 0;
while($row = $query->fetch(PDO::FETCH_ASSOC)) {
$count++;
$id = $row["id"];
$imageUrl = $row["imageUrl"];
$siteUrl = $row["siteUrl"];
$title = $row["title"];
$alt = $row["alt"];
if($title) {
$displayText = $title;
} else if($alt) {
$displayText = $alt;
} else {
$displayText = $imageUrl;
}
$resultsHtml .= "<div class='gridItem image$count'>
<a href='$imageUrl' data-fancybox data-caption='$displayText' data-siteurl='$siteUrl'>
<script>
$(document).ready(function() {
loadImage(\"$imageUrl\",\"image$count\");
});
</script>
<span class='details'>$displayText</span>
</a>
</div>";
}
$resultsHtml .= "</div>";
return $resultsHtml;
}
}
?>

View file

@ -0,0 +1,73 @@
<?php
class SiteResultsProvider {
private $con;
public function __construct($con) {
$this->con = $con;
}
public function getNumResults($term) {
$query = $this->con->prepare("SELECT COUNT(*) as total
FROM sites WHERE title LIKE :term
OR url LIKE :term
OR keywords LIKE :term
OR description LIKE :term");
$searchTerm = "%". $term . "%";
$query->bindParam(":term", $searchTerm);
$query->execute();
$row = $query->fetch(PDO::FETCH_ASSOC);
return $row["total"];
}
public function getResultsHtml($page, $pageSize, $term) {
$fromLimit = ($page - 1) * $pageSize;
$query = $this->con->prepare("SELECT *
FROM sites WHERE title LIKE :term
OR url LIKE :term
OR keywords LIKE :term
OR description LIKE :term
ORDER BY clicks DESC
LIMIT :fromLimit, :pageSize");
$searchTerm = "%". $term . "%";
$query->bindParam(":term", $searchTerm);
$query->bindParam(":fromLimit", $fromLimit, PDO::PARAM_INT);
$query->bindParam(":pageSize", $pageSize, PDO::PARAM_INT);
$query->execute();
$resultsHtml = "<div class='siteResults'>";
while($row = $query->fetch(PDO::FETCH_ASSOC)) {
$id = $row["id"];
$url = $row["url"];
$title = $row["title"];
$description = $row["description"];
$title = $this->trimField($title, 55);
$description = $this->trimField($description, 230);
$resultsHtml .= "<div class='resultContainer'>
<h3 class='title'>
<a class='result' href='$url' data-linkId='$id'>
$title
</a>
</h3>
<span class='url'>$url</span>
<span class='description'>$description</span>
</div>";
}
$resultsHtml .= "</div>";
return $resultsHtml;
}
private function trimField($string, $characterLimit) {
$dots = strlen($string) > $characterLimit ? "..." : "";
return substr($string, 0, $characterLimit) . $dots;
}
}
?>

10
Yo/config.php Normal file
View file

@ -0,0 +1,10 @@
<?php
ob_start();
try {
$con = new PDO("mysql:dbname=yo;host=localhost", "root", "");
$con->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING);
} catch(PDOExeption $e) {
echo "Connection failed: " . $e->getMessage();
}
?>

162
Yo/crawl.php Normal file
View file

@ -0,0 +1,162 @@
<?php
include("config.php");
include("classes/DomDocumentParser.php");
$alreadyCrawled = array();
$crawling = array();
$alreadyFoundImages = array();
function linkExists($url) {
global $con;
$query = $con->prepare("SELECT * FROM sites WHERE url = :url");
$query->bindParam(":url", $url);
$query->execute();
return $query->rowCount() != 0;
}
function insertLink($url, $title, $description, $keywords) {
global $con;
$query = $con->prepare("INSERT INTO sites(url, title, description, keywords)
VALUES(:url, :title, :description, :keywords)");
$query->bindParam(":url", $url);
$query->bindParam(":title", $title);
$query->bindParam(":description", $description);
$query->bindParam(":keywords", $keywords);
return $query->execute();
}
function insertImage($url, $src, $alt, $title) {
global $con;
$query = $con->prepare("INSERT INTO images(siteUrl, imageUrl, alt, title)
VALUES(:siteUrl, :imageUrl, :alt, :title)");
$query->bindParam(":siteUrl", $url);
$query->bindParam(":imageUrl", $src);
$query->bindParam(":alt", $alt);
$query->bindParam(":title", $title);
return $query->execute();
}
function createLink($src, $url) {
$scheme = parse_url($url)["scheme"]; // http
$host = parse_url($url)["host"]; // www.wsj.com
if(substr($src, 0, 2) == "//") {
$src = $scheme . ":" . $src;
} else if(substr($src, 0, 1) == "/") {
$src = $scheme . "://" . $host . $src;
} else if(substr($src, 0, 2) == "./") {
$src = $scheme . "://" . $host . dirname(parse_url($url)["path"]) . substr($src, 1);
} else if(substr($src, 0, 3) == "../") {
$src = $scheme . "://" . $host . "/" . $src;
} else if(substr($src, 0, 5) != "https" && substr($src, 0, 4) != "http") {
$src = $scheme . "://" . $host . "/" . $src;
}
return $src;
}
function getDetails($url) {
global $alreadyFoundImages;
$parser = new DomDocumentParser($url);
$titleArray = $parser->getTitleTags();
if(sizeof($titleArray) == 0 || $titleArray->item(0) == NULL) {
return;
}
$title = $titleArray->item(0)->nodeValue;
$title = str_replace("\n", "", $title);
if($title == "") {
return;
}
$description = "";
$keywords = "";
$metasArray = $parser->getMetatags();
foreach($metasArray as $meta) {
if($meta->getAttribute("name") == "description") {
$description = $meta->getAttribute("content");
}
if($meta->getAttribute("name") == "keywords") {
$keywords = $meta->getAttribute("content");
}
}
$description = str_replace("\n", "", $description);
$keywords = str_replace("\n", "", $keywords);
if(linkExists($url)) {
echo "$url already exists<br>";
} else if(insertLink($url, $title, $description, $keywords)) {
echo "SUCCESS: $url<br>";
} else {
echo "ERROR: Failed to insert $url<br>";
}
$imageArray = $parser->getImages();
foreach($imageArray as $image) {
$src = $image->getAttribute("src");
$alt = $image->getAttribute("alt");
$title = $image->getAttribute("title");
if(!$title && !$alt) {
continue;
}
$src = createLink($src, $url);
if(!in_array($src, $alreadyFoundImages)) {
$alreadyFoundImages[] = $src;
// insert the image
echo "INSERT: " . insertImage($url, $src, $alt, $title);
}
}
}
function followLinks($url) {
global $alreadyCrawled;
global $crawling;
$parser = new DomDocumentParser($url);
$linkList = $parser->getLinks();
foreach($linkList as $link) {
$href = $link->getAttribute("href");
if(strpos($href, "#") !== false) {
continue;
} else if(substr($href, 0, 11) == "javascript:") {
continue;
}
$href = createLink($href, $url);
if(!in_array($href, $alreadyCrawled)) {
$alreadyCrawled[] = $href;
$crawling[] = $href;
getDetails($href);
}
}
array_shift($crawling);
foreach($crawling as $site) {
followLinks($site);
}
}
$startUrl = "https://www.wsj.com/";
followLinks($startUrl);
?>

36
Yo/index.php Normal file
View file

@ -0,0 +1,36 @@
<!DOCTYPE html>
<html>
<head>
<title>Yo!</title>
<meta name="description" content="Search the web for sites and images.">
<meta name="keywords" content="Search engine, yo, websites">
<meta name="author" content="Anqi Chen">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" type="text/css" href="assets/css/style.css">
</head>
<body>
<div class="wrapper indexPage">
<div class="mainSection">
<div class="logoContainer">
<img src="assets/images/logo.png" title="Logo of our site" alt="Site logo">
</div>
<div class="searchContainer">
<form action="search.php" method="GET">
<input class="searchBox" type="text" name="term" placeholder="Yo! What's up today?">
<input class="searchButton" type="submit" value="Search">
</form>
</div>
</div>
</div>
</body>
</html>

157
Yo/search.php Normal file
View file

@ -0,0 +1,157 @@
<?php
include("config.php");
include("classes/SiteResultsProvider.php");
include("classes/ImageResultsProvider.php");
if(isset($_GET["term"])) {
$term = $_GET["term"];
} else {
exit("A search term is required");
}
$type = isset($_GET["type"]) ? $_GET["type"] : "sites";
$page = isset($_GET["page"]) ? $_GET["page"] : 1;
?>
<!DOCTYPE html>
<html>
<head>
<title>Yo!What's up?</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/fancyapps/fancybox@3.5.7/dist/jquery.fancybox.min.css" />
<link rel="stylesheet" type="text/css" href="assets/css/style.css">
<script src="https://code.jquery.com/jquery-3.4.1.min.js" integrity="sha256-CSXorXvZcTkaix6Yvo6HppcZGetbYMGWSFlBw8HfCJo=" crossorigin="anonymous"></script>
</head>
<body>
<div class="wrapper">
<div class="header">
<div class="headerContent">
<div class="logoContainer">
<a href="index.php">
<img src="assets/images/logo.png">
</a>
</div>
<div class="searchContainer">
<form action="search.php" method="GET">
<div class="searchBarContainer">
<input type="hidden" name="type" value="<?php echo $type; ?>">
<input class="searchBox" type="text" name="term" value="<?php echo $term; ?>">
<button class="searchButton">
<img src="assets/images/icons/search.png">
</button>
</div>
</form>
</div>
</div>
<div class="tabsContainer">
<ul class="tabList">
<li class="<?php echo $type == 'sites' ? 'active' : '' ?>">
<a href='<?php echo "search.php?term=$term&type=sites"; ?>'>
Sites
</a>
</li>
<li class="<?php echo $type == 'images' ? 'active' : '' ?>">
<a href='<?php echo "search.php?term=$term&type=images"; ?>'>
Images
</a>
</li>
</ul>
</div>
</div>
<div class="mainResultsSection">
<?php
if($type == "sites") {
$resultsProvider = new SiteResultsProvider($con);
$pageSize = 20;
} else {
$resultsProvider = new ImageResultsProvider($con);
$pageSize = 30;
}
$numResults = $resultsProvider->getNumResults($term);
echo "<p class='resultsCount'>$numResults results found</p>";
echo $resultsProvider->getResultsHtml($page, $pageSize, $term);
?>
</div>
<div class="paginationContainer">
<div class="pageButtons">
<div class="pageNumberContainer">
<img src="assets/images/pageStart.png">
</div>
<?php
$pagesToShow = 10;
$numPages = ceil($numResults / $pageSize);
$pagesLeft = min($pagesToShow, $numPages);
$currentPage = $page - floor($pagesToShow / 2);
if($currentPage < 1) {
$currentPage = 1;
}
if($currentPage + $pagesLeft > $numPages + 1) {
$currentPage = $numPages + 1 - $pagesLeft;
}
while($pagesLeft != 0 && $currentPage <= $numPages) {
if($currentPage == $page) {
echo "<div class='pageNumberContainer'>
<img src='assets/images/pageSelected.png'>
<span class='pageNumber'>$currentPage</span>
</div>";
}
else {
echo "<div class='pageNumberContainer'>
<a href='search.php?term=$term&type=$type&page=$currentPage'>
<img src='assets/images/page.png'>
<span class='pageNumber'>$currentPage</span>
</a>
</div>";
}
$currentPage++;
$pagesLeft--;
}
?>
<div class="pageNumberContainer">
<img src="assets/images/pageEnd.png">
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/gh/fancyapps/fancybox@3.5.7/dist/jquery.fancybox.min.js"></script>
<script src="https://unpkg.com/masonry-layout@4/dist/masonry.pkgd.min.js"></script>
<script type="text/javascript" src="assets/js/script.js"></script>
</body>
</html>