Skip to content

Commit

Permalink
Implement simple playlists of songs for events.
Browse files Browse the repository at this point in the history
  • Loading branch information
bzamecnik committed Jun 3, 2012
1 parent d4ccd82 commit 8bada68
Show file tree
Hide file tree
Showing 10 changed files with 281 additions and 4 deletions.
11 changes: 10 additions & 1 deletion nette/app/components/EventList.latte
@@ -1,6 +1,12 @@
<style type="text/css">
table.eventlist td.action {
width: 13em;
}
</style>

{snippet}
{if isset($events) and count($events) > 0}
<table class="table table-striped table-bordered table-condensed">
<table class="table table-striped table-bordered table-condensed eventlist">
<thead>
<tr>
<th class="date_start"><i class="icon-time"></i>&nbsp;Kdy</th>
Expand Down Expand Up @@ -28,6 +34,9 @@
<i class="icon-pencil"></i>
</a>
{/if}
<a href="{plink Playlist: eventId=>$event->id}" class="btn" title="Playlist">
<i class="icon-list"></i>
</a>
{if $event->attendance_active}
<a href="{plink Attendance:event $event->id}" class="btn" title="Účast">
<i class="icon-check"></i>
Expand Down
1 change: 1 addition & 0 deletions nette/app/config/config.neon
Expand Up @@ -37,6 +37,7 @@ common:
attendances: Attendances
userBindings: UserBindings
songs: Songs
playlistItems: PlaylistItems


production < common:
Expand Down
15 changes: 15 additions & 0 deletions nette/app/models/PlaylistItems.php
@@ -0,0 +1,15 @@
<?php

use Nette\Database\Connection,
Nette\Database\Table\Selection;

/**
* An ordered many-to-many mapping between songs and events.
*/
class PlaylistItems extends Selection
{
public function __construct(\Nette\Database\Connection $connection)
{
parent::__construct('corale_playlist_item', $connection);
}
}
105 changes: 105 additions & 0 deletions nette/app/presenters/PlaylistPresenter.php
@@ -0,0 +1,105 @@
<?php

use Nette\Application\UI\Form;

class PlaylistPresenter extends BasePresenter
{
/** @persistent */
private $eventId;

/** @var Songs */
private $songQuery;

public function actionUpdateItem($eventId, $songId, $order) {
$this->ensureLoggedUser();

$this->eventId = $eventId;
$values = array();
if ($order) {
$values['ord'] = $order;
}
$key = array('event_id' => $eventId, 'song_id' => $songId);
$playlistItem = $this->context->createPlaylistItems()->where($key)->fetch();
if ($playlistItem) {
$this->context->createPlaylistItems()->where($key)->update($values);
$this->flashMessage("Písnička v playlistu byla upravena.");
} else {
$values['event_id'] = $eventId;
$values['song_id'] = $songId;
$this->context->createPlaylistItems()->insert($values);
$this->flashMessage("Písnička byla přidána do playlistu.");
}
$this->redirect('edit', array('eventId' => $eventId));
}

public function actionDelete($eventId, $songId) {
$this->ensureLoggedUser();

$key = array('event_id' => $eventId, 'song_id' => $songId);
$this->context->createPlaylistItems()->where($key)->delete();
// TODO: hande the situation where the playlist item is badly specified
// or does not exist
$this->flashMessage("Písnička byla smazána z playlistu.");
$this->redirect('edit', array('eventId' => $eventId));
}

public function actionDefault($eventId) {
$this->eventId = $eventId;
}

public function actionEdit($eventId) {
$this->eventId = $eventId;

$this->ensureLoggedUser();
}

public function renderDefault() {
$this->template->eventId = $this->eventId;
$this->template->playlist = $this->getPlaylist($this->eventId);
}

public function renderEdit() {
$this->template->eventId = $this->eventId;
$this->template->playlist = $this->getPlaylist($this->eventId);
}

public function getPlaylist($eventId) {
$query = <<<EOQ
SELECT s.id id, s.title title, p.ord `order`
FROM corale_song s
JOIN corale_playlist_item p ON s.id = p.song_id
JOIN corale_event e ON e.id = p.event_id
WHERE p.event_id = ?
ORDER BY p.ord, s.title
EOQ;
return $this->context->createPlaylistItems()->getConnection()->query($query, $eventId);
}

// TODO: use AJAX
public function actionAdd($eventId, $query)
{
$this->eventId = $eventId;
$this->songQuery = $query;
$this["songSearchForm"]->setDefaults(array('query' => $this->songQuery));
}

public function renderAdd() {
$q = $this->context->createSongs();
if ($this->songQuery) {
$term = "%$this->songQuery%";
$q->where("title LIKE ? OR author LIKE ? OR description LIKE ?",
array($term, $term, $term));
}
$q->order('title');
$this->template->songs = $q;
$this->template->eventId = $this->eventId;
}

protected function createComponentSongSearchForm()
{
$form = new Form();
$form->addText('query', 'Hledaný text');
$form->addSubmit('search', 'Hledat');
return $form;
}
}
9 changes: 6 additions & 3 deletions nette/app/templates/Event/details.latte
Expand Up @@ -36,13 +36,16 @@

