diff --git a/appinfo/database.xml b/appinfo/database.xml
index 5389d05..99b4165 100644
--- a/appinfo/database.xml
+++ b/appinfo/database.xml
@@ -108,4 +108,37 @@
-
\ No newline at end of file
+
+ *dbprefix*quicknotes_shares
+
+
+ id
+ integer
+ true
+ true
+ true
+ true
+ 8
+
+
+ note_id
+ integer
+ true
+ true
+ 8
+
+
+ shared_user
+ text
+ 200
+
+
+
+ shared_group
+ text
+ 200
+
+
+
+
+
diff --git a/appinfo/info.xml b/appinfo/info.xml
index 4fe1fab..f0b9b76 100644
--- a/appinfo/info.xml
+++ b/appinfo/info.xml
@@ -5,7 +5,7 @@
Quick notes with text, check lists and pictures
AGPL
Matias De lellis
- 0.1.0
+ 0.1.1
QuickNotes
tool
https://github.com/matiasdelellis
@@ -15,4 +15,4 @@
-
\ No newline at end of file
+
diff --git a/appinfo/routes.php b/appinfo/routes.php
index 6062580..1ab9af6 100644
--- a/appinfo/routes.php
+++ b/appinfo/routes.php
@@ -7,6 +7,11 @@ return [
'routes' => [
['name' => 'page#index', 'url' => '/', 'verb' => 'GET'],
['name' => 'note_api#preflighted_cors', 'url' => '/api/0.1/{path}',
- 'verb' => 'OPTIONS', 'requirements' => ['path' => '.+']]
+ 'verb' => 'OPTIONS', 'requirements' => ['path' => '.+']],
+ ['name' => 'note#get_user_groups_and_users_with_share', 'url' => '/api/0.1/getusergroups', 'verb' => 'POST'],
+ ['name' => 'note#add_group_share', 'url' => '/api/0.1/groups/addshare', 'verb' => 'POST'],
+ ['name' => 'note#remove_group_share', 'url' => '/api/0.1/groups/removeshare', 'verb' => 'POST'],
+ ['name' => 'note#add_user_share', 'url' => '/api/0.1/users/addshare', 'verb' => 'POST'],
+ ['name' => 'note#remove_user_share', 'url' => '/api/0.1/users/removeshare', 'verb' => 'POST']
]
-];
\ No newline at end of file
+];
diff --git a/controller/notecontroller.php b/controller/notecontroller.php
index 5c5b847..6658a76 100644
--- a/controller/notecontroller.php
+++ b/controller/notecontroller.php
@@ -14,23 +14,28 @@ namespace OCA\QuickNotes\Controller;
use OCP\IRequest;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\DataResponse;
+use OCP\AppFramework\Http\JSONResponse;
use OCP\AppFramework\Controller;
use OCA\QuickNotes\Db\Note;
use OCA\QuickNotes\Db\Color;
+use OCA\QuickNotes\Db\NoteShare;
use OCA\QuickNotes\Db\NoteMapper;
use OCA\QuickNotes\Db\ColorMapper;
+use OCA\QuickNotes\Db\NoteShareMapper;
class NoteController extends Controller {
private $notemapper;
private $colormapper;
+ private $notesharemapper;
private $userId;
- public function __construct($AppName, IRequest $request, NoteMapper $notemapper, ColorMapper $colormapper, $UserId) {
+ public function __construct($AppName, IRequest $request, NoteMapper $notemapper, NoteShareMapper $notesharemapper, ColorMapper $colormapper, $UserId) {
parent::__construct($AppName, $request);
$this->notemapper = $notemapper;
$this->colormapper = $colormapper;
+ $this->notesharemapper = $notesharemapper;
$this->userId = $UserId;
}
@@ -39,6 +44,33 @@ class NoteController extends Controller {
*/
public function index() {
$notes = $this->notemapper->findAll($this->userId);
+ foreach($notes as $note) {
+ $note->setIsShared(false);
+ $sharedWith = $this->notesharemapper->getSharesForNote($note->getId());
+ if(count($sharedWith) > 0) {
+ $shareList = array();
+ foreach($sharedWith as $share) {
+ $shareList[] = $share->getSharedUser();
+ }
+ $note->setSharedWith(implode(", ", $shareList));
+ } else {
+ $note->setSharedWith(null);
+ }
+ }
+ $shareEntries = $this->notesharemapper->findForUser($this->userId);
+ $shares = array();
+ foreach($shareEntries as $entry) {
+ try {
+ //find is only to check if current user is owner
+ $this->notemapper->find($entry->getNoteId(), $this->userId);
+ //user is owner, nothing to do
+ } catch(\OCP\AppFramework\Db\DoesNotExistException $e) {
+ $share = $this->notemapper->findById($entry->getNoteId());
+ $share->setIsShared(true);
+ $shares[] = $share;
+ }
+ }
+ $notes = array_merge($notes, $shares);
// Insert true color to response
foreach ($notes as $note) {
$note->setColor($this->colormapper->find($note->getColorId())->getColor());
@@ -151,6 +183,8 @@ class NoteController extends Controller {
}
$oldcolorid = $note->getColorId();
+ $this->notesharemapper->deleteByNoteId($note->getId());
+
// Delete note.
$this->notemapper->delete($note);
@@ -162,4 +196,94 @@ class NoteController extends Controller {
return new DataResponse($note);
}
-}
\ No newline at end of file
+
+ /**
+ * @NoAdminRequired
+ */
+ public function getUserGroupsAndUsersWithShare($noteId) {
+ $userMgr = \OC::$server->getUserManager();
+ $grpMgr = \OC::$server->getGroupManager();
+ $users = array();
+ $groups = array();
+ if($grpMgr->isAdmin($this->userId)) {
+ $igroups = $grpMgr->search("");
+ $iusers = $userMgr->search("");
+ foreach($igroups as $g) {
+ $groups[] = $g->getGID();
+ }
+ foreach($iusers as $u) {
+ $users[] = $u->getUID();
+ }
+ } else {
+ $igroups = $grpMgr->getUserGroups($userMgr->get($this->userId));
+ foreach($igroups as $g) {
+ $iusers = $g->getUsers();
+ foreach($iusers as $u) {
+ $users[] = $u->getUID();
+ }
+ $groups[] = $g->getGID();
+ }
+ }
+
+ $users = array_unique($users);
+ if(($i = array_search($this->userId, $users)) !== false) {
+ unset($users[$i]);
+ }
+ $pos_users = array();
+ $pos_groups = array();
+ $shares = $this->notesharemapper->getSharesForNote($noteId);
+ foreach($shares as $s) {
+ $shareType = $s->getSharedUser();
+ if(strlen($shareType) != 0) {
+ if(($i = array_search($shareType, $users)) !== false) {
+ unset($users[$i]);
+ $pos_users[] = $shareType;
+ }
+ } else {
+ $shareType = $s->getSharedGroup();
+ if(($i = array_search($shareType, $groups)) !== false) {
+ unset($groups[$i]);
+ $pos_groups[] = $shareType;
+ }
+ }
+ }
+ $params = array('groups' => $groups, 'users' => $users, 'posGroups' => $pos_groups, 'posUsers' => $pos_users);
+ return new JSONResponse($params);
+ }
+
+ /**
+ * @NoAdminRequired
+ */
+ public function addGroupShare($groupId, $noteId) {
+ $share = new NoteShare();
+ $share->setSharedGroup($groupId);
+ $share->setNoteId($noteId);
+ $this->notesharemapper->insert($share);
+ }
+
+ /**
+ * @NoAdminRequired
+ */
+ public function removeGroupShare($groupId, $noteId) {
+ $share = $this->notesharemapper->findByNoteAndGroup($noteId, $groupId);
+ $this->notesharemapper->delete($share);
+ }
+
+ /**
+ * @NoAdminRequired
+ */
+ public function addUserShare($userId, $noteId) {
+ $share = new NoteShare();
+ $share->setSharedUser($userId);
+ $share->setNoteId($noteId);
+ $this->notesharemapper->insert($share);
+ }
+
+ /**
+ * @NoAdminRequired
+ */
+ public function removeUserShare($userId, $noteId) {
+ $share = $this->notesharemapper->findByNoteAndUser($noteId, $userId);
+ $this->notesharemapper->delete($share);
+ }
+}
diff --git a/css/style.css b/css/style.css
index 75e7523..26a219c 100644
--- a/css/style.css
+++ b/css/style.css
@@ -31,6 +31,10 @@
top: 6px;
}
+.save-button #unshare-button {
+ display: none;
+}
+
#div-content .save-button {
float: right;
padding-right: 8px;
@@ -47,8 +51,6 @@
/* Grid Note */
#div-content .note-title {
- height: 28px;
- width: calc(100% - 20px);
font-size: 18px;
font-weight: bold;
text-overflow: ellipsis;
@@ -56,6 +58,16 @@
overflow: hidden;
}
+#div-content .shared-title, #div-content .shared-title-owner {
+ float: right;
+ margin: 2px;
+ opacity: 0.5;
+}
+
+#div-content .shared-title-owner {
+ margin-right: 25px;
+}
+
.noselect {
-webkit-touch-callout: none; /* iOS Safari */
-webkit-user-select: none; /* Chrome/Safari/Opera */
@@ -141,6 +153,19 @@ div[data-placeholder]:not([data-placeholder=""]):empty::before {
content: attr(data-placeholder);
}
+#note-share-options {
+ display: none;
+ padding-bottom: 5px;
+}
+
+.selected-share:hover, .unselected-share:hover, .selected-share span:hover, .unselected-share span:hover {
+ cursor: pointer;
+}
+
+.selected-share, .unselected-share {
+ padding-left: 5px;
+}
+
/* Modal Content */
.modal-content {
diff --git a/db/note.php b/db/note.php
index 301e229..a96095a 100644
--- a/db/note.php
+++ b/db/note.php
@@ -12,6 +12,8 @@ class Note extends Entity implements JsonSerializable {
protected $timestamp;
protected $colorId;
protected $userId;
+ protected $sharedWith;
+ protected $isShared;
protected $color;
@@ -26,7 +28,10 @@ class Note extends Entity implements JsonSerializable {
'content' => $this->content,
'timestamp' => $this->timestamp,
'colorid' => $this->colorId,
- 'color' => $this->color
+ 'color' => $this->color,
+ 'userid' => $this->userId,
+ 'sharedwith' => $this->sharedWith,
+ 'isshared' => $this->isShared
];
}
-}
\ No newline at end of file
+}
diff --git a/db/notemapper.php b/db/notemapper.php
index 1cb3261..50ce0c0 100644
--- a/db/notemapper.php
+++ b/db/notemapper.php
@@ -11,11 +11,23 @@ class NoteMapper extends Mapper {
parent::__construct($db, 'quicknotes_notes', '\OCA\QuickNotes\Db\Note');
}
+ /**
+ * @param int $id
+ * @param string $userId
+ * @throws \OCP\AppFramework\Db\DoesNotExistException if not found
+ * @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException if more than one result
+ * @return Note
+ */
public function find($id, $userId) {
$sql = 'SELECT * FROM *PREFIX*quicknotes_notes WHERE id = ? AND user_id = ?';
return $this->findEntity($sql, [$id, $userId]);
}
+ public function findById($id) {
+ $sql = 'SELECT * FROM *PREFIX*quicknotes_notes WHERE id = ?';
+ return $this->findEntity($sql, [$id]);
+ }
+
public function findAll($userId) {
$sql = 'SELECT * FROM *PREFIX*quicknotes_notes WHERE user_id = ?';
return $this->findEntities($sql, [$userId]);
@@ -29,4 +41,4 @@ class NoteMapper extends Mapper {
return $row['count'];
}
-}
\ No newline at end of file
+}
diff --git a/db/noteshare.php b/db/noteshare.php
new file mode 100644
index 0000000..bf32689
--- /dev/null
+++ b/db/noteshare.php
@@ -0,0 +1,22 @@
+ $this->id,
+ 'noteid' => $this->noteId,
+ 'shareduser' => $this->sharedUser,
+ 'sharedgroup' => $this->sharedGroup
+ ];
+ }
+}
diff --git a/db/notesharemapper.php b/db/notesharemapper.php
new file mode 100644
index 0000000..aefbd45
--- /dev/null
+++ b/db/notesharemapper.php
@@ -0,0 +1,48 @@
+findEntity($sql, [$id, $userId]);
+ }*/
+
+ public function findForUser($userId) {
+ $sql = 'SELECT * FROM *PREFIX*quicknotes_shares WHERE shared_user = ?';
+ return $this->findEntities($sql, [$userId]);
+ }
+
+ public function findForGroup($groupId) {
+ $sql = 'SELECT * FROM *PREFIX*quicknotes_shares WHERE shared_group = ?';
+ return $this->findEntities($sql, [$groupId]);
+ }
+
+ public function findByNoteAndUser($noteId, $userId) {
+ $sql = 'SELECT * FROM *PREFIX*quicknotes_shares WHERE shared_user = ? AND note_id = ?';
+ return $this->findEntity($sql, [$userId, $noteId]);
+ }
+
+ public function findByNoteAndGroup($noteId, $groupId) {
+ $sql = 'SELECT * FROM *PREFIX*quicknotes_shares WHERE shared_group = ? AND note_id = ?';
+ return $this->findEntity($sql, [$groupId, $noteId]);
+ }
+
+ public function getSharesForNote($noteId) {
+ $sql = 'SELECT * FROM *PREFIX*quicknotes_shares WHERE note_id = ?';
+ return $this->findEntities($sql, [$noteId]);
+ }
+
+ public function deleteByNoteId($noteId) {
+ $sql = 'DELETE FROM *PREFIX*quicknotes_shares WHERE note_id = ?';
+ $this->execute($sql, [$noteId]);
+ }
+}
diff --git a/js/script.js b/js/script.js
index c23f6ed..06aacfa 100644
--- a/js/script.js
+++ b/js/script.js
@@ -24,6 +24,65 @@ var Notes = function (baseUrl) {
this._activeNote = undefined;
};
+var moveToUnselectedShare = function() {
+ var curr = $(this).clone();
+ var groupIndex = curr.html().indexOf('(group)');
+ var id = $('.note-active').data('id');
+ if(groupIndex >= 0) {
+ var groupId = curr.html().substring(0, groupIndex);
+ var formData = {
+ groupId : groupId,
+ noteId : id
+ };
+ $.post(OC.generateUrl('/apps/quicknotes/api/0.1/groups/removeshare'), formData, function(data){
+ });
+ } else {
+ var userId = curr.html();
+ var formData = {
+ userId : userId,
+ noteId : id
+ };
+ $.post(OC.generateUrl('/apps/quicknotes/api/0.1/users/removeshare'), formData, function(data){
+ });
+ }
+ curr.switchClass('selected-share', 'unselected-share', 0);
+ curr.hide();
+ curr.click(moveToSelectedShare);
+ $(curr).appendTo($('#share-neg'));
+ $(this).remove();
+ var pos = $('#share-pos');
+ if(pos.children().length == 0) pos.hide();
+}
+
+var moveToSelectedShare = function() {
+ var curr = $(this).clone();
+ var groupIndex = curr.html().indexOf('(group)');
+ var id = $('.note-active').data('id');
+ if(groupIndex >= 0) {
+ var groupId = curr.html().substring(0, groupIndex);
+ var formData = {
+ groupId : groupId,
+ noteId : id
+ };
+ $.post(OC.generateUrl('/apps/quicknotes/api/0.1/groups/addshare'), formData, function(data){
+ });
+ } else {
+ var userId = curr.html();
+ var formData = {
+ userId : userId,
+ noteId : id
+ };
+ $.post(OC.generateUrl('/apps/quicknotes/api/0.1/users/addshare'), formData, function(data){
+ });
+ }
+ curr.switchClass('unselected-share', 'selected-share', 0);
+ curr.click(moveToUnselectedShare);
+ $(curr).appendTo($('#share-pos'));
+ $(this).remove();
+ $('#share-pos').show();
+ $('#share-search').val('');
+}
+
Notes.prototype = {
load: function (id) {
var self = this;
@@ -269,6 +328,7 @@ View.prototype = {
$("#app-content").on("click", ".quicknote", function (event) {
event.stopPropagation(); // Not work so need fix on next binding..
+ if($(this).hasClass('shared')) return; //shares don't allow editing
var modalnote = $("#modal-note-editable .quicknote");
var modalid = modalnote.data('id');
if (modalid > 0) return;
@@ -335,6 +395,80 @@ View.prototype = {
modalnote.css("background-color", color);
});
+ // handle share editing notes.
+ $('#modal-note-div #share-button').click(function (event) {
+ var id = $('.note-active').data('id');
+ var formData = {
+ noteId: id
+ }
+ $.post(OC.generateUrl('/apps/quicknotes/api/0.1/getusergroups'), formData, function(data) {
+ var shareOptions = $('#note-share-options');
+ var groups = data.groups;
+ var users = data.users;
+ var pos_groups = data.posGroups;
+ var pos_users = data.posUsers;
+ var neg = $('#share-neg');
+ var pos = $('#share-pos');
+ var sear = $('#share-search');
+ for(var i=0; i= 0) {
+ $(lis[i]).show();
+ } else {
+ $(lis[i]).hide();
+ }
+ }
+ }
+ modalNote.outerHeight(startHeight + shareOptions.outerHeight(true));
+ });
+ });
+ });
+
// handle cancel editing notes.
$('#modal-note-div #cancel-button').click(function (event) {
self.cancelEdit();
@@ -384,6 +518,18 @@ View.prototype = {
$('#app-navigation .any-color').addClass('icon-checkmark');
});
+ $('#shared-with-you').click(function () {
+ $('.notes-grid').isotope({ filter: function() {
+ return $(this).children().hasClass('shared');
+ } });
+ });
+
+ $('#shared-by-you').click(function () {
+ $('.notes-grid').isotope({ filter: function() {
+ return $(this).children().hasClass('shareowner');
+ } });
+ });
+
// create a new note
var self = this;
$('#new-note').click(function () {
diff --git a/templates/part.navigation.php b/templates/part.navigation.php
index 247255a..34e5a3c 100644
--- a/templates/part.navigation.php
+++ b/templates/part.navigation.php
@@ -4,6 +4,8 @@