Add current project

This commit is contained in:
Neil Brommer 2017-12-16 14:25:50 -08:00
parent 61df06fa4e
commit 44f29bf758
15 changed files with 2688 additions and 1 deletions

3
.gitignore vendored Normal file
View file

@ -0,0 +1,3 @@
.vscode/
lib/
info.php

View file

@ -1 +1,17 @@
SnippetManager # Snippet Manager
An instance of this site is hosted at [snippets.neilbrommer.com](https://snippets.neilbrommer.com)
A tool for keeping pieces of useful code and programming resources.
Includes a RESTful API and a web client.
There is currently no login system; all data is public.
## Libraries
[JQuery](https://jquery.com/)
[Popper.js](https://popper.js.org/)
[Bootstrap](https://getbootstrap.com/)

55
css/multiTable.css Normal file
View file

@ -0,0 +1,55 @@
.navbar {
margin-top: 20px;
margin-bottom: 20px;
}
html {
padding-bottom: 50px;
}
pre {
tab-size: 4;
-moz-tab-size: 4;
background-color: rgb(245, 245, 245);
padding: 10px;
}
.langList {
margin-bottom: 5px;
}
.addSection {
margin-top: 15px;
}
.snippetBody {
font-family: monospace;
}
.requiredField {
color: red;
}
#successAlert {
display: none;
}
.card-header {
padding-top: 7px;
padding-bottom: 7px;
}
.langSelect {
width: 100%;
}
#tablesImg {
width: 100%;
}
.resultsContainer {
white-space: pre-wrap;
background-color: rgb(245, 245, 245);
max-height: 600px;
margin-bottom: 0px;
}

139
css/prism.css Normal file
View file

@ -0,0 +1,139 @@
/* http://prismjs.com/download.html?themes=prism&languages=markup+css+clike+javascript+abap+actionscript+ada+apacheconf+apl+applescript+c+asciidoc+aspnet+autohotkey+autoit+bash+basic+batch+bison+brainfuck+bro+cpp+csharp+arduino+coffeescript+ruby+css-extras+d+dart+django+diff+docker+eiffel+elixir+erlang+fsharp+flow+fortran+gherkin+git+glsl+go+graphql+groovy+haml+handlebars+haskell+haxe+http+icon+inform7+ini+j+java+jolie+json+julia+keyman+kotlin+latex+less+livescript+lolcode+lua+makefile+markdown+matlab+mel+mizar+monkey+n4js+nasm+nginx+nim+nix+nsis+objectivec+ocaml+opencl+oz+parigp+parser+pascal+perl+php+php-extras+powershell+processing+prolog+properties+protobuf+pug+puppet+pure+python+q+qore+r+jsx+renpy+reason+rest+rip+roboconf+crystal+rust+sas+sass+scss+scala+scheme+smalltalk+smarty+sql+stylus+swift+tcl+textile+twig+typescript+vbnet+verilog+vhdl+vim+wiki+xojo+yaml */
/**
* prism.js default theme for JavaScript, CSS and HTML
* Based on dabblet (http://dabblet.com)
* @author Lea Verou
*/
code[class*="language-"],
pre[class*="language-"] {
color: black;
background: none;
text-shadow: 0 1px white;
font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
text-align: left;
white-space: pre;
word-spacing: normal;
word-break: normal;
word-wrap: normal;
line-height: 1.5;
-moz-tab-size: 4;
-o-tab-size: 4;
tab-size: 4;
-webkit-hyphens: none;
-moz-hyphens: none;
-ms-hyphens: none;
hyphens: none;
}
pre[class*="language-"]::-moz-selection, pre[class*="language-"] ::-moz-selection,
code[class*="language-"]::-moz-selection, code[class*="language-"] ::-moz-selection {
text-shadow: none;
background: #b3d4fc;
}
pre[class*="language-"]::selection, pre[class*="language-"] ::selection,
code[class*="language-"]::selection, code[class*="language-"] ::selection {
text-shadow: none;
background: #b3d4fc;
}
@media print {
code[class*="language-"],
pre[class*="language-"] {
text-shadow: none;
}
}
/* Code blocks */
pre[class*="language-"] {
padding: 1em;
margin: .5em 0;
overflow: auto;
}
:not(pre) > code[class*="language-"],
pre[class*="language-"] {
background: #f5f2f0;
}
/* Inline code */
:not(pre) > code[class*="language-"] {
padding: .1em;
border-radius: .3em;
white-space: normal;
}
.token.comment,
.token.prolog,
.token.doctype,
.token.cdata {
color: slategray;
}
.token.punctuation {
color: #999;
}
.namespace {
opacity: .7;
}
.token.property,
.token.tag,
.token.boolean,
.token.number,
.token.constant,
.token.symbol,
.token.deleted {
color: #905;
}
.token.selector,
.token.attr-name,
.token.string,
.token.char,
.token.builtin,
.token.inserted {
color: #690;
}
.token.operator,
.token.entity,
.token.url,
.language-css .token.string,
.style .token.string {
color: #a67f59;
background: hsla(0, 0%, 100%, .5);
}
.token.atrule,
.token.attr-value,
.token.keyword {
color: #07a;
}
.token.function {
color: #DD4A68;
}
.token.regex,
.token.important,
.token.variable {
color: #e90;
}
.token.important,
.token.bold {
font-weight: bold;
}
.token.italic {
font-style: italic;
}
.token.entity {
cursor: help;
}

13
dbTools.php Normal file
View file

@ -0,0 +1,13 @@
<?php
function buildPDO() {
include_once "info.php";
$dsn = "mysql:host=$host;dbname=$db;charset=$charset";
$opt = [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false,
];
return new PDO($dsn, $user, $pass, $opt);
}
?>

413
documentation.html Normal file
View file

@ -0,0 +1,413 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="A new tab page">
<meta name="author" content="Neil Brommer">
<link rel="icon" href="favicon.ico">
<title>Documentation - Multi-Table Service</title>
<!-- Bootstrap core CSS -->
<link href="lib/bootstrap/css/bootstrap.min.css" rel="stylesheet">
<!-- Custom styles -->
<link href="css/multiTable.css" rel="stylesheet">
</head>
<body>
<div id="wrapper" class="container">
<nav class="navbar rounded navbar-expand-md navbar-dark bg-dark mb-4">
<a class="navbar-brand" href="#">Snippets</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarCollapse" aria-controls="navbarCollapse"
aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarCollapse">
<ul class="navbar-nav mr-auto">
<li>
<a class="nav-link" href="index.html">Web Client</a>
</li>
<li>
<a class="nav-link active" href="documentation.html">API</a>
</li>
</ul>
<form class="form-inline mt-2 mt-md-0">
</form>
</div>
</nav>
<h1>About</h1>
<p>
This app keeps a set of code snippets and programming resources categorized by language/library.
Each snippet or resource can be associated with multiple languages and vice versa.
</p>
<p>Features I plan to add to the backend:</p>
<ul>
<li>A login system</li>
<li>Add a column for snippets to indicate the language the snippet is written in</li>
<li>Search</li>
</ul>
<p>Features I plan to add to the web client:</p>
<ul>
<li>Syntax highlighting via <a href="http://prismjs.com/" target="_blamk">Prism</a></li>
<li>When a single language is selected, show all related snippets and resources</li>
<li>Deep linking</li>
</ul>
<hr>
<h1>Tables</h1>
<p>There are three main tables:</p>
<ul>
<li>language</li>
<li>snippet</li>
<li>resource</li>
</ul>
<p>And three tables used to link these tables together:</p>
<ul>
<li>langLang</li>
<li>langSnippet</li>
<li>langResource</li>
</ul>
<img id="tablesImg" src="tables.png" class="img-fluid rounded my-3 p-2 border">
<p>Each of the three main tables has a corresponding API endpoint (off of the base URL webdev.neilbrommer.com/multiTable/):</p>
<ul>
<li>lang.php</li>
<li>snippet.php</li>
<li>resource.php</li>
</ul>
<hr>
<h1>API</h1>
<p>
Each of the endpoints is off of the base URL of <u>https://webdev.neilbrommer.com/multiTable/</u>.
SSL is required.
</p>
<p>Arguments that are <u>underlined</u> are optional.</p>
<h3 class="mt-5">lang.php</h3>
<p>For managing the list of languages.</p>
<div class="row border mb-4 rounded">
<form id="languageForm" class="py-3 col-md-4">
<p>webdev.neilbrommer.com<wbr>/multiTable<wbr>/lang.php</p>
<div class="form-group">
<select id="languageType" class="custom-select">
<option value="GET" selected>GET</option>
<option value="POST">POST</option>
<option value="PUT">PUT</option>
<option value="DELETE">DELETE</option>
</select>
</div>
<div class="form-group">
<label for="languageLangID">langID</label>
<input id="languageLangID" type="number" class="form-control">
</div>
<div class="form-group">
<label for="languageLangName">langName</label>
<input id="languageLangName" type="text" class="form-control">
</div>
<div class="form-group">
<label for="languageLangDesc">langDescription</label>
<input id="languageLangDesc" type="text" class="form-control">
</div>
<div class="form-group">
<label for="languageAssocLang">associatedLang</label>
<input id="languageAssocLang" type="number" class="form-control">
</div>
<button id="languageSubmit" type="submit" class="btn btn-primary">Submit</button>
</form>
<pre class="col-md-8 p-1 resultsContainer"><code id="languageResults">Results will appear here</code></pre>
</div>
<dl>
<dt>GET</dt>
<dd>
<p>
Each of these returns a JSON object with <code>langID</code>, <code>langName</code>, <code>langDescription</code>,
a list of related <code>languages</code>, a list of <code>snippets</code>, and a list of <code>resources</code>.
</p>
<ul>
<li>
No arguments: returns all language entries.
</li>
<li>
<code>langID</code> (number): returns the specified language.
</li>
</ul>
</dd>
<dt>POST</dt>
<p>Returns the <code>langID</code> of the new entry.</p>
<dd>
<ul>
<li>
<code>langName</code> (string), <u><code>langDescription</code></u> (string), <u><code>associatedLang</code></u>
(number): Creates a new language with the specified information. <code>associatedLang</code> is the
<code>langID</code> of the language you want to link to the one being created. More languages can be linked later
via <code>PUT</code>.
</li>
</ul>
</dd>
<dt>PUT</dt>
<p>Returns no data.</p>
<dd>
<ul>
<li>
<code>langID</code> (number), <u><code>langName</code></u> (string), <u><code>langDescription</code></u>
(string), <u><code>associatedLang</code></u> (number): Modifies any provided fields for the language
specified by the <code>langID</code>.
</li>
</ul>
</dd>
<dt>DELETE</dt>
<p>Returns no data.</p>
<dd>
<ul>
<li>
<code>langID</code> (number): Deletes the specified language entry, including all connections to snippets,
resources, and other languages.
</li>
<li>
<code>langID</code> (number), <code>associatedLang</code> (number): Removes the connection between the two
specified languages.
</li>
</ul>
</dd>
</dl>
<h3 class="mt-5">snippet.php</h3>
<p>For managing the list of snippets.</p>
<div class="row border mb-4 rounded">
<form id="snippetForm" class="py-3 col-md-4">
<p class="wrapping">webdev.neilbrommer.com<wbr>/multiTable<wbr>/snippet.php</p>
<div class="form-group">
<select id="snippetType" class="custom-select">
<option value="GET" selected>GET</option>
<option value="POST">POST</option>
<option value="PUT">PUT</option>
<option value="DELETE">DELETE</option>
</select>
</div>
<div class="form-group">
<label for="snippetSnippetID">snippetID</label>
<input id="snippetSnippetID" type="number" class="form-control">
</div>
<div class="form-group">
<label for="snippetSnippetName">snippetName</label>
<input id="snippetSnippetName" type="text" class="form-control">
</div>
<div class="form-group">
<label for="snippetSnippetDesc">snippetDescription</label>
<input id="snippetSnippetDesc" type="text" class="form-control">
</div>
<div class="form-group">
<label for="snippetSnippetBody">snippet</label>
<textarea id="snippetSnippetBody" class="form-control"></textarea>
</div>
<div class="form-group">
<label for="snippetLangID">langID</label>
<input id="snippetLangID" type="number" class="form-control">
</div>
<button id="snippetSubmit" type="submit" class="btn btn-primary">Submit</button>
</form>
<pre class="col-md-8 p-1 resultsContainer"><code id="snippetResults">Results will appear here</code></pre>
</div>
<dl>
<dt>GET</dt>
<dd>
<p>
Each of these returns a JSON object with <code>snippetID</code>, <code>snippetName</code>,
<code>snippetDescription</code>, <code>snippet</code>, and a list of related <code>languages</code>.
</p>
<ul>
<li>
No arguments: Returns a list of all snippets.
</li>
<li>
<code>snippetID</code> (number): Returns the entry for the specified language.
</li>
<li>
<code>langID</code> (number): Returns a list of snippets related to the given language.
</li>
</ul>
</dd>
<dt>POST</dt>
<dd>
<p>Returns the <code>snippetID</code> of the new snippet.</p>
<ul>
<li>
<code>snippetName</code> (string), <u><code>langID</code></u> (number),
<u><code>snippetDescription</code></u> (string), <u><code>snippet</code></u> (string):
Creates a new snippet with the given values.
Only one language can be associated with a snippet at creation. More can be added later via
<code>PUT</code>.
</li>
</ul>
</dd>
<dt>PUT</dt>
<dd>
<p>Returns nothing</p>
<ul>
<li>
<code>snippetID</code> (number), <u><code>langID</code></u> (number),
<u><code>snippetName</code></u> (string), <u><code>snippetDescription</code></u> (string),
<u><code>snippet</code></u> (string): Modifies the specified information.
</li>
</ul>
</dd>
<dt>DELETE</dt>
<dd>
<p>Returns nothing</p>
<ul>
<li>
<code>snippetID</code> (number): Deletes the entry for the specified snippet.
</li>
<li>
<code>snippetID</code> (number), <code>langID</code> (number): Removes the connection between
the specified snippet and language.
</li>
</ul>
</dd>
</dl>
<h3 class="mt-5">resource.php</h3>
<p>For managing the list of resources.</p>
<p>
The API for resource.php is nearly identical to the one for snippet.php with <code>resourceLink</code>
in place of <code>snippet</code>.
</p>
<div class="row border mb-4 rounded">
<form id="resourceForm" class="py-3 col-md-4">
<p class="wrapping">webdev.neilbrommer.com<wbr>/multiTable<wbr>/resource.php</p>
<div class="form-group">
<select id="resourceType" class="custom-select">
<option value="GET" selected>GET</option>
<option value="POST">POST</option>
<option value="PUT">PUT</option>
<option value="DELETE">DELETE</option>
</select>
</div>
<div class="form-group">
<label for="resourceResourceID">resourceID</label>
<input id="resourceResourceID" type="number" class="form-control">
</div>
<div class="form-group">
<label for="resourceResourceName">resourceName</label>
<input id="resourceResourceName" type="text" class="form-control">
</div>
<div class="form-group">
<label for="resourceResourceDesc">resourceDescription</label>
<input id="resourceResourceDesc" type="text" class="form-control">
</div>
<div class="form-group">
<label for="resourceResourceLink">resourceLink</label>
<input id="resourceResourceLink" type="url" class="form-control">
</div>
<div class="form-group">
<label for="resourceLangID">langID</label>
<input id="resourceLangID" type="number" class="form-control">
</div>
<button id="resourceSubmit" type="submit" class="btn btn-primary">Submit</button>
</form>
<pre class="col-md-8 p-1 resultsContainer"><code id="resourceResults">Results will appear here</code></pre>
</div>
<dl>
<dt>GET</dt>
<dd>
<p>
Returns JSON encoded resources with a <code>resourceID</code>, a <code>resourceName</code>, a
<code>resourceDescription</code>, a <code>resourceLink</code>, and a list of related
<code>languages</code>.
</p>
<ul>
<li>
No arguments: Returns a list of all resource entries.
</li>
<li>
<code>langID</code> (number): Returns a list of resources related to the specified language.
</li>
<li>
<code>resourceID</code> (number): Returns the specified resource entry.
</li>
</ul>
</dd>
<dt>POST</dt>
<dd>
<p>
Returns the <code>resourceID</code> of the newly created resource.
</p>
<ul>
<li>
<code>resourceName</code> (string), <u><code>langID</code></u> (number),
<u><code>resourceDescription</code></u> (string), and <u><code>resourceLink</code></u> (string):
Creates a resource with the given information.
Only one language can be associated at creation. More can be added via <code>PUT</code>.
</li>
</ul>
</dd>
<dt>PUT</dt>
<dd>
<p>Returns nothing.</p>
<ul>
<li>
<code>resourceID</code> (number), <u><code>resourceName</code></u> (string),
<u><code>langID</code></u> (number), <u><code>resourceDescription</code></u> (string), and
<u><code>resourceLink</code></u> (string): Modifies the specified data for the specified
resource.
</li>
</ul>
</dd>
<dt>DELETE</dt>
<dd>
<p>Returns nothing.</p>
<ul>
<li>
<code>resourceID</code> (number): Deletes the specified resource.
</li>
<li>
<code>resourceID</code> (number), <code>langID</code> (number): Removes the connection between
the specified resource and language.
</li>
</ul>
</dd>
</dl>
</div>
<!-- Bootstrap core JavaScript
================================================== -->
<!-- Placed at the end of the document so the pages load faster -->
<script src="lib/jquery/jquery-3.2.1.min.js"></script>
<script src="lib/popper/popper.js"></script>
<script src="lib/bootstrap/js/bootstrap.min.js"></script>
<!-- custom JavaScript -->
<script src="js/docs.js"></script>
</body>

BIN
favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

362
index.html Normal file
View file

@ -0,0 +1,362 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="A new tab page">
<meta name="author" content="Neil Brommer">
<link rel="icon" href="favicon.ico">
<title>Multi-Table Service</title>
<!-- Bootstrap core CSS -->
<link href="lib/bootstrap/css/bootstrap.min.css" rel="stylesheet">
<!-- Custom styles -->
<link href="css/multiTable.css" rel="stylesheet">
</head>
<body>
<div id="wrapper" class="container">
<nav class="navbar rounded navbar-expand-md navbar-dark bg-dark mb-4">
<a class="navbar-brand" href="#">Snippets</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarCollapse" aria-controls="navbarCollapse"
aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarCollapse">
<ul class="navbar-nav mr-auto">
<li class="nav-item">
<a id="langLink" class="nav-link active" href="#">Languages</a>
</li>
<li>
<a id="snippetLink" class="nav-link" href="#">Snippets</a>
</li>
<li>
<a id="resourceLink" class="nav-link" href="#">Resources</a>
</li>
<li>
<a class="nav-link" href="documentation.html">API</a>
</li>
</ul>
<form class="form-inline mt-2 mt-md-0">
<button class="btn btn-primary ml-sm-2" type="button" data-toggle="modal" data-target="#addModal">Add</button>
</form>
</div>
</nav>
<div id="successAlert" class="alert alert-success"></div>
<div id="mainList">
</div>
</div>
<div id="addModal" class="modal fade" tabindex="-1" role="dialog">
<div class="modal-dialog modal-lg" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Add Item</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<ul class="nav nav-tabs" id="tabContent">
<li class="nav-item">
<a id="addLangTab" class="nav-link active" href="#addLang" data-toggle="tab">Language</a>
</li>
<li class="nav-item">
<a id="addSnippetTab" class="nav-link" href="#addSnippet" data-toggle="tab">Snippet</a>
</li>
<li class="nav-item">
<a id="addResourceTab" class="nav-link" href="#addResource" data-toggle="tab">Resource</a>
</li>
</ul>
<div class="tab-content">
<div id="addLang" class="addSection tab-pane fade show active">
<form id="formAddLang">
<div class="form-group">
<label for="txtLangName">Name</label>
<input id="txtLangName" type="text" class="form-control addField" required>
</div>
<div class="form-group">
<label for="selectLangLangs">Languages</label>
<button type="button" class="btn btn-info btn-sm float-right clearLangSelect">Clear</button>
<select id="selectLangLangs" class="custom-select langSelect" multiple size="9"></select>
<small class="form-text text-muted">Hold Ctrl to select multiple</small>
</div>
<div class="form-group">
<label for="txtLangDesc">Description</label>
<textarea id="txtLangDesc" class="form-control addField"></textarea>
</div>
</form>
</div>
<div id="addSnippet" class="addSection tab-pane fade">
<form id="formAddSnippet">
<div class="form-group">
<label for="txtSnippetName">Name</label>
<input id="txtSnippetName" type="text" class="form-control addField" required>
</div>
<div class="form-group">
<label for="selectSnippetLangs">Languages</label>
<button type="button" class="btn btn-info btn-sm float-right clearLangSelect">Clear</button>
<select id="selectSnippetLangs" class="custom-select langSelect" multiple size="9"></select>
<small class="form-text text-muted">Hold Ctrl to select multiple</small>
</div>
<div class="form-group">
<label for="txtSnippetDesc">Description</label>
<textarea id="txtSnippetDesc" class="form-control addField"></textarea>
</div>
<div class="form-group">
<label for="txtSnippetBody">Snippet</label>
<textarea id="txtSnippetBody" class="form-control addField snippetBody" rows="10" wrap="off"></textarea>
</div>
</form>
</div>
<div id="addResource" class="addSection tab-pane fade">
<form id="formAddResource">
<div class="form-group">
<label for="txtResourceName">Name</label>
<input id="txtResourceName" type="text" class="form-control addField" required>
</div>
<div class="form-group">
<label for="selectResourceLangs">Languages</label>
<button type="button" class="btn btn-info btn-sm float-right clearLangSelect">Clear</button>
<select id="selectResourceLangs" class="custom-select langSelect" multiple size="9"></select>
<small class="form-text text-muted">Hold Ctrl to select multiple</small>
</div>
<div class="form-group">
<label for="txtResourceDesc">Description</label>
<textarea id="txtResourceDesc" class="form-control addField"></textarea>
</div>
<div class="form-group">
<label for="txtResourceLink">Link</label>
<input id="txtResourceLink" type="url" class="form-control addField">
</div>
</form>
</div>
</div>
</div>
<div class="modal-footer">
<button id="btnAddItem" type="button" class="btn btn-primary">Add</button>
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
<div id="editLangModal" class="modal fade" tabindex="-1" role="dialog">
<div class="modal-dialog modal-lg" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Edit Language</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<form id="formEditLang">
<input id="editLangID" type="hidden">
<div class="form-group">
<label class="custom-control custom-checkbox">
<input type="checkbox" id="editLangNameChk" class="custom-control-input editChk" data-for="#txtEditLangName">
<span class="custom-control-indicator"></span>
<span class="custom-control-description">Name</span>
</label>
<input id="txtEditLangName" type="text" class="form-control editField" disabled>
</div>
<div class="form-group">
<label class="custom-control custom-checkbox">
<input type="checkbox" id="editLangLangsChk" class="custom-control-input editChk" data-for="#selectEditLangLangs">
<span class="custom-control-indicator"></span>
<span class="custom-control-description">Languages</span>
</label>
<button type="button" class="btn btn-info btn-sm float-right clearLangSelect">Clear</button>
<select id="selectEditLangLangs" class="custom-select editField langSelect" multiple size="9" disabled></select>
<small class="form-text text-muted">Hold Ctrl to select multiple</small>
</div>
<div class="form-group">
<label class="custom-control custom-checkbox">
<input type="checkbox" id="editLangDescChk" class="custom-control-input editChk" data-for="#txtEditLangDesc">
<span class="custom-control-indicator"></span>
<span class="custom-control-description">Description</span>
</label>
<textarea id="txtEditLangDesc" class="form-control editField" disabled></textarea>
</div>
</form>
</div>
<div class="modal-footer">
<button id="btnSaveLang" type="button" class="btn btn-primary">Save</button>
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
<div id="editSnippetModal" class="modal fade" tabindex="-1" role="dialog">
<div class="modal-dialog modal-lg" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Edit Snippet</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<form id="formEditSnippet">
<input id="editSnippetID" type="hidden">
<div class="form-group">
<label class="custom-control custom-checkbox">
<input type="checkbox" id="editSnippetNameChk" class="custom-control-input editChk" data-for="#txtEditSnippetName">
<span class="custom-control-indicator"></span>
<span class="custom-control-description">Name</span>
</label>
<input id="txtEditSnippetName" type="text" class="form-control editField" disabled>
</div>
<div class="form-group">
<label class="custom-control custom-checkbox">
<input type="checkbox" id="editSnippetLangsChk" class="custom-control-input editChk" data-for="#selectEditSnippetLangs">
<span class="custom-control-indicator"></span>
<span class="custom-control-description">Languages</span>
</label>
<button type="button" class="btn btn-info btn-sm float-right clearLangSelect">Clear</button>
<select id="selectEditSnippetLangs" class="custom-select editField langSelect" multiple size="9" disabled></select>
<small class="form-text text-muted">Hold Ctrl to select multiple</small>
</div>
<div class="form-group">
<label class="custom-control custom-checkbox">
<input type="checkbox" id="editSnippetDescChk" class="custom-control-input editChk" data-for="#txtEditSnippetDesc">
<span class="custom-control-indicator"></span>
<span class="custom-control-description">Description</span>
</label>
<textarea id="txtEditSnippetDesc" class="form-control editField" disabled></textarea>
</div>
<div class="form-group">
<label class="custom-control custom-checkbox">
<input type="checkbox" id="editSnippetBodyChk" class="custom-control-input editChk" data-for="#txtEditSnippetBody">
<span class="custom-control-indicator"></span>
<span class="custom-control-description">Snippet</span>
</label>
<textarea id="txtEditSnippetBody" class="form-control snippetBody editField" rows="10" wrap="off" disabled></textarea>
</div>
</form>
</div>
<div class="modal-footer">
<button id="btnSaveSnippet" type="button" class="btn btn-primary">Save</button>
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
<div id="editResourceModal" class="modal fade" tabindex="-1" role="dialog">
<div class="modal-dialog modal-lg" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Edit Resource</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<form id="formEditResource">
<input id="editResourceID" type="hidden">
<div class="form-group">
<label class="custom-control custom-checkbox">
<input type="checkbox" id="editResourceNameChk" class="custom-control-input editChk" data-for="#txtEditResourceName">
<span class="custom-control-indicator"></span>
<span class="custom-control-description">Name</span>
</label>
<input id="txtEditResourceName" type="text" class="form-control editField" disabled>
</div>
<div class="form-group">
<label class="custom-control custom-checkbox">
<input type="checkbox" id="editResourceLangsChk" class="custom-control-input editChk" data-for="#selectEditResourceLangs">
<span class="custom-control-indicator"></span>
<span class="custom-control-description">Languages</span>
</label>
<button type="button" class="btn btn-info btn-sm float-right clearLangSelect">Clear</button>
<select id="selectEditResourceLangs" class="custom-select editField langSelect" multiple size="9" disabled></select>
<small class="form-text text-muted">Hold Ctrl to select multiple</small>
</div>
<div class="form-group">
<label class="custom-control custom-checkbox">
<input type="checkbox" id="editResourceDescChk" class="custom-control-input editChk" data-for="#txtEditResourceDesc">
<span class="custom-control-indicator"></span>
<span class="custom-control-description">Description</span>
</label>
<textarea id="txtEditResourceDesc" class="form-control editField" disabled></textarea>
</div>
<div class="form-group">
<label class="custom-control custom-checkbox">
<input type="checkbox" id="editResourceLinkChk" class="custom-control-input editChk" data-for="#txtEditResourceLink">
<span class="custom-control-indicator"></span>
<span class="custom-control-description">Link</span>
</label>
<input id="txtEditResourceLink" type="url" class="form-control editField" disabled>
</div>
</form>
</div>
<div class="modal-footer">
<button id="btnSaveResource" type="button" class="btn btn-primary">Save</button>
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
<div id="confirmDeleteModal" class="modal fade" tabindex="-1" role="dialog">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Confirm Delete</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<p id="deleteText">Are you sure?</p>
</div>
<div class="modal-footer">
<button id="btnConfirmDelete" type="button" class="btn btn-danger">Delete</button>
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
<div id="errorModal" class="modal fade" tabindex="-1" role="dialog">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header text-white bg-danger">
<h5 class="modal-title">Error</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<p id="errorText">ERROR</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
<!-- Bootstrap core JavaScript
================================================== -->
<!-- Placed at the end of the document so the pages load faster -->
<script src="lib/jquery/jquery-3.2.1.min.js"></script>
<script src="lib/popper/popper.js"></script>
<script src="lib/bootstrap/js/bootstrap.min.js"></script>
<script src="js/main.js"></script>
</body>

174
js/docs.js Normal file
View file

@ -0,0 +1,174 @@
$(document).ready(function () {
$("#languageForm").submit(languageSubmit);
$("#snippetForm").submit(snippetSubmit);
$("#resourceForm").submit(resourceSubmit);
});
function languageSubmit(e) {
e.preventDefault();
var type = $("#languageType").val();
var data = {};
if (type == "GET" || type == "DELETE") {
var langID = $("#languageLangID").val();
var assoc = $("#languageAssocLang").val();
if (langID != "")
data['langID'] = langID;
if (assoc != "")
data['associatedLang'] = assoc;
} else if (type == "POST") {
var langName = $("#languageLangName").val();
var langDesc = $("#languageLangDesc").val();
var assoc = $("#languageAssocLang").val();
if (langName != "")
data['langName'] = langName;
if (langDesc != "")
data['langDescription'] = langDesc;
if (assoc != "")
data['associatedLang'] = assoc;
} else { // PUT
var langID = $("#languageLangID").val();
var langName = $("#languageLangName").val();
var langDesc = $("#languageLangDesc").val();
var assoc = $("#languageAssocLang").val();
if (langID != "")
data['langID'] = langID;
if (langName != "")
data['langName'] = langName;
if (langDesc != "")
data['langDescription'] = langDesc;
if (assoc != "")
data['associatedLang'] = assoc;
}
makeRequest(type, "lang.php", data, $("#languageResults"));
}
function snippetSubmit(e) {
e.preventDefault();
var type = $("#snippetType").val();
var data = {};
if (type == "GET" || type == "DELETE") {
var snippetID = $("#snippetSnippetID").val();
var assoc = $("#snippetLangID").val();
if (snippetID != "")
data['snippetID'] = snippetID;
if (assoc != "")
data['langID'] = assoc;
} else if (type == "POST") {
var snippetName = $("#snippetSnippetName").val();
var snippetDesc = $("#snippetSnippetDesc").val();
var body = $("#snippetSnippetBody").val();
var assoc = $("#snippetLangID").val();
if (snippetName != "")
data['snippetName'] = snippetName;
if (snippetDesc != "")
data['snippetDescription'] = snippetDesc;
if (body != "")
data['snippet'] = body;
if (assoc != "")
data['langID'] = assoc;
} else { // PUT
var snippetID = $("#snippetSnippetID").val();
var snippetName = $("#snippetSnippetName").val();
var snippetDesc = $("#snippetSnippetDesc").val();
var body = $("#snippetSnippetBody").val();
var assoc = $("#snippetLangID").val();
if (snippetID != "")
data['snippetID'] = snippetID;
if (snippetName != "")
data['snippetName'] = snippetName;
if (snippetDesc != "")
data['snippetDescription'] = snippetDesc;
if (body != "")
data['snippet'] = body;
if (assoc != "")
data['langID'] = assoc;
}
makeRequest(type, "snippet.php", data, $("#snippetResults"));
}
function resourceSubmit(e) {
e.preventDefault();
var type = $("#resourceType").val();
var data = {};
if (type == "GET" || type == "DELETE") {
var resourceID = $("#resourceResourceID").val();
var assoc = $("#resourceLangID").val();
if (resourceID != "")
data['resourceID'] = resourceID;
if (assoc != "")
data['langID'] = assoc;
} else if (type == "POST") {
var resourceName = $("#resourceResourceName").val();
var resourceDesc = $("#resourceResourceDesc").val();
var link = $("#resourceResourceLink").val();
var assoc = $("#resourceLangID").val();
if (resourceName != "")
data['resourceName'] = resourceName;
if (resourceDesc != "")
data['resourceDescription'] = resourceDesc;
if (link != "")
data['resourceLink'] = link;
if (assoc != "")
data['langID'] = assoc;
} else { // PUT
var resourceID = $("#resourceResourceID").val();
var resourceName = $("#resourceResourceName").val();
var resourceDesc = $("#resourceResourceDesc").val();
var link = $("#resourceResourceLink").val();
var assoc = $("#resourceLangID").val();
if (resourceID != "")
data['resourceID'] = resourceID;
if (resourceName != "")
data['resourceName'] = resourceName;
if (resourceDesc != "")
data['resourceDescription'] = resourceDesc;
if (link != "")
data['resourceLink'] = link;
if (assoc != "")
data['langID'] = assoc;
}
makeRequest(type, "resource.php", data, $("#resourceResults"));
}
function makeRequest(type, endpoint, data, resultsSection) {
$.ajax({
url: endpoint,
type: type,
data: data,
success: function (res, status, xhr) { insertSuccess(res, xhr, resultsSection); },
error: function (xhr, status, err) { insertError(xhr, resultsSection); }
});
}
function insertSuccess(results, xhr, resultsSection) {
var str = xhr.getAllResponseHeaders() + "\r\nStatus Code: " + xhr.status + " " + xhr.statusText;
if (results != null) {
if (typeof results === 'string' || results instanceof String)
str += "\r\n\r\n" + results;
else
str += "\r\n\r\n" + JSON.stringify(results, null, 4);
}
resultsSection.text(str);
}
function insertError(xhr, resultsSection) {
var str = xhr.getAllResponseHeaders() + "\r\nStatus Code: " + xhr.status + " " + xhr.statusText;
if (xhr.responseText != null)
str += "\r\n\r\n" + xhr.responseText;
resultsSection.text(str);
}

739
js/main.js Normal file
View file

@ -0,0 +1,739 @@
var selectList = [];
$(document).ready(function () {
loadLangs();
$("#langLink").click(function (evt) {
evt.preventDefault();
loadLangs();
});
$("#snippetLink").click(function (evt) {
evt.preventDefault();
loadSnippets();
});
$("#resourceLink").click(function (evt) {
evt.preventDefault();
loadResources();
});
$("#addModal").on("show.bs.modal", function (e) {
$("#addLangTab").tab("show");
$(".addField").val("");
buildLangSelect();
});
$("#editLangModal").on("shown.bs.modal", function (e) { $(".langSelect").val(selectList); });
$("#editSnippetModal").on("shown.bs.modal", function (e) { $(".langSelect").val(selectList); });
$("#editResourceModal").on("shown.bs.modal", function (e) { $(".langSelect").val(selectList); });
$("#addModal").on("shown.bs.modal", function (e) { $("#txtLangName").focus(); });
$("#addLangTab").on("shown.bs.tab", function (e) { $("#txtLangName").focus(); });
$("#addSnippetTab").on("shown.bs.tab", function (e) { $("#txtSnippetName").focus(); });
$("#addResourceTab").on("shown.bs.tab", function (e) { $("#txtResourceName").focus(); });
$("#btnAddItem").click(addNewItem);
$("#btnSaveLang").click(saveLang);
$("#btnSaveSnippet").click(saveSnippet);
$("#btnSaveResource").click(saveResource);
$(".clearLangSelect").click(function () { $(".langSelect").val([]); });
$(".editChk").change(checkBoxChange);
});
function buildLangSelect() {
$.ajax({
url: "lang.php",
type: "GET",
success: addSelectLangs,
error: displayError
});
}
function addSelectLangs(langList) {
var selectors = $(".langSelect");
selectors.empty();
for (var i = 0; i < langList.length; i++) {
var cur = langList[i];
$("<option>").val(cur.langID).text(cur.langName).appendTo(selectors);
}
}
function checkBoxChange() {
var me = $(this);
$(me.data("for")).attr("disabled", !me.prop("checked"));
}
function addNewItem() {
if ($("#addLangTab").hasClass("active")) {
if ($("#formAddLang")[0].checkValidity()) {
var data = {};
data['langName'] = $("#txtLangName").val().trim();
if ($("#txtLangDesc").val().trim() != "") {
data['langDescription'] = $("#txtLangDesc").val().trim();
}
var langs = $("#selectLangLangs").val();
$.ajax({
url: "lang.php",
type: "POST",
data: data,
success: function (res) { addLangLangs(res, data['langName'], langs); },
error: displayError
});
}
} else if ($("#addSnippetTab").hasClass("active")) {
if ($("#formAddSnippet")[0].checkValidity()) {
var data = {};
data['snippetName'] = $("#txtSnippetName").val().trim();
if ($("#txtSnippetDesc").val().trim() != "") {
data['snippetDescription'] = $("#txtSnippetDesc").val().trim();
}
if ($("#txtSnippetBody").val().trim() != "") {
data['snippet'] = $("#txtSnippetBody").val().trim();
}
var langs = $("#selectSnippetLangs").val();
$.ajax({
url: "snippet.php",
type: "POST",
data: data,
success: function (res) { addSnippetLangs(res, data['snippetName'], langs) },
error: displayError
});
}
} else {
if ($("#formAddResource")[0].checkValidity()) {
var data = {};
data['resourceName'] = $("#txtResourceName").val().trim();
if ($("#txtResourceDesc").val().trim() != "") {
data['resourceDescription'] = $("#txtResourceDesc").val().trim();
}
if ($("#txtResourceLink").val().trim() != "") {
data['resourceLink'] = $("#txtResourceLink").val().trim();
}
var langs = $("#selectResourceLangs").val();
$.ajax({
url: "resource.php",
type: "POST",
data: data,
success: function (res) { addResourceLangs(res, data['resourceName'], langs); },
error: displayError
});
}
}
}
function addLangLangs(langID, langName, langIDList) {
if (langIDList != null) {
for (var i = 0; i < langIDList.length; i++) {
$.ajax({
url: "lang.php",
type: "PUT",
data: {langID: langID, associatedLang: langIDList[i]},
error: displayError
});
}
}
successAlert("Successfully Added language: " + langName, "lang");
}
function addSnippetLangs(snippetID, snippetName, langIDList) {
if (langIDList != null) {
for (var i = 0; i < langIDList.length; i++) {
$.ajax({
url: "snippet.php",
type: "PUT",
data: {snippetID: snippetID, langID: langIDList[i]},
error: displayError
});
}
}
successAlert("Successfully Added snippet: " + snippetName, "snippet");
}
function addResourceLangs(resourceID, resourceName, langIDList) {
if (langIDList != null) {
for (var i = 0; i < langIDList.length; i++) {
$.ajax({
url: "resource.php",
type: "PUT",
data: {resourceID: resourceID, langID: langIDList[i]},
error: displayError
});
}
}
successAlert("Successfully Added resource: " + resourceName, "resource");
}
function successAlert(text, type) {
var alert = $("#successAlert");
if (alert.attr("display") == "none") {
alert.slideUp();
}
alert.text(text).slideDown().delay(5000).slideUp();
if (type.toLowerCase() == "lang")
loadLangs();
else if (type.toLowerCase() == "snippet")
loadSnippets();
else if (type.toLowerCase() == "resource")
loadResources();
$(".modal").modal('hide');
}
function loadResources() {
$("#langLink").removeClass("active");
$("#snippetLink").removeClass("active");
$("#resourceLink").addClass("active");
$("#mainList").empty();
$.ajax({
url: "resource.php",
type: "GET",
success: addResourceList,
error: displayError
});
}
function addResourceList(resourceList) {
var mainList = $("#mainList");
for (var i = 0; i < resourceList.length; i++) {
$("<div>").attr({ "id": "resource-" + resourceList[i].resourceID }).appendTo(mainList);
$.ajax({
url: "resource.php?resourceID=" + resourceList[i].resourceID,
type: "GET",
success: addResource,
error: displayError
});
}
}
function addResource(resource) {
var link = $('<a>').attr({
"id": "resource-" + resource.resourceID + "-link",
"href": resource.resourceLink,
"target": "_blank"
}).text(resource.resourceLink);
buildCard("resource", resource.resourceID, resource.resourceName, resource.languages, resource.resourceDescription, link);
}
function showResource(resourceID) {
deselectNav();
$("#mainList").empty();
$("<div>").attr("id", "resource-" + resourceID).appendTo($("#mainList"));
$.ajax({
url: "resource.php?resourceID=" + resourceID,
type: "GET",
success: function (result) { addResource(result); },
error: function (result) { displayError(result); }
});
}
function loadSnippets() {
$("#langLink").removeClass("active");
$("#snippetLink").addClass("active");
$("#resourceLink").removeClass("active");
$("#mainList").empty();
$.ajax({
url: "snippet.php",
type: "GET",
success: function (result) { addSnippets(result) },
error: function (result) { displayError(result) }
});
}
function addSnippets(snippetList) {
var mainList = $("#mainList");
for (var i = 0; i < snippetList.length; i++) {
// add thesse so that everything is loaded in order regardless of async
$("<div>").attr({ "id": "snippet-" + snippetList[i].snippetID }).appendTo(mainList);
$.ajax({
url: "snippet.php?snippetID=" + snippetList[i].snippetID,
type: "GET",
success: function (result) { addSnippet(result) },
error: function (result) { displayError(result) }
});
}
}
function addSnippet(snippet) {
var code = $('<pre><code>').attr("id", "snippet-" + snippet.snippetID + "-body").text(snippet.snippet);
buildCard("snippet", snippet.snippetID, snippet.snippetName, snippet.languages, snippet.snippetDescription, code);
}
function showSnippet(snippetID) {
deselectNav();
$("#mainList").empty();
$("<div>").attr("id", "snippet-" + snippetID).appendTo($("#mainList"));
$.ajax({
url: "snippet.php?snippetID=" + snippetID,
type: "GET",
success: function (result) { addSnippet(result); },
error: function (result) { displayError(result); }
});
}
function showLang(langID) {
deselectNav();
$("#mainList").empty();
$("<div>").attr("id", "lang-" + langID).appendTo($("#mainList"));
$.ajax({
url: "lang.php?langID=" + langID,
type: "GET",
success: function (result) { addLang(result) },
error: function (result) { displayError(result) }
});
}
function loadLangs() {
$("#langLink").addClass("active");
$("#snippetLink").removeClass("active");
$("#resourceLink").removeClass("active");
$("#mainList").empty();
$.ajax({
url: "lang.php",
type: "GET",
success: function (result) { addLangs(result) },
error: function (result) { displayError(result) }
});
}
function addLangs(langList) {
var mainList = $("#mainList");
for (var i = 0; i < langList.length; i++) {
$("<div>").attr({ "id": "lang-" + langList[i].langID }).appendTo(mainList);
$.ajax({
url: "lang.php?langID=" + langList[i].langID,
type: "GET",
success: function (result) { addLang(result) },
error: function (result) { displayError(result) }
});
}
}
function addLang(lang) {
var body = $('<div>');
// build list of snippet links
if (lang.snippets.length != 0) {
$('<hr>').appendTo(body);
$('<span>').addClass("card-text text-muted mr-3").text("Snippets:").appendTo(body);
var snippets = lang.snippets;
for (var i = 0; i < snippets.length; i++) {
var cur = snippets[i];
var snippetID = cur.snippetID;
$("<a>").attr({
"id": "snippet-" + snippetID,
"class": "card-link",
"href": "#",
"data-snippetID": snippetID,
"onClick": "handleSnippetClicked(event, " + snippetID + ")"
}).text(cur.snippetName).appendTo(body);
}
}
//build list of resource links
if (lang.resources.length != 0) {
$('<hr>').appendTo(body);
$('<span>').addClass("card-text text-muted mr-3").text("Resources:").appendTo(body);
var resources = lang.resources;
for (var i = 0; i < resources.length; i++) {
var cur = resources[i];
var resourceID = cur.resourceID;
$("<a>").attr({
"id": "resource-" + resourceID,
"class": "card-link",
"href": "#",
"data-resourceID": resourceID,
"onclick": "handleResourceClicked(event, " + resourceID + ")"
}).text(cur.resourceName).appendTo(body);
}
}
buildCard("lang", lang.langID, lang.langName, lang.languages, lang.langDescription, body);
}
function buildCard(type, id, name, languageList, DescriptionText, body) {
var card = $('<div>').addClass("card mb-3").attr("id", type + "-" + id + "-card");
var header = $('<div>').addClass("card-header").appendTo(card);
$('<h3>').attr("id", type + "-" + id + "-name").addClass("d-inline").text(name).appendTo(header);
$('<span>').attr("id", type + "-" + id + "-id").addClass("text-muted ml-3").text("#" + id).appendTo(header);
$("<button>").attr({
"type": "button",
"class": "btn btn-danger float-right",
"data-id": id,
"data-type": type
}).text("Delete").click(deleteClicked).appendTo(header);
$("<button>").attr({
"type": "button",
"class": "btn btn-info float-right mr-2",
"data-id": id,
"data-type": type
}).text("Edit").click(editClicked).appendTo(header);
var cardBody = $('<div>').addClass("card-body").appendTo(card);
var langList = $("<div>").addClass("langList").appendTo(cardBody);
var langString = [];
if (languageList != null && languageList.length > 0) {
for (var i = 0; i < languageList.length; i++) {
var cur = languageList[i];
langString[i] = cur.langID;
$("<a>").attr({
"class": "card-link",
"href": "#",
"onClick": "handleLangClicked(event, " + cur.langID + ")"
}).text(cur.langName).appendTo(langList);
}
card.attr("data-langs", JSON.stringify(langString));
} else {
card.attr("data-langs", "[]");
}
$('<p>').attr("id", type + "-" + id + "-description").addClass("card-text").text(DescriptionText).appendTo(cardBody);
cardBody.append(body);
card.append(cardBody);
card.appendTo($("#" + type + "-" + id));
}
function editClicked() {
var button = $(this);
var type = button.data("type");
var id = button.data("id");
$(".editChk").prop("checked", false);
$(".editField").prop("disabled", true);
if (type == "lang")
initEditLang(id);
else if (type == "snippet")
initEditSnippet(id);
else
initEditResource(id);
}
function initEditLang(langID) {
$("#editLangID").val(langID);
$("#txtEditLangName").val($("#lang-" + langID + "-name").text());
$("#txtEditLangDesc").val($("#lang-" + langID + "-description").text());
buildLangSelect();
selectList = $("#lang-" + langID + "-card").data("langs");
$("#editLangModal").modal('show');
}
function initEditSnippet(snippetID) {
$("#editSnippetID").val(snippetID);
$("#txtEditSnippetName").val($("#snippet-" + snippetID + "-name").text());
$("#txtEditSnippetDesc").val($("#snippet-" + snippetID + "-description").text());
$("#txtEditSnippetBody").val($("#snippet-" + snippetID + "-body").text());
buildLangSelect();
selectList = $("#snippet-" + snippetID + "-card").data("langs");
$("#editSnippetModal").modal('show');
}
function initEditResource(resourceID) {
$("#editResourceID").val(resourceID);
$("#txtEditResourceName").val($("#resource-" + resourceID + "-name").text());
$("#txtEditResourceDesc").val($("#resource-" + resourceID + "-description").text());
$("#txtEditResourceLink").val($("#resource-" + resourceID + "-link").text());
buildLangSelect();
selectList = $("#resource-" + resourceID + "-card").data("langs");
$("#editResourceModal").modal('show');
}
function saveLang() {
var changeName = $("#editLangNameChk").prop("checked");
var changeLang = $("#editLangLangsChk").prop("checked");
var changeDesc = $("#editLangDescChk").prop("checked");
if (!changeName && !changeDesc && !changeLang)
return;
var langID = $("#editLangID").val();
var langName = $("#txtEditLangName").val().trim();
var data = {};
data['langID'] = langID;
if (changeName && langName == "") {
displayError({responseText: "Name cannot be empty"});
return;
}
if (changeName && langName != "")
data['langName'] = langName;
if (changeDesc)
data['langDescription'] = $("#txtEditLangDesc").val();
if (changeName || changeDesc) {
$.ajax({
url: "lang.php",
type: "PUT",
data: data,
success: function () { successAlert("Successfully saved language: " + langName, "lang") },
error: displayError
});
}
if (changeLang) {
var curLangsList = $("#lang-" + langID + "-card").data("langs");
var newLangsList = $("#selectEditLangLangs").val().map(function (item) {
return parseInt(item, 10);
});
var langsToAdd = inANotInB(newLangsList, curLangsList);
var langsToDelete = inANotInB(curLangsList, newLangsList);
addLangsLang(langID, langName, langsToAdd, langsToDelete != 0);
deleteLangsLang(langID, langName, langsToDelete);
}
}
function addLangsLang(langID, langName, langList, forceUpdate) {
for (var i = 0; i < langList.length; i++) {
$.ajax({
url: "lang.php",
type: "PUT",
data: {langID: langID, associatedLang: langList[i]},
error: displayError
});
}
if (forceUpdate || langList.length != 0)
successAlert("Successfully saved language: " + langName, "lang")
}
function deleteLangsLang(langID, langName, langList) {
for (var i = 0; i < langList.length; i++) {
$.ajax({
url: "lang.php",
type: "DELETE",
data: {langID: langID, associatedLang: langList[i]},
error: displayError
});
}
}
function saveSnippet() {
var changeName = $("#editSnippetNameChk").prop("checked");
var changeLang = $("#editSnippetLangsChk").prop("checked");
var changeDesc = $("#editSnippetDescChk").prop("checked");
var changeBody = $("#editSnippetBodyChk").prop("checked");
if (!changeName && !changeLang && !changeDesc && !changeBody)
return;
var snippetID = $("#editSnippetID").val();
var snippetName = $("#txtEditSnippetName").val().trim();
var data = {};
data['snippetID'] = snippetID;
if (changeName && snippetName == "") {
displayError({responseText: "Name cannot be empty"});
return;
}
if (changeName)
data['snippetName'] = snippetName;
if (changeDesc)
data['snippetDescription'] = $("#txtEditSnippetDesc").val().trim();
if (changeBody)
data['snippet'] = $("#txtEditSnippetBody").val().trim();
$.ajax({
url: "snippet.php",
type: "PUT",
data: data,
success: function () { successAlert("Successfully saved snippet: " + snippetName, "snippet"); },
error: displayError
});
if (changeLang) {
var curLangsList = $("#snippet-" + snippetID + "-card").data("langs");
var newLangsList = $("#selectEditSnippetLangs").val().map(function (item) {
return parseInt(item, 10);
});
var langsToAdd = inANotInB(newLangsList, curLangsList);
addLangsSnippet(snippetID, langsToAdd);
var langsToDelete = inANotInB(curLangsList, newLangsList);
deleteLangsSnippet(snippetID, langsToDelete);
}
}
function addLangsSnippet(snippetID, langList) {
for (var i = 0; i < langList.length; i++) {
$.ajax({
url: "snippet.php",
type: "PUT",
data: {snippetID: snippetID, langID: langList[i]},
error: displayError
});
}
}
function deleteLangsSnippet(snippetID, langList) {
for (var i = 0; i < langList.length; i++) {
$.ajax({
url: "snippet.php",
type: "DELETE",
data: {snippetID: snippetID, langID: langList[i]},
error: displayError
});
}
}
function saveResource() {
var changeName = $("#editResourceNameChk").prop("checked");
var changeLang = $("#editResourceLangsChk").prop("checked");
var changeDesc = $("#editResourceDescChk").prop("checked");
var changeLink = $("#editResourceLinkChk").prop("checked");
if (!changeName && !changeLang && !changeDesc && !changeLink)
return;
var resourceID = $("#editResourceID").val();
var resourceName = $("#txtEditResourceName").val().trim();
var data = {};
data['resourceID'] = resourceID;
if (changeName && resourceName == "") {
displayError({responseText: "Name cannot be empty"});
return;
}
if (changeName)
data['resourceName'] = resourceName;
if (changeDesc)
data['resourceDescription'] = $("#txtEditResourceDesc").val().trim();
if (changeLink)
data['resourceLink'] = $("#txtEditResourceLink").val().trim();
$.ajax({
url: "resource.php",
type: "PUT",
data: data,
success: function () { successAlert("Successfully saved resource: " + resourceName, "resource"); },
error: displayError
});
if (changeLang) {
var curLangsList = $("#resource-" + resourceID + "-card").data("langs");
var newLangsList = $("#selectEditResourceLangs").val().map(function (item) {
return parseInt(item, 10);
});
var langsToAdd = inANotInB(newLangsList, curLangsList);
addLangsResource(resourceID, langsToAdd);
var langsToDelete = inANotInB(curLangsList, newLangsList);
deleteLangsResource(resourceID, langsToDelete);
}
}
function addLangsResource(resourceID, langList) {
for (var i = 0; i < langList.length; i++) {
$.ajax({
url: "resource.php",
type: "PUT",
data: {resourceID: resourceID, langID: langList[i]},
error: displayError
});
}
}
function deleteLangsResource(resourceID, langList) {
for (var i = 0; i < langList.length; i++) {
$.ajax({
url: "resource.php",
type: "DELETE",
data: {resourceID: resourceID, langID: langList[i]},
error: displayError
});
}
}
function deleteClicked() {
var button = $(this);
var type = button.data("type");
var id = button.data("id");
$("#btnConfirmDelete").off('click').click(function () { deleteItem(type, id); });
$("#confirmDeleteModal").modal('show');
}
function deleteItem(type, id) {
var data = {};
data[type + "ID"] = id;
$.ajax({
url: type + ".php",
type: "DELETE",
data: data,
success: function (res) { successAlert("Item successfully deleted", type); },
error: displayError
});
}
function handleSnippetClicked(evt, snippetID) {
evt.preventDefault();
showSnippet(snippetID);
}
function handleResourceClicked(evt, resourceID) {
evt.preventDefault();
showResource(resourceID);
}
function handleLangClicked(evt, langID) {
// TODO: show related snippets and resources
evt.preventDefault();
showLang(langID);
}
function deselectNav() {
$("#langLink").removeClass("active");
$("#snippetLink").removeClass("active");
$("#resourceLink").removeClass("active");
}
function displayError(error, str) {
console.log(error);
$("#errorText").text(error.responseText);
$("#errorModal").modal('show');
}
function inANotInB(a, b) {
var list = [];
for (var i = 0; i < a.length; i++) {
if (b.indexOf(a[i]) == -1)
list.push(a[i]);
}
return list;
}

284
lang.php Normal file
View file

@ -0,0 +1,284 @@
<?php
// table language: langID, langName, langDescription
// table langLang: langID, associatedLang
include_once "dbTools.php";
if ($_SERVER["REQUEST_METHOD"] == 'GET') {
$pdo = buildPDO();
if (isset(($_GET['langID'])) && isset($_GET['associatedLang'])) {
http_response_code(422);
header("Content-type: text/plain; charset=utf-8");
die("Both langID and associatedLang are specified");
}
if (isset($_GET['langID'])) {
$id = $_GET['langID'];
$stmt = $pdo->prepare("SELECT * FROM language WHERE langID=?;");
$stmt->execute([$id]);
$lang = $stmt->fetchAll(PDO::FETCH_ASSOC);
if (count($lang) <= 0) {
http_response_code(404);
header("Content-type: text/plain; charset=utf-8");
die("No such language");
}
$lang = buildLangList($pdo, $lang);
$lang = buildSnippetList($pdo, $lang);
$lang = buildResourceList($pdo, $lang);
header("Content-type: application/json; charset=utf-8");
echo json_encode($lang[0]);
} elseif (isset($_GET['associatedLang'])) {
$assoc = $_GET['associatedLang'];
if ($assoc == '') {
header("Content-type: text/plain; charset=utf-8");
die("associatedLang is given, but empty");
}
$stmt = $pdo->prepare("SELECT langID, langName, langDescription FROM language NATURAL JOIN langLang Where associatedLang=?;");
$stmt->execute([$assoc]);
$langList = $stmt->fetchAll(PDO::FETCH_ASSOC);
$langList = buildLangList($pdo, $langList);
$langList = buildSnippetList($pdo, $langList);
$langList = buildResourceList($pdo, $langList);
header("Content-type: application/json; charset=utf-8");
echo json_encode($langList);
} else {
$stmt = $pdo->prepare("SELECT * FROM language ORDER BY langID;");
$stmt->execute();
$langList = $stmt->fetchAll(PDO::FETCH_ASSOC);
$langList = buildLangList($pdo, $langList);
$langList = buildSnippetList($pdo, $langList);
$langList = buildResourceList($pdo, $langList);
header("Content-type: application/json; charset=utf-8");
echo json_encode($langList);
}
} elseif ($_SERVER["REQUEST_METHOD"] == 'POST') {
if (!isset($_POST['langName']) || trim($_POST['langName']) == '') {
http_response_code(422);
header("Content-type: text/plain; charset=utf-8");
die("A name is required");
}
$name = trim($_POST['langName']);
$pdo = buildPDO();
$stmt = $pdo->prepare("SELECT * FROM language WHERE langName=?;");
$stmt->execute([$name]);
if ($stmt->rowCount() > 0) {
http_response_code(422);
header("Content-type: text/plain; charset=utf-8");
die("A language with the given name already exists");
}
$desc = '';
if (isset($_POST['langDescription'])) {
$desc = trim($_POST['langDescription']);
}
$assoc = null;
if (isset($_POST['associatedLang'])) {
$assoc = trim($_POST['associatedLang']);
if ($assoc == '') {
http_response_code(422);
header("Content-type: text/plain; charset=utf-8");
die("Associated language is given, but empty");
}
}
$stmt = $pdo->prepare("INSERT INTO language VALUES (NULL, ?, ?);");
$stmt->execute([$name, $desc]);
$newID = $pdo->lastInsertId();
if (isset($assoc)) {
$stmt = $pdo->prepare("INSERT INTO langLang VALUES (?, ?);");
$stmt->execute([$newID, $assoc]);
$stmt->execute([$assoc, $newID]); // make it two way
}
http_response_code(201);
header("Content-type: text/plain; charset=utf-8");
echo $newID;
} elseif ($_SERVER["REQUEST_METHOD"] == 'PUT') {
$pdo = buildPDO();
// send request as url encoded form
$_PUT = array();
parse_str(file_get_contents("php://input"), $_PUT);
if (!isset($_PUT['langID']) || trim($_PUT['langID']) === '') {
http_response_code(422);
die("A langID is required");
}
$id = trim($_PUT['langID']);
$name = null;
if (isset($_PUT['langName']) && trim($_PUT['langName']) != '') {
$name = trim($_PUT['langName']);
}
$pdo = buildPDO();
$stmt = $pdo->prepare("SELECT * FROM language WHERE langName=?;");
$stmt->execute([$name]);
if ($stmt->rowCount() > 0) {
http_response_code(422);
header("Content-type: text/plain; charset=utf-8");
die("A language with the given name already exists");
}
$desc = null;
if (isset($_PUT['langDescription'])) {
$desc = trim($_PUT['langDescription']);
}
$assoc = null;
if (isset($_PUT['associatedLang'])) {
$assoc = trim($_PUT['associatedLang']);
if ($assoc == '') {
http_response_code(422);
header("Content-type: text/plain; charset=utf-8");
die("Associated language is given, but empty");
}
}
if (isset($name) && isset($desc)) {
$stmt = $pdo->prepare("UPDATE language SET langName=?, langDescription=? WHERE langID=?;");
$stmt->execute([$name, $desc, $id]);
} elseif (isset($name)) {
$stmt = $pdo->prepare("UPDATE language SET langName=? WHERE langID=?;");
$stmt->execute([$name, $id]);
} elseif (isset($desc)) {
$stmt = $pdo->prepare("UPDATE language SET langDescription=? WHERE langID=?;");
$stmt->execute([$desc, $id]);
} elseif (!isset($assoc)) {
http_response_code(422);
header("Content-type: text/plain; charset=utf-8");
die("Nothing given to update");
}
if (isset($assoc)) {
$stmt = $pdo->prepare("Select * FROM langLang WHERE langID=? AND associatedLang=?;");
$stmt->execute([$id, $assoc]);
if ($stmt->rowCount() > 0) {
http_response_code(422);
header("Content-type: text/plain; charset=utf-8");
die("The given langID-langID connection already exists");
}
$stmt = $pdo->prepare("INSERT INTO langLang VALUES (?, ?);");
$stmt->execute([$id, $assoc]);
$stmt->execute([$assoc, $id]); // make it two way
}
http_response_code(200);
} elseif ($_SERVER["REQUEST_METHOD"] == 'DELETE') {
// send request as url encoded form
$_DELETE = array();
parse_str(file_get_contents("php://input"), $_DELETE);
if (!isset($_DELETE['langID']) || trim($_DELETE['langID']) == '') {
http_response_code(422);
header("Content-type: text/plain; charset=utf-8");
die("A langID is required");
}
$id = trim($_DELETE['langID']);
$pdo = buildPDO();
if (isset($_DELETE['associatedLang'])) {
$assoc = trim($_DELETE['associatedLang']);
if ($assoc == '') {
http_response_code(422);
header("Content-type: text/plain; charset=utf-8");
die("Associated language is given, but empty");
}
$stmt = $pdo->prepare("DELETE FROM langLang WHERE langID=? AND associatedLang=?;");
$stmt->execute([$id, $assoc]);
$stmt->execute([$assoc, $id]);
if ($stmt->rowCount() == 0) {
http_response_code(404);
header("Content-type: text/plain; charset=utf-8");
die("No such item");
}
} else {
$stmt = $pdo->prepare("DELETE FROM language WHERE langID=?;");
$stmt->execute([$id]);
if ($stmt->rowCount() == 0) {
http_response_code(404);
header("Content-type: text/plain; charset=utf-8");
die("No such item");
}
$stmt = $pdo->prepare("DELETE FROM langSnippet WHERE langID=?;");
$stmt->execute([$id]);
$stmt = $pdo->prepare("DELETE FROM langResource WHERE langID=?;");
$stmt->execute([$id]);
}
http_response_code(204);
} elseif ($_SERVER["REQUEST_METHOD"] == 'OPTIONS') {
header("Content-type: text/plain; charset=utf-8");
echo "Allow: GET,POST,PUT,DELETE";
} else {
http_response_code(405);
}
function buildLangList($pdo, $langList)
{
for ($i = 0; $i < count($langList); $i++) {
$id = $langList[$i]['langID'];
$langList[$i]['languages'] = array();
$stmt = $pdo->prepare("SELECT langID, langName FROM langLang NATURAL JOIN language WHERE associatedLang=?");
$stmt->execute([$id]);
$newList = $stmt->fetchAll(PDO::FETCH_ASSOC);
for ($j = 0; $j < count($newList); $j++) {
$langList[$i]['languages'][$j] = $newList[$j];
}
}
return $langList;
}
function buildSnippetList($pdo, $langList)
{
for ($j = 0; $j < count($langList); $j++) {
$id = $langList[$j]['langID'];
$langList[$j]['snippets'] = array();
$stmt = $pdo->prepare("SELECT snippetID, snippetName FROM langSnippet NATURAL JOIN snippet WHERE langID=?;");
$stmt->execute([$id]);
$snippetList = $stmt->fetchAll(PDO::FETCH_ASSOC);
for ($i = 0; $i < count($snippetList); $i++) {
$langList[$j]['snippets'][$i] = $snippetList[$i];
}
}
return $langList;
}
function buildResourceList($pdo, $langList)
{
for ($j = 0; $j < count($langList); $j++) {
$id = $langList[$j]['langID'];
$langList[$j]['resources'] = array();
$stmt = $pdo->prepare("SELECT resourceID, resourceName FROM langResource NATURAL JOIN resource WHERE langID=?;");
$stmt->execute([$id]);
$resourceList = $stmt->fetchAll(PDO::FETCH_ASSOC);
for ($i = 0; $i < count($resourceList); $i++) {
$langList[$j]['resources'][$i] = $resourceList[$i];
}
}
return $langList;
}

238
resource.php Normal file
View file

@ -0,0 +1,238 @@
<?php
// resourceID, resourceName, resourceDescription, resourceLink
// intermediary table that relates resourceIDs to langIDs
// using an intermediary allows for each snippet to be related to many languages
include_once "dbTools.php";
if ($_SERVER["REQUEST_METHOD"] == 'GET') {
$pdo = buildPDO();
if (isset($_GET['resourseID']) && isset($_GET['langID'])) {
http_response_code(422);
header("Content-type: text/plain; charset=utf-8");
die("Both resourceID and langID are specified. Only one can be given");
} else if (isset($_GET['resourceID'])) {
if (trim($_GET['resourceID']) == '') {
http_response_code(422);
header("Content-type: text/plain; charset=utf-8");
die("Empty resourceID");
}
$resourceID = $_GET['resourceID'];
$stmt = $pdo->prepare("SELECT * FROM resource WHERE resourceID=?;");
$stmt->execute([$resourceID]);
$resources = $stmt->fetchAll(PDO::FETCH_ASSOC);
if (count($resources) <= 0) {
http_response_code(404);
header("Content-type: text/plain; charset=utf-8");
die("No such resource");
}
$resources = buildLangList($pdo, $resources);
header("Content-type: application/json; charset=utf-8");
echo json_encode($resources[0]);
} elseif (isset($_GET['langID'])) {
if (trim($_GET['langID']) == '') {
http_response_code(422);
header("Content-type: text/plain; charset=utf-8");
die("Empty langID");
}
$langID = $_GET['langID'];
$stmt = $pdo->prepare("SELECT DISTINCT resourceID, resourceName, resourceDescription, resourceLink FROM resource NATURAL JOIN langResource WHERE langID=? ORDER BY resourceID;");
$stmt->execute([$langID]);
$resources = $stmt->fetchAll(PDO::FETCH_ASSOC);
$resources = buildLangList($pdo, $resources);
header("Content-type: application/json; charset=utf-8");
echo json_encode($resources);
} else {
$stmt = $pdo->prepare("SELECT * FROM resource ORDER BY resourceID;");
$stmt->execute();
$resources = $stmt->fetchAll(PDO::FETCH_ASSOC);
$resources = buildLangList($pdo, $resources);
header("Content-type: application/json; charset=utf-8");
echo json_encode($resources);
}
} elseif ($_SERVER["REQUEST_METHOD"] == 'POST') {
$pdo = buildPDO();
if (!isset($_POST['resourceName']) || trim($_POST['resourceName']) == '') {
http_response_code(422);
header("Content-type: text/plain; charset=utf-8");
die("A resourceName is required");
}
$name = $_POST['resourceName'];
$desc = '';
if (isset($_POST['resourceDescription'])) {
$desc = trim($_POST['resourceDescription']);
}
$link = '';
if (isset($_POST['resourceLink'])) {
$link = trim($_POST['resourceLink']);
}
$langID = NULL;
if (isset($_POST['langID'])) {
$langID = $_POST['langID'];
}
$stmt = $pdo->prepare("INSERT INTO resource (resourceID, resourceName, resourceDescription, resourceLink) VALUES (NULL, ?, ?, ?);");
$stmt->execute([$name, $desc, $link]);
$resourceID = $pdo->lastInsertId();
if (isset($langID)) {
$stmt = $pdo->prepare("INSERT INTO langResource (resourceID, langID) VALUES (?, ?);");
$stmt->execute([$resourceID, $langID]);
}
http_response_code(201);
header("Content-type: text/plain; charset=utf-8");
echo $resourceID;
} elseif ($_SERVER["REQUEST_METHOD"] == 'PUT') {
$pdo = buildPDO();
// send request as url encoded form
$_PUT = array();
parse_str(file_get_contents("php://input"), $_PUT);
if (!isset($_PUT['resourceID']) && trim($_PUT['resourceID']) == '') {
http_response_code(422);
header("Content-type: text/plain; charset=utf-8");
die("resourceID is required");
}
$resourceID = trim($_PUT['resourceID']);
$name = null;
if (isset($_PUT['resourceName'])) {
$name = trim($_PUT['resourceName']);
}
$desc = null;
if (isset($_PUT['resourceDescription'])) {
$desc = trim($_PUT['resourceDescription']);
}
$link = null;
if (isset($_PUT['resourceLink'])) {
$link = trim($_PUT['resourceLink']);
}
$langID = null;
if (isset($_PUT['langID'])) {
$langID = trim($_PUT['langID']);
}
if (isset($langID) && $langID == '') {
http_response_code(422);
header("Content-type: text/plain; charset=utf-8");
die("langID is empty");
}
if (isset($langID)) {
$stmt = $pdo->prepare("SELECT * FROM langResource WHERE resourceID=? AND langID=?;");
$stmt->execute([$resourceID, $langID]);
if ($stmt->rowCount() > 0) {
http_response_code(422);
header("Content-type: text/plain; charset=utf-8");
die("The given resourceID-langID connection already exists");
}
}
if (isset($name)) {
$stmt = $pdo->prepare("UPDATE resource SET resourceName=? WHERE resourceID=?;");
$stmt->execute([$name, $resourceID]);
}
if (isset($desc)) {
$stmt = $pdo->prepare("UPDATE resource SET resourceDescription=? WHERE resourceID=?;");
$stmt->execute([$desc, $resourceID]);
}
if (isset($link)) {
$stmt = $pdo->prepare("UPDATE resource SET resourceLink=? WHERE resourceID=?;");
$stmt->execute([$link, $resourceID]);
}
if (isset($langID)) {
$stmt = $pdo->prepare("INSERT INTO langResource (resourceID, langID) VALUES (?, ?);");
$stmt->execute([$resourceID, $langID]);
}
http_response_code(204);
} elseif ($_SERVER["REQUEST_METHOD"] == 'DELETE') {
$pdo = buildPDO();
// send request as url encoded form
$_DELETE = array();
parse_str(file_get_contents("php://input"), $_DELETE);
// can delete either the snippet itself or an association with a lang
if (!isset($_DELETE['resourceID']) || trim($_DELETE['resourceID'])== '') {
http_response_code(422);
header("Content-type: text/plain; charset=utf-8");
die("snippetID is required");
}
$resourceID = trim($_DELETE['resourceID']);
$langID = null;
if (isset($_DELETE['langID'])) {
$langID = trim($_DELETE['langID']);
}
if (isset($langID) && $langID == '') {
http_response_code(422);
header("Content-type: text/plain; charset=utf-8");
die("langId is given, but empty");
}
$rows = null;
if (isset($langID)) {
$stmt = $pdo->prepare("DELETE FROM langResource WHERE resourceID=? AND langID=?;");
$stmt->execute([$resourceID, $langID]);
$rows = $stmt->rowCount();
} else {
$stmt = $pdo->prepare("DELETE FROM resource WHERE resourceID=?;");
$stmt->execute([$resourceID]);
$rows = $stmt->rowCount();
if ($rows > 0) {
$stmt = $pdo->prepare("DELETE FROM langResource WHERE resourceID=?;");
$stmt->execute([$resourceID]);
}
}
if ($rows == 0) {
http_response_code(404);
header("Content-type: text/plain; charset=utf-8");
die("No such item");
}
http_response_code(204);
} else if ($_SERVER["REQUEST_METHOD"] == 'OPTIONS') {
header("Content-type: text/plain; charset=utf-8");
echo "Allow: GET,POST,PUT,DELETE";
} else {
http_response_code(405);
}
function buildLangList($pdo, $resources) {
for ($j = 0; $j < count($resources); $j++) {
$resourceID = $resources[$j]['resourceID'];
$resources[$j]['languages'] = array();
$stmt = $pdo->prepare("SELECT langID, langName FROM langResource NATURAL JOIN language WHERE resourceID=? ORDER BY langID;");
$stmt->execute([$resourceID]);
$langList = $stmt->fetchAll(PDO::FETCH_ASSOC);
for ($i = 0; $i < count($langList); $i++) {
$resources[$j]['languages'][$i] = $langList[$i];
}
}
return $resources;
}
?>

250
snippet.php Normal file
View file

@ -0,0 +1,250 @@
<?php
// table snippet: snippetID, snippetName, snippetDescription, snippet
// table langSnippet: intermediary table that relates snippetIDs to langIDs
// using an intermediary allows for each snippet to be related to many languages
include_once "dbTools.php";
if ($_SERVER["REQUEST_METHOD"] == 'GET') {
$pdo = buildPDO();
if (isset($_GET['snippetID']) && isset($_GET['langID'])) {
http_response_code(422);
header("Content-type: text/plain; charset=utf-8");
die("Both snippetID and langID are specified. Only one can be given");
} else if (isset($_GET['snippetID'])) {
if (trim($_GET['snippetID']) == '') {
http_response_code(422);
header("Content-type: text/plain; charset=utf-8");
die("Empty snippetID");
}
$snippetID = $_GET['snippetID'];
$stmt = $pdo->prepare("SELECT * FROM snippet WHERE snippetID=? ORDER BY snippetID;");
$stmt->execute([$snippetID]);
$snippets = $stmt->fetchAll(PDO::FETCH_ASSOC);
if (count($snippets) <= 0) {
http_response_code(404);
header("Content-type: text/plain; charset=utf-8");
die("No such snippet");
}
$snippets = buildLangList($pdo, $snippets);
header("Content-type: application/json; charset=utf-8");
echo json_encode($snippets[0]);
} elseif (isset($_GET['langID'])) {
if (trim($_GET['langID']) == '') {
http_response_code(422);
header("Content-type: text/plain; charset=utf-8");
die("Empty langID");
}
$langID = $_GET['langID'];
$stmt = $pdo->prepare("SELECT DISTINCT snippetID, snippetName, snippetDescription, snippet FROM snippet NATURAL JOIN langSnippet WHERE langID=? ORDER BY snippetID;");
$stmt->execute([$langID]);
$snippets = $stmt->fetchAll(PDO::FETCH_ASSOC);
if (count($snippets) <= 0) {
http_response_code(404);
header("Content-type: text/plain; charset=utf-8");
die("No snippets for the given langID");
}
$snippets = buildLangList($pdo, $snippets);
header("Content-type: application/json; charset=utf-8");
echo json_encode($snippets);
} else {
$stmt = $pdo->prepare("SELECT * FROM snippet ORDER BY snippetID;");
$stmt->execute();
$snippets = $stmt->fetchAll(PDO::FETCH_ASSOC);
$snippets = buildLangList($pdo, $snippets);
header("Content-type: application/json; charset=utf-8");
echo json_encode($snippets);
}
} elseif ($_SERVER["REQUEST_METHOD"] == 'POST') {
$pdo = buildPDO();
if (!isset($_POST['snippetName']) || trim($_POST['snippetName']) == '') {
http_response_code(422);
header("Content-type: text/plain; charset=utf-8");
die("A snippet name is required");
}
$name = trim($_POST['snippetName']);
$desc = '';
if (isset($_POST['snippetDescription'])) {
$desc = trim($_POST['snippetDescription']);
}
$snippet = '';
if (isset($_POST['snippet'])) {
$snippet = trim($_POST['snippet']);
}
$langID = NULL;
if (isset($_POST['langID'])) {
$langID = $_POST['langID'];
}
$stmt = $pdo->prepare("INSERT INTO snippet (snippetID, snippetName, snippetDescription, snippet) VALUES (NULL, ?, ?, ?);");
$stmt->execute([$name, $desc, $snippet]);
$snippetID = $pdo->lastInsertId();
if (isset($langID)) {
$stmt = $pdo->prepare("INSERT INTO langSnippet (snippetID, langID) VALUES (?, ?);");
$stmt->execute([$snippetID, $langID]);
}
http_response_code(201);
header("Content-type: application/json; charset=utf-8");
echo $snippetID;
} elseif ($_SERVER["REQUEST_METHOD"] == 'PUT') {
$pdo = buildPDO();
// send request as url encoded form
$_PUT = array();
parse_str(file_get_contents("php://input"), $_PUT);
if (!isset($_PUT['snippetID']) || trim($_PUT['snippetID']) == '') {
http_response_code(422);
header("Content-type: text/plain; charset=utf-8");
die("snippetID is required");
}
$snippetID = trim($_PUT['snippetID']);
$name = null;
if (isset($_PUT['snippetName'])) {
$name = trim($_PUT['snippetName']);
}
if (isset($name) && $name == '') {
http_response_code(422);
header("Content-type: text/plain; charset=utf-8");
die("snippetName cannot be empty");
}
$desc = null;
if (isset($_PUT['snippetDescription'])) {
$desc = trim($_PUT['snippetDescription']);
}
$body = null;
if (isset($_PUT['snippet'])) {
$body = trim($_PUT['snippet']);
}
$langID = null;
if (isset($_PUT['langID'])) {
$langID = trim($_PUT['langID']);
}
if (isset($langID) && $langID == '') {
http_response_code(422);
header("Content-type: text/plain; charset=utf-8");
die("langID is empty");
}
if (isset($langID)) {
$stmt = $pdo->prepare("SELECT * FROM langSnippet WHERE snippetID=? AND langID=?;");
$stmt->execute([$snippetID, $langID]);
if ($stmt->rowCount() > 0) {
http_response_code(422);
header("Content-type: text/plain; charset=utf-8");
die("The given snippetID-langID connection already exists");
}
}
if (isset($name)) {
$stmt = $pdo->prepare("UPDATE snippet SET snippetName=? WHERE snippetID=?;");
$stmt->execute([$name, $snippetID]);
}
if (isset($desc)) {
$stmt = $pdo->prepare("UPDATE snippet SET snippetDescription=? WHERE snippetID=?;");
$stmt->execute([$desc, $snippetID]);
}
if (isset($body)) {
$stmt = $pdo->prepare("UPDATE snippet SET snippet=? WHERE snippetID=?;");
$stmt->execute([$body, $snippetID]);
}
if (isset($langID)) {
$stmt = $pdo->prepare("INSERT INTO langSnippet (snippetID, langID) VALUES (?, ?);");
$stmt->execute([$snippetID, $langID]);
}
http_response_code(204);
} elseif ($_SERVER["REQUEST_METHOD"] == 'DELETE') {
$pdo = buildPDO();
// send request as url encoded form
$_DELETE = array();
parse_str(file_get_contents("php://input"), $_DELETE);
// can delete either the snippet itself or an association with a lang
if (!isset($_DELETE['snippetID']) || trim($_DELETE['snippetID'])== '') {
http_response_code(422);
header("Content-type: text/plain; charset=utf-8");
die("snippetID is required");
}
$snippetID = trim($_DELETE['snippetID']);
$langID = null;
if (isset($_DELETE['langID'])) {
$langID = trim($_DELETE['langID']);
}
if (isset($langID) && $langID == '') {
http_response_code(422);
header("Content-type: text/plain; charset=utf-8");
die("langId is given, but empty");
}
$rows = null;
if (isset($langID)) {
$stmt = $pdo->prepare("DELETE FROM langSnippet WHERE snippetID=? AND langID=?;");
$stmt->execute([$snippetID, $langID]);
$rows = $stmt->rowCount();
} else {
$stmt = $pdo->prepare("DELETE FROM snippet WHERE snippetID=?;");
$stmt->execute([$snippetID]);
$rows = $stmt->rowCount();
if ($rows > 0) {
$stmt = $pdo->prepare("DELETE FROM langSnippet WHERE snippetID=?;");
$stmt->execute([$snippetID]);
}
}
if ($rows == 0) {
http_response_code(404);
header("Content-type: text/plain; charset=utf-8");
die("No such item");
}
http_response_code(204);
} else if ($_SERVER["REQUEST_METHOD"] == 'OPTIONS') {
header("Content-type: text/plain; charset=utf-8");
echo "Allow: GET,POST,PUT,DELETE";
} else {
http_response_code(405);
}
function buildLangList($pdo, $snippets) {
for ($j = 0; $j < count($snippets); $j++) {
$snippetID = $snippets[$j]['snippetID'];
$snippets[$j]['languages'] = array();
$stmt = $pdo->prepare("SELECT langID, langName FROM langSnippet NATURAL JOIN language WHERE snippetID=? ORDER BY langID;");
$stmt->execute([$snippetID]);
$langList = $stmt->fetchAll(PDO::FETCH_ASSOC);
for ($i = 0; $i < count($langList); $i++) {
$snippets[$j]['languages'][$i] = $langList[$i];
}
}
return $snippets;
}
?>

BIN
tables.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

1
tables.xml Normal file
View file

@ -0,0 +1 @@
<mxfile userAgent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/7.6.6 Chrome/58.0.3029.110 Electron/1.7.5 Safari/537.36" version="7.6.6" editor="www.draw.io" type="device"><diagram id="d667a6e1-8b29-6e91-a70b-4ec352606e5d" name="Page-1">7Vzfc6soFP5r8ridCGqSx3vb3h8z3Z07tzO7dx9tpAlbIy4hTbJ//aKCiUAam6LSpHkpHhXwnO+DA5zTAbxebL7SKJv/TmKUDMAw3gzgzQAAb+gD/ieXbEtJGAjBjOJYPLQT3OP/kHxTSFc4Rsvag4yQhOGsLpySNEVTVpNFlJJ1/bFHktRbzaIZ0gT30yjRpX/hmM1l77zJ7sY3hGdz0XToi44/RNOnGSWrVLQ3APCx+JW3F5GsSzy/nEcxWe+J4O0AXlNCWFlabK5RkutWqq1878uBu1W/KUpZkxeEWZ6jZCU+PYnS2SpXT9k/tpU6WTJKnip18J5+Xs8xQ/dZNM2fWHMkcNmcLRJ+5fFilOBZystT3hdEuUDvnOjvM6IMbfZEorNfEVkgRrf8EXHXD4XiBLA8eb3eN5OQzfcsVAkjAY1ZVfdOPbwgNGTWFtS0tUxxliHmprLAsK4sOO5SWb6mLIqWZADChLfyeZlFKS/P8vKKcqUIOa91/5abih2BPhUbaIqVynsk/JP21RX+uyLyxm/LYpj9xB/wQLbZ3ZS6zpl/L/FcWaOs85A15lGWF+mcLB5Wy9yumn3yCsQI7434dYYo5t+c20K+92Mn6oUno7o5gW8wJzCZE1gwZ9iiOX9yxinsugB7VrNxL/YcGeypqHnv+1Eaf8odFn55+3MRpdtC5xFlmhRtMPsl3+Llv3l5WNTAO/hr/6K4cxVU1rsmCaFFy3BY/PidwkdBsawveSgay3WMuRv0kh0EovaHIt7fGWI1jwLFNQ9Lt1XjoZKiJGL4ue6ZmQwk2vhBcEEbSe5AAUOg2Lj8HvHWvqukVASVijy1olILWkUFXqoPbwShSUsQOgwHCa4SNsfh5TVFCNQREjiGEE/xKeHkRIQApSKgVmQPIXKh1ANEauNPCRfzCNQUIkCHSOgYRALFkw7giRAJlYp8tSKLEJHzZocQkUDw1LHi8PjSCCL+O4DIWLHs6FSIKBVBtSJ7EIGehohcn/fiMiUp0lyFGmTMnscoqONJ1MMlX3Del+L5fxBjW+FHRitGuIhQNiczkkbJHSGZaOQw2hohxzOMLrAhdBojoqlrKDtj3dcX2xDfb5o6+rxfOFvmVpEuf0xWDwm6reQLrjCce+besNEyoJf9jfqgDAxufmurcDC2xJ0rEGijpSPcMThv/XFH33yzyp0/okXjZfJZsMfze6XPxBZ9Rs7Sx+C19EcffTvWKn0OUqcUx/hZFb2t2Ru0nFKcMUzSI01zca31c+SyuvLslstQX1icxOXaqsExN9Kw09Ufl9s6ATjG5XOkDhz3SR3fEnVUL3LoEHVCnTqgN+q0edpyYcsvqB6zdEqcoa7UVxHHDW6MXOKG6eTKFjcubnmlRqp0yw5bG3vK6sqlaWXsEnXGLVLnY2nVJ4/Vff1uV1bQEo8dXllNdB77vfHYdPJug1NUBOJcmIsY9LrHCH1bs6CzW/RSn06wR3amNfZ0PRO+6LRezhQY9ru7GLTjy7rEYpnj4ASLTXEj75nFH/5sLXa91/1OGNohs8NHBXIV6gaZQctkvsPp00W5tBM1FLDbjR1rLq2r+zpviRi2T5+2gk4u8LgAwDpxKj50Q5x2dlJcmncM4Sb9EaflcJML444H1U3ITp220dlPOobwjqYB5va502aC53siziNv6M/BkTSBV2xjTCZXY7j7+T1OR9BS9LCyDnJpOjKEffRHqbbCPt7jvr5lWo1epFW3M5XfZnjP3UtnsYqVnUmk1uxosPbhGAYl5RWajNlaIrXfViZ1gwQ245GPzGEbmDLYlEHteErbobA43RpdpbBBZZ/fPzURVo190TJq7aWw+abIi5ZBcjQR33DScDYgCSYKSE7Oc1QqajHPMWgjz7HL86NX48YUVBF25GzJxi9+/WJhBtaG5E7dKflf26yuUlzmjfR3euGNbPyDN2/nTdAlb/jl7j/2ldPT7t8iwtv/AQ==</diagram></mxfile>