Skip to content

Commit ccda93e

Browse files
committedOct 16, 2020
Moar things
1 parent e1ef07a commit ccda93e

File tree

9 files changed

+266
-63
lines changed

9 files changed

+266
-63
lines changed
 

‎README.md

+2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
![Notes for Craft CMS](resources/imgs/banner.png)
22
# Notes
33
A note taking field type for Craft CMS
4+
5+
![Field](resources/imgs/preview.png)

‎resources/imgs/preview.png

123 KB
Loading

‎src/Field.php

+4-43
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,7 @@
1010

1111
use Craft;
1212
use craft\base\ElementInterface;
13-
use craft\db\Query;
14-
use craft\elements\User;
15-
use craft\helpers\ArrayHelper;
16-
use craft\helpers\DateTimeHelper;
1713
use ether\notes\web\NotesAsset;
18-
use yii\db\Expression;
1914

2015
/**
2116
* Class Field
@@ -55,44 +50,10 @@ public function normalizeValue ($value, ElementInterface $element = null)
5550
if (!$element)
5651
return [];
5752

58-
$where = [
59-
'elementId' => $element->id,
60-
];
61-
62-
if ($this->getIsTranslatable($element))
63-
$where['siteId'] = $element->siteId;
64-
65-
$rawNotes = (new Query())
66-
->select('id, note, userId, dateCreated')
67-
->from(self::$table)
68-
->where($where)
69-
->orderBy('dateCreated desc')
70-
->all();
71-
72-
$select = <<<SQL
73-
[[elements]].[[id]],
74-
CASE WHEN NULLIF([[users.firstName]], '') is null THEN [[users.username]] ELSE CONCAT([[users.firstName]], " ", [[users.lastName]]) END
75-
SQL;
76-
77-
$users = User::find()
78-
->select(new Expression($select))
79-
->id(ArrayHelper::getColumn($rawNotes, 'userId'))
80-
->pairs();
81-
82-
$notes = [];
83-
84-
foreach ($rawNotes as $note)
85-
{
86-
$notes[] = new Note([
87-
'id' => $note['id'],
88-
'note' => $note['note'],
89-
'author' => $users[$note['userId']],
90-
'userId' => $note['userId'],
91-
'date' => DateTimeHelper::toDateTime($note['dateCreated'])->format(self::$dateFormat),
92-
]);
93-
}
94-
95-
return $notes;
53+
return Notes::getInstance()->do->getNotesByElement(
54+
$element,
55+
$this->getIsTranslatable($element)
56+
);
9657
}
9758

9859
public function getSettingsHtml ()

‎src/Note.php

+92-2
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,11 @@
88

99
namespace ether\notes;
1010

11+
use craft\base\Element;
1112
use craft\base\Model;
1213
use craft\elements\User;
14+
use craft\helpers\ArrayHelper;
15+
use craft\records\Site;
1316

1417
/**
1518
* Class Note
@@ -20,15 +23,102 @@
2023
class Note extends Model
2124
{
2225

26+
private static $_eagerIds = [];
27+
private static $_eagerElements = [
28+
'elements' => [],
29+
'users' => [],
30+
'sites' => [],
31+
];
32+
2333
public $id;
34+
2435
public $note;
2536
public $author;
26-
public $userId;
2737
public $date;
2838

39+
public $elementId;
40+
public $siteId;
41+
public $userId;
42+
43+
public $dateCreated;
44+
public $dateUpdated;
45+
46+
public function __construct ($config = [])
47+
{
48+
parent::__construct($config);
49+
50+
self::setEagerId('elements', $config['elementId']);
51+
self::setEagerId('users', $config['userId']);
52+
self::setEagerId('sites', $config['siteId']);
53+
}
54+
55+
// Getters
56+
// =========================================================================
57+
58+
/**
59+
* @return Element|null
60+
*/
61+
public function getElement ()
62+
{
63+
self::cacheEagerElements('elements', Element::class);
64+
65+
return @self::$_eagerElements['elements'][$this->elementId];
66+
}
67+
68+
/**
69+
* @return Site|null
70+
*/
71+
public function getSite ()
72+
{
73+
self::cacheEagerElements('sites', Site::class);
74+
75+
return @self::$_eagerElements['sites'][$this->siteId];
76+
}
77+
78+
/**
79+
* @return User|null
80+
*/
2981
public function getUser ()
3082
{
31-
return User::findOne(['id' => $this->userId]);
83+
self::cacheEagerElements('users', User::class);
84+
85+
return @self::$_eagerElements['users'][$this->userId];
86+
}
87+
88+
// Lite eager loading
89+
// =========================================================================
90+
91+
/**
92+
* Will eager load the required elements
93+
*
94+
* @param string $key
95+
* @param $cls
96+
*/
97+
private static function cacheEagerElements (string $key, $cls)
98+
{
99+
if (!empty(self::$_eagerElements[$key])) return;
100+
101+
self::$_eagerElements[$key] = ArrayHelper::index(
102+
$cls::findAll(['id' => self::$_eagerIds[$key]]),
103+
'id'
104+
);
105+
}
106+
107+
/**
108+
* Stores an array of IDs to eager load against the given key
109+
*
110+
* @param string $key
111+
* @param int $id
112+
*/
113+
private static function setEagerId (string $key, int $id)
114+
{
115+
if (empty(self::$_eagerIds[$key]))
116+
self::$_eagerIds[$key] = [$id];
117+
else
118+
{
119+
self::$_eagerIds[$key][] = $id;
120+
self::$_eagerIds[$key] = array_unique(self::$_eagerIds[$key]);
121+
}
32122
}
33123

