Compare commits
5 Commits
8cd60b8052
...
master
Author | SHA1 | Date | |
---|---|---|---|
6caa17a909 | |||
3e2bb5c4a3 | |||
083572dfe3 | |||
0875406bf8 | |||
4b53121953 |
82
README.md
82
README.md
@ -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
80
README2.md
Normal 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
|
@ -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>
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -33,6 +33,9 @@ class WikiContent {
|
||||
},
|
||||
element: this.textarea,
|
||||
hideIcons:[],
|
||||
insertTexts:{
|
||||
image:['']
|
||||
},
|
||||
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);
|
||||
});
|
||||
}
|
||||
}
|
@ -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>
|
||||
|
7
js/easy-markdown-editor-master/dist/easymde.min.css
vendored
Normal file
7
js/easy-markdown-editor-master/dist/easymde.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
7
js/easy-markdown-editor-master/dist/easymde.min.js
vendored
Normal file
7
js/easy-markdown-editor-master/dist/easymde.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@ -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']))
|
||||
);
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
Reference in New Issue
Block a user