Compare commits

...

5 Commits

Author SHA1 Message Date
jdg
6caa17a909 Moved to gitthub 2022-06-26 10:35:06 +00:00
jdg
3e2bb5c4a3 Highlight the page until it's saved 2022-06-26 10:04:15 +00:00
jdg
083572dfe3 Added events to know when a content is changed/saved 2022-06-25 10:28:39 +00:00
jdg
0875406bf8 x 2022-06-25 09:23:59 +00:00
jdg
4b53121953 Added missing files 2022-05-21 08:23:32 +00:00
12 changed files with 161 additions and 111 deletions

View File

@ -8,85 +8,3 @@ This app allow me to display the folders where I organize some documentation wit
Many things could be wrong (this is my first app for nextcloud), I would appreciate any comments/help
## Building the app
The app can be built by using the provided Makefile by running:
make
This requires the following things to be present:
* make
* which
* tar: for building the archive
* curl: used if phpunit and composer are not installed to fetch them from the web
* npm: for building and testing everything JS, only required if a package.json is placed inside the **js/** folder
The make command will install or update Composer dependencies if a composer.json is present and also **npm run build** if a package.json is present in the **js/** folder. The npm **build** script should use local paths for build systems and package managers, so people that simply want to build the app won't need to install npm libraries globally, e.g.:
**package.json**:
```json
"scripts": {
"test": "node node_modules/gulp-cli/bin/gulp.js karma",
"prebuild": "npm install && node_modules/bower/bin/bower install && node_modules/bower/bin/bower update",
"build": "node node_modules/gulp-cli/bin/gulp.js"
}
```
## Publish to App Store
First get an account for the [App Store](http://apps.nextcloud.com/) then run:
make && make appstore
The archive is located in build/artifacts/appstore and can then be uploaded to the App Store.
## Running tests
You can use the provided Makefile to run all tests by using:
make test
This will run the PHP unit and integration tests and if a package.json is present in the **js/** folder will execute **npm run test**
Of course you can also install [PHPUnit](http://phpunit.de/getting-started.html) and use the configurations directly:
phpunit -c phpunit.xml
or:
phpunit -c phpunit.integration.xml
for integration tests
// Markdown editor: https://simplemde.com/
// https://github.com/Ionaru/easy-markdown-editor
//---
https://docs.nextcloud.com/server/latest/developer_manual/app_development/tutorial.html
cd nextcloud
php -S localhost:8080
podman run --name=nextcloud --replace=true -p 8080:80 -v /absolute/path/to/apps:/var/www/html/custom_apps docker.io/nextcloud
sudo docker run --name=nextcloud -p 8080:80 -v /absolute/path/to/apps:/var/www/html/custom_apps nextcloud
Version000000Date20220302210900
//---
php ./occ migrations:execute <appId> <versionNumber>
Example: sudo -u www-data php ./occ migrations:execute mywiki 000000Date20220302210900
https://c.infdj.com/apps/files/?dir=/Documents/Manuals%20-%20Drivers/drivers/MAD&fileid=19227
https://docs.nextcloud.com/server/latest/developer_manual/digging_deeper/api.html
phpunit -c phpunit.integration.xml
https://github.com/nextcloud/nextcloud-vue

80
README2.md Normal file
View File

@ -0,0 +1,80 @@
## Building the app
The app can be built by using the provided Makefile by running:
make
This requires the following things to be present:
* make
* which
* tar: for building the archive
* curl: used if phpunit and composer are not installed to fetch them from the web
* npm: for building and testing everything JS, only required if a package.json is placed inside the **js/** folder
The make command will install or update Composer dependencies if a composer.json is present and also **npm run build** if a package.json is present in the **js/** folder. The npm **build** script should use local paths for build systems and package managers, so people that simply want to build the app won't need to install npm libraries globally, e.g.:
**package.json**:
```json
"scripts": {
"test": "node node_modules/gulp-cli/bin/gulp.js karma",
"prebuild": "npm install && node_modules/bower/bin/bower install && node_modules/bower/bin/bower update",
"build": "node node_modules/gulp-cli/bin/gulp.js"
}
```
## Publish to App Store
First get an account for the [App Store](http://apps.nextcloud.com/) then run:
make && make appstore
The archive is located in build/artifacts/appstore and can then be uploaded to the App Store.
## Running tests
You can use the provided Makefile to run all tests by using:
make test
This will run the PHP unit and integration tests and if a package.json is present in the **js/** folder will execute **npm run test**
Of course you can also install [PHPUnit](http://phpunit.de/getting-started.html) and use the configurations directly:
phpunit -c phpunit.xml
or:
phpunit -c phpunit.integration.xml
for integration tests
// Markdown editor: https://simplemde.com/
// https://github.com/Ionaru/easy-markdown-editor
//---
https://docs.nextcloud.com/server/latest/developer_manual/app_development/tutorial.html
cd nextcloud
php -S localhost:8080
podman run --name=nextcloud --replace=true -p 8080:80 -v /absolute/path/to/apps:/var/www/html/custom_apps docker.io/nextcloud
sudo docker run --name=nextcloud -p 8080:80 -v /absolute/path/to/apps:/var/www/html/custom_apps nextcloud
Version000000Date20220302210900
//---
php ./occ migrations:execute <appId> <versionNumber>
Example: sudo -u www-data php ./occ migrations:execute mywiki 000000Date20220302210900
https://c.infdj.com/apps/files/?dir=/Documents/Manuals%20-%20Drivers/drivers/MAD&fileid=19227
https://docs.nextcloud.com/server/latest/developer_manual/digging_deeper/api.html
phpunit -c phpunit.integration.xml
https://github.com/nextcloud/nextcloud-vue

View File

@ -11,9 +11,9 @@
<namespace>MyWiki</namespace>
<category>office</category>
<category>organization</category>
<bugs>https://git.jd.guillen.io/wiki4nextcloud/issues</bugs>
<bugs>https://git.jd.guillen.io/mywiki/issues</bugs>
<dependencies>
<nextcloud min-version="12" max-version="24"/>
<nextcloud min-version="12" max-version="25"/>
</dependencies>
<navigations>
<navigation>

View File

@ -1,3 +1,7 @@
#app-navigation:not(.vue) > ul > li > a[data-id].modified {
color:red;
}
li[data-id="wikis"] select {
width: calc(100% - 50px);
}
@ -5,15 +9,15 @@ li[data-id="wikis"] select {
li[class^="wikiPage-lvl-"] a::before {
display: inline-block;
}
.wikiPage-lvl-1 a::before {
.wikiPage-lvl-1>a::before {
content: "•";
width: 1em;
}
.wikiPage-lvl-2 a::before {
.wikiPage-lvl-2>a::before {
content: "• •";
width: 1.5em;
}
.wikiPage-lvl-3 a::before {
.wikiPage-lvl-3>a::before {
content: "• • •";
width: 2em;
}

View File

@ -33,6 +33,9 @@ class WikiContent {
},
element: this.textarea,
hideIcons:[],
insertTexts:{
image:['![](', '/index.php/core/preview?fileId=XXXX&x=3840&y=2160&a=true)']
},
minHeight:height+"px",
maxHeight:height+"px",
sideBySideFullscreen: false,
@ -44,7 +47,13 @@ class WikiContent {
this.timeout = null;
this.mde.codemirror.on("change", (instance, changeObj) => {
if(self.loading) return;
console.log(changeObj);
var event = new CustomEvent("myWiki::change", {detail:{ wikiId:self.wikiId,pageId:self.pageId }});
document.dispatchEvent(event);
clearTimeout(self.timeout);
self.timeout = setTimeout(()=>self._mde_save(), this.waitSecondsToAutoSave);
});
@ -55,8 +64,10 @@ class WikiContent {
this.save(this.mde.value());
}
_mde_set(content) {
this.loading = true;
this.mde.clearAutosavedValue();
this.mde.value(content);
this.loading = false;
}
_mde_get() {
return this.mde.value();
@ -114,23 +125,27 @@ class WikiContent {
}
save(content) {
const self = this;
console.info(`JDG :: Saving wiki page ${self.wikiId}-${self.pageId}`);
if (self.wikiId<=0 || self.pageId<=0) {
const wikiId = this.wikiId;
const pageId = this.pageId;
console.info(`JDG :: Saving wiki page ${wikiId}-${pageId}`);
if (wikiId<=0 || pageId<=0) {
return;
}
var baseUrl = OC.generateUrl('/apps/mywiki/wiki/'+self.wikiId);
var baseUrl = OC.generateUrl('/apps/mywiki/wiki/'+wikiId);
$.ajax({
url: baseUrl+'/'+self.pageId,
url: baseUrl+'/'+pageId,
type: 'PUT',
contentType: 'application/json',
data: JSON.stringify({title:null, content:content})
}).done(function (response) {
console.info(`JDG :: WikiContent.save(${self.wikiId}, ${self.pageId})`, response);
console.info(`JDG :: WikiContent.save(${wikiId}, ${pageId})`, response);
var event = new CustomEvent("myWiki::saved", {detail:{ wikiId:wikiId,pageId:pageId }});
document.dispatchEvent(event);
}).fail(function (response, code) {
OC.dialogs.alert('Error', t(appName,'Error saving wiki page({wikiId}, {pageId})',{wikiId:self.wikiId,pageId:self.pageId}));
console.error(`JDG :: WikiContent.save(${self.wikiId}, ${self.pageId})`, response);
OC.dialogs.alert('Error', t(appName,'Error saving wiki page({wikiId}, {pageId})',{wikiId:wikiId,pageId:pageId}));
console.error(`JDG :: WikiContent.save(${wikiId}, ${pageId})`, response);
});
}
}

View File

@ -5,13 +5,26 @@ class WikiPages {
* The container is the <ul> for the navigation panel
*/
constructor(container, onClickLoadPage) {
const self = this;
this.ul = container;
this._onClickLoadPage = onClickLoadPage;
document.addEventListener("myWiki::change", function(e) {
console.log("myWiki::change",e.detail);
self.ul.querySelector(`li[data-page-id="${e.detail.pageId}"] a`).classList.add('modified');
});
document.addEventListener("myWiki::saved", function(e) {
console.log("myWiki::saved", e.detail);
self.ul.querySelector(`li[data-page-id="${e.detail.pageId}"] a`).classList.remove('modified');
});
}
clear() {
this.wikiId = null;
this.ul.querySelectorAll('[data-page-id]').forEach( x=>x.remove() );
document.querySelectorAll('#app-navigation .active').forEach(e=>e.class.remove('active'))
}
getWikiId() {
@ -53,6 +66,11 @@ class WikiPages {
});
}
highlightSelectedPage(pageId) {
this.ul.querySelectorAll('li[data-page-id]').forEach( x=>x.querySelector('a').classList.remove('active') );
this.ul.querySelector(`li[data-page-id="${pageId}"] a`).classList.add('active');
}
// -----------------------------------------------------------------------------------------
addListener(root) {
const self = this;
@ -67,9 +85,12 @@ class WikiPages {
onClickLoadPage(e) {
const li = e.target.closest("li[data-page-id]");
let pageId = li.dataset.pageId;
this.highlightSelectedPage(pageId);
this._onClickLoadPage(this.wikiId, pageId);
}
onClickEdit(e) {
const li = e.target.closest("li[data-page-id]");
li.querySelector("input").value = li.querySelector("a").innerText;
@ -167,6 +188,7 @@ class WikiPages {
} while(nextNode && nextNode.dataset.pid!=parent.dataset.pid);
}
let link2folder = OC.generateUrl('/apps/files?fileid='+pageId);
let li = document.createElement("li");
li.classList.add(`wikiPage-lvl-${lvl}`);
li.dataset.pageId = pageId;
@ -190,7 +212,7 @@ class WikiPages {
<div class="app-navigation-entry-menu">
<ul>
<li>
<button data-id="openFolder" class="icon-folder">Open Folder</button>
<a data-id="openFolder" class="icon-folder" href="${link2folder}">Open Folder</a>
</li>
<li>
<button data-id="add" class="icon-add">Add Page</button>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -45,12 +45,12 @@ class WikiMapper extends QBMapper {
if ($filter) {
if (array_key_exists('title',$filter) ) {
$qb->where(
$qb->andwhere(
$qb->expr()->eq('title', $qb->createNamedParameter($filter['title']))
);
}
if (array_key_exists('fileId',$filter) ) {
$qb->where(
$qb->andwhere(
$qb->expr()->eq('file_id', $qb->createNamedParameter($filter['fileId']))
);
}

View File

@ -81,8 +81,7 @@ class WikiHelper {
if ( $this->wikiFolder->nodeExists($path) ) {
$this->getFileByName(self::WIKI_FILE)->putContent($data);
} else {
$this->wikiFolder
->newFile(self::WIKI_FILE, $data);
$this->wikiFolder->newFile(self::WIKI_FILE, $data);
}
} catch(\Exception $ex) {
return false;
@ -154,26 +153,22 @@ class WikiHelper {
public function getWikiPageContent($id): string {
try {
$pageFolder = $this->getFolderById($id);
$path = $pageFolder->getInternalPath().'/'.self::WIKI_FILE_CONTENT;
if ( $this->wikiFolder->nodeExists($path) ) {
return $this->getFileByName(self::WIKI_FILE_CONTENT)->getContent();
if ( $pageFolder->nodeExists(self::WIKI_FILE_CONTENT) ) {
return $pageFolder->get(self::WIKI_FILE_CONTENT)->getContent();
}
$this->wikiFolder->newFile(self::WIKI_FILE_CONTENT, '');
} catch(\Exception $ex) {
return null;
}
return '';
}
}
public function update(int $id, string $content) {
try {
$pageFolder = $this->getFolderById($id);
$path = $pageFolder->getInternalPath().'/'.self::WIKI_FILE_CONTENT;
if ( $this->wikiFolder->nodeExists($path) ) {
$this->getFileByName(self::WIKI_FILE_CONTENT)->putContent($content);
if ( $pageFolder->nodeExists(self::WIKI_FILE_CONTENT) ) {
$pageFolder->get(self::WIKI_FILE_CONTENT)->putContent($content);
} else {
$this->wikiFolder
->newFile(self::WIKI_FILE_CONTENT, $content);
$pageFolder->newFile(self::WIKI_FILE_CONTENT, $content);
}
} catch(\Exception $ex) {
return false;

View File

@ -45,7 +45,9 @@ class WikiPageService {
public function find(int $wikiId, int $id, string $userId) {
try {
$wiki = $this->mapper->find($wikiId, $userId);
$wikiPageContent = $this->wikiHelper->setFolderId($wiki->getFileId())->getWikiPageContent($id);
$wikiPageContent = $this->wikiHelper
->setFolderId($wiki->getFileId())
->getWikiPageContent($id);
} catch(Exception $e) {
$this->handleException($e);
}