34124
}

‎src/Notes.php

+5
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
*
1919
* @author Ether Creative
2020
* @package ether\notes
21+
* @property Service $do
2122
*/
2223
class Notes extends Plugin
2324
{
@@ -26,6 +27,10 @@ public function init ()
2627
{
2728
parent::init();
2829

30+
$this->setComponents([
31+
'do' => Service::class,
32+
]);
33+
2934
Event::on(
3035
Fields::class,
3136
Fields::EVENT_REGISTER_FIELD_TYPES,

‎src/Service.php

+107
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
<?php
2+
/**
3+
* Notes for Craft CMS
4+
*
5+
* @link https://ethercreative.co.uk
6+
* @copyright Copyright (c) 2020 Ether Creative
7+
*/
8+
9+
namespace ether\notes;
10+
11+
use Craft;
12+
use craft\base\Component;
13+
use craft\base\ElementInterface;
14+
use craft\db\Query;
15+
use craft\elements\User;
16+
use craft\helpers\ArrayHelper;
17+
use craft\helpers\DateTimeHelper;
18+
use DateTime;
19+
use yii\db\Expression;
20+
21+
/**
22+
* Class Service
23+
*
24+
* @author Ether Creative
25+
* @package ether\notes
26+
*/
27+
class Service extends Component
28+
{
29+
30+
public function add ($elementId, $siteId, $userId, $note)
31+
{
32+
$data = compact('siteId', 'elementId', 'userId', 'note');
33+
34+
Craft::$app->getDb()->createCommand()
35+
->insert(Field::$table, $data)
36+
->execute();
37+
38+
$id = (new Query())
39+
->select('id')
40+
->from(Field::$table)
41+
->where($data)
42+
->scalar();
43+
44+
$user = User::findOne(['id' => $userId]);
45+
$meta = $user->fullName ?: $user->username . ' &bull; ' . (new DateTime())->format(Field::$dateFormat);
46+
47+
return compact('meta', 'id');
48+
}
49+
50+
public function delete ($id)
51+
{
52+
Craft::$app->getDb()->createCommand()
53+
->delete(Field::$table, compact('id'))
54+
->execute();
55+
}
56+
57+
public function getNotesByElement (ElementInterface $element, $translatable = false)
58+
{
59+
$where = [
60+
'elementId' => $element->id,
61+
];
62+
63+
if ($translatable)
64+
$where['siteId'] = $element->siteId;
65+
66+
$rawNotes = (new Query())
67+
->select('*')
68+
->from(Field::$table)
69+
->where($where)
70+
->orderBy('dateCreated desc')
71+
->all();
72+
73+
$userIds = ArrayHelper::getColumn($rawNotes, 'userId');
74+
75+
$select = <<<SQL
76+
[[elements]].[[id]],
77+
CASE WHEN NULLIF([[users.firstName]], '') is null THEN [[users.username]] ELSE CONCAT([[users.firstName]], " ", [[users.lastName]]) END
78+
SQL;
79+
80+
$users = User::find()
81+
->select(new Expression($select))
82+
->id($userIds)
83+
->pairs();
84+
85+
$notes = [];
86+
87+
foreach ($rawNotes as $note)
88+
{
89+
$notes[] = new Note([
90+
'id' => $note['id'],
91+
'note' => $note['note'],
92+
'author' => $users[$note['userId']],
93+
'date' => DateTimeHelper::toDateTime($note['dateCreated'])->format(Field::$dateFormat),
94+
95+
'elementId' => $note['elementId'],
96+
'siteId' => $note['siteId'],
97+
'userId' => $note['userId'],
98+
99+
'dateCreated' => $note['dateCreated'],
100+
'dateUpdated' => $note['dateUpdated'],
101+
]);
102+
}
103+
104+
return $notes;
105+
}
106+
107+
}

‎src/controllers/FieldController.php

+15-12
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,8 @@
99
namespace ether\notes\controllers;
1010

1111
use Craft;
12-
use craft\elements\User;
1312
use craft\web\Controller;
14-
use DateTime;
15-
use ether\notes\Field;
13+
use ether\notes\Notes;
1614

1715
/**
1816
* Class FieldController
@@ -32,17 +30,22 @@ public function actionAdd ()
3230
$userId = $request->getRequiredBodyParam('userId');
3331
$note = $request->getRequiredBodyParam('note');
3432

35-
Craft::$app->getDb()->createCommand()
36-
->insert(
37-
Field::$table,
38-
compact('siteId', 'elementId', 'userId', 'note')
39-
)
40-
->execute();
33+
$res = Notes::getInstance()->do->add(
34+
$elementId,
35+
$siteId,
36+
$userId,
37+
$note
38+
);
4139

42-
$user = User::findOne(['id' => $userId]);
43-
$meta = $user->fullName ?: $user->username . ' &bull; ' . (new DateTime())->format(Field::$dateFormat);
40+
return $this->asJson($res);
41+
}
42+
43+
public function actionDelete ()
44+
{
45+
$id = Craft::$app->getRequest()->getRequiredBodyParam('id');
46+
Notes::getInstance()->do->delete($id);
4447

45-
return $this->asJson(compact('meta'));
48+
return $this->asJson(1);
4649
}
4750

4851
}

‎src/web/NotesAsset.php

+3
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
namespace ether\notes\web;
1010

11+
use Craft;
1112
use craft\web\AssetBundle;
1213

1314
/**
@@ -25,6 +26,8 @@ public function init ()
2526
$this->css = ['notes.css'];
2627
$this->js = ['notes.js'];
2728

29+
Craft::$app->getView()->registerTranslations('app', ['Delete']);
30+
2831
parent::init();
2932
}
3033

‎src/web/notes.js

+38-6
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ function NotesField ([ns, siteId, elementId, userId, allowDeleting]) {
44
, spin = document.getElementById('fields-' + ns + '-spin')
55
, notes = document.getElementById('fields-' + ns + '-notes');
66

7-
add.addEventListener('click', async () => {
7+
const save = async () => {
88
const note = input.value.trim();
99
input.value = '';
1010
if (note === '') return;
@@ -13,14 +13,46 @@ function NotesField ([ns, siteId, elementId, userId, allowDeleting]) {
1313
data: { siteId, elementId, userId, note },
1414
});
1515
const li = document.createElement('li')
16-
, p = document.createElement('p')
17-
, s = document.createElement('small');
18-
// TODO: add delete button
16+
, d = document.createElement('div')
17+
, p = document.createElement('p')
18+
, s = document.createElement('small')
19+
, a = document.createElement('a');
1920
p.textContent = note;
2021
s.innerHTML = data.meta;
21-
li.appendChild(p);
22-
li.appendChild(s);
22+
d.appendChild(p);
23+
d.appendChild(s);
24+
li.appendChild(d);
25+
if (allowDeleting) {
26+
a.className = 'delete icon';
27+
a.setAttribute('role', 'button');
28+
a.setAttribute('title', Craft.t('app', 'Delete'));
29+
a.setAttribute('data-delete-note', data.id);
30+
a.addEventListener('click', NotesField.delete);
31+
li.appendChild(a);
32+
}
2333
notes.insertBefore(li, notes.firstElementChild);
2434
spin.classList.add('hidden');
35+
};
36+
37+
input.addEventListener('keydown', e => {
38+
if (!(e.key === 'Enter' && e.metaKey)) return;
39+
save();
40+
});
41+
42+
Array.from(document.querySelectorAll('[data-delete-note]')).forEach(a => {
43+
a.addEventListener('click', NotesField.delete);
2544
});
45+
46+
add.addEventListener('click', save);
2647
}
48+
49+
NotesField.delete = async e => {
50+
e.preventDefault();
51+
if (!confirm('Are you sure?')) return;
52+
const id = e.target.dataset.deleteNote|0;
53+
await Craft.sendActionRequest('post', 'notes/field/delete', {
54+
data: { id },
55+
});
56+
const li = e.target.parentNode;
57+
li.parentNode.removeChild(li);
58+
};

0 commit comments

Comments
 (0)
Please sign in to comment.