<div>
<a n:href="Event:" class="btn">&larr;&nbsp;Seznam událostí</a>
{if $user->isLoggedIn()}
<a n:href="Event:edit $event->id" class="btn"><i class="icon-pencil"></i>&nbsp;Upravit</a>
{/if}
<a {if $event->attendance_active}href="{link Attendance:event $event->id}"{/if}
class="btn {(!$event->attendance_active) ? 'disabled' : 'btn-info'}">
<i class="icon-check {$event->attendance_active ? icon-white}"></i>&nbsp;Vyplnit účast
</a>
{if $user->isLoggedIn()}
<a n:href="Event:edit $event->id" class="btn"><i class="icon-pencil"></i>&nbsp;Upravit</a>
{/if}
<a href="{link Playlist: eventId=>$event->id}" class="btn btn-info">
<i class="icon-list icon-white"></i>&nbsp;Playlist
</a>
<div class="pull-right">
{include deleteEventButton.latte}
</div>
Expand Down
5 changes: 5 additions & 0 deletions nette/app/templates/Playlist/add.latte
@@ -0,0 +1,5 @@
{block content}
{include 'songSearchForm.latte'}

{include 'songSearchList.latte'}
{/block}
31 changes: 31 additions & 0 deletions nette/app/templates/Playlist/default.latte
@@ -0,0 +1,31 @@
{var $title = 'Playlist'}

{block content}

{if $user->isLoggedIn()}
<p>
<a href="{link Event:details $eventId}" class="btn">
<i class="icon-search"></i>&nbsp;Detaily události
</a>
<a href="{link edit eventId=>$eventId}" class="btn btn-primary">
<i class="icon-pencil icon-white"></i>&nbsp;Upravit
</a>
</p>
{/if}

{foreach $playlist as $song}
{first}
{var $i = 0}
<table class="table table-condensed playlist">
{/first}
{? $i++}
<tr>
<td style="width: 2em">{$i}.</td>
<td><a href="{link Song:details $song->id}">{$song->title}</td>
</tr>
{last}
</table>
{/last}
{/foreach}

{/block}
68 changes: 68 additions & 0 deletions nette/app/templates/Playlist/edit.latte
@@ -0,0 +1,68 @@
{var $title = 'Playlist'}

{block content}

<p>
<a href="{link Event:details $eventId}" class="btn">
<i class="icon-search"></i>&nbsp;Detaily události
</a>
<a href="{link default eventId=>$eventId}" class="btn">
<i class="icon-list"></i>&nbsp;Zobrazit playlist
</a>
<a href="{link add eventId=>$eventId}" class="btn btn-success">
<i class="icon-plus icon-white"></i>&nbsp;Přidat písničku
</a>
</p>


{* TODO: AJAXify
<p>
<a href="#playlistItemAddModal" data-toggle="modal" class="btn btn-success">
<i class="icon-plus icon-white"></i>&nbsp;Přidat písničku
</a>
</p>
<div class="modal hide" id="playlistItemAddModal">
<div class="modal-header">
<a class="close" data-dismiss="modal">×</a>
<h3>Přidat písničky do playlistu</h3>
</div>
<div class="modal-body">
{control songSearchPanel}
</div>
<div class="modal-footer">
<a href="#" class="btn" data-dismiss="modal">Zpět</a>
</div>
</div>
*}

<style type="text/css">
table.playlist th.action, table.playlist td.action {
width: 9em;
text-align: center;
}
</style>

{foreach $playlist as $song}
{first}
{var $i = 0}
<table class="table table-condensed playlist">
{/first}
{? $i++}
<tr>
<td style="width: 2em">{$i}.</td>
<td class="song-title"><a href="{link Song:details $song->id}">{$song->title}</td>
<td class="action">
<a {if $song->order > 0}href="{link Playlist:updateItem eventId=>$eventId, songId=>$song->id, order=>$song->order-1}"{/if}
class="btn{if $song->order <= 0} disabled{/if}" title="Posunout nahoru"><i class="icon-chevron-up"></i></a>
<a href="{link Playlist:updateItem eventId=>$eventId, songId=>$song->id, order=>$song->order+1}"
class="btn" title="Posunout dolů"><i class="icon-chevron-down"></i></a>
<a href="{link Playlist:delete eventId=>$eventId, songId=>$song->id}"
class="btn" title="Odstranit z playlistu"><i class="icon-remove"></i></a>
</td>
</tr>
{last}
</table>
{/last}
{/foreach}

{/block}
7 changes: 7 additions & 0 deletions nette/app/templates/Playlist/songSearchForm.latte
@@ -0,0 +1,7 @@
{form songSearchForm action=>"playlist/add", method=>"GET", class=>"form-search"}
{input query class=>"input-medium search-query"}
{input search class=>"btn"}
{if isset($query) && $query}
<a n:href="add id=>''" class="btn">Zrušit hledání</a>
{/if}
{/form}
33 changes: 33 additions & 0 deletions nette/app/templates/Playlist/songSearchList.latte
@@ -0,0 +1,33 @@
{if isset($songs) and count($songs) > 0}
<style type="text/css">
table th.action, table td.action {
width: 7em;
text-align: center;
}
</style>

<table class="table table-striped table-bordered table-condensed">
<thead>
<tr>
<th class="title"><i class="icon-music"></i>&nbsp;Název</th>
<th class="author"><i class="icon-music"></i>&nbsp;Autor</th>
<th class="action"><i class="icon-cog"></i></th>
</tr>
</thead>
<tbody>
{foreach $songs as $song}
<tr n:class="$iterator->isOdd() ? odd : even">
<td class="title"><a href="{link Song:details $song->id}"><strong>{$song->title}</strong></a></td>
<td class="author">{$song->author}</td>
<td class="action">
<a href="{link updateItem eventId=>$eventId, songId=>$song->id}" class="btn btn-success" title="Přidat do playlistu">
<i class="icon-plus icon-white"></i>
</a>
</td>
</tr>
{/foreach}
</tbody>
</table>
{else}
<p>Žádné písničky.</p>
{/if}

0 comments on commit 8bada68

Please sign in to comment.